diff --git a/.gitattributes b/.gitattributes index 21fd53cb14..fee51d19c2 100644 --- a/.gitattributes +++ b/.gitattributes @@ -8,6 +8,9 @@ test/prism/fixtures/unparser/**/*.txt linguist-vendored test/prism/fixtures/whitequark/**/*.txt linguist-vendored test/prism/snapshots/**/*.txt linguist-generated +rbi/generated/**/*.rbi linguist-generated +sig/generated/**/*.rbs linguist-generated + # All .rb files should have LF line ending, even on Windows, regardless of the git config core.autocrlf value. # All .txt should have their line endings as committed in the repository (there are some intentional CR in there), # regardless of the git config core.autocrlf value. diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 6e302f18de..bc0a555340 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -9,7 +9,7 @@ updates: patterns: - "*" - package-ecosystem: 'maven' - directory: '/java-wasm' + directory: '/java/wasm' schedule: interval: 'weekly' groups: diff --git a/.github/workflows/cpp-bindings.yml b/.github/workflows/cpp-bindings.yml index 54593ff548..5b1bfba795 100644 --- a/.github/workflows/cpp-bindings.yml +++ b/.github/workflows/cpp-bindings.yml @@ -10,6 +10,7 @@ on: - "*akefile*" branches: - main + - ruby-4.0 pull_request: jobs: @@ -28,6 +29,6 @@ jobs: - name: Compile prism run: bundle exec rake compile - name: Compile C++ - run: g++ -o ./cpp_test cpp/test.cpp build/static/*.o build/static/util/*.o -Iinclude + run: g++ -o ./cpp_test cpp/test.cpp build/static/*.o -Iinclude - name: Run C++ run: ./cpp_test diff --git a/.github/workflows/cruby-bindings.yml b/.github/workflows/cruby-bindings.yml index d635e550f0..6fed2aa44a 100644 --- a/.github/workflows/cruby-bindings.yml +++ b/.github/workflows/cruby-bindings.yml @@ -9,6 +9,8 @@ on: branches: - main pull_request: + branches: + - main jobs: test-all: diff --git a/.github/workflows/documentation.yml b/.github/workflows/documentation.yml index c5258a2f5e..e3fc52303c 100644 --- a/.github/workflows/documentation.yml +++ b/.github/workflows/documentation.yml @@ -8,6 +8,7 @@ on: push: branches: - main + - ruby-4.0 pull_request: jobs: diff --git a/.github/workflows/java-wasm-bindings.yml b/.github/workflows/java-wasm-bindings.yml index 3a0a066334..352c1f7b4d 100644 --- a/.github/workflows/java-wasm-bindings.yml +++ b/.github/workflows/java-wasm-bindings.yml @@ -7,12 +7,15 @@ on: - "include/" - "src/" - "*akefile*" + - "java/" + - "java/wasm/" branches: - main + - ruby-4.0 pull_request: jobs: - build: + build-wasm: runs-on: ubuntu-latest steps: - uses: actions/checkout@v6 @@ -38,14 +41,14 @@ jobs: uses: actions/setup-java@v5 with: distribution: 'temurin' - java-version: '17' + java-version: '21' cache: maven - name: Run the tests run: mvn -B install - working-directory: java-wasm + working-directory: java/wasm - - uses: actions/upload-artifact@v6 + - uses: actions/upload-artifact@v7 with: name: prism.wasm - path: java-wasm/src/test/resources/prism.wasm + path: java/wasm/src/test/resources/prism.wasm diff --git a/.github/workflows/javascript-bindings.yml b/.github/workflows/javascript-bindings.yml index 6e7d51f282..2e4330b2be 100644 --- a/.github/workflows/javascript-bindings.yml +++ b/.github/workflows/javascript-bindings.yml @@ -9,6 +9,7 @@ on: - "*akefile*" branches: - main + - ruby-4.0 pull_request: jobs: @@ -34,7 +35,7 @@ jobs: - name: Build the project run: make wasm WASI_SDK_PATH=$(pwd)/wasi-sdk-25.0-x86_64-linux - - uses: actions/upload-artifact@v6 + - uses: actions/upload-artifact@v7 with: name: prism.wasm path: javascript/src/prism.wasm diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index d134bf68b7..6b3888f57d 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -8,6 +8,7 @@ on: push: branches: - main + - ruby-4.0 pull_request: jobs: @@ -79,7 +80,7 @@ jobs: shell: bash build-ibm: - if: github.repository == 'ruby/prism' && false + if: github.repository == 'ruby/prism' strategy: fail-fast: false matrix: @@ -187,6 +188,18 @@ jobs: - name: Run Java Loader test run: PRISM_SERIALIZE_ONLY_SEMANTICS_FIELDS=1 JRUBY_OPTS="-J-ea" bundle exec rake test:java_loader + build-java-truffleruby: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v6 + - name: Set up JRuby + uses: ruby/setup-ruby@v1 + with: + ruby-version: jruby + bundler-cache: true + - name: Run Java Loader test + run: PRISM_SERIALIZE_ONLY_SEMANTICS_FIELDS=1 PRISM_JAVA_BACKEND=truffleruby bundle exec rake compile + lex-ruby: runs-on: ubuntu-latest steps: @@ -257,7 +270,7 @@ jobs: bundler-cache: true - run: bundle config --local frozen false - run: bundle exec rake build:dev - - uses: actions/upload-artifact@v6 + - uses: actions/upload-artifact@v7 with: name: gem-package path: pkg @@ -308,7 +321,7 @@ jobs: - uses: ruby/setup-ruby@v1 with: ruby-version: ${{ matrix.target.ruby }} - - uses: actions/download-artifact@v7 + - uses: actions/download-artifact@v8 with: name: gem-package path: pkg diff --git a/.github/workflows/publish-npm.yml b/.github/workflows/publish-npm.yml index e14746a155..5a27aecd4c 100644 --- a/.github/workflows/publish-npm.yml +++ b/.github/workflows/publish-npm.yml @@ -42,5 +42,17 @@ jobs: - name: Update npm run: npm install -g npm@latest - - run: npm publish + - name: Determine npm tag + id: npm-tag + run: | + PACKAGE_VERSION=$(node -p "require('./javascript/package.json').version") + LATEST_VERSION=$(npm view @ruby/prism version 2>/dev/null || echo "0.0.0") + if npx semver "$PACKAGE_VERSION" -r ">$LATEST_VERSION"; then + echo "tag=latest" >> "$GITHUB_OUTPUT" + else + MINOR=$(echo "$PACKAGE_VERSION" | cut -d. -f1,2) + echo "tag=stable-$MINOR" >> "$GITHUB_OUTPUT" + fi + + - run: npm publish --tag ${{ steps.npm-tag.outputs.tag }} working-directory: javascript diff --git a/.github/workflows/rust-bindings.yml b/.github/workflows/rust-bindings.yml index 51e90fdaf0..850646c772 100644 --- a/.github/workflows/rust-bindings.yml +++ b/.github/workflows/rust-bindings.yml @@ -10,6 +10,7 @@ on: - "*akefile*" branches: - main + - ruby-4.0 pull_request: env: diff --git a/.github/workflows/sync-ruby.yml b/.github/workflows/sync-ruby.yml index 66a736ed85..55401876f9 100644 --- a/.github/workflows/sync-ruby.yml +++ b/.github/workflows/sync-ruby.yml @@ -1,7 +1,9 @@ name: Sync ruby on: push: - branches: [main] + branches: + - main + - ruby-4.0 jobs: sync: name: Sync ruby @@ -10,9 +12,17 @@ jobs: steps: - uses: actions/checkout@v6 + - name: Set ruby/ruby target branch + id: target + run: | + case "$GITHUB_REF_NAME" in + main) echo "ref=master" >> "$GITHUB_OUTPUT" ;; + ruby-4.0) echo "ref=ruby_4_0" >> "$GITHUB_OUTPUT" ;; + esac + - name: Create GitHub App token id: app-token - uses: actions/create-github-app-token@v2 + uses: actions/create-github-app-token@v3 with: app-id: 2060836 private-key: ${{ secrets.RUBY_SYNC_DEFAULT_GEMS_PRIVATE_KEY }} @@ -26,7 +36,7 @@ jobs: repo: ruby workflow_file_name: sync_default_gems.yml github_token: ${{ steps.app-token.outputs.token }} - ref: master + ref: ${{ steps.target.outputs.ref }} client_payload: | {"gem":"${{ github.event.repository.name }}","before":"${{ github.event.before }}","after":"${{ github.event.after }}"} propagate_failure: true diff --git a/.gitignore b/.gitignore index 6706482b64..fcdec6fb0a 100644 --- a/.gitignore +++ b/.gitignore @@ -31,7 +31,7 @@ out.svg /fuzz/output/ /gemfiles/typecheck/bin/ /include/prism/ast.h -/include/prism/diagnostic.h +/include/prism/internal/diagnostic.h /javascript/node_modules/ /javascript/package-lock.json /javascript/src/deserialize.js @@ -39,9 +39,10 @@ out.svg /javascript/src/visitor.js /javascript/src/prism.wasm /javascript/src/*.d.ts -/java/org/prism/AbstractNodeVisitor.java -/java/org/prism/Loader.java -/java/org/prism/Nodes.java +/java/org/ruby_lang/prism/AbstractNodeVisitor.java +/java/org/ruby_lang/prism/Loader.java +/java/org/ruby_lang/prism/Nodes.java +/java/wasm/src/test/resources/prism.wasm /lib/prism/compiler.rb /lib/prism/dispatcher.rb /lib/prism/dot_visitor.rb @@ -54,17 +55,12 @@ out.svg /lib/prism/visitor.rb /sorbet/ /src/diagnostic.c +/src/json.c /src/node.c /src/prettyprint.c /src/serialize.c -/src/token_type.c +/src/tokens.c /src/**/*.o -/sig/prism.rbs -/sig/prism/dsl.rbs -/sig/prism/mutation_compiler.rbs -/sig/prism/node.rbs -/sig/prism/visitor.rbs -/sig/prism/_private/dot_visitor.rbs /rbi/prism/dsl.rbi /rbi/prism/node.rbi /rbi/prism/visitor.rbi @@ -74,3 +70,5 @@ compile_commands.json .vscode/ tags + +wasi-sdk* diff --git a/CHANGELOG.md b/CHANGELOG.md index 1f384df570..5f9d3b5a4e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,6 +19,15 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) a - Fixed location of opening tokens when invalid syntax is parsed. - Fix RBI for parsing options. +## [1.8.1] - 2026-03-16 (Ruby 4.0.2 maintenance release) + +### Changed + +- Fix `not` binding power in endless methods. +- Correctly handle `and?` and similar on Ruby 4.0. +- Fix error message for block/lambda with `...` argument. +- Fix `in` handling. + ## [1.8.0] - 2026-01-12 ### Added @@ -746,6 +755,7 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) a [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.1]: https://github.com/ruby/prism/compare/v1.8.0...v1.8.1 [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 diff --git a/Doxyfile b/Doxyfile index 8827dea28d..adfd78c431 100644 --- a/Doxyfile +++ b/Doxyfile @@ -23,7 +23,8 @@ PROJECT_NAME = "Prism Ruby parser" OUTPUT_DIRECTORY = doc JAVADOC_AUTOBRIEF = YES OPTIMIZE_OUTPUT_FOR_C = YES -INPUT = src src/util include include/prism include/prism/util +INPUT = include/prism.h include/prism +EXCLUDE = include/prism/internal HTML_OUTPUT = c SORT_MEMBER_DOCS = NO GENERATE_LATEX = NO diff --git a/Gemfile.lock b/Gemfile.lock index 7e23a63cd5..f42c601c6f 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -10,24 +10,25 @@ GEM benchmark-ips (2.14.0) date (3.5.1) date (3.5.1-java) - erb (6.0.1) - erb (6.0.1-java) + erb (6.0.2) + erb (6.0.2-java) ffi (1.17.3) io-console (0.8.2) io-console (0.8.2-java) - irb (1.16.0) + irb (1.17.0) pp (>= 0.6.0) + prism (>= 1.3.0) rdoc (>= 4.0.0) reline (>= 0.4.2) jar-dependencies (0.5.5) mini_portile2 (2.8.9) - nokogiri (1.19.0) + nokogiri (1.19.1) mini_portile2 (~> 2.8.2) racc (~> 1.4) - nokogiri (1.19.0-java) + nokogiri (1.19.1-java) racc (~> 1.4) onigmo (0.1.0) - parser (3.3.10.0) + parser (3.3.10.2) ast (~> 2.4.1) racc power_assert (3.0.1) @@ -45,7 +46,7 @@ GEM rake (13.3.1) rake-compiler (1.3.1) rake - rdoc (7.0.3) + rdoc (7.2.0) erb psych (>= 4.0.0) tsort @@ -58,7 +59,7 @@ GEM sexp_processor (~> 4.16) sexp_processor (4.17.5) stringio (3.2.0) - test-unit (3.7.6) + test-unit (3.7.7) power_assert tsort (0.2.0) diff --git a/Makefile b/Makefile index 265d478734..039efee72c 100644 --- a/Makefile +++ b/Makefile @@ -12,7 +12,7 @@ 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) +JAVA_WASM_CFLAGS := -g0 -O2 -Oz -std=c99 -Wall -Werror -Wextra -Wpedantic -Wundef -Wconversion -Wno-missing-braces -fno-sanitize=all -fno-stack-protector -fPIC -fvisibility=hidden -Wimplicit-fallthrough $(JAVA_WASM_CFLAGS) CC ?= cc AR ?= ar ARFLAGS ?= -r$(V0:1=v) @@ -31,7 +31,7 @@ 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 +java-wasm: java/wasm/src/test/resources/prism.wasm build/libprism.$(SOEXT): $(SHARED_OBJECTS) $(ECHO) "linking $@ with $(CC)" @@ -45,15 +45,21 @@ 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 \ + -DPRISM_EXPORT_SYMBOLS -DPRISM_EXCLUDE_PRETTYPRINT -DPRISM_EXCLUDE_JSON \ -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) +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) + $(Q) $(MAKEDIRS) $(@D) + $(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 $@" @@ -69,12 +75,12 @@ 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 $@ $^ + $(Q) afl-clang-lto $(DEBUG_FLAGS) $(CPPFLAGS) $(CFLAGS) $(FUZZ_FLAGS) -O0 -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 $^ + $(Q) AFL_LLVM_CMPLOG=1 afl-clang-lto $(DEBUG_FLAGS) $(CPPFLAGS) $(CFLAGS) $(FUZZ_FLAGS) -O0 -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 $@ $^ + $(Q) afl-clang-lto $(DEBUG_FLAGS) $(CPPFLAGS) $(CFLAGS) $(FUZZ_FLAGS) -O0 -fsanitize=fuzzer,address -ggdb3 -std=c99 -Iinclude -o $@ $^ fuzz-debug: $(ECHO) "entering debug shell" diff --git a/README.md b/README.md index e92ef7bfb3..a4a9b6d340 100644 --- a/README.md +++ b/README.md @@ -29,8 +29,10 @@ The repository contains the infrastructure for both a shared library (libprism) ├── 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 +├── java +| ├── api Java API for Prism +| ├── native Java native bindings for the shared library +| └── wasm Java WASM bindings for the shared library ├── javascript JavaScript WASM bindings for the shared library ├── lib │   ├── prism Ruby library files @@ -44,7 +46,6 @@ The repository contains the infrastructure for both a shared library (libprism) │ └── 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 diff --git a/Rakefile b/Rakefile index b06877107e..e6a86cdb15 100644 --- a/Rakefile +++ b/Rakefile @@ -45,8 +45,7 @@ elsif RUBY_ENGINE == "jruby" ext.name = "prism" ext.ext_dir = "java" ext.lib_dir = "tmp" - ext.source_version = "1.8" - ext.target_version = "1.8" + ext.release = "21" ext.gem_spec = Gem::Specification.load("prism.gemspec") end end @@ -55,6 +54,7 @@ end CLOBBER.concat(Prism::Template::TEMPLATES) CLOBBER.concat(["build"]) CLOBBER << "lib/prism/prism.#{RbConfig::CONFIG["DLEXT"]}" +CLOBBER << "java/wasm/src/main/resources/prism.wasm" Prism::Template::TEMPLATES.each do |filepath| desc "Generate #{filepath}" diff --git a/Steepfile b/Steepfile index 433e53cd29..e6e1a8efb5 100644 --- a/Steepfile +++ b/Steepfile @@ -1,21 +1,18 @@ # frozen_string_literal: true target :lib do + check "lib" signature "sig" - library "cgi" # in lib/prism/dot_visitor.rb (Prism::DotVisitor) - - check "lib" + library "cgi" + library "pp" - # TODO: Type-checking these files is still WIP - ignore "lib/prism/desugar_compiler.rb" - ignore "lib/prism/lex_compat.rb" - ignore "lib/prism/serialize.rb" - ignore "lib/prism/ffi.rb" + # Ignored because it requires other libraries. ignore "lib/prism/translation" - ignore "lib/prism/polyfill/append_as_bytes.rb" - ignore "lib/prism/polyfill/byteindex.rb" - ignore "lib/prism/polyfill/scan_byte.rb" - ignore "lib/prism/polyfill/unpack1.rb" + # Ignored because they are only for older Rubies. + ignore "lib/prism/polyfill" + + # Ignored because we do not want to overlap with the C extension. + ignore "lib/prism/ffi.rb" end diff --git a/bin/mcp b/bin/mcp new file mode 100755 index 0000000000..1833d2f70b --- /dev/null +++ b/bin/mcp @@ -0,0 +1,188 @@ +#!/usr/bin/env ruby +# frozen_string_literal: true + +# This script is a server that can be run on your local machine, typically given +# to an LLM to reduce token usage and standardize interactions. It listens for +# JSON-RPC requests on stdin, processes them using the defined tools, and writes +# responses to stdout. +# +# To test out the server locally, it is easiest to pipe requests directly into +# stdin and then format JSON responses with a tool like `jq` for readability. +# Here are some example requests you can make to the server: +# +# echo '{"jsonrpc":"2.0","id":"1","method":"ping"}' | bin/mcp | jq +# echo '{"jsonrpc":"2.0","id":"2","method":"tools/list"}' | bin/mcp | jq +# echo '{"jsonrpc":"2.0","id":"1","method":"tools/call","params":{"name":"rake","arguments":{"target":"compile"}}}' | bin/mcp | jq +# echo '{"jsonrpc":"2.0","id":"1","method":"tools/call","params":{"name":"c_function","arguments":{"filepath":"src/prism.c","function_name":"pm_parser_init"}}}' | bin/mcp | jq +# echo '{"jsonrpc":"2.0","id":"1","method":"tools/call","params":{"name":"ruby_method","arguments":{"filepath":"lib/prism.rb","method_name":"load"}}}' | bin/mcp | jq +# + +def silence_output + original_stdout = $stdout.dup + original_stderr = $stderr.dup + + File.open(File::NULL, "w") do |null| + $stdout.reopen(null) + $stderr.reopen(null) + yield + end +ensure + $stdout.reopen(original_stdout) + $stderr.reopen(original_stderr) +end + +require "bundler/inline" +gemfile do + source "https://rubygems.org" + gem "mcp" + gem "open3" + gem "prism", path: File.expand_path("..", __dir__) + + silence_output do + gem "ffi-clang" + require "ffi/clang" + end +end + +class RakeTool < MCP::Tool + tool_name "rake" + description "Run rake on a specific target." + + input_schema( + properties: { + target: { type: "string", enum: ["clean", "compile", "test"], description: "The rake target to build." } + }, + required: ["target"] + ) + output_schema( + properties: { + success: { type: "boolean" }, + stderr: { type: "string" } + }, + required: ["success"] + ) + + def self.call(server_context:, target:) + _, stderr, status = + Dir.chdir(File.expand_path("..", __dir__)) do + Open3.capture3("bundle", "exec", "rake", target) + end + + result = + if status.success? + { success: true } + else + { success: false, stderr: stderr.strip } + end + + output_schema.validate_result(result) + MCP::Tool::Response.new([{ type: "text", text: result.to_json }], structured_content: result) + end +end + +class CFunctionTool < MCP::Tool + tool_name "c_function" + description "Extracts a C function from a source file." + + input_schema( + properties: { + filepath: { type: "string", description: "Path to the C source file." }, + function_name: { type: "string", description: "The name of the function to extract." } + }, + required: ["filepath", "function_name"] + ) + output_schema( + properties: { + body: { type: "string" } + }, + required: ["body"] + ) + + class << self + def call(server_context:, filepath:, function_name:) + if !File.readable?(filepath) + MCP::Tool::Response.new([MCP::Content::Text.new("Invalid filepath").to_h], error: true) + elsif !(body = find(filepath, function_name)) + MCP::Tool::Response.new([MCP::Content::Text.new("Function not found").to_h], error: true) + else + result = { body: body } + output_schema.validate_result(result) + + MCP::Tool::Response.new([{ type: "text", text: result.to_json }], structured_content: result) + end + end + + private + + def extract(cursor) + location = cursor.definition.location + return unless (filepath = location.file) + + extent = cursor.extent + start_offset = extent.start.offset + File.open(filepath, "rb") do |file| + file.pread(extent.end.offset - start_offset, start_offset) + end + end + + def find(filepath, function_name) + translation_unit = FFI::Clang::Index.new.parse_translation_unit(filepath) + translation_unit.cursor.find { |cursor, _| break extract(cursor) if cursor.declaration? && cursor.spelling == function_name } + end + end +end + +class RubyMethodTool < MCP::Tool + tool_name "ruby_method" + description "Extracts a Ruby method from a source file." + + input_schema( + properties: { + filepath: { type: "string", description: "Path to the Ruby source file." }, + method_name: { type: "string", description: "The name of the method to extract." } + }, + required: ["filepath", "method_name"] + ) + output_schema( + properties: { + body: { type: "string" } + }, + required: ["body"] + ) + + class << self + def call(server_context:, filepath:, method_name:) + if !File.readable?(filepath) + MCP::Tool::Response.new([MCP::Content::Text.new("Invalid filepath").to_h], error: true) + elsif !(result = Prism.parse_file(filepath)).success? + MCP::Tool::Response.new([MCP::Content::Text.new("Failed to parse file").to_h], error: true) + elsif !(body = find(result.value, method_name)) + MCP::Tool::Response.new([MCP::Content::Text.new("Method not found").to_h], error: true) + else + result = { body: body } + output_schema.validate_result(result) + + MCP::Tool::Response.new([{ type: "text", text: result.to_json }], structured_content: result) + end + end + + private + + def find(node, method_name) + name = method_name.to_sym + node.breadth_first_search do |child| + break child.slice if child.is_a?(Prism::DefNode) && child.name == name + end + end + end +end + +tools = [ + RakeTool, + CFunctionTool, + RubyMethodTool +] + +server = MCP::Server.new(name: "Prism", tools: tools) +transport = MCP::Server::Transports::StdioTransport.new(server) +transport.open diff --git a/bin/prism b/bin/prism index dbd0ddb174..43bdea1f36 100755 --- a/bin/prism +++ b/bin/prism @@ -8,7 +8,7 @@ module Prism class CLI def run(argv) case argv.shift - when "benchmark" then benchmark(argv) + when "bench" then bench(argv) when "bundle" then bundle(argv) when "console" then console when "dot" then dot(argv) @@ -25,7 +25,7 @@ module Prism else puts <<~TXT Usage: - bin/prism benchmark [source_file] + bin/prism bench [file ...] bin/prism bundle [...] bin/prism console bin/prism dot [source] @@ -49,46 +49,34 @@ module Prism # Commands ############################################################################ - # bin/prism benchmark [source_file] - def benchmark(argv) + # bin/prism bench [file ...] + # Measures raw parse throughput using Prism.profile (no Ruby AST creation). + def bench(argv) require "benchmark/ips" - require "parser/current" - require "ripper" - require "ruby_parser" - - filepath = argv.fetch(0) { File.expand_path("../lib/prism/node.rb", __dir__) } - source = File.read(filepath) - - Benchmark.ips do |x| - x.report("Prism") do - Prism.parse(source, filepath: filepath) - end - - x.report("Ripper::SexpBuilder") do - Ripper.sexp_raw(source, filepath) - end - x.report("Prism::Translation::Ripper::SexpBuilder") do - Prism::Translation::Ripper.sexp_raw(source, filepath) + files = + if argv.any? + argv.map { |path| [path, File.read(path)] } + else + Dir[File.join(__dir__, "../test/prism/fixtures/**/*.txt")].sort.map { |path| [path, File.read(path)] } + + Dir[File.join(__dir__, "../lib/**/*.rb")].sort.map { |path| [path, File.read(path)] } end - x.report("Parser::CurrentRuby") do - Parser::CurrentRuby.parse(source, filepath) - end + total_bytes = files.sum { |_, source| source.bytesize } + puts "Benchmarking #{files.size} files (#{total_bytes} bytes total)" + puts - x.report("Prism::Translation::Parser") do - Prism::Translation::Parser.parse(source, filepath) - end + Benchmark.ips do |x| + x.time = 10 + x.warmup = 3 - x.report("RubyParser") do - RubyParser.new.parse(source, filepath) + x.report("parse (all files)") do + files.each { |_, source| Prism.profile(source) } end - x.report("Prism::Translation::RubyParser") do - Prism::Translation::RubyParser.new.parse(source, filepath) + x.report("parse (fixtures only)") do + files.each { |path, source| Prism.profile(source) if path.end_with?(".txt") } end - - x.compare! end end @@ -166,7 +154,6 @@ module Prism source, filepath = read_source(argv) result = Prism.parse(source) - if result.errors.any? puts result.errors_format else @@ -270,17 +257,17 @@ module Prism # bin/prism parser [source] def parser(argv) - require "parser/ruby34" + require "parser/current" source, filepath = read_source(argv) buffer = Parser::Source::Buffer.new(filepath, 1) buffer.source = source puts "Parser:" - parser_parse(Parser::Ruby34.new, buffer) + parser_parse(Parser::CurrentRuby.new, buffer) puts "Prism:" - parser_parse(Prism::Translation::Parser34.new, buffer) + parser_parse(Prism::Translation::ParserCurrent.new, buffer) end # bin/prism ripper [source] diff --git a/config.yml b/config.yml index 4e1560481e..1ea1bcf7f1 100644 --- a/config.yml +++ b/config.yml @@ -17,6 +17,8 @@ errors: - ARGUMENT_FORWARDING_UNBOUND - ARGUMENT_NO_FORWARDING_AMPERSAND - ARGUMENT_NO_FORWARDING_ELLIPSES + - ARGUMENT_NO_FORWARDING_ELLIPSES_LAMBDA + - ARGUMENT_NO_FORWARDING_ELLIPSES_BLOCK - ARGUMENT_NO_FORWARDING_STAR - ARGUMENT_NO_FORWARDING_STAR_STAR - ARGUMENT_SPLAT_AFTER_ASSOC_SPLAT @@ -62,6 +64,7 @@ errors: - DEF_ENDLESS - DEF_ENDLESS_PARAMETERS - DEF_ENDLESS_SETTER + - DEF_ENDLESS_DO_BLOCK - DEF_NAME - DEF_PARAMS_TERM - DEF_PARAMS_TERM_PAREN @@ -245,7 +248,9 @@ errors: - PATTERN_TERM_PAREN - PIPEPIPEEQ_MULTI_ASSIGN - REGEXP_ENCODING_OPTION_MISMATCH + - REGEXP_ESCAPED_NON_ASCII_IN_UTF8 - REGEXP_INCOMPAT_CHAR_ENCODING + - REGEXP_INVALID_CHAR_PROPERTY - REGEXP_INVALID_UNICODE_RANGE - REGEXP_NON_ESCAPED_MBC - REGEXP_PARSE_ERROR @@ -491,6 +496,8 @@ tokens: comment: "def" - name: KEYWORD_DEFINED comment: "defined?" + - name: KEYWORD_DO_BLOCK + comment: "do keyword for a block attached to a command" - name: KEYWORD_DO_LOOP comment: "do keyword for a predicate in a while, until, or for loop" - name: KEYWORD_END_UPCASE @@ -817,7 +824,7 @@ nodes: - name: keyword_loc type: location comment: | - The location of the `alias` keyword. + The Location of the `alias` keyword. alias $foo $bar ^^^^^ @@ -865,7 +872,7 @@ nodes: - name: keyword_loc type: location comment: | - Represents the location of the `alias` keyword. + Represents the Location of the `alias` keyword. alias foo bar ^^^^^ @@ -895,7 +902,7 @@ nodes: - name: operator_loc type: location comment: | - Represents the alternation operator location. + Represents the alternation operator Location. foo => bar | baz ^ @@ -931,7 +938,7 @@ nodes: - name: operator_loc type: location comment: | - The location of the `and` keyword or the `&&` operator. + The Location of the `and` keyword or the `&&` operator. left and right ^^^ @@ -966,7 +973,7 @@ nodes: - name: opening_loc type: location? comment: | - Represents the optional source location for the opening token. + Represents the optional source Location for the opening token. [1,2,3] # "[" %w[foo bar baz] # "%w[" @@ -975,7 +982,7 @@ nodes: - name: closing_loc type: location? comment: | - Represents the optional source location for the closing token. + Represents the optional source Location for the closing token. [1,2,3] # "]" %w[foo bar baz] # "]" @@ -1031,14 +1038,14 @@ nodes: - name: opening_loc type: location? comment: | - Represents the opening location of the array pattern. + 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. + Represents the closing Location of the array pattern. foo in [1, 2] ^ @@ -1046,19 +1053,19 @@ nodes: 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 @@ -1089,7 +1096,7 @@ nodes: - name: operator_loc type: location? comment: | - The location of the `=>` operator, if present. + The Location of the `=>` operator, if present. { foo => bar } ^^ @@ -1111,7 +1118,7 @@ nodes: - name: operator_loc type: location comment: | - The location of the `**` operator. + The Location of the `**` operator. { **x } ^^ @@ -1140,7 +1147,7 @@ nodes: - name: begin_keyword_loc type: location? comment: | - Represents the location of the `begin` keyword. + Represents the Location of the `begin` keyword. begin x end ^^^^^ @@ -1167,7 +1174,7 @@ nodes: Represents the else clause within the begin block. begin x; rescue y; else z; end - ^^^^^^ + ^^^^^^^^^^^ - name: ensure_clause type: node? kind: EnsureNode @@ -1179,7 +1186,7 @@ nodes: - name: end_keyword_loc type: location? comment: | - Represents the location of the `end` keyword. + Represents the Location of the `end` keyword. begin x end ^^^ @@ -1200,11 +1207,11 @@ nodes: 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. + Represents the Location of the `&` operator. foo(&args) ^ @@ -1212,7 +1219,7 @@ nodes: Represents a block argument using `&`. bar(&args) - ^^^^^^^^^^ + ^^^^^ - name: BlockLocalVariableNode flags: ParameterFlags fields: @@ -1265,14 +1272,14 @@ nodes: - name: opening_loc type: location comment: | - Represents the location of the opening `{` or `do`. + 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`. + Represents the Location of the closing `}` or `end`. [1, 2, 3].each { |i| puts x } ^ @@ -1295,14 +1302,14 @@ nodes: - name: name_loc type: location? comment: | - Represents the location of the block parameter name. + Represents the Location of the block parameter name. def a(&b) ^ - name: operator_loc type: location comment: | - Represents the location of the `&` operator. + Represents the Location of the `&` operator. def a(&b) ^ @@ -1342,7 +1349,7 @@ nodes: - name: opening_loc type: location? comment: | - Represents the opening location of the block parameters. + Represents the opening Location of the block parameters. -> (a, b = 1; local) { } ^ @@ -1353,7 +1360,7 @@ nodes: - name: closing_loc type: location? comment: | - Represents the closing location of the block parameters. + Represents the closing Location of the block parameters. -> (a, b = 1; local) { } ^ @@ -1383,7 +1390,7 @@ nodes: - name: keyword_loc type: location comment: | - The location of the `break` keyword. + The Location of the `break` keyword. break foo ^^^^^ @@ -1406,14 +1413,14 @@ nodes: - name: call_operator_loc type: location? comment: | - Represents the location of the call operator. + Represents the Location of the call operator. foo.bar &&= value ^ - name: message_loc type: location? comment: | - Represents the location of the message. + Represents the Location of the message. foo.bar &&= value ^^^ @@ -1434,7 +1441,7 @@ nodes: - name: operator_loc type: location comment: | - Represents the location of the operator. + Represents the Location of the operator. foo.bar &&= value ^^^ @@ -1471,7 +1478,7 @@ nodes: - name: call_operator_loc type: location? comment: | - Represents the location of the call operator. + Represents the Location of the call operator. foo.bar ^ @@ -1488,14 +1495,15 @@ nodes: - name: message_loc type: location? comment: | - Represents the location of the message. + Represents the Location of the message. foo.bar ^^^ - name: opening_loc type: location? comment: | - Represents the location of the left parenthesis. + Represents the Location of the left parenthesis. + foo(bar) ^ - name: arguments @@ -1509,14 +1517,14 @@ nodes: - name: closing_loc type: location? comment: | - Represents the location of the right parenthesis. + 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. + Represents the Location of the equal sign, in the case that this is an attribute write. foo.bar = value ^ @@ -1567,14 +1575,14 @@ nodes: - name: call_operator_loc type: location? comment: | - Represents the location of the call operator. + Represents the Location of the call operator. foo.bar += value ^ - name: message_loc type: location? comment: | - Represents the location of the message. + Represents the Location of the message. foo.bar += value ^^^ @@ -1602,7 +1610,7 @@ nodes: - name: binary_operator_loc type: location comment: | - Represents the location of the binary operator. + Represents the Location of the binary operator. foo.bar += value ^^ @@ -1633,14 +1641,14 @@ nodes: - name: call_operator_loc type: location? comment: | - Represents the location of the call operator. + Represents the Location of the call operator. foo.bar ||= value ^ - name: message_loc type: location? comment: | - Represents the location of the message. + Represents the Location of the message. foo.bar ||= value ^^^ @@ -1661,7 +1669,7 @@ nodes: - name: operator_loc type: location comment: | - Represents the location of the operator. + Represents the Location of the operator. foo.bar ||= value ^^^ @@ -1692,7 +1700,7 @@ nodes: - name: call_operator_loc type: location comment: | - Represents the location of the call operator. + Represents the Location of the call operator. foo.bar = 1 ^ @@ -1706,7 +1714,7 @@ nodes: - name: message_loc type: location comment: | - Represents the location of the message. + Represents the Location of the message. foo.bar = 1 ^^^ @@ -1744,7 +1752,7 @@ nodes: - name: operator_loc type: location comment: | - Represents the location of the `=>` operator. + Represents the Location of the `=>` operator. foo => bar ^^ @@ -1752,7 +1760,7 @@ nodes: Represents assigning to a local variable in pattern matching. foo => [bar => baz] - ^^^^^^^^^^^^ + ^^^^^^^^^^ - name: CaseMatchNode fields: - name: predicate @@ -1762,7 +1770,7 @@ nodes: 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 @@ -1778,18 +1786,18 @@ nodes: 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. + 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. + Represents the Location of the `end` keyword. case true; in false; end ^^^ @@ -1825,18 +1833,18 @@ nodes: 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. + 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. + Represents the Location of the `end` keyword. case true; when false; end ^^^ @@ -1854,7 +1862,7 @@ nodes: - name: class_keyword_loc type: location comment: | - Represents the location of the `class` keyword. + Represents the Location of the `class` keyword. class Foo end ^^^^^ @@ -1867,7 +1875,7 @@ nodes: - name: inheritance_operator_loc type: location? comment: | - Represents the location of the `<` operator. + Represents the Location of the `<` operator. class Foo < Bar ^ @@ -1887,13 +1895,12 @@ nodes: comment: | Represents the body of the class. - class Foo - foo - ^^^ + class Foo; bar; end + ^^^ - name: end_keyword_loc type: location comment: | - Represents the location of the `end` keyword. + Represents the Location of the `end` keyword. class Foo end ^^^ @@ -1920,14 +1927,14 @@ nodes: - name: name_loc type: location comment: | - Represents the location of the variable name. + Represents the Location of the variable name. @@target &&= value ^^^^^^^^ - name: operator_loc type: location comment: | - Represents the location of the `&&=` operator. + Represents the Location of the `&&=` operator. @@target &&= value ^^^ @@ -2015,7 +2022,7 @@ nodes: - name: name_loc type: location comment: | - The location of the variable name. + The Location of the variable name. @@foo = :bar ^^^^^ @@ -2033,7 +2040,7 @@ nodes: - name: operator_loc type: location comment: | - The location of the `=` operator. + The Location of the `=` operator. @@foo = :bar ^ @@ -2129,7 +2136,7 @@ nodes: - name: delimiter_loc type: location comment: | - The location of the `::` delimiter. + The Location of the `::` delimiter. ::Foo ^^ @@ -2139,7 +2146,7 @@ nodes: - name: name_loc type: location comment: | - The location of the name of the constant. + The Location of the name of the constant. ::Foo ^^^ @@ -2215,7 +2222,7 @@ nodes: - name: operator_loc type: location comment: | - The location of the `=` operator. + The Location of the `=` operator. ::ABC = 123 ^ @@ -2275,7 +2282,7 @@ nodes: - name: name_loc type: location comment: | - The location of the constant name. + The Location of the constant name. FOO = 1 ^^^ @@ -2293,7 +2300,7 @@ nodes: - name: operator_loc type: location comment: | - The location of the `=` operator. + The Location of the `=` operator. FOO = :bar ^ @@ -2474,7 +2481,7 @@ nodes: - name: opening_loc type: location? comment: | - The location of the opening brace. + The Location of the opening brace. foo in [*bar, baz, *qux] ^ @@ -2484,7 +2491,7 @@ nodes: - name: closing_loc type: location? comment: | - The location of the closing brace. + The Location of the closing brace. foo in [*bar, baz, *qux] ^ @@ -2574,28 +2581,28 @@ nodes: - name: for_keyword_loc type: location comment: | - The location of the `for` keyword. + The Location of the `for` keyword. for i in a end ^^^ - name: in_keyword_loc type: location comment: | - The location of the `in` keyword. + 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. + 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. + The Location of the `end` keyword. for i in a end ^^^ @@ -2723,7 +2730,7 @@ nodes: - name: name_loc type: location comment: | - The location of the global variable's name. + The Location of the global variable's name. $foo = :bar ^^^^ @@ -2741,7 +2748,7 @@ nodes: - name: operator_loc type: location comment: | - The location of the `=` operator. + The Location of the `=` operator. $foo = :bar ^ @@ -2755,7 +2762,7 @@ nodes: - name: opening_loc type: location comment: | - The location of the opening brace. + The Location of the opening brace. { a => b } ^ @@ -2775,7 +2782,7 @@ nodes: - name: closing_loc type: location comment: | - The location of the closing brace. + The Location of the closing brace. { a => b } ^ @@ -2826,7 +2833,7 @@ nodes: - name: opening_loc type: location? comment: | - The location of the opening brace. + The Location of the opening brace. foo => { a: 1 } ^ @@ -2836,7 +2843,7 @@ nodes: - name: closing_loc type: location? comment: | - The location of the closing brace. + The Location of the closing brace. foo => { a: 1 } ^ @@ -2862,7 +2869,7 @@ nodes: - name: if_keyword_loc type: location? comment: | - The location of the `if` keyword if present. + The Location of the `if` keyword if present. bar if foo ^^ @@ -2887,7 +2894,7 @@ nodes: - name: then_keyword_loc type: location? comment: | - The location of the `then` keyword (if present) or the `?` in a ternary expression, `nil` otherwise. + The Location of the `then` keyword (if present) or the `?` in a ternary expression, `nil` otherwise. if foo then bar end ^^^^ @@ -2928,7 +2935,7 @@ nodes: - name: end_keyword_loc type: location? comment: | - The location of the `end` keyword if present, `nil` otherwise. + The Location of the `end` keyword if present, `nil` otherwise. if foo bar @@ -3213,7 +3220,7 @@ nodes: - name: name_loc type: location comment: | - The location of the variable name. + The Location of the variable name. @_x = 1 ^^^ @@ -3231,7 +3238,7 @@ nodes: - name: operator_loc type: location comment: | - The location of the `=` operator. + The Location of the `=` operator. @x = y ^ @@ -3538,7 +3545,7 @@ nodes: - name: name_loc type: location comment: | - The location of the variable name. + The Location of the variable name. foo = :bar ^^^ @@ -3560,7 +3567,7 @@ nodes: - name: operator_loc type: location comment: | - The location of the `=` operator. + The Location of the `=` operator. x = :y ^ @@ -3660,7 +3667,7 @@ nodes: - name: operator_loc type: location comment: | - The location of the operator. + The Location of the operator. foo => bar ^^ @@ -3781,14 +3788,14 @@ nodes: - name: lparen_loc type: location? comment: | - The location of the opening parenthesis. + The Location of the opening parenthesis. a, (b, c) = 1, 2, 3 ^ - name: rparen_loc type: location? comment: | - The location of the closing parenthesis. + The Location of the closing parenthesis. a, (b, c) = 1, 2, 3 ^ @@ -3870,21 +3877,21 @@ nodes: - name: lparen_loc type: location? comment: | - The location of the opening parenthesis. + The Location of the opening parenthesis. (a, b, c) = 1, 2, 3 ^ - name: rparen_loc type: location? comment: | - The location of the closing parenthesis. + The Location of the closing parenthesis. (a, b, c) = 1, 2, 3 ^ - name: operator_loc type: location comment: | - The location of the operator. + The Location of the operator. a, b, c = 1, 2, 3 ^ @@ -3919,6 +3926,18 @@ nodes: nil ^^^ + - name: NoBlockParameterNode + 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: NoKeywordsParameterNode fields: - name: operator_loc @@ -4018,7 +4037,7 @@ nodes: - name: operator_loc type: location comment: | - The location of the `or` keyword or the `||` operator. + The Location of the `or` keyword or the `||` operator. left or right ^^ @@ -4052,6 +4071,9 @@ nodes: - on error: NoKeywordsParameterNode # On parsing error of `f(..., ...)`, the first forwarding parameter is moved here: - on error: ForwardingParameterNode + # On parsing error of `f(&nil, &foo)`/`f(&foo, &nil)`, the first forwarding parameter is moved here: + - on error: BlockParameterNode + - on error: NoBlockParameterNode - name: keywords type: node[] kind: @@ -4065,7 +4087,9 @@ nodes: - NoKeywordsParameterNode - name: block type: node? - kind: BlockParameterNode + kind: + - BlockParameterNode + - NoBlockParameterNode comment: | Represents the list of parameters on a method, block, or lambda definition. @@ -4101,21 +4125,21 @@ nodes: - name: operator_loc type: location comment: | - The location of the `^` operator + The Location of the `^` operator foo in ^(bar) ^ - name: lparen_loc type: location comment: | - The location of the opening parenthesis. + The Location of the opening parenthesis. foo in ^(bar) ^ - name: rparen_loc type: location comment: | - The location of the closing parenthesis. + The Location of the closing parenthesis. foo in ^(bar) ^ @@ -4145,7 +4169,7 @@ nodes: - name: operator_loc type: location comment: | - The location of the `^` operator + The Location of the `^` operator foo in ^bar ^ @@ -4223,7 +4247,7 @@ nodes: - name: operator_loc type: location comment: | - The location of the `..` or `...` operator. + The Location of the `..` or `...` operator. comment: | Represents the use of the `..` or `...` operators. @@ -4449,7 +4473,7 @@ nodes: 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 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. @@ -4576,7 +4600,7 @@ nodes: - name: keyword_loc type: location comment: | - The location of the `unless` keyword. + The Location of the `unless` keyword. unless cond then bar end ^^^^^^ @@ -4597,7 +4621,7 @@ nodes: - name: then_keyword_loc type: location? comment: | - The location of the `then` keyword, if present. + The Location of the `then` keyword, if present. unless cond then bar end ^^^^ @@ -4617,11 +4641,11 @@ nodes: 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. + The Location of the `end` keyword, if present. unless cond then bar end ^^^ diff --git a/cpp/test.cpp b/cpp/test.cpp index af993c9fca..3d21c99769 100644 --- a/cpp/test.cpp +++ b/cpp/test.cpp @@ -5,20 +5,20 @@ extern "C" { #include int main() { - pm_parser_t parser; - pm_parser_init(&parser, reinterpret_cast("1 + 2"), 5, NULL); + pm_arena_t *arena = pm_arena_new(); + pm_parser_t *parser = pm_parser_new(arena, reinterpret_cast("1 + 2"), 5, NULL); - pm_node_t *root = pm_parse(&parser); - pm_buffer_t buffer = { 0 }; + pm_node_t *root = pm_parse(parser); + pm_buffer_t *buffer = pm_buffer_new(); - pm_prettyprint(&buffer, &parser, root); - pm_buffer_append_byte(&buffer, '\0'); + pm_prettyprint(buffer, parser, root); - std::cout << buffer.value << std::endl; + std::string_view view(pm_buffer_value(buffer), pm_buffer_length(buffer)); + std::cout << view << std::endl; - pm_buffer_free(&buffer); - pm_node_destroy(&parser, root); - pm_parser_free(&parser); + pm_buffer_free(buffer); + pm_parser_free(parser); + pm_arena_free(arena); return 0; } diff --git a/doc/index.css b/doc/index.css index b4683cf24f..e33d019791 100644 --- a/doc/index.css +++ b/doc/index.css @@ -1,84 +1,186 @@ :root { - --color-background: #1c1f26; - --color-text: white; + --color-ruby: #CC342D; + --color-ruby-dark: #A50C07; + --color-bg: #FFFFFF; + --color-bg-alt: #F7F7F7; + --color-text: #3E4451; + --color-text-light: #6B7280; + --color-border: #E5E7EB; } -body { - background-color: var(--color-background); - font-family: "Noto Sans", "Helvetica Neue", Helvetica, sans-serif; +* { margin: 0; + padding: 0; + box-sizing: border-box; } -header { - height: 10vh; +body { + background-color: var(--color-bg); + color: var(--color-text); + font-family: "Noto Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif; + line-height: 1.6; } -h1 { - color: var(--color-text); +header { + background-color: var(--color-ruby); + color: white; + padding: 3rem 2rem; text-align: center; - line-height: 10vh; - margin: 0; +} + +header h1 { + font-size: 2.25rem; + font-weight: 700; + margin-bottom: 0.5rem; +} + +header p { + font-size: 1.1rem; +} + +:focus-visible { + outline: 2px solid var(--color-ruby); + outline-offset: 2px; } main { - display: grid; - grid-template-rows: 1fr 1fr; - grid-template-columns: 1fr 1fr; - height: 36vh; - margin: 2vh 20vw; + max-width: 56rem; + margin: 0 auto; + padding: 0 2rem; } -@media only screen and (max-width: 1200px) { - main { - margin: 2vh 10vw; - } +section { + margin-top: 2.5rem; } -@media only screen and (max-width: 900px) { - main { - grid-template-rows: repeat(1fr); - grid-template-columns: 1fr; - height: 72vh; - } +section h2 { + font-size: 1.25rem; + font-weight: 700; + margin-bottom: 1rem; + color: var(--color-text); +} + +section p { + color: var(--color-text-light); + font-size: 0.9375rem; + margin-bottom: 0.75rem; +} + +.playground-link { + display: inline-block; + margin-top: 0.5rem; + padding: 0.625rem 1.25rem; + background: var(--color-ruby); + color: white; + text-decoration: none; + border-radius: 0.375rem; + font-size: 0.9375rem; + font-weight: 600; + transition: background-color 150ms ease; +} + +.playground-link:hover { + background: var(--color-ruby-dark); +} + +pre { + background: var(--color-bg-alt); + border: 1px solid var(--color-border); + border-radius: 0.375rem; + padding: 0.75rem 1rem; + font-size: 0.875rem; + overflow-x: auto; + margin-bottom: 0.75rem; +} + +code { + font-family: "SFMono-Regular", Consolas, "Liberation Mono", Menlo, monospace; } -@media only screen and (max-width: 600px) { - main { - margin: 2vh 5vw; +/* API reference cards */ +.grid { + display: grid; + grid-template-columns: repeat(2, 1fr); + gap: 1rem; +} + +@media (max-width: 640px) { + .grid { + grid-template-columns: 1fr; } } .reference { - border-radius: 1em; - box-sizing: border-box; - color: var(--color-text); - height: 18vh; - padding: 4vh 0; - text-align: center; + display: flex; + align-items: center; + gap: 1.25rem; + padding: 1.25rem; + border: 1px solid var(--color-border); + border-radius: 0.5rem; text-decoration: none; - transition: background-color 100ms ease; - vertical-align: middle; + color: var(--color-text); + background: var(--color-bg); + transition: border-color 150ms ease, box-shadow 150ms ease; +} + +.reference:hover { + border-color: var(--color-ruby); + box-shadow: 0 2px 8px rgba(204, 52, 45, 0.1); } -.reference > img { - height: 10vh; - width: 10vh; - margin-right: 1em; - vertical-align: middle; +.reference img { + width: 2.5rem; + height: 2.5rem; + flex-shrink: 0; } -.reference > h2 { - display: inline; +.reference h3 { + font-size: 1rem; + font-weight: 600; margin: 0; - text-decoration: underline; + color: var(--color-ruby); } -.reference:hover { - background-color: rgba(255, 255, 255, 0.1); +.reference span { + display: block; + font-size: 0.8125rem; + color: var(--color-text-light); + margin-top: 0.125rem; } -#logo { +/* Guide links */ +.guide { display: block; - height: 50vh; - margin: 0 auto; + padding: 0.625rem 0.875rem; + border: 1px solid var(--color-border); + border-radius: 0.375rem; + text-decoration: none; + color: var(--color-text); + font-size: 0.875rem; + transition: border-color 150ms ease, color 150ms ease; +} + +.guide:hover { + border-color: var(--color-ruby); + color: var(--color-ruby); +} + +/* Footer */ +footer { + margin-top: 3rem; + padding: 1.5rem 2rem; + text-align: center; + border-top: 1px solid var(--color-border); + display: flex; + justify-content: center; + gap: 2rem; +} + +footer a { + color: var(--color-text-light); + font-size: 0.875rem; +} + +footer a:hover { + color: var(--color-ruby); } diff --git a/doc/index.html b/doc/index.html index 9e0e38cb60..b3af99e367 100644 --- a/doc/index.html +++ b/doc/index.html @@ -2,37 +2,86 @@ - - Prism - - + + + Prism Ruby Parser + + + +
-

Prism Ruby parser

+

Prism

+

A portable, error-tolerant Ruby parser

- - C -

C reference

-
- - Ruby -

Ruby reference

-
- - Rust -

Rust reference

-
- - Java -

Java reference

-
+
+

Try it

+

Parse Ruby code in your browser — no installation required.

+ Open playground → +
+ +
+

Getting started

+

Prism is bundled with CRuby 3.3+ and available as a gem for earlier versions.

+
gem install prism
+
+ +
+

API Reference

+ +
+ +
+

Guides

+ +
diff --git a/doc/playground.css b/doc/playground.css new file mode 100644 index 0000000000..9666ae4bfe --- /dev/null +++ b/doc/playground.css @@ -0,0 +1,336 @@ +:root { + --color-ruby: #CC342D; + --color-ruby-light: rgba(204, 52, 45, 0.15); + --color-bg: #FFFFFF; + --color-bg-alt: #F7F7F7; + --color-text: #3E4451; + --color-text-light: #6B7280; + --color-border: #E5E7EB; + --color-highlight: rgba(204, 52, 45, 0.12); +} + +* { margin: 0; padding: 0; box-sizing: border-box; } + +body { + font-family: "Noto Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif; + color: var(--color-text); + height: 100vh; + display: flex; + flex-direction: column; +} + +:focus-visible { + outline: 2px solid var(--color-ruby); + outline-offset: 2px; +} + +.skip-link { + position: absolute; + left: -9999px; + top: 0; + background: var(--color-ruby); + color: white; + padding: 0.5rem 1rem; + z-index: 200; + font-size: 0.875rem; + text-decoration: none; +} + +.skip-link:focus { + left: 0; +} + +nav { + background: var(--color-ruby); + color: white; + padding: 0.5rem 1rem; + display: flex; + align-items: center; + justify-content: space-between; + flex-shrink: 0; + gap: 0.5rem; +} + +nav h1 { font-size: 1.1rem; font-weight: 600; } +nav a { color: white; text-decoration: none; font-size: 0.8125rem; } +nav a:hover { text-decoration: underline; } +nav a:focus-visible { outline-color: white; } + +.nav-left { + display: flex; + align-items: center; + gap: 0.75rem; + min-width: 0; +} + +.nav-right { + display: flex; + align-items: center; + gap: 0.5rem; +} + +@media (max-width: 540px) { + .nav-version, .nav-right a { display: none; } +} + +.nav-version { + font-size: 0.75rem; + color: rgba(255,255,255,0.9); +} + +.nav-select, .nav-button { + font-family: inherit; + font-size: 0.8125rem; + height: 1.75rem; + padding: 0 0.5rem; + border: 1px solid rgba(255,255,255,0.4); + border-radius: 4px; + background: rgba(255,255,255,0.15); + color: white; + cursor: pointer; +} + +.nav-select:hover, .nav-button:hover { + background: rgba(255,255,255,0.25); +} + +.nav-select:focus-visible, .nav-button:focus-visible { + outline-color: white; +} + +.nav-select option { + color: var(--color-text); + background: white; +} + +main { + flex: 1; + display: flex; + flex-direction: column; + min-height: 0; +} + +#loading { + flex: 1; + display: flex; + align-items: center; + justify-content: center; + color: var(--color-text-light); +} + +#loading.hidden { + display: none; +} + +.editor { + flex: 1; + display: none; + grid-template-columns: 1fr 1fr; + min-height: 0; +} + +.editor.ready { display: grid; } + +@media (max-width: 1024px) { + .editor { + grid-template-columns: 1fr; + grid-template-rows: minmax(40vh, 1fr) minmax(40vh, 1fr); + overflow: auto; + } +} + +#monaco-container { + border-right: 1px solid var(--color-border); + min-width: 0; + overflow: hidden; +} + +.output-panel { + display: flex; + flex-direction: column; + overflow: hidden; +} + +.tabs { + display: flex; + flex-wrap: wrap; + align-items: center; + border-bottom: 1px solid var(--color-border); + background: var(--color-bg); + flex-shrink: 0; +} + +.tabs button[role="tab"] { + padding: 0.625rem 1rem; + border: none; + background: none; + font-family: inherit; + font-size: 0.8125rem; + color: var(--color-text-light); + cursor: pointer; + border-bottom: 2px solid transparent; +} + +.tabs button[role="tab"]:hover { color: var(--color-text); } +.tabs button[role="tab"].active { + color: var(--color-ruby); + border-bottom-color: var(--color-ruby); +} + +.tabs-spacer { + flex: 1; +} + +.tab-action { + font-family: inherit; + font-size: 0.75rem; + padding: 0.25rem 0.5rem; + margin-right: 0.375rem; + border: 1px solid var(--color-border); + border-radius: 3px; + background: var(--color-bg); + color: var(--color-text-light); + cursor: pointer; +} + +.tab-action:hover { + color: var(--color-text); + border-color: var(--color-text-light); +} + +#output { + flex: 1; + overflow: auto; + background: var(--color-bg-alt); + font-family: "SFMono-Regular", Consolas, "Liberation Mono", Menlo, monospace; + font-size: 13px; + line-height: 1.5; + padding: 0.75rem; +} + +/* AST tree styles */ +.tree-node { + white-space: pre; +} + +.tree-type { + color: var(--color-ruby); + font-weight: 600; + cursor: pointer; +} + +.tree-type:hover { + text-decoration: underline; +} + +.tree-loc { + color: var(--color-text-light); + cursor: pointer; +} + +.tree-loc:hover { + color: var(--color-ruby); +} + +.tree-toggle { + display: inline-block; + cursor: pointer; + user-select: none; + color: var(--color-text-light); + background: none; + border: none; + padding: 0.125em 0.25em; + margin-right: 0.125em; + font: inherit; + line-height: inherit; + border-radius: 2px; +} + +.tree-toggle:hover { + color: var(--color-ruby); + background: var(--color-highlight); +} + +.tree-children.collapsed { + display: none; +} + +.tree-field { + color: var(--color-text-light); +} + +.tree-value { + color: var(--color-text); +} + +.tree-string { + color: #953800; +} + +.tree-null { + color: var(--color-text-light); + font-style: italic; +} + +.tree-connector { + color: #d1d5db; +} + +.tree-flag { + display: inline-block; + padding: 0 0.375em; + border-radius: 3px; + font-size: 0.85em; + vertical-align: middle; + background: rgba(204, 52, 45, 0.08); + color: var(--color-ruby); +} + +.tree-highlight { + background: var(--color-highlight); + border-radius: 2px; +} + +.prism-highlight { + background: var(--color-ruby-light) !important; +} + +.diagnostics-line { + padding: 0.25rem 0; +} + +.diagnostics-line[data-sl] { + cursor: pointer; +} + +.error-text { color: var(--color-ruby); } +.warning-text { color: #b45309; } + +.noscript-message { + padding: 2rem; + text-align: center; + color: var(--color-text-light); +} + +.empty-message { + padding: 0.5rem; +} + +.share-toast { + position: fixed; + bottom: 1rem; + left: 50%; + transform: translateX(-50%); + padding: 0.5rem 1rem; + background: var(--color-text); + color: white; + border-radius: 4px; + font-size: 0.8125rem; + z-index: 100; + opacity: 0; + transition: opacity 200ms; +} + +.share-toast.visible { + opacity: 1; +} diff --git a/doc/playground.html b/doc/playground.html new file mode 100644 index 0000000000..cc1e17367f --- /dev/null +++ b/doc/playground.html @@ -0,0 +1,63 @@ + + + + + + + + Prism - Playground + + + + + + + + + + + +
+
Loading…
+ + +
+
+
+
+ + + + + +
+
+
+
+
+ + + + + + + diff --git a/doc/playground.js b/doc/playground.js new file mode 100644 index 0000000000..f593b96afe --- /dev/null +++ b/doc/playground.js @@ -0,0 +1,499 @@ +import { WASI } from "https://unpkg.com/@bjorn3/browser_wasi_shim@latest/dist/index.js"; +import { parsePrism } from "https://unpkg.com/@ruby/prism@latest/src/parsePrism.js"; + +const output = document.getElementById("output"); +const editorDiv = document.getElementById("editor"); +const loading = document.getElementById("loading"); +const toast = document.getElementById("toast"); + +// Load Prism WASM and Monaco, show error if either fails +let instance, monaco; +try { + const [wasmResult] = await Promise.all([ + WebAssembly.compileStreaming(fetch("https://unpkg.com/@ruby/prism@latest/src/prism.wasm")) + .then(wasm => { + const wasi = new WASI([], [], []); + return WebAssembly.instantiate(wasm, { wasi_snapshot_preview1: wasi.wasiImport }) + .then(inst => { wasi.initialize(inst); return inst; }); + }), + fetch("https://unpkg.com/@ruby/prism@latest/package.json") + .then(r => r.json()) + .then(pkg => { document.getElementById("version").textContent = `v${pkg.version}`; }) + .catch(() => {}) + ]); + instance = wasmResult; + + monaco = await new Promise((resolve, reject) => { + require.config({ paths: { vs: "https://cdn.jsdelivr.net/npm/monaco-editor@0.52.2/min/vs" } }); + require(["vs/editor/editor.main"], resolve, reject); + }); +} catch (error) { + loading.textContent = `Failed to load: ${error.message}`; + throw error; +} + +// Example snippets +const EXAMPLES = { + fibonacci: `def fibonacci(n) + case n + when 0 + 0 + when 1 + 1 + else + fibonacci(n - 1) + fibonacci(n - 2) + end +end +`, + pattern: `case [1, [2, 3]] +in [Integer => a, [Integer => b, Integer => c]] + puts "matched: \#{a}, \#{b}, \#{c}" +in [Integer => a, *rest] + puts "first: \#{a}, rest: \#{rest}" +end + +config = { name: "prism", version: "1.0" } + +case config +in { name: /^pr/ => name, version: } + puts "\#{name} v\#{version}" +end +`, + heredoc: `name = "World" + +message = <<~HEREDOC + Hello, \#{name}! + Today is \#{Time.now}. +HEREDOC + +query = <<~SQL.strip + SELECT * + FROM users + WHERE active = true +SQL +`, + blocks: `numbers = [1, 2, 3, 4, 5] + +squares = numbers.map { |n| n ** 2 } +evens = numbers.select(&:even?) + +doubler = ->(x) { x * 2 } + +numbers.each do |n| + puts doubler.call(n) +end + +def with_logging(&block) + puts "start" + result = block.call + puts "end" + result +end +`, + class: `class Person + attr_accessor :name, :age + + def initialize(name, age) + @name = name + @age = age + end + + def greeting = "Hi, I'm \#{name}!" + + def <=>(other) + age <=> other.age + end + + private + + def validate! + raise ArgumentError, "Invalid age" unless age&.positive? + end +end +` +}; + +// URL-safe base64 encode/decode (RFC 4648 §5) +function encodeSource(str) { + const bytes = new TextEncoder().encode(str); + let binary = ""; + for (let i = 0; i < bytes.length; i++) binary += String.fromCharCode(bytes[i]); + return btoa(binary).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, ""); +} + +function decodeSource(str) { + const padded = str.replace(/-/g, "+").replace(/_/g, "/") + "==".slice(0, (4 - str.length % 4) % 4); + return new TextDecoder().decode(Uint8Array.from(atob(padded), ch => ch.codePointAt(0))); +} + +// Read initial source from URL hash or use default +function sourceFromHash() { + const hash = location.hash.slice(1); + if (!hash) return null; + try { return decodeSource(hash); } catch { return null; } +} + +const initialSource = sourceFromHash() || EXAMPLES.fibonacci; + +monaco.editor.defineTheme("prism", { + base: "vs", + inherit: true, + rules: [], + colors: { "editorLineNumber.foreground": "#6B7280" } +}); + +const monacoEditor = monaco.editor.create(document.getElementById("monaco-container"), { + theme: "prism", + value: initialSource, + language: "ruby", + minimap: { enabled: false }, + fontSize: 14, + lineHeight: 21, + scrollBeyondLastLine: false, + automaticLayout: true, + tabSize: 2 +}); + +let currentTab = "ast"; +let lastResult = null; +let lastSource = ""; +let currentDecorations = []; + +// Tab switching +const tabs = Array.from(document.querySelectorAll("[role=tab]")); + +function activateTab(tab) { + tabs.forEach(t => { + t.classList.remove("active"); + t.setAttribute("aria-selected", "false"); + t.setAttribute("tabindex", "-1"); + }); + tab.classList.add("active"); + tab.setAttribute("aria-selected", "true"); + tab.setAttribute("tabindex", "0"); + tab.focus(); + currentTab = tab.dataset.tab; + render(); +} + +document.querySelector(".tabs").addEventListener("click", (event) => { + const tab = event.target.closest("[role=tab]"); + if (tab) activateTab(tab); +}); + +document.querySelector(".tabs").addEventListener("keydown", (event) => { + const tab = event.target.closest("[role=tab]"); + if (!tab) return; + const index = tabs.indexOf(tab); + let next = -1; + if (event.key === "ArrowRight") next = (index + 1) % tabs.length; + else if (event.key === "ArrowLeft") next = (index - 1 + tabs.length) % tabs.length; + else if (event.key === "Home") next = 0; + else if (event.key === "End") next = tabs.length - 1; + if (next >= 0) { + event.preventDefault(); + activateTab(tabs[next]); + } +}); + +// Examples dropdown +document.getElementById("examples").addEventListener("change", (event) => { + const key = event.target.value; + if (key && EXAMPLES[key]) { + monacoEditor.setValue(EXAMPLES[key]); + monacoEditor.focus(); + } +}); + +// Share button +document.getElementById("share").addEventListener("click", async () => { + try { + await navigator.clipboard.writeText(location.href); + showToast("Link copied to clipboard"); + } catch { + showToast("Copy the URL from the address bar"); + } +}); + +let toastTimeout = null; +function showToast(message) { + if (toastTimeout) clearTimeout(toastTimeout); + toast.textContent = message; + toast.classList.add("visible"); + toastTimeout = setTimeout(() => toast.classList.remove("visible"), 2000); +} + +// Shared toggle helper +function setToggleState(toggle, collapsed) { + const treeitem = toggle.closest(".tree-node"); + const children = treeitem.nextElementSibling; + if (!children || !children.classList.contains("tree-children")) return; + children.classList.toggle("collapsed", collapsed); + toggle.textContent = collapsed ? "▶" : "▼"; + treeitem.setAttribute("aria-expanded", String(!collapsed)); +} + +document.getElementById("collapse-all").addEventListener("click", () => { + output.querySelectorAll(".tree-toggle").forEach(toggle => setToggleState(toggle, true)); +}); + +document.getElementById("expand-all").addEventListener("click", () => { + output.querySelectorAll(".tree-toggle").forEach(toggle => setToggleState(toggle, false)); +}); + +// Convert byte offset to line:column using the source string +function offsetToLineCol(source, offset) { + let line = 1, col = 0; + for (let i = 0; i < offset && i < source.length; i++) { + if (source[i] === "\n") { line++; col = 0; } + else { col++; } + } + return { line, col }; +} + + +function formatLoc(source, loc) { + if (!loc || loc.startOffset === undefined) return null; + const start = offsetToLineCol(source, loc.startOffset); + const end = offsetToLineCol(source, loc.startOffset + loc.length); + return { start, end, text: `${start.line}:${start.col}-${end.line}:${end.col}` }; +} + +function locDataAttrs(loc) { + if (!loc) return ""; + return ` data-sl="${loc.start.line}" data-sc="${loc.start.col}" data-el="${loc.end.line}" data-ec="${loc.end.col}"`; +} + +// Highlight a range in Monaco +function highlightRange(startLine, startCol, endLine, endCol) { + currentDecorations = monacoEditor.deltaDecorations(currentDecorations, [{ + range: new monaco.Range(startLine, startCol + 1, endLine, endCol + 1), + options: { + className: "prism-highlight", + isWholeLine: false + } + }]); +} + +function clearHighlight() { + currentDecorations = monacoEditor.deltaDecorations(currentDecorations, []); +} + +// Detect whether a value is a Prism AST node (class instance with location) +function isNode(value) { + return value && typeof value === "object" && !Array.isArray(value) && value.location && value.constructor && value.constructor.name !== "Object"; +} + +// Get the node type name from the class name +function nodeType(node) { + return node.constructor?.name || "Unknown"; +} + +// Get the enumerable field names, skipping internal ones +const SKIP_FIELDS = new Set(["nodeID", "location", "flags"]); +function nodeFields(node) { + const fields = []; + for (const key of Object.getOwnPropertyNames(node)) { + if (!SKIP_FIELDS.has(key) && !key.startsWith("#")) fields.push(key); + } + return fields; +} + +// Decode flags by calling is*() predicate methods on the node's prototype +const flagNamesCache = new WeakMap(); +function flagPredicateNames(proto) { + let names = flagNamesCache.get(proto); + if (!names) { + names = Object.getOwnPropertyNames(proto).filter(n => n.startsWith("is") && typeof proto[n] === "function"); + flagNamesCache.set(proto, names); + } + return names; +} + +function activeFlags(node) { + const proto = Object.getPrototypeOf(node); + if (!proto) return []; + const flags = []; + for (const name of flagPredicateNames(proto)) { + try { if (node[name]()) flags.push(name.slice(2)); } catch (e) {} + } + return flags; +} + +// Check if a node has child nodes (not just scalar fields) +function hasChildNodes(fields, node) { + for (const field of fields) { + const value = node[field]; + if (isNode(value)) return true; + if (Array.isArray(value) && value.some(isNode)) return true; + } + return false; +} + +const CONNECTOR = { last: "└── ", mid: "├── ", lastPad: " ", midPad: "│ " }; + +// Build the AST tree as interactive HTML +function renderNode(node, source, prefix, isLast, isRoot) { + if (!isNode(node)) return ""; + + const type = nodeType(node); + const childPrefix = isRoot ? "" : prefix + (isLast ? CONNECTOR.lastPad : CONNECTOR.midPad); + const fields = nodeFields(node); + const foldable = hasChildNodes(fields, node); + const escapedType = escapeHtml(type); + + let html = `
`; + if (!isRoot) html += ``; + if (foldable) html += ``; + + const loc = formatLoc(source, node.location); + const locAttrs = locDataAttrs(loc); + + html += `@ ${escapedType}`; + if (loc) html += ` (${loc.text})`; + html += `
`; + + html += `
`; + const flags = activeFlags(node); + const hasFields = fields.length > 0; + if (flags.length > 0) { + const flagConnector = hasFields ? CONNECTOR.mid : CONNECTOR.last; + html += `
flags: ${flags.map(f => `${escapeHtml(f)}`).join(" ")}
`; + } + fields.forEach((field, idx) => { + const value = node[field]; + const fieldIsLast = idx === fields.length - 1; + const fieldConnector = fieldIsLast ? CONNECTOR.last : CONNECTOR.mid; + const fieldChildPrefix = childPrefix + (fieldIsLast ? CONNECTOR.lastPad : CONNECTOR.midPad); + + if (value === null || value === undefined) { + html += `
${escapeHtml(field)}:
`; + } else if (Array.isArray(value)) { + if (value.length === 0) { + html += `
${escapeHtml(field)}: []
`; + } else { + html += `
${escapeHtml(field)}: (${value.length} item${value.length === 1 ? "" : "s"})
`; + value.forEach((item, i) => { + if (isNode(item)) { + html += renderNode(item, source, fieldChildPrefix, i === value.length - 1); + } else { + const itemConnector = i === value.length - 1 ? CONNECTOR.last : CONNECTOR.mid; + html += `
${escapeHtml(JSON.stringify(item))}
`; + } + }); + } + } else if (isNode(value)) { + html += `
${escapeHtml(field)}:
`; + html += renderNode(value, source, fieldChildPrefix, true); + } else if (typeof value === "object" && value.startOffset !== undefined) { + const fieldLoc = formatLoc(source, value); + if (fieldLoc) { + html += `
${escapeHtml(field)}: ${fieldLoc.text}
`; + } + } else if (typeof value === "string") { + html += `
${escapeHtml(field)}: ${escapeHtml(JSON.stringify(value))}
`; + } else { + html += `
${escapeHtml(field)}: ${escapeHtml(String(value))}
`; + } + }); + html += `
`; + + return html; +} + +const ESCAPE_MAP = { "&": "&", "<": "<", ">": ">", '"': """ }; +function escapeHtml(str) { + return str.replace(/[&<>"]/g, ch => ESCAPE_MAP[ch]); +} + +// Render a single diagnostic line +function renderDiagnostic(source, item, kind) { + const loc = formatLoc(source, item.location); + const cssClass = kind === "Error" ? "error-text" : "warning-text"; + return `
${kind}: ${escapeHtml(item.message)}${loc ? ` (${loc.text})` : ""}
`; +} + +// Delegated event handlers on #output (attached once, survive re-renders) +output.addEventListener("click", (event) => { + const toggle = event.target.closest(".tree-toggle"); + if (toggle) { + const treeitem = toggle.closest(".tree-node"); + const collapsed = treeitem.getAttribute("aria-expanded") === "true"; + setToggleState(toggle, collapsed); + return; + } + + const locEl = event.target.closest("[data-sl]"); + if (locEl) { + const sl = parseInt(locEl.dataset.sl); + const sc = parseInt(locEl.dataset.sc); + monacoEditor.revealLineInCenter(sl); + monacoEditor.setPosition({ lineNumber: sl, column: sc + 1 }); + monacoEditor.focus(); + } +}); + +output.addEventListener("mouseenter", (event) => { + const locEl = event.target.closest("[data-sl]"); + if (!locEl) return; + highlightRange(parseInt(locEl.dataset.sl), parseInt(locEl.dataset.sc), parseInt(locEl.dataset.el), parseInt(locEl.dataset.ec)); + (locEl.closest(".tree-node") || locEl).classList.add("tree-highlight"); +}, true); + +output.addEventListener("mouseleave", (event) => { + const locEl = event.target.closest("[data-sl]"); + if (!locEl) return; + clearHighlight(); + (locEl.closest(".tree-node") || locEl).classList.remove("tree-highlight"); +}, true); + + +function render() { + if (!lastResult) return; + + output.setAttribute("aria-labelledby", currentTab === "ast" ? "tab-ast" : "tab-diagnostics"); + + switch (currentTab) { + case "ast": + const tree = renderNode(lastResult.value, lastSource, "", true, true); + output.innerHTML = tree + ? `
${tree}
` + : `
${escapeHtml(lastResult.error || "Failed to parse.")}
`; + break; + + case "diagnostics": + const errors = lastResult.errors || []; + const warnings = lastResult.warnings || []; + if (errors.length === 0 && warnings.length === 0) { + output.innerHTML = `
No errors or warnings.
`; + } else { + let html = ""; + for (const err of errors) html += renderDiagnostic(lastSource, err, "Error"); + for (const warn of warnings) html += renderDiagnostic(lastSource, warn, "Warning"); + output.innerHTML = html; + } + break; + } +} + +let timeout = null; +function parse() { + if (timeout) clearTimeout(timeout); + timeout = setTimeout(() => { + lastSource = monacoEditor.getValue(); + history.replaceState(null, "", `#${encodeSource(lastSource)}`); + try { + lastResult = parsePrism(instance.exports, lastSource); + } catch (e) { + lastResult = { value: null, error: e.message, errors: [], warnings: [] }; + } + render(); + }, 200); +} + +monacoEditor.onDidChangeModelContent(parse); + +// Ready +loading.classList.add("hidden"); +editorDiv.classList.add("ready"); +parse(); diff --git a/doc/robots.txt b/doc/robots.txt new file mode 100644 index 0000000000..c2a49f4fb8 --- /dev/null +++ b/doc/robots.txt @@ -0,0 +1,2 @@ +User-agent: * +Allow: / diff --git a/docs/build_system.md b/docs/build_system.md index 3595c69741..40db4a17fc 100644 --- a/docs/build_system.md +++ b/docs/build_system.md @@ -87,7 +87,7 @@ If you need to use memory allocation functions implemented outside of the standa * 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`. +For further clarity, refer to `include/prism/internal/allocator.h`. ### Building prism from source as a C library @@ -113,7 +113,6 @@ MAKEFLAGS="-j10" bundle exec rake compile * `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/docs/configuration.md b/docs/configuration.md index 04887d6d9c..30001b7db7 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -5,11 +5,12 @@ A lot of code in prism's repository is templated from a single configuration fil * `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 +* `include/prism/node_new.h` - for defining the functions that create the nodes in C * `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 +* `java/org/ruby_lang/prism/AbstractNodeVisitor.java` - for defining the visitor interface for the nodes in Java +* `java/org/ruby_lang/prism/Loader.java` - for defining how to deserialize the nodes in Java +* `java/org/ruby_lang/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 diff --git a/docs/encoding.md b/docs/encoding.md index a9090a9880..8f2fd18d0a 100644 --- a/docs/encoding.md +++ b/docs/encoding.md @@ -107,7 +107,7 @@ For each of these encodings, prism provides functions for checking if the subseq ## 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`. +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_encoding_changed_callback_set`. 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, @@ -117,5 +117,5 @@ 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); +pm_parser_encoding_changed_callback_set(pm_parser_t *parser, pm_encoding_changed_callback_t callback); ``` diff --git a/docs/fuzzing.md b/docs/fuzzing.md index b6ec6112a8..c142394b63 100644 --- a/docs/fuzzing.md +++ b/docs/fuzzing.md @@ -5,8 +5,7 @@ We use fuzzing to test the various entrypoints to the library. The fuzzer we use ``` fuzz ├── corpus -│   ├── parse fuzzing corpus for parsing (a symlink to our fixtures) -│   └── regexp fuzzing corpus for regexp +│   └── parse fuzzing corpus for parsing (a symlink to our fixtures) ├── dict a AFL++ dictionary containing various tokens ├── docker │   └── Dockerfile for building a container with the fuzzer toolchain @@ -14,8 +13,6 @@ fuzz ├── 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 @@ -23,16 +20,14 @@ fuzz ## Usage -There are currently three fuzzing targets +There is currently one fuzz target: - `pm_serialize_parse` (parse) -- `pm_regexp_parse` (regexp) -Respectively, fuzzing can be performed with +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 @@ -43,8 +38,6 @@ 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. ``` @@ -55,7 +48,7 @@ Note, that this may make reproducing bugs difficult as they may depend on memory ``` make fuzz-debug # enter the docker container with build tools -make build/fuzz.heisenbug.parse # or .regexp +make build/fuzz.heisenbug.parse ./build/fuzz.heisenbug.parse path-to-problem-input ``` diff --git a/docs/releasing.md b/docs/releasing.md index a7d3cf5eb3..6d0f892926 100644 --- a/docs/releasing.md +++ b/docs/releasing.md @@ -23,9 +23,9 @@ ruby -pi -e 'gsub(/PRISM_VERSION_PATCH \d+/, %Q{PRISM_VERSION_PATCH #{ENV["PRISM 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(/\d+, "prism major/, %Q{#{ENV["PRISM_MAJOR"]}, "prism major})' templates/java/org/ruby_lang/prism/Loader.java.erb +ruby -pi -e 'gsub(/\d+, "prism minor/, %Q{#{ENV["PRISM_MINOR"]}, "prism minor})' templates/java/org/ruby_lang/prism/Loader.java.erb +ruby -pi -e 'gsub(/\d+, "prism patch/, %Q{#{ENV["PRISM_PATCH"]}, "prism patch})' templates/java/org/ruby_lang/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 diff --git a/docs/serialization.md b/docs/serialization.md index ec395f8847..d087698baa 100644 --- a/docs/serialization.md +++ b/docs/serialization.md @@ -106,6 +106,7 @@ The header is structured like the following table: | error* | errors | | varuint | number of warnings | | warning* | warnings | +| `1` | `1` if the source is continuable (incomplete but could become valid with more input), `0` otherwise | | `4` | content pool offset | | varuint | content pool size | @@ -126,7 +127,7 @@ Every field on the node is then appended to the serialized string. The fields ca * `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). +* `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 bytes (_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. @@ -135,23 +136,14 @@ Every field on the node is then appended to the serialized string. The fields ca * `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: +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. Every constant is embedded in the serialization. Each 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. +After the constant pool, the contents of the 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 @@ -167,8 +159,8 @@ typedef struct { size_t capacity; } pm_buffer_t; -// Free the memory associated with the buffer. -void pm_buffer_free(pm_buffer_t *); +// Free the memory held by the buffer. +void pm_buffer_cleanup(pm_buffer_t *); // Parse and serialize the AST represented by the given source to the given // buffer. @@ -180,12 +172,12 @@ Typically you would use a stack-allocated `pm_buffer_t` and call `pm_serialize_p ```c void serialize(const uint8_t *source, size_t length) { - pm_buffer_t buffer = { 0 }; - pm_serialize_parse(&buffer, source, length, NULL); + pm_buffer_t *buffer = pm_buffer_new(); + pm_serialize_parse(buffer, source, length, NULL); // Do something with the serialized string. - pm_buffer_free(&buffer); + pm_buffer_free(buffer); } ``` diff --git a/docs/testing.md b/docs/testing.md index a42ee2e270..ade07bfbb5 100644 --- a/docs/testing.md +++ b/docs/testing.md @@ -14,42 +14,59 @@ These test specific prism implementation details like comments, errors, and regu 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. +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. If the snapshot already exists but differs you can run with `UPDATE_SNAPSHOTS=1` to regenerate them. -### Testing against repositories +When working on a specific example, you can limit tests to a single fixture via `FOCUS=strings.txt`. -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. +#### Versioning -## 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 -``` +Prism is capable of parsing code with different version specifiers. The test suite reflects this: +* Test files under `test/prism/snapshots/*.txt` are validated against all supported versions. +* Test files under `test/prism/snapshots/[MAJOR].[MINOR]/*.txt` are validated against all versions starting from the given version. +* Test files under `test/prism/snapshots/[MAJOR].[MINOR]-[MAJOR].[MINOR]/*.txt` are validated against the given version range. -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 -``` +Should you add logic specific to a new ruby version, validate the new behavior but also that old behavior remains the same. -`bin/lex` can also be used with `-e` and then source code, like this: +### Error tests -``` -$ bin/lex -e "1 + 2" -``` +Error tests are similar to snapshot tests and expectations are located under `test/prism/errors/`. +They contain invalid syntax and rendered errors, similar to how `ruby -c` would show them. +Error tests are versioned in the same way that snapshot tests are and also support `FOCUS`/`UPDATE_SNAPSHOTS`. -2. **bin/parse** takes a filepath and outputs prism's parsed node structure generated from reading the file. +## 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. **bin/prism** contains a multitude of options to aid you, depending on which area +of prism you are working on. `bin/prism --help` prints a list of all available commands. + +Commands that take a `[source]` can be called in three different ways: +1. `bin/prism parse` will use `test.rb` as input. +1. `bin/prism parse foo.rb` will read from the given path. +1. `bin/prism parse -e "1 + 2"` will use the given source code. + +`bin/prism parse` is aliased as `bin/parse` for for convenience. + +### Validating your changes against real code + +If you create a change which is not expected to have a large impact on real code, you can use +`bin/compare` to validate that assumption. It compiles two versions of prism (for example +your feature branch and main), comparing ast, errors and warnings. Once done, a report is +printed showcasing in what way files changed, if any (syntax valid -> syntax invalid, changed AST, etc.) + +```sh +$ bin/compare main feature-branch ~/all-of-rubygems` +... +0/3157385 +1000/3157385 +... +3157000/3157385 +Oops: +intermine-1.05.00/lib/intermine/lists.rb changed from valid(true) to valid(false) +whistle-0.1.1/lib/resource.rb changed from valid(true) to valid(false) +Took 2661.5390081949999512 seconds ``` -$ bin/parse test.rb -``` - -`bin/parse` can also be used with `-e` and then source code, like this: -``` -$ bin/parse -e "1 + 2" -``` +It is up to you to aquire code samples. Here are some resources you can use for this purpose: +* https://github.com/jeromedalbert/real-world-ruby-apps +* https://github.com/eliotsykes/real-world-rails +* https://github.com/rubygems/rubygems-mirror + Requires some work to unpack the downloaded gems. You should also run with `RUBYGEMS_MIRROR_ONLY_LATEST=TRUE`. diff --git a/ext/prism/api_pack.c b/ext/prism/api_pack.c deleted file mode 100644 index 98509ae65c..0000000000 --- a/ext/prism/api_pack.c +++ /dev/null @@ -1,276 +0,0 @@ -#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/ext/prism/extconf.rb b/ext/prism/extconf.rb index ae1691c979..9283d62b47 100644 --- a/ext/prism/extconf.rb +++ b/ext/prism/extconf.rb @@ -118,9 +118,8 @@ def add_libprism_source(path) src_list path end -$srcs = src_list("$(srcdir)") + - add_libprism_source("$(srcdir)/../../src") + - add_libprism_source("$(srcdir)/../../src/util") +$srcs = src_list("$(srcdir)") + add_libprism_source("$(srcdir)/../../src") +$headers += Dir["#{$srcdir}/../../include/**/*.h"] # Finally, we'll create the `Makefile` that is going to be used to configure and # build the C extension. diff --git a/ext/prism/extension.c b/ext/prism/extension.c index 363144970c..9f9169cfff 100644 --- a/ext/prism/extension.c +++ b/ext/prism/extension.c @@ -4,6 +4,8 @@ #include #endif +#include + // NOTE: this file should contain only bindings. All non-trivial logic should be // in libprism so it can be shared its the various callers. @@ -64,18 +66,6 @@ check_string(VALUE value) { 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 */ @@ -148,10 +138,8 @@ build_options_scopes(pm_options_t *options, VALUE scopes) { // 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"); - } + pm_options_scope_t *options_scope = pm_options_scope_mut(options, scope_index); + pm_options_scope_init(options_scope, locals_count); // Iterate over the locals and add them to the scope. for (size_t local_index = 0; local_index < locals_count; local_index++) { @@ -164,7 +152,7 @@ build_options_scopes(pm_options_t *options, VALUE scopes) { } // Add the local to the scope. - pm_string_t *scope_local = &options_scope->locals[local_index]; + pm_string_t *scope_local = pm_options_scope_local_mut(options_scope, local_index); const char *name = rb_id2name(SYM2ID(local)); pm_string_constant_init(scope_local, name, strlen(name)); } @@ -205,18 +193,14 @@ build_options_i(VALUE key, VALUE value, VALUE argument) { rb_exc_raise(rb_exc_new_cstr(rb_cPrismCurrentVersionError, ruby_version)); } } else if (RSTRING_LEN(value) == 7 && strncmp(version, "nearest", 7) == 0) { - const char *nearest_version; - - if (ruby_version[0] < '3' || (ruby_version[0] == '3' && ruby_version[2] < '3')) { - nearest_version = "3.3"; - } else if (ruby_version[0] > '4' || (ruby_version[0] == '4' && ruby_version[2] > '1')) { - nearest_version = "4.1"; - } else { - nearest_version = ruby_version; - } - - if (!pm_options_version_set(options, nearest_version, 3)) { - rb_raise(rb_eArgError, "invalid nearest version: %s", nearest_version); + if (!pm_options_version_set(options, ruby_version, 3)) { + // Prism doesn't know this specific version. Is it lower? + if (ruby_version[0] < '3' || (ruby_version[0] == '3' && ruby_version[2] < '3')) { + pm_options_version_set_lowest(options); + } else { + // Must be higher. + pm_options_version_set_highest(options); + } } } else if (!pm_options_version_set(options, version, RSTRING_LEN(value))) { rb_raise(rb_eArgError, "invalid version: %" PRIsVALUE, value); @@ -282,7 +266,7 @@ build_options(VALUE argument) { */ static void extract_options(pm_options_t *options, VALUE filepath, VALUE keywords) { - options->line = 1; // default + pm_options_line_set(options, 1); /* default */ if (!NIL_P(keywords)) { struct build_options_data data = { .options = options, .keywords = keywords }; @@ -310,36 +294,46 @@ extract_options(pm_options_t *options, VALUE filepath, VALUE keywords) { /** * 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) { +static VALUE +string_options(int argc, VALUE *argv, pm_options_t *options) { VALUE string; VALUE keywords; rb_scan_args(argc, argv, "1:", &string, &keywords); + if (!RB_TYPE_P(string, T_STRING)) { + pm_options_free(options); + rb_raise(rb_eTypeError, "wrong argument type %"PRIsVALUE" (expected String)", rb_obj_class(string)); + } + extract_options(options, Qnil, keywords); - input_load_string(input, string); + return 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) { +static pm_source_t * +file_options(int argc, VALUE *argv, pm_options_t *options, VALUE *encoded_filepath) { VALUE filepath; VALUE keywords; rb_scan_args(argc, argv, "1:", &filepath, &keywords); - Check_Type(filepath, T_STRING); + 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)); + } + *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; + const char *source = (const char *) pm_string_source(pm_options_filepath(options)); + pm_source_init_result_t result; + pm_source_t *pm_src = pm_source_file_new(source, &result); - switch (result = pm_string_file_init(input, source)) { - case PM_STRING_INIT_SUCCESS: + switch (result) { + case PM_SOURCE_INIT_SUCCESS: break; - case PM_STRING_INIT_ERROR_GENERIC: { + case PM_SOURCE_INIT_ERROR_GENERIC: { pm_options_free(options); #ifdef _WIN32 @@ -351,7 +345,7 @@ file_options(int argc, VALUE *argv, pm_string_t *input, pm_options_t *options, V rb_syserr_fail(e, source); break; } - case PM_STRING_INIT_ERROR_DIRECTORY: + case PM_SOURCE_INIT_ERROR_DIRECTORY: pm_options_free(options); rb_syserr_fail(EISDIR, source); break; @@ -360,6 +354,8 @@ file_options(int argc, VALUE *argv, pm_string_t *input, pm_options_t *options, V rb_raise(rb_eRuntimeError, "Unknown error (%d) initializing file: %s", result, source); break; } + + return pm_src; } #ifndef PRISM_EXCLUDE_SERIALIZATION @@ -372,77 +368,82 @@ file_options(int argc, VALUE *argv, pm_string_t *input, pm_options_t *options, V * 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)) { +dump_input(const uint8_t *input, size_t input_length, const pm_options_t *options) { + pm_buffer_t *buffer = pm_buffer_new(); + if (!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_arena_t *arena = pm_arena_new(); + pm_parser_t *parser = pm_parser_new(arena, input, input_length, options); - pm_node_t *node = pm_parse(&parser); - pm_serialize(&parser, node, &buffer); + 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); + VALUE result = rb_str_new(pm_buffer_value(buffer), pm_buffer_length(buffer)); + pm_buffer_free(buffer); + pm_parser_free(parser); + pm_arena_free(arena); return result; } /** + * :markup: markdown * call-seq: - * Prism::dump(source, **options) -> String + * dump(source, **options) -> String * * Dump the AST corresponding to the given string to a string. For supported - * options, see Prism::parse. + * 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); + pm_options_t *options = pm_options_new(); + VALUE string = string_options(argc, argv, options); + + const uint8_t *source = (const uint8_t *) RSTRING_PTR(string); + size_t length = RSTRING_LEN(string); #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); + memcpy(dup, source, length); + source = (const uint8_t *) dup; #endif - VALUE value = dump_input(&input, &options); - if (options.freeze) rb_obj_freeze(value); + VALUE value = dump_input(source, length, options); + if (pm_options_freeze(options)) rb_obj_freeze(value); #ifdef PRISM_BUILD_DEBUG +#ifdef xfree_sized + xfree_sized(dup, length); +#else xfree(dup); +#endif #endif - pm_string_free(&input); - pm_options_free(&options); + pm_options_free(options); return value; } /** + * :markup: markdown * call-seq: - * Prism::dump_file(filepath, **options) -> String + * dump_file(filepath, **options) -> String * * Dump the AST corresponding to the given file to a string. For supported - * options, see Prism::parse. + * options, see Prism.parse. */ static VALUE dump_file(int argc, VALUE *argv, VALUE self) { - pm_string_t input; - pm_options_t options = { 0 }; + pm_options_t *options = pm_options_new(); VALUE encoded_filepath; - file_options(argc, argv, &input, &options, &encoded_filepath); + pm_source_t *src = file_options(argc, argv, options, &encoded_filepath); - VALUE value = dump_input(&input, &options); - pm_string_free(&input); - pm_options_free(&options); + VALUE value = dump_input(pm_source_source(src), pm_source_length(src), options); + pm_source_free(src); + pm_options_free(options); return value; } @@ -484,26 +485,33 @@ parser_location(VALUE source, bool freeze, uint32_t start, uint32_t length) { */ static inline VALUE parser_comment(VALUE source, bool freeze, const pm_comment_t *comment) { - VALUE argv[] = { PARSER_LOCATION(source, freeze, comment->location) }; - VALUE type = (comment->type == PM_COMMENT_EMBDOC) ? rb_cPrismEmbDocComment : rb_cPrismInlineComment; + VALUE argv[] = { PARSER_LOCATION(source, freeze, pm_comment_location(comment)) }; + VALUE type = (pm_comment_type(comment) == PM_COMMENT_EMBDOC) ? rb_cPrismEmbDocComment : rb_cPrismInlineComment; return rb_class_new_instance_freeze(1, argv, type, freeze); } +typedef struct { + VALUE comments; + VALUE source; + bool freeze; +} parser_comments_each_data_t; + +static void +parser_comments_each(const pm_comment_t *comment, void *data) { + parser_comments_each_data_t *each_data = (parser_comments_each_data_t *) data; + VALUE value = parser_comment(each_data->source, each_data->freeze, comment); + rb_ary_push(each_data->comments, value); +} + /** * 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(source, freeze, comment); - rb_ary_push(comments, value); - } + VALUE comments = rb_ary_new_capa(pm_parser_comments_size(parser)); + + parser_comments_each_data_t each_data = { comments, source, freeze }; + pm_parser_comments_each(parser, parser_comments_each, &each_data); if (freeze) rb_obj_freeze(comments); return comments; @@ -514,27 +522,38 @@ parser_comments(const pm_parser_t *parser, VALUE source, bool freeze) { */ static inline VALUE parser_magic_comment(VALUE source, bool freeze, const pm_magic_comment_t *magic_comment) { - VALUE key_loc = parser_location(source, freeze, magic_comment->key.start, magic_comment->key.length); - VALUE value_loc = parser_location(source, freeze, magic_comment->value.start, magic_comment->value.length); + pm_location_t key = pm_magic_comment_key(magic_comment); + pm_location_t value = pm_magic_comment_value(magic_comment); + + VALUE key_loc = parser_location(source, freeze, key.start, key.length); + VALUE value_loc = parser_location(source, freeze, value.start, value.length); + VALUE argv[] = { key_loc, value_loc }; return rb_class_new_instance_freeze(2, argv, rb_cPrismMagicComment, freeze); } +typedef struct { + VALUE magic_comments; + VALUE source; + bool freeze; +} parser_magic_comments_each_data_t; + +static void +parser_magic_comments_each(const pm_magic_comment_t *magic_comment, void *data) { + parser_magic_comments_each_data_t *each_data = (parser_magic_comments_each_data_t *) data; + VALUE value = parser_magic_comment(each_data->source, each_data->freeze, magic_comment); + rb_ary_push(each_data->magic_comments, value); +} + /** * 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(source, freeze, magic_comment); - rb_ary_push(magic_comments, value); - } + VALUE magic_comments = rb_ary_new_capa(pm_parser_magic_comments_size(parser)); + + parser_magic_comments_each_data_t each_data = { magic_comments, source, freeze }; + pm_parser_magic_comments_each(parser, parser_magic_comments_each, &each_data); if (freeze) rb_obj_freeze(magic_comments); return magic_comments; @@ -546,85 +565,109 @@ parser_magic_comments(const pm_parser_t *parser, VALUE source, bool freeze) { */ static VALUE parser_data_loc(const pm_parser_t *parser, VALUE source, bool freeze) { - if (parser->data_loc.length == 0) { + const pm_location_t *data_loc = pm_parser_data_loc(parser); + + if (data_loc->length == 0) { return Qnil; } else { - return parser_location(source, freeze, parser->data_loc.start, parser->data_loc.length); + return parser_location(source, freeze, data_loc->start, data_loc->length); } } +typedef struct { + VALUE errors; + rb_encoding *encoding; + VALUE source; + bool freeze; +} parser_errors_each_data_t; + +static void +parser_errors_each(const pm_diagnostic_t *diagnostic, void *data) { + parser_errors_each_data_t *each_data = (parser_errors_each_data_t *) data; + + VALUE type = ID2SYM(rb_intern(pm_diagnostic_type(diagnostic))); + VALUE message = rb_obj_freeze(rb_enc_str_new_cstr(pm_diagnostic_message(diagnostic), each_data->encoding)); + VALUE location = PARSER_LOCATION(each_data->source, each_data->freeze, pm_diagnostic_location(diagnostic)); + + pm_error_level_t error_level = pm_diagnostic_error_level(diagnostic); + 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, each_data->freeze); + rb_ary_push(each_data->errors, value); +} + /** * 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(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 errors = rb_ary_new_capa(pm_parser_errors_size(parser)); - VALUE argv[] = { type, message, location, level }; - VALUE value = rb_class_new_instance_freeze(4, argv, rb_cPrismParseError, freeze); - rb_ary_push(errors, value); - } + parser_errors_each_data_t each_data = { errors, encoding, source, freeze }; + pm_parser_errors_each(parser, parser_errors_each, &each_data); if (freeze) rb_obj_freeze(errors); return errors; } +typedef struct { + VALUE warnings; + rb_encoding *encoding; + VALUE source; + bool freeze; +} parser_warnings_each_data_t; + +static void +parser_warnings_each(const pm_diagnostic_t *diagnostic, void *data) { + parser_warnings_each_data_t *each_data = (parser_warnings_each_data_t *) data; + + VALUE type = ID2SYM(rb_intern(pm_diagnostic_type(diagnostic))); + VALUE message = rb_obj_freeze(rb_enc_str_new_cstr(pm_diagnostic_message(diagnostic), each_data->encoding)); + VALUE location = PARSER_LOCATION(each_data->source, each_data->freeze, pm_diagnostic_location(diagnostic)); + + pm_warning_level_t warning_level = pm_diagnostic_warning_level(diagnostic); + 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, each_data->freeze); + rb_ary_push(each_data->warnings, value); +} + /** * 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(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 warnings = rb_ary_new_capa(pm_parser_warnings_size(parser)); - VALUE argv[] = { type, message, location, level }; - VALUE value = rb_class_new_instance_freeze(4, argv, rb_cPrismParseWarning, freeze); - rb_ary_push(warnings, value); - } + parser_warnings_each_data_t each_data = { warnings, encoding, source, freeze }; + pm_parser_warnings_each(parser, parser_warnings_each, &each_data); if (freeze) rb_obj_freeze(warnings); return warnings; @@ -642,10 +685,11 @@ parse_result_create(VALUE class, const pm_parser_t *parser, VALUE value, rb_enco parser_data_loc(parser, source, freeze), parser_errors(parser, encoding, source, freeze), parser_warnings(parser, encoding, source, freeze), + pm_parser_continuable(parser) ? Qtrue : Qfalse, source }; - return rb_class_new_instance_freeze(7, result_argv, class, freeze); + return rb_class_new_instance_freeze(8, result_argv, class, freeze); } /******************************************************************************/ @@ -670,11 +714,11 @@ typedef struct { * 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; +parse_lex_token(pm_parser_t *parser, pm_token_t *token, void *data) { + parse_lex_data_t *parse_lex_data = (parse_lex_data_t *) 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)); + VALUE yields = rb_assoc_new(value, INT2FIX(pm_parser_lex_state(parser))); if (parse_lex_data->freeze) { rb_obj_freeze(value); @@ -691,8 +735,8 @@ parse_lex_token(void *data, pm_parser_t *parser, pm_token_t *token) { */ 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); + parse_lex_data_t *parse_lex_data = (parse_lex_data_t *) pm_parser_lex_callback_data(parser); + parse_lex_data->encoding = rb_enc_find(pm_parser_encoding_name(parser)); // 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 @@ -737,43 +781,38 @@ parse_lex_encoding_changed_callback(pm_parser_t *parser) { * 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); +parse_lex_input(const uint8_t *input, size_t input_length, const pm_options_t *options, bool return_nodes) { + pm_arena_t *arena = pm_arena_new(); + pm_parser_t *parser = pm_parser_new(arena, input, input_length, options); + pm_parser_encoding_changed_callback_set(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); + VALUE source_string = rb_str_new((const char *) input, input_length); + VALUE offsets = rb_ary_new_capa(pm_parser_line_offsets(parser)->size); + VALUE source = rb_funcall(rb_cPrismSource, rb_id_source_for, 3, source_string, LONG2NUM(pm_parser_start_line(parser)), offsets); parse_lex_data_t parse_lex_data = { .source = source, .tokens = rb_ary_new(), .encoding = rb_utf8_encoding(), - .freeze = options->freeze, + .freeze = pm_options_freeze(options), }; 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, - }; + pm_parser_lex_callback_set(parser, parse_lex_token, data); - parser.lex_callback = &lex_callback; - pm_node_t *node = pm_parse(&parser); + 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); + /* Update the Source object with the correct encoding and line offsets, + * which are only available after pm_parse() completes. */ + rb_encoding *encoding = rb_enc_find(pm_parser_encoding_name(parser)); 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])); + const pm_line_offset_list_t *line_offsets = pm_parser_line_offsets(parser); + for (size_t index = 0; index < line_offsets->size; index++) { + rb_ary_store(offsets, (long) index, ULONG2NUM(line_offsets->offsets[index])); } - if (options->freeze) { + if (pm_options_freeze(options)) { rb_obj_freeze(source_string); rb_obj_freeze(offsets); rb_obj_freeze(source); @@ -783,58 +822,57 @@ parse_lex_input(pm_string_t *input, const pm_options_t *options, bool return_nod 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, pm_ast_new(parser, node, parse_lex_data.encoding, source, pm_options_freeze(options))); 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); + if (pm_options_freeze(options)) rb_obj_freeze(value); + result = parse_result_create(rb_cPrismParseLexResult, parser, value, parse_lex_data.encoding, source, pm_options_freeze(options)); } else { - result = parse_result_create(rb_cPrismLexResult, &parser, parse_lex_data.tokens, parse_lex_data.encoding, source, options->freeze); + result = parse_result_create(rb_cPrismLexResult, parser, parse_lex_data.tokens, parse_lex_data.encoding, source, pm_options_freeze(options)); } - pm_node_destroy(&parser, node); - pm_parser_free(&parser); + pm_parser_free(parser); + pm_arena_free(arena); return result; } /** + * :markup: markdown * call-seq: - * Prism::lex(source, **options) -> LexResult + * 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. + * 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); + pm_options_t *options = pm_options_new(); + VALUE string = string_options(argc, argv, options); - VALUE result = parse_lex_input(&input, &options, false); - pm_string_free(&input); - pm_options_free(&options); + VALUE result = parse_lex_input((const uint8_t *) RSTRING_PTR(string), RSTRING_LEN(string), options, false); + pm_options_free(options); return result; } /** + * :markup: markdown * call-seq: - * Prism::lex_file(filepath, **options) -> LexResult + * 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. + * 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 }; + pm_options_t *options = pm_options_new(); VALUE encoded_filepath; - file_options(argc, argv, &input, &options, &encoded_filepath); + pm_source_t *src = file_options(argc, argv, options, &encoded_filepath); - VALUE value = parse_lex_input(&input, &options, false); - pm_string_free(&input); - pm_options_free(&options); + VALUE value = parse_lex_input(pm_source_source(src), pm_source_length(src), options, false); + pm_source_free(src); + pm_options_free(options); return value; } @@ -847,30 +885,32 @@ lex_file(int argc, VALUE *argv, VALUE self) { * 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); +parse_input(const uint8_t *input, size_t input_length, const pm_options_t *options) { + pm_arena_t *arena = pm_arena_new(); + pm_parser_t *parser = pm_parser_new(arena, input, input_length, options); - pm_node_t *node = pm_parse(&parser); - rb_encoding *encoding = rb_enc_find(parser.encoding->name); + pm_node_t *node = pm_parse(parser); + rb_encoding *encoding = rb_enc_find(pm_parser_encoding_name(parser)); - 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); + bool freeze = pm_options_freeze(options); + VALUE source = pm_source_new(parser, encoding, freeze); + VALUE value = pm_ast_new(parser, node, encoding, source, freeze); + VALUE result = parse_result_create(rb_cPrismParseResult, parser, value, encoding, source, freeze); - if (options->freeze) { + if (freeze) { rb_obj_freeze(source); } - pm_node_destroy(&parser, node); - pm_parser_free(&parser); + pm_parser_free(parser); + pm_arena_free(arena); return result; } /** + * :markup: markdown * call-seq: - * Prism::parse(source, **options) -> ParseResult + * parse(source, **options) -> ParseResult * * Parse the given string and return a ParseResult instance. The options that * are supported are: @@ -914,46 +954,50 @@ parse_input(pm_string_t *input, const pm_options_t *options) { */ static VALUE parse(int argc, VALUE *argv, VALUE self) { - pm_string_t input; - pm_options_t options = { 0 }; - string_options(argc, argv, &input, &options); + pm_options_t *options = pm_options_new(); + VALUE string = string_options(argc, argv, options); + + const uint8_t *source = (const uint8_t *) RSTRING_PTR(string); + size_t length = RSTRING_LEN(string); #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); + memcpy(dup, source, length); + source = (const uint8_t *) dup; #endif - VALUE value = parse_input(&input, &options); + VALUE value = parse_input(source, length, options); #ifdef PRISM_BUILD_DEBUG +#ifdef xfree_sized + xfree_sized(dup, length); +#else xfree(dup); +#endif #endif - pm_string_free(&input); - pm_options_free(&options); + pm_options_free(options); return value; } /** + * :markup: markdown * call-seq: - * Prism::parse_file(filepath, **options) -> ParseResult + * parse_file(filepath, **options) -> ParseResult * * Parse the given file and return a ParseResult instance. For supported - * options, see Prism::parse. + * options, see Prism.parse. */ static VALUE parse_file(int argc, VALUE *argv, VALUE self) { - pm_string_t input; - pm_options_t options = { 0 }; + pm_options_t *options = pm_options_new(); VALUE encoded_filepath; - file_options(argc, argv, &input, &options, &encoded_filepath); + pm_source_t *src = file_options(argc, argv, options, &encoded_filepath); - VALUE value = parse_input(&input, &options); - pm_string_free(&input); - pm_options_free(&options); + VALUE value = parse_input(pm_source_source(src), pm_source_length(src), options); + pm_source_free(src); + pm_options_free(options); return value; } @@ -962,55 +1006,54 @@ parse_file(int argc, VALUE *argv, VALUE self) { * 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); +profile_input(const uint8_t *input, size_t input_length, const pm_options_t *options) { + pm_arena_t *arena = pm_arena_new(); + pm_parser_t *parser = pm_parser_new(arena, input, input_length, options); - pm_node_t *node = pm_parse(&parser); - pm_node_destroy(&parser, node); - pm_parser_free(&parser); + pm_parse(parser); + pm_parser_free(parser); + pm_arena_free(arena); } /** + * :markup: markdown * call-seq: - * Prism::profile(source, **options) -> nil + * 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. + * options, see Prism.parse. */ static VALUE profile(int argc, VALUE *argv, VALUE self) { - pm_string_t input; - pm_options_t options = { 0 }; + pm_options_t *options = pm_options_new(); + VALUE string = string_options(argc, argv, options); - string_options(argc, argv, &input, &options); - profile_input(&input, &options); - pm_string_free(&input); - pm_options_free(&options); + profile_input((const uint8_t *) RSTRING_PTR(string), RSTRING_LEN(string), options); + pm_options_free(options); return Qnil; } /** + * :markup: markdown * call-seq: - * Prism::profile_file(filepath, **options) -> nil + * 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. + * options, see Prism.parse. */ static VALUE profile_file(int argc, VALUE *argv, VALUE self) { - pm_string_t input; - pm_options_t options = { 0 }; + pm_options_t *options = pm_options_new(); VALUE encoded_filepath; - file_options(argc, argv, &input, &options, &encoded_filepath); + pm_source_t *src = file_options(argc, argv, options, &encoded_filepath); - profile_input(&input, &options); - pm_string_free(&input); - pm_options_free(&options); + profile_input(pm_source_source(src), pm_source_length(src), options); + pm_source_free(src); + pm_options_free(options); return Qnil; } @@ -1045,11 +1088,12 @@ parse_stream_fgets(char *string, int size, void *stream) { } /** + * :markup: markdown * call-seq: - * Prism::parse_stream(stream, **options) -> ParseResult + * 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. + * instance. The options that are supported are the same as Prism.parse. */ static VALUE parse_stream(int argc, VALUE *argv, VALUE self) { @@ -1057,22 +1101,24 @@ parse_stream(int argc, VALUE *argv, VALUE self) { VALUE keywords; rb_scan_args(argc, argv, "1:", &stream, &keywords); - pm_options_t options = { 0 }; - extract_options(&options, Qnil, keywords); + pm_options_t *options = pm_options_new(); + extract_options(options, Qnil, keywords); - pm_parser_t parser; - pm_buffer_t buffer; + pm_source_t *src = pm_source_stream_new((void *) stream, parse_stream_fgets, parse_stream_eof); + pm_arena_t *arena = pm_arena_new(); + pm_parser_t *parser; - 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); + pm_node_t *node = pm_parse_stream(&parser, arena, src, options); + rb_encoding *encoding = rb_enc_find(pm_parser_encoding_name(parser)); - 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); + VALUE source = pm_source_new(parser, encoding, pm_options_freeze(options)); + VALUE value = pm_ast_new(parser, node, encoding, source, pm_options_freeze(options)); + VALUE result = parse_result_create(rb_cPrismParseResult, parser, value, encoding, source, pm_options_freeze(options)); - pm_node_destroy(&parser, node); - pm_buffer_free(&buffer); - pm_parser_free(&parser); + pm_source_free(src); + pm_parser_free(parser); + pm_arena_free(arena); + pm_options_free(options); return result; } @@ -1081,116 +1127,114 @@ parse_stream(int argc, VALUE *argv, VALUE self) { * 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); +parse_input_comments(const uint8_t *input, size_t input_length, const pm_options_t *options) { + pm_arena_t *arena = pm_arena_new(); + pm_parser_t *parser = pm_parser_new(arena, input, input_length, options); - pm_node_t *node = pm_parse(&parser); - rb_encoding *encoding = rb_enc_find(parser.encoding->name); + pm_parse(parser); + rb_encoding *encoding = rb_enc_find(pm_parser_encoding_name(parser)); - VALUE source = pm_source_new(&parser, encoding, options->freeze); - VALUE comments = parser_comments(&parser, source, options->freeze); + VALUE source = pm_source_new(parser, encoding, pm_options_freeze(options)); + VALUE comments = parser_comments(parser, source, pm_options_freeze(options)); - pm_node_destroy(&parser, node); - pm_parser_free(&parser); + pm_parser_free(parser); + pm_arena_free(arena); return comments; } /** + * :markup: markdown * call-seq: - * Prism::parse_comments(source, **options) -> Array + * parse_comments(source, **options) -> Array * * Parse the given string and return an array of Comment objects. For supported - * options, see Prism::parse. + * 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); + pm_options_t *options = pm_options_new(); + VALUE string = string_options(argc, argv, options); - VALUE result = parse_input_comments(&input, &options); - pm_string_free(&input); - pm_options_free(&options); + VALUE result = parse_input_comments((const uint8_t *) RSTRING_PTR(string), RSTRING_LEN(string), options); + pm_options_free(options); return result; } /** + * :markup: markdown * call-seq: - * Prism::parse_file_comments(filepath, **options) -> Array + * parse_file_comments(filepath, **options) -> Array * * Parse the given file and return an array of Comment objects. For supported - * options, see Prism::parse. + * options, see Prism.parse. */ static VALUE parse_file_comments(int argc, VALUE *argv, VALUE self) { - pm_string_t input; - pm_options_t options = { 0 }; + pm_options_t *options = pm_options_new(); VALUE encoded_filepath; - file_options(argc, argv, &input, &options, &encoded_filepath); + pm_source_t *src = file_options(argc, argv, options, &encoded_filepath); - VALUE value = parse_input_comments(&input, &options); - pm_string_free(&input); - pm_options_free(&options); + VALUE value = parse_input_comments(pm_source_source(src), pm_source_length(src), options); + pm_source_free(src); + pm_options_free(options); return value; } /** + * :markup: markdown * call-seq: - * Prism::parse_lex(source, **options) -> ParseLexResult + * 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. + * the tokens. If you only need one or the other, use either Prism.parse or + * Prism.lex. * - * For supported options, see Prism::parse. + * 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); + pm_options_t *options = pm_options_new(); + VALUE string = string_options(argc, argv, options); - VALUE value = parse_lex_input(&input, &options, true); - pm_string_free(&input); - pm_options_free(&options); + VALUE value = parse_lex_input((const uint8_t *) RSTRING_PTR(string), RSTRING_LEN(string), options, true); + pm_options_free(options); return value; } /** + * :markup: markdown * call-seq: - * Prism::parse_lex_file(filepath, **options) -> ParseLexResult + * 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. + * 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. + * 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 }; + pm_options_t *options = pm_options_new(); VALUE encoded_filepath; - file_options(argc, argv, &input, &options, &encoded_filepath); + pm_source_t *src = file_options(argc, argv, options, &encoded_filepath); - VALUE value = parse_lex_input(&input, &options, true); - pm_string_free(&input); - pm_options_free(&options); + VALUE value = parse_lex_input(pm_source_source(src), pm_source_length(src), options, true); + pm_source_free(src); + pm_options_free(options); return value; } @@ -1199,45 +1243,45 @@ parse_lex_file(int argc, VALUE *argv, VALUE self) { * 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); +parse_input_success_p(const uint8_t *input, size_t input_length, const pm_options_t *options) { + pm_arena_t *arena = pm_arena_new(); + pm_parser_t *parser = pm_parser_new(arena, input, input_length, options); - pm_node_t *node = pm_parse(&parser); - pm_node_destroy(&parser, node); + pm_parse(parser); - VALUE result = parser.error_list.size == 0 ? Qtrue : Qfalse; - pm_parser_free(&parser); + VALUE result = pm_parser_errors_size(parser) == 0 ? Qtrue : Qfalse; + pm_parser_free(parser); + pm_arena_free(arena); return result; } /** + * :markup: markdown * call-seq: - * Prism::parse_success?(source, **options) -> bool + * parse_success?(source, **options) -> bool * * Parse the given string and return true if it parses without errors. For - * supported options, see Prism::parse. + * 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); + pm_options_t *options = pm_options_new(); + VALUE string = string_options(argc, argv, options); - VALUE result = parse_input_success_p(&input, &options); - pm_string_free(&input); - pm_options_free(&options); + VALUE result = parse_input_success_p((const uint8_t *) RSTRING_PTR(string), RSTRING_LEN(string), options); + pm_options_free(options); return result; } /** + * :markup: markdown * call-seq: - * Prism::parse_failure?(source, **options) -> bool + * parse_failure?(source, **options) -> bool * * Parse the given string and return true if it parses with errors. For - * supported options, see Prism::parse. + * supported options, see Prism.parse. */ static VALUE parse_failure_p(int argc, VALUE *argv, VALUE self) { @@ -1245,33 +1289,34 @@ parse_failure_p(int argc, VALUE *argv, VALUE self) { } /** + * :markup: markdown * call-seq: - * Prism::parse_file_success?(filepath, **options) -> bool + * parse_file_success?(filepath, **options) -> bool * * Parse the given file and return true if it parses without errors. For - * supported options, see Prism::parse. + * 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 }; + pm_options_t *options = pm_options_new(); VALUE encoded_filepath; - file_options(argc, argv, &input, &options, &encoded_filepath); + pm_source_t *src = file_options(argc, argv, options, &encoded_filepath); - VALUE result = parse_input_success_p(&input, &options); - pm_string_free(&input); - pm_options_free(&options); + VALUE result = parse_input_success_p(pm_source_source(src), pm_source_length(src), options); + pm_source_free(src); + pm_options_free(options); return result; } /** + * :markup: markdown * call-seq: - * Prism::parse_file_failure?(filepath, **options) -> bool + * parse_file_failure?(filepath, **options) -> bool * * Parse the given file and return true if it parses with errors. For - * supported options, see Prism::parse. + * supported options, see Prism.parse. */ static VALUE parse_file_failure_p(int argc, VALUE *argv, VALUE self) { @@ -1301,8 +1346,9 @@ string_query(pm_string_query_t result) { } /** + * :markup: markdown * call-seq: - * Prism::StringQuery::local?(string) -> bool + * 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 @@ -1315,8 +1361,9 @@ string_query_local_p(VALUE self, VALUE string) { } /** + * :markup: markdown * call-seq: - * Prism::StringQuery::constant?(string) -> bool + * 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 @@ -1329,8 +1376,9 @@ string_query_constant_p(VALUE self, VALUE string) { } /** + * :markup: markdown * call-seq: - * Prism::StringQuery::method_name?(string) -> bool + * method_name?(string) -> bool * * Returns true if the string constitutes a valid method name. */ @@ -1438,5 +1486,4 @@ Init_prism(void) { // Next, initialize the other APIs. Init_prism_api_node(); - Init_prism_pack(); } diff --git a/ext/prism/extension.h b/ext/prism/extension.h index a2aaa6388f..d0cbc2ff53 100644 --- a/ext/prism/extension.h +++ b/ext/prism/extension.h @@ -14,7 +14,6 @@ VALUE pm_ast_new(const pm_parser_t *parser, const pm_node_t *node, rb_encoding * 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/fuzz/asan.ignore b/fuzz/asan.ignore deleted file mode 100644 index 01a130893c..0000000000 --- a/fuzz/asan.ignore +++ /dev/null @@ -1,5 +0,0 @@ -# for now, we ignore the encoding functions as they -# can read out of bounds -[address] -src:src/enc/* - diff --git a/fuzz/corpus/regexp/1 b/fuzz/corpus/regexp/1 deleted file mode 100644 index 9e2c270196..0000000000 --- a/fuzz/corpus/regexp/1 +++ /dev/null @@ -1 +0,0 @@ -(?#comment) diff --git a/fuzz/corpus/regexp/2 b/fuzz/corpus/regexp/2 deleted file mode 100644 index f4119666b1..0000000000 --- a/fuzz/corpus/regexp/2 +++ /dev/null @@ -1 +0,0 @@ -(?:abcd) diff --git a/fuzz/corpus/regexp/3 b/fuzz/corpus/regexp/3 deleted file mode 100644 index 77ee883146..0000000000 --- a/fuzz/corpus/regexp/3 +++ /dev/null @@ -1 +0,0 @@ -(?:subexp) diff --git a/fuzz/corpus/regexp/4 b/fuzz/corpus/regexp/4 deleted file mode 100644 index 60396c3a43..0000000000 --- a/fuzz/corpus/regexp/4 +++ /dev/null @@ -1 +0,0 @@ -!"£$%^&*()/adfas" diff --git a/fuzz/corpus/regexp/6 b/fuzz/corpus/regexp/6 deleted file mode 100644 index 273d11e622..0000000000 --- a/fuzz/corpus/regexp/6 +++ /dev/null @@ -1 +0,0 @@ -word| ) diff --git a/fuzz/corpus/unescape/1 b/fuzz/corpus/unescape/1 deleted file mode 100644 index cd1ed7f860..0000000000 --- a/fuzz/corpus/unescape/1 +++ /dev/null @@ -1 +0,0 @@ -\r\n diff --git a/fuzz/corpus/unescape/2 b/fuzz/corpus/unescape/2 deleted file mode 100644 index dd3221c3a3..0000000000 --- a/fuzz/corpus/unescape/2 +++ /dev/null @@ -1 +0,0 @@ -\\\"\\' diff --git a/fuzz/corpus/unescape/3 b/fuzz/corpus/unescape/3 deleted file mode 100644 index 186bc5a9e0..0000000000 --- a/fuzz/corpus/unescape/3 +++ /dev/null @@ -1 +0,0 @@ -\b\0000 diff --git a/fuzz/parse.c b/fuzz/parse.c index be9094d86f..c04553909e 100644 --- a/fuzz/parse.c +++ b/fuzz/parse.c @@ -2,8 +2,7 @@ void harness(const uint8_t *input, size_t size) { - pm_buffer_t buffer; - pm_buffer_init(&buffer); - pm_serialize_parse(&buffer, input, size, NULL); - pm_buffer_free(&buffer); + pm_buffer_t *buffer = pm_buffer_new(); + pm_serialize_parse(buffer, input, size, NULL); + pm_buffer_free(buffer); } diff --git a/fuzz/regexp.c b/fuzz/regexp.c deleted file mode 100644 index 3837d74d57..0000000000 --- a/fuzz/regexp.c +++ /dev/null @@ -1,21 +0,0 @@ -#include - -void -regexp_name_callback(const pm_string_t *name, void *data) { - // Do nothing -} - -void -regexp_error_callback(const uint8_t *start, const uint8_t *end, const char *message, void *data) { - // Do nothing -} - -void -harness(const uint8_t *input, size_t size) { - pm_parser_t parser; - pm_parser_init(&parser, input, size, NULL); - - pm_regexp_parse(&parser, input, size, false, regexp_name_callback, NULL, regexp_error_callback, NULL); - - pm_parser_free(&parser); -} diff --git a/fuzz/regexp.sh b/fuzz/regexp.sh deleted file mode 100755 index 5cf615a095..0000000000 --- a/fuzz/regexp.sh +++ /dev/null @@ -1,5 +0,0 @@ -#!/bin/bash - -OUTPUT_DIR=$1 - -afl-fuzz -G 100 -c ./build/fuzz.regexp.cmplog -i ./fuzz/corpus/regexp -o "$OUTPUT_DIR" ./build/fuzz.regexp diff --git a/gemfiles/2.7/Gemfile b/gemfiles/2.7/Gemfile index 8d1fbb82cd..a3ac724e5e 100644 --- a/gemfiles/2.7/Gemfile +++ b/gemfiles/2.7/Gemfile @@ -10,6 +10,5 @@ gem "onigmo", platforms: :ruby gem "parser" gem "rake-compiler" gem "rake" -gem "rbs" gem "ruby_parser" gem "test-unit" diff --git a/gemfiles/2.7/Gemfile.lock b/gemfiles/2.7/Gemfile.lock index f75112d346..7eab6a5528 100644 --- a/gemfiles/2.7/Gemfile.lock +++ b/gemfiles/2.7/Gemfile.lock @@ -8,7 +8,7 @@ GEM specs: ast (2.4.3) onigmo (0.1.0) - parser (3.3.10.1) + parser (3.3.10.2) ast (~> 2.4.1) racc power_assert (3.0.1) @@ -16,7 +16,6 @@ GEM rake (13.3.1) rake-compiler (1.3.1) rake - rbs (3.1.3) ruby_parser (3.21.1) racc (~> 1.5) sexp_processor (~> 4.16) @@ -33,7 +32,6 @@ DEPENDENCIES prism! rake rake-compiler - rbs ruby_parser test-unit diff --git a/gemfiles/3.0/Gemfile b/gemfiles/3.0/Gemfile index c52f90eaf8..c5600d20f0 100644 --- a/gemfiles/3.0/Gemfile +++ b/gemfiles/3.0/Gemfile @@ -10,7 +10,5 @@ gem "onigmo", platforms: :ruby gem "parser" gem "rake-compiler" gem "rake" -gem "rbs" -gem "ruby_memcheck" gem "ruby_parser" gem "test-unit" diff --git a/gemfiles/3.0/Gemfile.lock b/gemfiles/3.0/Gemfile.lock index 9174ab2a9e..d295f1f173 100644 --- a/gemfiles/3.0/Gemfile.lock +++ b/gemfiles/3.0/Gemfile.lock @@ -7,13 +7,8 @@ GEM remote: https://rubygems.org/ specs: ast (2.4.3) - logger (1.7.0) - mini_portile2 (2.8.9) - nokogiri (1.17.2) - mini_portile2 (~> 2.8.2) - racc (~> 1.4) onigmo (0.1.0) - parser (3.3.10.1) + parser (3.3.10.2) ast (~> 2.4.1) racc power_assert (3.0.1) @@ -21,10 +16,6 @@ GEM rake (13.3.1) rake-compiler (1.3.1) rake - rbs (3.6.1) - logger - ruby_memcheck (3.0.1) - nokogiri ruby_parser (3.21.1) racc (~> 1.5) sexp_processor (~> 4.16) @@ -41,8 +32,6 @@ DEPENDENCIES prism! rake rake-compiler - rbs - ruby_memcheck ruby_parser test-unit diff --git a/gemfiles/3.1/Gemfile b/gemfiles/3.1/Gemfile index b17401ffc3..6f7e7b6a28 100644 --- a/gemfiles/3.1/Gemfile +++ b/gemfiles/3.1/Gemfile @@ -10,7 +10,5 @@ gem "onigmo", platforms: :ruby gem "parser" gem "rake-compiler" gem "rake" -gem "rbs" -gem "ruby_memcheck" gem "ruby_parser" gem "test-unit" diff --git a/gemfiles/3.1/Gemfile.lock b/gemfiles/3.1/Gemfile.lock index 8247f879fd..3b242f5c3c 100644 --- a/gemfiles/3.1/Gemfile.lock +++ b/gemfiles/3.1/Gemfile.lock @@ -7,13 +7,8 @@ GEM remote: https://rubygems.org/ specs: ast (2.4.3) - logger (1.7.0) - mini_portile2 (2.8.9) - nokogiri (1.18.10) - mini_portile2 (~> 2.8.2) - racc (~> 1.4) onigmo (0.1.0) - parser (3.3.10.1) + parser (3.3.10.2) ast (~> 2.4.1) racc power_assert (3.0.1) @@ -21,10 +16,6 @@ GEM rake (13.3.1) rake-compiler (1.3.1) rake - rbs (3.10.2) - logger - ruby_memcheck (3.0.1) - nokogiri ruby_parser (3.21.1) racc (~> 1.5) sexp_processor (~> 4.16) @@ -41,8 +32,6 @@ DEPENDENCIES prism! rake rake-compiler - rbs - ruby_memcheck ruby_parser test-unit diff --git a/gemfiles/3.2/Gemfile b/gemfiles/3.2/Gemfile index 009b09ec1a..7d82c2b76a 100644 --- a/gemfiles/3.2/Gemfile +++ b/gemfiles/3.2/Gemfile @@ -10,7 +10,5 @@ gem "onigmo", platforms: :ruby gem "parser" gem "rake-compiler" gem "rake" -gem "rbs" -gem "ruby_memcheck" gem "ruby_parser" gem "test-unit" diff --git a/gemfiles/3.2/Gemfile.lock b/gemfiles/3.2/Gemfile.lock index c9c75907d6..661e0434d9 100644 --- a/gemfiles/3.2/Gemfile.lock +++ b/gemfiles/3.2/Gemfile.lock @@ -7,13 +7,8 @@ GEM remote: https://rubygems.org/ specs: ast (2.4.3) - logger (1.7.0) - mini_portile2 (2.8.9) - nokogiri (1.19.0) - mini_portile2 (~> 2.8.2) - racc (~> 1.4) onigmo (0.1.0) - parser (3.3.10.1) + parser (3.3.10.2) ast (~> 2.4.1) racc power_assert (3.0.1) @@ -21,10 +16,6 @@ GEM rake (13.3.1) rake-compiler (1.3.1) rake - rbs (3.10.2) - logger - ruby_memcheck (3.0.1) - nokogiri ruby_parser (3.22.0) racc (~> 1.5) sexp_processor (~> 4.16) @@ -41,8 +32,6 @@ DEPENDENCIES prism! rake rake-compiler - rbs - ruby_memcheck ruby_parser test-unit diff --git a/gemfiles/3.3/Gemfile b/gemfiles/3.3/Gemfile index 73006ce144..f78c4cc9a2 100644 --- a/gemfiles/3.3/Gemfile +++ b/gemfiles/3.3/Gemfile @@ -10,7 +10,5 @@ gem "onigmo", platforms: :ruby gem "parser" gem "rake-compiler" gem "rake" -gem "rbs" -gem "ruby_memcheck" gem "ruby_parser" gem "test-unit" diff --git a/gemfiles/3.3/Gemfile.lock b/gemfiles/3.3/Gemfile.lock index d452f1bc2f..55d0a9103d 100644 --- a/gemfiles/3.3/Gemfile.lock +++ b/gemfiles/3.3/Gemfile.lock @@ -7,13 +7,8 @@ GEM remote: https://rubygems.org/ specs: ast (2.4.3) - logger (1.7.0) - mini_portile2 (2.8.9) - nokogiri (1.19.0) - mini_portile2 (~> 2.8.2) - racc (~> 1.4) onigmo (0.1.0) - parser (3.3.10.1) + parser (3.3.10.2) ast (~> 2.4.1) racc power_assert (3.0.1) @@ -21,10 +16,6 @@ GEM rake (13.3.1) rake-compiler (1.3.1) rake - rbs (3.10.2) - logger - ruby_memcheck (3.0.1) - nokogiri ruby_parser (3.22.0) racc (~> 1.5) sexp_processor (~> 4.16) @@ -41,8 +32,6 @@ DEPENDENCIES prism! rake rake-compiler - rbs - ruby_memcheck ruby_parser test-unit diff --git a/gemfiles/3.4/Gemfile b/gemfiles/3.4/Gemfile index 2e86887f58..c1b19d7d53 100644 --- a/gemfiles/3.4/Gemfile +++ b/gemfiles/3.4/Gemfile @@ -10,7 +10,5 @@ gem "onigmo", platforms: :ruby gem "parser" gem "rake-compiler" gem "rake" -gem "rbs" -gem "ruby_memcheck" gem "ruby_parser" gem "test-unit" diff --git a/gemfiles/3.4/Gemfile.lock b/gemfiles/3.4/Gemfile.lock index a1195453db..6857ef7c0b 100644 --- a/gemfiles/3.4/Gemfile.lock +++ b/gemfiles/3.4/Gemfile.lock @@ -7,13 +7,8 @@ GEM remote: https://rubygems.org/ specs: ast (2.4.3) - logger (1.7.0) - mini_portile2 (2.8.9) - nokogiri (1.19.0) - mini_portile2 (~> 2.8.2) - racc (~> 1.4) onigmo (0.1.0) - parser (3.3.10.1) + parser (3.3.10.2) ast (~> 2.4.1) racc power_assert (3.0.1) @@ -21,10 +16,6 @@ GEM rake (13.3.1) rake-compiler (1.3.1) rake - rbs (3.10.2) - logger - ruby_memcheck (3.0.1) - nokogiri ruby_parser (3.22.0) racc (~> 1.5) sexp_processor (~> 4.16) @@ -41,8 +32,6 @@ DEPENDENCIES prism! rake rake-compiler - rbs - ruby_memcheck ruby_parser test-unit diff --git a/gemfiles/4.0/Gemfile b/gemfiles/4.0/Gemfile index f3fef38fe8..538073fb0e 100644 --- a/gemfiles/4.0/Gemfile +++ b/gemfiles/4.0/Gemfile @@ -6,12 +6,9 @@ ruby "~> 4.0.0" gemspec path: "../.." -gem "ffi" gem "onigmo", platforms: :ruby gem "parser" gem "rake-compiler" gem "rake" -gem "rbs" -gem "ruby_memcheck" gem "ruby_parser" gem "test-unit" diff --git a/gemfiles/4.0/Gemfile.lock b/gemfiles/4.0/Gemfile.lock index c7670f7ea1..8514c58291 100644 --- a/gemfiles/4.0/Gemfile.lock +++ b/gemfiles/4.0/Gemfile.lock @@ -7,14 +7,8 @@ GEM remote: https://rubygems.org/ specs: ast (2.4.3) - ffi (1.17.3) - logger (1.7.0) - mini_portile2 (2.8.9) - nokogiri (1.19.0) - mini_portile2 (~> 2.8.2) - racc (~> 1.4) onigmo (0.1.0) - parser (3.3.10.1) + parser (3.3.10.2) ast (~> 2.4.1) racc power_assert (3.0.1) @@ -22,10 +16,6 @@ GEM rake (13.3.1) rake-compiler (1.3.1) rake - rbs (3.10.2) - logger - ruby_memcheck (3.0.1) - nokogiri ruby_parser (3.22.0) racc (~> 1.5) sexp_processor (~> 4.16) @@ -37,14 +27,11 @@ PLATFORMS ruby DEPENDENCIES - ffi onigmo parser prism! rake rake-compiler - rbs - ruby_memcheck ruby_parser test-unit diff --git a/gemfiles/4.1/Gemfile b/gemfiles/4.1/Gemfile index 3fde9d4ac1..8beda3da54 100644 --- a/gemfiles/4.1/Gemfile +++ b/gemfiles/4.1/Gemfile @@ -11,7 +11,5 @@ gem "onigmo", platforms: :ruby gem "parser" gem "rake-compiler" gem "rake" -gem "rbs" -gem "ruby_memcheck" gem "ruby_parser" gem "test-unit" diff --git a/gemfiles/4.1/Gemfile.lock b/gemfiles/4.1/Gemfile.lock index cd652a75b4..db51633a6d 100644 --- a/gemfiles/4.1/Gemfile.lock +++ b/gemfiles/4.1/Gemfile.lock @@ -8,13 +8,8 @@ GEM specs: ast (2.4.3) ffi (1.17.3) - logger (1.7.0) - mini_portile2 (2.8.9) - nokogiri (1.19.0) - mini_portile2 (~> 2.8.2) - racc (~> 1.4) onigmo (0.1.0) - parser (3.3.10.1) + parser (3.3.10.2) ast (~> 2.4.1) racc power_assert (3.0.1) @@ -22,10 +17,6 @@ GEM rake (13.3.1) rake-compiler (1.3.1) rake - rbs (3.10.2) - logger - ruby_memcheck (3.0.1) - nokogiri ruby_parser (3.22.0) racc (~> 1.5) sexp_processor (~> 4.16) @@ -43,8 +34,6 @@ DEPENDENCIES prism! rake rake-compiler - rbs - ruby_memcheck ruby_parser test-unit diff --git a/gemfiles/typecheck/Gemfile b/gemfiles/typecheck/Gemfile index b535d28729..3965a1c17d 100644 --- a/gemfiles/typecheck/Gemfile +++ b/gemfiles/typecheck/Gemfile @@ -2,11 +2,11 @@ source "https://rubygems.org" -gem "minitest" gem "parser" gem "rake-compiler" gem "rake" -gem "rbs" +gem "rbi" +gem "rbs-inline" gem "ruby_parser" gem "sorbet", "<= 0.6.12666" # until tapioca is bumped gem "steep", ">= 1.7.0.dev.1" diff --git a/gemfiles/typecheck/Gemfile.lock b/gemfiles/typecheck/Gemfile.lock index d3d512dc9e..a5363e44fb 100644 --- a/gemfiles/typecheck/Gemfile.lock +++ b/gemfiles/typecheck/Gemfile.lock @@ -23,8 +23,7 @@ GEM csv (3.3.5) drb (2.2.3) erubi (1.13.1) - ffi (1.17.3-arm64-darwin) - ffi (1.17.3-x86_64-linux-gnu) + ffi (1.17.3) fileutils (1.8.0) i18n (1.14.7) concurrent-ruby (~> 1.0) @@ -39,11 +38,11 @@ GEM mutex_m (0.3.0) netrc (0.11.0) parallel (1.27.0) - parser (3.3.10.1) + parser (3.3.10.2) ast (~> 2.4.1) racc power_assert (3.0.1) - prism (1.7.0) + prism (1.9.0) racc (1.8.1) rainbow (3.1.1) rake (13.3.1) @@ -52,17 +51,21 @@ GEM rb-fsevent (0.11.2) rb-inotify (0.11.1) ffi (~> 1.0) - rbi (0.3.7) + rbi (0.3.9) prism (~> 1.0) rbs (>= 3.4.4) - rbs (3.10.2) + rbs (3.10.3) logger + tsort + rbs-inline (0.13.0) + prism (>= 0.29) + rbs (>= 3.8.0) rexml (3.4.4) ruby_parser (3.22.0) racc (~> 1.5) sexp_processor (~> 4.16) securerandom (0.4.1) - sexp_processor (4.17.4) + sexp_processor (4.17.5) sorbet (0.6.12666) sorbet-static (= 0.6.12666) sorbet-runtime (0.6.12666) @@ -111,11 +114,12 @@ GEM test-unit (3.7.7) power_assert thor (1.4.0) + tsort (0.2.0) tzinfo (2.0.6) concurrent-ruby (~> 1.0) unicode-display_width (3.2.0) unicode-emoji (~> 4.1) - unicode-emoji (4.1.0) + unicode-emoji (4.2.0) uri (1.1.1) yard (0.9.37) yard-sorbet (0.9.0) @@ -127,11 +131,11 @@ PLATFORMS x86_64-linux DEPENDENCIES - minitest parser rake rake-compiler - rbs + rbi + rbs-inline ruby_parser sorbet (<= 0.6.12666) steep (>= 1.7.0.dev.1) diff --git a/include/prism.h b/include/prism.h index c1ce582997..b342bb32c6 100644 --- a/include/prism.h +++ b/include/prism.h @@ -6,305 +6,25 @@ #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" +#ifdef __cplusplus +extern "C" { +#endif + +#include "prism/arena.h" #include "prism/ast.h" +#include "prism/buffer.h" #include "prism/diagnostic.h" +#include "prism/json.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/serialize.h" +#include "prism/source.h" +#include "prism/stream.h" +#include "prism/string_query.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 list The list of comments to serialize. - * @param buffer The buffer to serialize to. - */ -void pm_serialize_comment_list(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 * @@ -327,27 +47,27 @@ PRISM_EXPORTED_FUNCTION pm_string_query_t pm_string_query_method_name(const uint * * @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: + * In order to parse Ruby code, the functions that you are going to want to use + * and be aware of are: * - * * `pm_parser_t` - the main parser structure - * * `pm_parser_init()` - initialize a parser + * * `pm_arena_new()` - create a new arena to hold all AST-lifetime allocations + * * `pm_parser_new()` - allocate and initialize a new 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 + * * `pm_parser_free()` - free the parser and its internal memory + * * `pm_arena_free()` - free all AST-lifetime memory * * 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_arena_t *arena = pm_arena_new(); + * pm_parser_t *parser = pm_parser_new(arena, source, length, NULL); * - * pm_node_t *root = pm_parse(&parser); + * pm_node_t *root = pm_parse(parser); * printf("PARSED!\n"); * - * pm_node_destroy(&parser, root); - * pm_parser_free(&parser); + * pm_parser_free(parser); + * pm_arena_free(arena); * } * ``` * @@ -360,24 +80,23 @@ PRISM_EXPORTED_FUNCTION pm_string_query_t pm_string_query_method_name(const uint * 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: + * parse Ruby code. The functions that you are 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_buffer_new()` - create a new buffer + * * `pm_buffer_free()` - free the buffer and its internal memory * * `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_buffer_t *buffer = pm_buffer_new(); * - * pm_serialize_parse(&buffer, source, length, NULL); + * pm_serialize_parse(buffer, source, length, NULL); * printf("SERIALIZED!\n"); * - * pm_buffer_free(&buffer); + * pm_buffer_free(buffer); * } * ``` * @@ -388,20 +107,24 @@ PRISM_EXPORTED_FUNCTION pm_string_query_t pm_string_query_method_name(const uint * * ```c * void prettyprint(const uint8_t *source, size_t length) { - * pm_parser_t parser; - * pm_parser_init(&parser, source, length, NULL); + * pm_arena_t *arena = pm_arena_new(); + * pm_parser_t *parser = pm_parser_new(arena, source, length, NULL); * - * pm_node_t *root = pm_parse(&parser); - * pm_buffer_t buffer = { 0 }; + * pm_node_t *root = pm_parse(parser); + * pm_buffer_t *buffer = pm_buffer_new(); * - * pm_prettyprint(&buffer, &parser, root); - * printf("%*.s\n", (int) buffer.length, buffer.value); + * pm_prettyprint(buffer, parser, root); + * printf("%*.s\n", (int) pm_buffer_length(buffer), pm_buffer_value(buffer)); * - * pm_buffer_free(&buffer); - * pm_node_destroy(&parser, root); - * pm_parser_free(&parser); + * pm_buffer_free(buffer); + * pm_parser_free(parser); + * pm_arena_free(arena); * } * ``` */ +#ifdef __cplusplus +} +#endif + #endif diff --git a/include/prism/arena.h b/include/prism/arena.h new file mode 100644 index 0000000000..e1fa8fc6ad --- /dev/null +++ b/include/prism/arena.h @@ -0,0 +1,37 @@ +/** + * @file arena.h + * + * A bump allocator for the prism parser. + */ +#ifndef PRISM_ARENA_H +#define PRISM_ARENA_H + +#include "prism/compiler/exported.h" +#include "prism/compiler/nodiscard.h" +#include "prism/compiler/nonnull.h" + +#include + +/** + * An opaque pointer to an arena that is used for allocations. + */ +typedef struct pm_arena_t pm_arena_t; + +/** + * Returns a newly allocated and initialized arena. If the arena cannot be + * allocated, this function aborts the process. + * + * @returns A pointer to the newly allocated arena. It is the responsibility of + * the caller to free the arena using pm_arena_free when it is no longer + * needed. + */ +PRISM_EXPORTED_FUNCTION PRISM_NODISCARD pm_arena_t * pm_arena_new(void); + +/** + * Frees both the held memory and the arena itself. + * + * @param arena The arena to free. + */ +PRISM_EXPORTED_FUNCTION void pm_arena_free(pm_arena_t *arena) PRISM_NONNULL(1); + +#endif diff --git a/include/prism/buffer.h b/include/prism/buffer.h new file mode 100644 index 0000000000..24b572d2c3 --- /dev/null +++ b/include/prism/buffer.h @@ -0,0 +1,52 @@ +/** + * @file buffer.h + * + * A wrapper around a contiguous block of allocated memory. + */ +#ifndef PRISM_BUFFER_H +#define PRISM_BUFFER_H + +#include "prism/compiler/exported.h" +#include "prism/compiler/nodiscard.h" +#include "prism/compiler/nonnull.h" + +#include + +/** + * A wrapper around a contiguous block of allocated memory. + */ +typedef struct pm_buffer_t pm_buffer_t; + +/** + * Allocate and initialize a new buffer. If the buffer cannot be allocated, this + * function will abort the process. + * + * @returns A pointer to the initialized buffer. The caller is responsible for + * freeing the buffer with pm_buffer_free. + */ +PRISM_EXPORTED_FUNCTION PRISM_NODISCARD pm_buffer_t * pm_buffer_new(void); + +/** + * Free both the memory held by the buffer and the buffer itself. + * + * @param buffer The buffer to free. + */ +PRISM_EXPORTED_FUNCTION void pm_buffer_free(pm_buffer_t *buffer) PRISM_NONNULL(1); + +/** + * Return the value of the buffer. + * + * @param buffer The buffer to get the value of. + * @returns The value of the buffer. + */ +PRISM_EXPORTED_FUNCTION char * pm_buffer_value(const pm_buffer_t *buffer) PRISM_NONNULL(1); + +/** + * Return the length of the buffer. + * + * @param buffer The buffer to get the length of. + * @returns The length of the buffer. + */ +PRISM_EXPORTED_FUNCTION size_t pm_buffer_length(const pm_buffer_t *buffer) PRISM_NONNULL(1); + +#endif diff --git a/include/prism/comments.h b/include/prism/comments.h new file mode 100644 index 0000000000..2270d53889 --- /dev/null +++ b/include/prism/comments.h @@ -0,0 +1,43 @@ +/** + * @file comments.h + * + * Types and functions related to comments found during parsing. + */ +#ifndef PRISM_COMMENTS_H +#define PRISM_COMMENTS_H + +#include "prism/compiler/exported.h" +#include "prism/compiler/nodiscard.h" +#include "prism/compiler/nonnull.h" + +#include "prism/ast.h" + +#include + +/** 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; + +/** An opaque pointer to a comment found while parsing. */ +typedef struct pm_comment_t pm_comment_t; + +/** + * Returns the location associated with the given comment. + * + * @param comment the comment whose location we want to get + * @returns the location associated with the given comment + */ +PRISM_EXPORTED_FUNCTION pm_location_t pm_comment_location(const pm_comment_t *comment) PRISM_NONNULL(1); + +/** + * Returns the type associated with the given comment. + * + * @param comment the comment whose type we want to get + * @returns the type associated with the given comment. This can either be + * PM_COMMENT_INLINE or PM_COMMENT_EMBDOC. + */ +PRISM_EXPORTED_FUNCTION pm_comment_type_t pm_comment_type(const pm_comment_t *comment) PRISM_NONNULL(1); + +#endif diff --git a/include/prism/compiler/accel.h b/include/prism/compiler/accel.h new file mode 100644 index 0000000000..be23236d1d --- /dev/null +++ b/include/prism/compiler/accel.h @@ -0,0 +1,19 @@ +/** + * @file compiler/accel.h + */ +#ifndef PRISM_COMPILER_ACCEL_H +#define PRISM_COMPILER_ACCEL_H + +/** + * Platform detection for SIMD/fast-path implementations. At most one of these + * macros is defined, selecting the best available vectorization strategy. + */ +#if (defined(__aarch64__) && defined(__ARM_NEON)) || (defined(_MSC_VER) && defined(_M_ARM64)) +# define PRISM_HAS_NEON +#elif (defined(__x86_64__) && defined(__SSSE3__)) || (defined(_MSC_VER) && defined(_M_X64)) +# define PRISM_HAS_SSSE3 +#elif defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ +# define PRISM_HAS_SWAR +#endif + +#endif diff --git a/include/prism/compiler/align.h b/include/prism/compiler/align.h new file mode 100644 index 0000000000..22cb49a48c --- /dev/null +++ b/include/prism/compiler/align.h @@ -0,0 +1,36 @@ +/** + * @file compiler/align.h + */ +#ifndef PRISM_COMPILER_ALIGN_H +#define PRISM_COMPILER_ALIGN_H + +/** + * Compiler-agnostic macros for specifying alignment of types and variables. + */ +#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L /* C11 or later */ + /** Specify alignment for a type or variable. */ + #define PRISM_ALIGNAS _Alignas + + /** Get the alignment requirement of a type. */ + #define PRISM_ALIGNOF _Alignof +#elif defined(__GNUC__) || defined(__clang__) + /** Specify alignment for a type or variable. */ + #define PRISM_ALIGNAS(size) __attribute__((aligned(size))) + + /** Get the alignment requirement of a type. */ + #define PRISM_ALIGNOF(type) __alignof__(type) +#elif defined(_MSC_VER) + /** Specify alignment for a type or variable. */ + #define PRISM_ALIGNAS(size) __declspec(align(size)) + + /** Get the alignment requirement of a type. */ + #define PRISM_ALIGNOF(type) __alignof(type) +#else + /** Void because this platform does not support specifying alignment. */ + #define PRISM_ALIGNAS(size) + + /** Fallback to sizeof as alignment requirement of a type. */ + #define PRISM_ALIGNOF(type) sizeof(type) +#endif + +#endif diff --git a/include/prism/compiler/exported.h b/include/prism/compiler/exported.h new file mode 100644 index 0000000000..823773ecbb --- /dev/null +++ b/include/prism/compiler/exported.h @@ -0,0 +1,24 @@ +/** + * @file compiler/exported.h + */ +#ifndef PRISM_COMPILER_EXPORTED_H +#define PRISM_COMPILER_EXPORTED_H + +/** + * By default, we compile with -fvisibility=hidden. When this is enabled, we + * need to mark certain functions as being publically-visible. This macro does + * that in a compiler-agnostic way. + */ +#ifndef PRISM_EXPORTED_FUNCTION +# ifdef PRISM_EXPORT_SYMBOLS +# ifdef _WIN32 +# define PRISM_EXPORTED_FUNCTION __declspec(dllexport) extern +# else +# define PRISM_EXPORTED_FUNCTION __attribute__((__visibility__("default"))) extern +# endif +# else +# define PRISM_EXPORTED_FUNCTION +# endif +#endif + +#endif diff --git a/include/prism/compiler/fallthrough.h b/include/prism/compiler/fallthrough.h new file mode 100644 index 0000000000..ce1b450e8a --- /dev/null +++ b/include/prism/compiler/fallthrough.h @@ -0,0 +1,22 @@ +/** + * @file compiler/fallthrough.h + */ +#ifndef PRISM_COMPILER_FALLTHROUGH_H +#define PRISM_COMPILER_FALLTHROUGH_H + +/** + * 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/include/prism/compiler/filesystem.h b/include/prism/compiler/filesystem.h new file mode 100644 index 0000000000..f988909db8 --- /dev/null +++ b/include/prism/compiler/filesystem.h @@ -0,0 +1,32 @@ +/** + * @file compiler/filesystem.h + * + * Platform detection for mmap and filesystem support. + */ +#ifndef PRISM_COMPILER_FILESYSTEM_H +#define PRISM_COMPILER_FILESYSTEM_H + +/** + * 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 + +#endif diff --git a/include/prism/compiler/flex_array.h b/include/prism/compiler/flex_array.h new file mode 100644 index 0000000000..7504b5fdd3 --- /dev/null +++ b/include/prism/compiler/flex_array.h @@ -0,0 +1,19 @@ +/** + * @file compiler/flex_array.h + */ +#ifndef PRISM_COMPILER_FLEX_ARRAY_H +#define PRISM_COMPILER_FLEX_ARRAY_H + +/** + * A macro for helper define a flexible array member. C99 supports `data[]`, GCC + * supports `data[0]` as an extension, and older compilers require `data[1]`. + */ +#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) + #define PM_FLEX_ARRAY_LENGTH /* data[] */ +#elif defined(__GNUC__) && !defined(__STRICT_ANSI__) + #define PM_FLEX_ARRAY_LENGTH 0 /* data[0] */ +#else + #define PM_FLEX_ARRAY_LENGTH 1 /* data[1] */ +#endif + +#endif diff --git a/include/prism/compiler/force_inline.h b/include/prism/compiler/force_inline.h new file mode 100644 index 0000000000..e189d592d6 --- /dev/null +++ b/include/prism/compiler/force_inline.h @@ -0,0 +1,21 @@ +/** + * @file compiler/force_inline.h + */ +#ifndef PRISM_COMPILER_FORCE_INLINE_H +#define PRISM_COMPILER_FORCE_INLINE_H + +#include "prism/compiler/inline.h" + +/** + * Force a function to be inlined at every call site. Use sparingly — only for + * small, hot functions where the compiler's heuristics fail to inline. + */ +#if defined(_MSC_VER) +# define PRISM_FORCE_INLINE __forceinline +#elif defined(__GNUC__) || defined(__clang__) +# define PRISM_FORCE_INLINE PRISM_INLINE __attribute__((always_inline)) +#else +# define PRISM_FORCE_INLINE PRISM_INLINE +#endif + +#endif diff --git a/include/prism/compiler/format.h b/include/prism/compiler/format.h new file mode 100644 index 0000000000..32f4c3c6d7 --- /dev/null +++ b/include/prism/compiler/format.h @@ -0,0 +1,25 @@ +/** + * @file compiler/format.h + */ +#ifndef PRISM_COMPILER_FORMAT_H +#define PRISM_COMPILER_FORMAT_H + +/** + * 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(fmt_idx_, arg_idx_) __attribute__((format(__MINGW_PRINTF_FORMAT, fmt_idx_, arg_idx_))) +# else +# define PRISM_ATTRIBUTE_FORMAT(fmt_idx_, arg_idx_) __attribute__((format(printf, fmt_idx_, arg_idx_))) +# endif +#elif defined(__clang__) +# define PRISM_ATTRIBUTE_FORMAT(fmt_idx_, arg_idx_) __attribute__((__format__(__printf__, fmt_idx_, arg_idx_))) +#else +# define PRISM_ATTRIBUTE_FORMAT(fmt_idx_, arg_idx_) +#endif + +#endif diff --git a/include/prism/compiler/inline.h b/include/prism/compiler/inline.h new file mode 100644 index 0000000000..856a375691 --- /dev/null +++ b/include/prism/compiler/inline.h @@ -0,0 +1,17 @@ +/** + * @file compiler/inline.h + */ +#ifndef PRISM_COMPILER_INLINE_H +#define PRISM_COMPILER_INLINE_H + +/** + * 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 PRISM_INLINE __inline +#else +# define PRISM_INLINE inline +#endif + +#endif diff --git a/include/prism/compiler/nodiscard.h b/include/prism/compiler/nodiscard.h new file mode 100644 index 0000000000..ccd6c00719 --- /dev/null +++ b/include/prism/compiler/nodiscard.h @@ -0,0 +1,22 @@ +/** + * @file compiler/nodiscard.h + */ +#ifndef PRISM_COMPILER_NODISCARD_H +#define PRISM_COMPILER_NODISCARD_H + +/** + * Mark the return value of a function as important so that the compiler warns + * if a caller ignores it. This is useful for functions that return error codes + * or allocated resources that must be freed. + */ +#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 202311L +# define PRISM_NODISCARD [[nodiscard]] +#elif defined(__GNUC__) || defined(__clang__) +# define PRISM_NODISCARD __attribute__((__warn_unused_result__)) +#elif defined(_MSC_VER) +# define PRISM_NODISCARD _Check_return_ +#else +# define PRISM_NODISCARD +#endif + +#endif diff --git a/include/prism/compiler/nonnull.h b/include/prism/compiler/nonnull.h new file mode 100644 index 0000000000..9d19355665 --- /dev/null +++ b/include/prism/compiler/nonnull.h @@ -0,0 +1,18 @@ +/** + * @file compiler/nonnull.h + */ +#ifndef PRISM_COMPILER_NONNULL_H +#define PRISM_COMPILER_NONNULL_H + +/** + * Mark the parameters of a function as non-null. This allows the compiler to + * warn if a caller passes NULL for a parameter that should never be NULL. The + * arguments are the 1-based indices of the parameters. + */ +#if defined(__GNUC__) || defined(__clang__) +# define PRISM_NONNULL(...) __attribute__((__nonnull__(__VA_ARGS__))) +#else +# define PRISM_NONNULL(...) +#endif + +#endif diff --git a/include/prism/compiler/unused.h b/include/prism/compiler/unused.h new file mode 100644 index 0000000000..6a9e125dde --- /dev/null +++ b/include/prism/compiler/unused.h @@ -0,0 +1,18 @@ +/** + * @file compiler/unused.h + */ +#ifndef PRISM_COMPILER_UNUSED_H +#define PRISM_COMPILER_UNUSED_H + +/** + * 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_UNUSED __attribute__((unused)) +#else +# define PRISM_UNUSED +#endif + +#endif diff --git a/include/prism/constant_pool.h b/include/prism/constant_pool.h new file mode 100644 index 0000000000..dc03235c70 --- /dev/null +++ b/include/prism/constant_pool.h @@ -0,0 +1,81 @@ +/** + * @file 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/compiler/exported.h" +#include "prism/compiler/nodiscard.h" +#include "prism/compiler/nonnull.h" + +#include "prism/arena.h" + +#include +#include + +/** + * 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; + +/** A constant in the pool which effectively stores a string. */ +typedef struct pm_constant_t pm_constant_t; + +/** + * The overall constant pool, which stores constants found while parsing. + */ +typedef struct pm_constant_pool_t pm_constant_pool_t; + +/** + * Return a raw pointer to the start of a constant. + * + * @param constant The constant to get the start of. + * @returns A raw pointer to the start of the constant. + */ +PRISM_EXPORTED_FUNCTION const uint8_t * pm_constant_start(const pm_constant_t *constant) PRISM_NONNULL(1); + +/** + * Return the length of a constant. + * + * @param constant The constant to get the length of. + * @returns The length of the constant. + */ +PRISM_EXPORTED_FUNCTION size_t pm_constant_length(const pm_constant_t *constant) PRISM_NONNULL(1); + +/** + * Initialize a list of constant ids. + * + * @param list The list to initialize. + */ +PRISM_EXPORTED_FUNCTION void pm_constant_id_list_init(pm_constant_id_list_t *list) PRISM_NONNULL(1); + +/** + * Append a constant id to a list of constant ids. + * + * @param arena The arena to use for allocations. + * @param list The list to append to. + * @param id The constant id to append. + */ +PRISM_EXPORTED_FUNCTION void pm_constant_id_list_append(pm_arena_t *arena, pm_constant_id_list_t *list, pm_constant_id_t id) PRISM_NONNULL(1, 2); + +#endif diff --git a/include/prism/defines.h b/include/prism/defines.h deleted file mode 100644 index f6bd1dbe40..0000000000 --- a/include/prism/defines.h +++ /dev/null @@ -1,291 +0,0 @@ -/** - * @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 - -/** - * We need to align nodes in the AST to a pointer boundary so that it can be - * safely cast to different node types. Use PRISM_ALIGNAS/PRISM_ALIGNOF to - * specify alignment in a compiler-agnostic way. - */ -#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L /* C11 or later */ - /** Specify alignment for a type or variable. */ - #define PRISM_ALIGNAS _Alignas - - /** Get the alignment requirement of a type. */ - #define PRISM_ALIGNOF _Alignof -#elif defined(__GNUC__) || defined(__clang__) - /** Specify alignment for a type or variable. */ - #define PRISM_ALIGNAS(size) __attribute__((aligned(size))) - - /** Get the alignment requirement of a type. */ - #define PRISM_ALIGNOF(type) __alignof__(type) -#elif defined(_MSC_VER) - /** Specify alignment for a type or variable. */ - #define PRISM_ALIGNAS(size) __declspec(align(size)) - - /** Get the alignment requirement of a type. */ - #define PRISM_ALIGNOF(type) __alignof(type) -#else - /** Void because this platform does not support specifying alignment. */ - #define PRISM_ALIGNAS(size) - - /** Fallback to sizeof as alignment requirement of a type. */ - #define PRISM_ALIGNOF(type) sizeof(type) -#endif - -#endif diff --git a/include/prism/diagnostic.h b/include/prism/diagnostic.h new file mode 100644 index 0000000000..370061ec56 --- /dev/null +++ b/include/prism/diagnostic.h @@ -0,0 +1,93 @@ +/** + * @file diagnostic.h + * + * A list of diagnostics generated during parsing. + */ +#ifndef PRISM_DIAGNOSTIC_H +#define PRISM_DIAGNOSTIC_H + +#include "prism/compiler/exported.h" +#include "prism/compiler/nodiscard.h" +#include "prism/compiler/nonnull.h" + +#include "prism/ast.h" + +/** + * An opaque pointer to a diagnostic generated during parsing. + */ +typedef struct pm_diagnostic_t 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 type of the given diagnostic. + * + * @param diagnostic The diagnostic to get the type of. + * @returns The type of the given diagnostic. Note that this is a string + * representation of an internal ID, and is not meant to be relied upon as a + * stable identifier for the diagnostic. We do not guarantee that these will + * not change in the future. This is meant to be used for debugging and + * error reporting purposes, and not for programmatic checks. + */ +PRISM_EXPORTED_FUNCTION const char * pm_diagnostic_type(const pm_diagnostic_t *diagnostic) PRISM_NONNULL(1); + +/** + * Get the location of the given diagnostic. + * + * @param diagnostic The diagnostic to get the location of. + * @returns The location of the given diagnostic. + */ +PRISM_EXPORTED_FUNCTION pm_location_t pm_diagnostic_location(const pm_diagnostic_t *diagnostic) PRISM_NONNULL(1); + +/** + * Get the message of the given diagnostic. + * + * @param diagnostic The diagnostic to get the message of. + * @returns The message of the given diagnostic. + */ +PRISM_EXPORTED_FUNCTION const char * pm_diagnostic_message(const pm_diagnostic_t *diagnostic) PRISM_NONNULL(1); + +/** + * Get the error level associated with the given diagnostic. + * + * @param diagnostic The diagnostic to get the error level of. + * @returns The error level of the given diagnostic. If the diagnostic was a + * warning, or is in any way not an error, then the return value is + * undefined and should not be relied upon. + */ +PRISM_EXPORTED_FUNCTION pm_error_level_t pm_diagnostic_error_level(const pm_diagnostic_t *diagnostic) PRISM_NONNULL(1); + +/** + * Get the warning level associated with the given diagnostic. + * + * @param diagnostic The diagnostic to get the warning level of. + * @returns The warning level of the given diagnostic. If the diagnostic was an + * error, or is in any way not a warning, then the return value is + * undefined and should not be relied upon. + */ +PRISM_EXPORTED_FUNCTION pm_warning_level_t pm_diagnostic_warning_level(const pm_diagnostic_t *diagnostic) PRISM_NONNULL(1); + +#endif diff --git a/include/prism/excludes.h b/include/prism/excludes.h new file mode 100644 index 0000000000..8600622f63 --- /dev/null +++ b/include/prism/excludes.h @@ -0,0 +1,29 @@ +/** + * @file excludes.h + * + * A header file that defines macros to exclude certain features of the prism + * library. This is useful for reducing the size of the library when certain + * features are not needed. + */ +#ifndef PRISM_EXCLUDES_H +#define PRISM_EXCLUDES_H + +/** + * 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 prettyprint API. */ + #define PRISM_EXCLUDE_PRETTYPRINT + + /** Exclude the full set of encodings, using the minimal only. */ + #define PRISM_ENCODING_EXCLUDE_FULL +#endif + +#endif diff --git a/include/prism/integer.h b/include/prism/integer.h new file mode 100644 index 0000000000..9285986885 --- /dev/null +++ b/include/prism/integer.h @@ -0,0 +1,41 @@ +/** + * @file integer.h + * + * This module provides functions for working with arbitrary-sized integers. + */ +#ifndef PRISM_INTEGER_H +#define PRISM_INTEGER_H + +#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; + +#endif diff --git a/include/prism/internal/allocator.h b/include/prism/internal/allocator.h new file mode 100644 index 0000000000..6c54010dbf --- /dev/null +++ b/include/prism/internal/allocator.h @@ -0,0 +1,68 @@ +#ifndef PRISM_INTERNAL_ALLOCATOR_H +#define PRISM_INTERNAL_ALLOCATOR_H + +/* 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 + * #define xrealloc_sized my_realloc_sized // (optional) + * #define xfree_sized my_free_sized // (optional) + * #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 + +#ifndef xfree_sized + /* The free_sized function that should be used. This can be overridden with + * the PRISM_XALLOCATOR define. If not defined, defaults to calling xfree. + */ + #define xfree_sized(p, s) xfree(((void)(s), (p))) +#endif + +#ifndef xrealloc_sized + /* The xrealloc_sized function that should be used. This can be overridden + * with the PRISM_XALLOCATOR define. If not defined, defaults to calling + * xrealloc. */ + #define xrealloc_sized(p, ns, os) xrealloc((p), ((void)(os), (ns))) +#endif + +#ifdef PRISM_BUILD_DEBUG + #include "prism/internal/allocator_debug.h" +#endif + +#endif diff --git a/include/prism/internal/allocator_debug.h b/include/prism/internal/allocator_debug.h new file mode 100644 index 0000000000..846e96ba2d --- /dev/null +++ b/include/prism/internal/allocator_debug.h @@ -0,0 +1,88 @@ +#ifndef PRISM_INTERNAL_ALLOCATOR_DEBUG_H +#define PRISM_INTERNAL_ALLOCATOR_DEBUG_H + +#include +#include +#include + +static inline void * +pm_allocator_debug_malloc(size_t size) { + size_t *memory = xmalloc(size + sizeof(size_t)); + memory[0] = size; + return memory + 1; +} + +static inline void * +pm_allocator_debug_calloc(size_t nmemb, size_t size) { + size_t total_size = nmemb * size; + void *ptr = pm_allocator_debug_malloc(total_size); + memset(ptr, 0, total_size); + return ptr; +} + +static inline void * +pm_allocator_debug_realloc(void *ptr, size_t size) { + if (ptr == NULL) { + return pm_allocator_debug_malloc(size); + } + + size_t *memory = (size_t *)ptr; + void *raw_memory = memory - 1; + memory = (size_t *)xrealloc(raw_memory, size + sizeof(size_t)); + memory[0] = size; + return memory + 1; +} + +static inline void +pm_allocator_debug_free(void *ptr) { + if (ptr != NULL) { + size_t *memory = (size_t *)ptr; + xfree(memory - 1); + } +} + +static inline void +pm_allocator_debug_free_sized(void *ptr, size_t old_size) { + if (ptr != NULL) { + size_t *memory = (size_t *)ptr; + if (old_size != memory[-1]) { + fprintf(stderr, "[BUG] buffer %p was allocated with size %lu but freed with size %lu\n", ptr, memory[-1], old_size); + abort(); + } + xfree_sized(memory - 1, old_size + sizeof(size_t)); + } +} + +static inline void * +pm_allocator_debug_realloc_sized(void *ptr, size_t size, size_t old_size) { + if (ptr == NULL) { + if (old_size != 0) { + fprintf(stderr, "[BUG] realloc_sized called with NULL pointer and old size %lu\n", old_size); + abort(); + } + return pm_allocator_debug_malloc(size); + } + + size_t *memory = (size_t *)ptr; + if (old_size != memory[-1]) { + fprintf(stderr, "[BUG] buffer %p was allocated with size %lu but realloced with size %lu\n", ptr, memory[-1], old_size); + abort(); + } + return pm_allocator_debug_realloc(ptr, size); +} + +#undef xmalloc +#undef xrealloc +#undef xcalloc +#undef xfree +#undef xrealloc_sized +#undef xfree_sized + +#define xmalloc pm_allocator_debug_malloc +#define xrealloc pm_allocator_debug_realloc +#define xcalloc pm_allocator_debug_calloc +#define xfree pm_allocator_debug_free +#define xrealloc_sized pm_allocator_debug_realloc_sized +#define xfree_sized pm_allocator_debug_free_sized + +#endif diff --git a/include/prism/internal/arena.h b/include/prism/internal/arena.h new file mode 100644 index 0000000000..2e413b42bf --- /dev/null +++ b/include/prism/internal/arena.h @@ -0,0 +1,108 @@ +#ifndef PRISM_INTERNAL_ARENA_H +#define PRISM_INTERNAL_ARENA_H + +#include "prism/compiler/exported.h" +#include "prism/compiler/flex_array.h" +#include "prism/compiler/force_inline.h" +#include "prism/compiler/inline.h" + +#include "prism/arena.h" + +#include +#include + +/* + * A single block of memory in the arena. Blocks are linked via prev pointers so + * they can be freed by walking the chain. + */ +typedef struct pm_arena_block { + /* The previous block in the chain (for freeing). */ + struct pm_arena_block *prev; + + /* The total usable bytes in data[]. */ + size_t capacity; + + /* The number of bytes consumed so far. */ + size_t used; + + /* The block's data. */ + char data[PM_FLEX_ARRAY_LENGTH]; +} pm_arena_block_t; + +/* + * A bump allocator. Allocations are made by bumping a pointer within the + * current block. When a block is full, a new block is allocated and linked to + * the previous one. All blocks are freed at once by walking the chain. + */ +struct pm_arena_t { + /* The active block (allocate from here). */ + pm_arena_block_t *current; + + /* The number of blocks allocated. */ + size_t block_count; +}; + +/* + * Free all blocks in the arena. After this call, all pointers returned by + * pm_arena_alloc and pm_arena_zalloc are invalid. + */ +void pm_arena_cleanup(pm_arena_t *arena); + +/* + * Ensure the arena has at least `capacity` bytes available in its current + * block, allocating a new block if necessary. This allows callers to + * pre-size the arena to avoid repeated small block allocations. + */ +void pm_arena_reserve(pm_arena_t *arena, size_t capacity); + +/* + * Slow path for pm_arena_alloc: allocate a new block and return a pointer to + * the first `size` bytes. Do not call directly — use pm_arena_alloc instead. + */ +void * pm_arena_alloc_slow(pm_arena_t *arena, size_t size); + +/* + * Allocate memory from the arena. The returned memory is NOT zeroed. This + * function is infallible — it aborts on allocation failure. + * + * The fast path (bump pointer within the current block) is inlined at each + * call site. The slow path (new block allocation) is out-of-line. + */ +static PRISM_FORCE_INLINE void * +pm_arena_alloc(pm_arena_t *arena, size_t size, size_t alignment) { + if (arena->current != NULL) { + size_t used_aligned = (arena->current->used + alignment - 1) & ~(alignment - 1); + size_t needed = used_aligned + size; + + if (used_aligned >= arena->current->used && needed >= used_aligned && needed <= arena->current->capacity) { + arena->current->used = needed; + return arena->current->data + used_aligned; + } + } + + return pm_arena_alloc_slow(arena, size); +} + +/* + * Allocate zero-initialized memory from the arena. This function is infallible + * — it aborts on allocation failure. + */ +static PRISM_INLINE void * +pm_arena_zalloc(pm_arena_t *arena, size_t size, size_t alignment) { + void *ptr = pm_arena_alloc(arena, size, alignment); + memset(ptr, 0, size); + return ptr; +} + +/* + * Allocate memory from the arena and copy the given data into it. This is a + * convenience wrapper around pm_arena_alloc + memcpy. + */ +static PRISM_INLINE void * +pm_arena_memdup(pm_arena_t *arena, const void *src, size_t size, size_t alignment) { + void *dst = pm_arena_alloc(arena, size, alignment); + memcpy(dst, src, size); + return dst; +} + +#endif diff --git a/include/prism/internal/bit.h b/include/prism/internal/bit.h new file mode 100644 index 0000000000..b0111a4c2c --- /dev/null +++ b/include/prism/internal/bit.h @@ -0,0 +1,42 @@ +#ifndef PRISM_INTERNAL_BIT_H +#define PRISM_INTERNAL_BIT_H + +#include "prism/compiler/inline.h" + +/* + * Count trailing zero bits in a 64-bit value. Used by SWAR identifier scanning + * to find the first non-matching byte in a word. + * + * Precondition: v must be nonzero. The result is undefined when v == 0 + * (matching the behavior of __builtin_ctzll and _BitScanForward64). + */ +#if defined(__GNUC__) || defined(__clang__) +#define pm_ctzll(v) ((unsigned) __builtin_ctzll(v)) +#elif defined(_MSC_VER) +#include +#include + +static PRISM_INLINE unsigned +pm_ctzll(uint64_t v) { + unsigned long index; + _BitScanForward64(&index, v); + return (unsigned) index; +} +#else +#include + +static PRISM_INLINE unsigned +pm_ctzll(uint64_t v) { + unsigned c = 0; + v &= (uint64_t) (-(int64_t) v); + if (v & 0x00000000FFFFFFFFULL) c += 0; else c += 32; + if (v & 0x0000FFFF0000FFFFULL) c += 0; else c += 16; + if (v & 0x00FF00FF00FF00FFULL) c += 0; else c += 8; + if (v & 0x0F0F0F0F0F0F0F0FULL) c += 0; else c += 4; + if (v & 0x3333333333333333ULL) c += 0; else c += 2; + if (v & 0x5555555555555555ULL) c += 0; else c += 1; + return c; +} +#endif + +#endif diff --git a/include/prism/internal/buffer.h b/include/prism/internal/buffer.h new file mode 100644 index 0000000000..a849bbf8e6 --- /dev/null +++ b/include/prism/internal/buffer.h @@ -0,0 +1,91 @@ +#ifndef PRISM_INTERNAL_BUFFER_H +#define PRISM_INTERNAL_BUFFER_H + +#include "prism/compiler/format.h" + +#include "prism/buffer.h" + +#include +#include + +/* + * A simple memory buffer that stores data in a contiguous block of memory. + */ +struct pm_buffer_t { + /* 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; +}; + +/* Initialize a pm_buffer_t with the given capacity. */ +void pm_buffer_init(pm_buffer_t *buffer, size_t capacity); + +/* Free the memory held by the buffer. */ +void pm_buffer_cleanup(pm_buffer_t *buffer); + +/* Append the given amount of space as zeroes to the buffer. */ +void pm_buffer_append_zeroes(pm_buffer_t *buffer, size_t length); + +/* Append a formatted string to the buffer. */ +void pm_buffer_append_format(pm_buffer_t *buffer, const char *format, ...) PRISM_ATTRIBUTE_FORMAT(2, 3); + +/* Append a string to the buffer. */ +void pm_buffer_append_string(pm_buffer_t *buffer, const char *value, size_t 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); + +/* Append a single byte to the buffer. */ +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. */ +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. */ +void pm_buffer_append_varsint(pm_buffer_t *buffer, int32_t value); + +/* Append a double to the buffer. */ +void pm_buffer_append_double(pm_buffer_t *buffer, double value); + +/* Append a unicode codepoint to the buffer. */ +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. */ +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. */ +void pm_buffer_prepend_string(pm_buffer_t *buffer, const char *value, size_t length); + +/* Concatenate one buffer onto another. */ +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. + */ +void pm_buffer_clear(pm_buffer_t *buffer); + +/* Strip the whitespace from the end of the buffer. */ +void pm_buffer_rstrip(pm_buffer_t *buffer); + +/* Checks if the buffer includes the given value. */ +size_t pm_buffer_index(const pm_buffer_t *buffer, char 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); + +#endif diff --git a/include/prism/internal/char.h b/include/prism/internal/char.h new file mode 100644 index 0000000000..9a58fba8c5 --- /dev/null +++ b/include/prism/internal/char.h @@ -0,0 +1,139 @@ +#ifndef PRISM_INTERNAL_CHAR_H +#define PRISM_INTERNAL_CHAR_H + +#include "prism/compiler/force_inline.h" + +#include "prism/arena.h" +#include "prism/line_offset_list.h" + +#include +#include +#include + +/* Bit flag for whitespace characters in pm_byte_table. */ +#define PRISM_CHAR_BIT_WHITESPACE (1 << 0) + +/* Bit flag for inline whitespace characters in pm_byte_table. */ +#define PRISM_CHAR_BIT_INLINE_WHITESPACE (1 << 1) + +/* + * A lookup table for classifying bytes. Each entry is a bitfield of + * PRISM_CHAR_BIT_* flags. Defined in char.c. + */ +extern const uint8_t pm_byte_table[256]; + +/* Returns true if the given character is a whitespace character. */ +static PRISM_FORCE_INLINE bool +pm_char_is_whitespace(const uint8_t b) { + return (pm_byte_table[b] & PRISM_CHAR_BIT_WHITESPACE) != 0; +} + +/* Returns true if the given character is an inline whitespace character. */ +static PRISM_FORCE_INLINE bool +pm_char_is_inline_whitespace(const uint8_t b) { + return (pm_byte_table[b] & PRISM_CHAR_BIT_INLINE_WHITESPACE) != 0; +} + +/* + * Returns the number of characters at the start of the string that are inline + * whitespace (space/tab). Scans the byte table directly for use in hot paths. + */ +static PRISM_FORCE_INLINE size_t +pm_strspn_inline_whitespace(const uint8_t *string, ptrdiff_t length) { + 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_INLINE_WHITESPACE)) 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); + +/* + * 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_arena_t *arena, pm_line_offset_list_t *line_offsets, uint32_t start_offset); + +/* + * 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); + +/* + * 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); + +/* + * 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); + +/* + * 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); + +/* + * 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); + +/* + * 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); + +/* + * 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); + + +/* Returns 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. */ +bool pm_char_is_octal_digit(const uint8_t b); + +/* Returns 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. */ +bool pm_char_is_hexadecimal_digit(const uint8_t b); + +#endif diff --git a/include/prism/internal/comments.h b/include/prism/internal/comments.h new file mode 100644 index 0000000000..bb3039a658 --- /dev/null +++ b/include/prism/internal/comments.h @@ -0,0 +1,20 @@ +#ifndef PRISM_INTERNAL_COMMENTS_H +#define PRISM_INTERNAL_COMMENTS_H + +#include "prism/comments.h" + +#include "prism/internal/list.h" + +/* A comment found while parsing. */ +struct pm_comment_t { + /* The embedded base node. */ + pm_list_node_t node; + + /* The location of the comment in the source. */ + pm_location_t location; + + /* The type of the comment. */ + pm_comment_type_t type; +}; + +#endif diff --git a/include/prism/internal/constant_pool.h b/include/prism/internal/constant_pool.h new file mode 100644 index 0000000000..fa2be783f5 --- /dev/null +++ b/include/prism/internal/constant_pool.h @@ -0,0 +1,117 @@ +#ifndef PRISM_INTERNAL_CONSTANT_POOL_H +#define PRISM_INTERNAL_CONSTANT_POOL_H + +#include "prism/constant_pool.h" + +#include "prism/arena.h" + +#include + +/* A constant in the pool which effectively stores a string. */ +struct pm_constant_t { + /* A pointer to the start of the string. */ + const uint8_t *start; + + /* The length of the string. */ + size_t length; +}; + +/* + * 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; + + /* + * A pointer to the start of the string, stored directly in the bucket to + * avoid a pointer chase to the constants array during probing. + */ + const uint8_t *start; + + /* The length of the string. */ + size_t length; +} pm_constant_pool_bucket_t; + +/* The overall constant pool, which stores constants found while parsing. */ +struct pm_constant_pool_t { + /* 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; +}; + +/* + * 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 + +/* Initialize a list of constant ids with a given capacity. */ +void pm_constant_id_list_init_capacity(pm_arena_t *arena, pm_constant_id_list_t *list, size_t capacity); + +/* 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); + +/* 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); + +/* Initialize a new constant pool with a given capacity. */ +void pm_constant_pool_init(pm_arena_t *arena, pm_constant_pool_t *pool, uint32_t capacity); + +/* 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); + +/* + * 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); + +/* + * 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. + */ +pm_constant_id_t pm_constant_pool_insert_shared(pm_arena_t *arena, 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. + */ +pm_constant_id_t pm_constant_pool_insert_owned(pm_arena_t *arena, 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. + */ +pm_constant_id_t pm_constant_pool_insert_constant(pm_arena_t *arena, pm_constant_pool_t *pool, const uint8_t *start, size_t length); + +#endif diff --git a/include/prism/encoding.h b/include/prism/internal/encoding.h similarity index 79% rename from include/prism/encoding.h rename to include/prism/internal/encoding.h index 5f7724821f..62392ef970 100644 --- a/include/prism/encoding.h +++ b/include/prism/internal/encoding.h @@ -1,128 +1,95 @@ -/** - * @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" +#ifndef PRISM_INTERNAL_ENCODING_H +#define PRISM_INTERNAL_ENCODING_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. - */ + /* 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. - */ +/* 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); -/** +/* * 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 @@ -130,9 +97,7 @@ bool pm_encoding_utf_8_isupper_char(const uint8_t *b, ptrdiff_t n); */ extern const uint8_t pm_encoding_unicode_table[256]; -/** - * These are all of the encodings that prism supports. - */ +/* These are all of the encodings that prism supports. */ typedef enum { PM_ENCODING_UTF_8 = 0, PM_ENCODING_US_ASCII, @@ -140,8 +105,8 @@ typedef enum { 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. +/* 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, @@ -233,50 +198,44 @@ typedef enum { PM_ENCODING_MAXIMUM } pm_encoding_type_t; -/** - * This is the table of all of the encodings that prism supports. - */ +/* 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); diff --git a/include/prism/internal/integer.h b/include/prism/internal/integer.h new file mode 100644 index 0000000000..7c9767e323 --- /dev/null +++ b/include/prism/internal/integer.h @@ -0,0 +1,68 @@ +/* + * This module provides functions for working with arbitrary-sized integers. + */ +#ifndef PRISM_INTERNAL_INTEGER_H +#define PRISM_INTERNAL_INTEGER_H + +#include "prism/buffer.h" +#include "prism/integer.h" + +#include + +/* + * 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. + */ +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. + */ +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. + */ +void pm_integers_reduce(pm_integer_t *numerator, pm_integer_t *denominator); + +/* Convert an integer to a decimal string. */ +void pm_integer_string(pm_buffer_t *buffer, const pm_integer_t *integer); + +#endif diff --git a/include/prism/internal/isinf.h b/include/prism/internal/isinf.h new file mode 100644 index 0000000000..41c160f56d --- /dev/null +++ b/include/prism/internal/isinf.h @@ -0,0 +1,16 @@ +#ifndef PRISM_INTERNAL_ISINF_H +#define PRISM_INTERNAL_ISINF_H + +/* + * isinf on POSIX systems 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 + +#endif diff --git a/include/prism/internal/line_offset_list.h b/include/prism/internal/line_offset_list.h new file mode 100644 index 0000000000..dac9f7052e --- /dev/null +++ b/include/prism/internal/line_offset_list.h @@ -0,0 +1,34 @@ +#ifndef PRISM_INTERNAL_LINE_OFFSET_LIST_H +#define PRISM_INTERNAL_LINE_OFFSET_LIST_H + +#include "prism/compiler/force_inline.h" + +#include "prism/arena.h" +#include "prism/line_offset_list.h" + +/* Initialize a new line offset list with the given capacity. */ +void pm_line_offset_list_init(pm_arena_t *arena, pm_line_offset_list_t *list, size_t capacity); + +/* Clear out the offsets that have been appended to the list. */ +void pm_line_offset_list_clear(pm_line_offset_list_t *list); + +/* Append a new offset to the list (slow path with resize). */ +void pm_line_offset_list_append_slow(pm_arena_t *arena, pm_line_offset_list_t *list, uint32_t cursor); + +/* Append a new offset to the list. */ +static PRISM_FORCE_INLINE void +pm_line_offset_list_append(pm_arena_t *arena, pm_line_offset_list_t *list, uint32_t cursor) { + if (list->size < list->capacity) { + list->offsets[list->size++] = cursor; + } else { + pm_line_offset_list_append_slow(arena, list, 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. + */ +int32_t pm_line_offset_list_line(const pm_line_offset_list_t *list, uint32_t cursor, int32_t start_line); + +#endif diff --git a/include/prism/util/pm_list.h b/include/prism/internal/list.h similarity index 56% rename from include/prism/util/pm_list.h rename to include/prism/internal/list.h index f544bb2943..0ab59ef32a 100644 --- a/include/prism/util/pm_list.h +++ b/include/prism/internal/list.h @@ -1,19 +1,9 @@ -/** - * @file pm_list.h - * - * An abstract linked list. - */ -#ifndef PRISM_LIST_H -#define PRISM_LIST_H +#ifndef PRISM_INTERNAL_LIST_H +#define PRISM_INTERNAL_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. @@ -44,60 +34,29 @@ * iteration and appending of new nodes. */ typedef struct pm_list_node { - /** A pointer to the next node in the list. */ + /* 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. */ + /* The size of the list. */ size_t size; - /** A pointer to the head of the list. */ + /* A pointer to the head of the list. */ pm_list_node_t *head; - /** A pointer to the tail of the list. */ + /* 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); +/* Returns the size of the list. */ +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. - */ +/* Append a node to the given list. */ 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/include/prism/internal/magic_comments.h b/include/prism/internal/magic_comments.h new file mode 100644 index 0000000000..72a581c5d7 --- /dev/null +++ b/include/prism/internal/magic_comments.h @@ -0,0 +1,23 @@ +#ifndef PRISM_INTERNAL_MAGIC_COMMENTS_H +#define PRISM_INTERNAL_MAGIC_COMMENTS_H + +#include "prism/magic_comments.h" + +#include "prism/internal/list.h" + +/* + * This is a node in the linked list of magic comments that we've found while + * parsing. + */ +struct pm_magic_comment_t { + /* The embedded base node. */ + pm_list_node_t node; + + /* The key of the magic comment. */ + pm_location_t key; + + /* The value of the magic comment. */ + pm_location_t value; +}; + +#endif diff --git a/include/prism/internal/memchr.h b/include/prism/internal/memchr.h new file mode 100644 index 0000000000..63c738387d --- /dev/null +++ b/include/prism/internal/memchr.h @@ -0,0 +1,15 @@ +#ifndef PRISM_INTERNAL_MEMCHR_H +#define PRISM_INTERNAL_MEMCHR_H + +#include "prism/internal/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. + */ +void * pm_memchr(const void *source, int character, size_t number, bool encoding_changed, const pm_encoding_t *encoding); + +#endif diff --git a/include/prism/internal/node.h b/include/prism/internal/node.h new file mode 100644 index 0000000000..ca6d5616d7 --- /dev/null +++ b/include/prism/internal/node.h @@ -0,0 +1,32 @@ +#ifndef PRISM_INTERNAL_NODE_H +#define PRISM_INTERNAL_NODE_H + +#include "prism/node.h" + +#include "prism/compiler/force_inline.h" + +#include "prism/arena.h" + +/* + * Slow path for pm_node_list_append: grow the list and append the node. + * Do not call directly — use pm_node_list_append instead. + */ +void pm_node_list_append_slow(pm_arena_t *arena, pm_node_list_t *list, pm_node_t *node); + +/* Append a new node onto the end of the node list. */ +static PRISM_FORCE_INLINE void +pm_node_list_append(pm_arena_t *arena, pm_node_list_t *list, pm_node_t *node) { + if (list->size < list->capacity) { + list->nodes[list->size++] = node; + } else { + pm_node_list_append_slow(arena, list, node); + } +} + +/* Prepend a new node onto the beginning of the node list. */ +void pm_node_list_prepend(pm_arena_t *arena, pm_node_list_t *list, pm_node_t *node); + +/* Concatenate the given node list onto the end of the other node list. */ +void pm_node_list_concat(pm_arena_t *arena, pm_node_list_t *list, pm_node_list_t *other); + +#endif diff --git a/include/prism/internal/options.h b/include/prism/internal/options.h new file mode 100644 index 0000000000..7e37742a8b --- /dev/null +++ b/include/prism/internal/options.h @@ -0,0 +1,212 @@ +#ifndef PRISM_INTERNAL_OPTIONS_H +#define PRISM_INTERNAL_OPTIONS_H + +#include "prism/options.h" + +/* A scope of locals surrounding the code that is being parsed. */ +struct pm_options_scope_t { + /* 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; +}; + +/* + * 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. */ +struct pm_options_t { + /* + * 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; +}; + +/* Free the internal memory associated with the options. */ +void pm_options_cleanup(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 | + * | `2` | use the version of prism that is vendored in CRuby 3.4.0 | + * | `3` | use the version of prism that is vendored in CRuby 4.0.0 | + * | `4` | use the version of prism that is vendored in CRuby 4.1.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. + */ +void pm_options_read(pm_options_t *options, const char *data); + +#endif diff --git a/include/prism/internal/parser.h b/include/prism/internal/parser.h new file mode 100644 index 0000000000..3afe226757 --- /dev/null +++ b/include/prism/internal/parser.h @@ -0,0 +1,957 @@ +#ifndef PRISM_INTERNAL_PARSER_H +#define PRISM_INTERNAL_PARSER_H + +#include "prism/compiler/accel.h" + +#include "prism/internal/arena.h" +#include "prism/internal/constant_pool.h" +#include "prism/internal/encoding.h" +#include "prism/internal/list.h" +#include "prism/internal/options.h" +#include "prism/internal/static_literals.h" + +#include "prism/ast.h" +#include "prism/line_offset_list.h" +#include "prism/parser.h" + +#include +#include +#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 + +/* + * 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; + +/* 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; + + /* + * A bloom filter over constant IDs stored in this set. Used to quickly + * reject lookups for names that are definitely not present, avoiding the + * cost of a linear scan or hash probe. + */ + uint32_t bloom; + + /* 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_t { + /* The arena used for all AST-lifetime allocations. Caller-owned. */ + pm_arena_t *arena; + + /* The arena used for parser metadata (comments, diagnostics, etc.). */ + pm_arena_t metadata_arena; + + /* + * 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; + + /* + * A single-entry cache for pm_parser_constant_id_raw. Avoids redundant + * constant pool lookups when the same token is resolved multiple times + * (e.g., once during lexing for local variable detection, and again + * during parsing for node creation). + */ + struct { + const uint8_t *start; + const uint8_t *end; + pm_constant_id_t id; + } constant_cache; + + /* 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; + + /* + * 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. + */ + struct { + /* + * 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. + */ + pm_lex_callback_t callback; + + /* + * 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; + } 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 line offsets in the source file. */ + pm_line_offset_list_t line_offsets; + + /* + * State communicated from the lexer to the parser for integer tokens. + */ + struct { + /* + * A flag indicating the base of the integer (binary, octal, decimal, + * hexadecimal). Set during lexing and read during node creation. + */ + pm_node_flags_t base; + + /* + * When lexing a decimal integer that fits in a uint32_t, we compute + * the value during lexing to avoid re-scanning the digits during + * parsing. If lexed is true, this holds the result and + * pm_integer_parse can be skipped. + */ + uint32_t value; + + /* Whether value holds a valid pre-computed integer. */ + bool lexed; + } integer; + + /* + * 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 parsing the body of an endless method + * definition. In this context, PM_TOKEN_KEYWORD_DO_BLOCK should not be + * consumed by commands (it should bubble up to the outer context). + */ + bool in_endless_def_body; + + /* Whether or not we're currently recovering from a syntax error. */ + bool recovering; + + /* + * Whether or not the source being parsed could become valid if more input + * were appended. This is set to false when the parser encounters a token + * that is definitively wrong (e.g., a stray `end` or `]`) as opposed to + * merely incomplete. + */ + bool continuable; + + /* + * 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; + + /* + * By default, Ruby always warns about mismatched indentation. This can be + * toggled with a magic comment. + */ + bool warn_mismatched_indentation; + +#if defined(PRISM_HAS_NEON) || defined(PRISM_HAS_SSSE3) || defined(PRISM_HAS_SWAR) + /* + * Cached lookup tables for pm_strpbrk's SIMD fast path. Avoids rebuilding + * the nibble-based tables on every call when the charset hasn't changed + * (which is the common case during string/regex/list lexing). + */ + struct { + /* The cached charset (null-terminated, max 11 chars + NUL). */ + uint8_t charset[12]; + + /* Nibble-based low lookup table for SIMD matching. */ + uint8_t low_lut[16]; + + /* Nibble-based high lookup table for SIMD matching. */ + uint8_t high_lut[16]; + + /* Scalar fallback table (4 x 64-bit bitmasks covering all ASCII). */ + uint64_t table[4]; + } strpbrk_cache; +#endif +}; + +/* + * Initialize a parser with the given start and end pointers. + */ +void pm_parser_init(pm_arena_t *arena, pm_parser_t *parser, const uint8_t *source, size_t size, const pm_options_t *options); + +/* + * Free the memory held by the given parser. + * + * This does not free the `pm_options_t` object that was used to initialize the + * parser. + */ +void pm_parser_cleanup(pm_parser_t *parser); + +#endif diff --git a/include/prism/internal/regexp.h b/include/prism/internal/regexp.h new file mode 100644 index 0000000000..3710c984fc --- /dev/null +++ b/include/prism/internal/regexp.h @@ -0,0 +1,41 @@ +#ifndef PRISM_INTERNAL_REGEXP_H +#define PRISM_INTERNAL_REGEXP_H + +#include "prism/ast.h" +#include "prism/parser.h" + +/* + * Accumulation state for named capture groups found during regexp parsing. + * The caller initializes this with the call node and passes it to + * pm_regexp_parse. The regexp parser populates match and names as groups + * are found. + */ +typedef struct { + /* The call node wrapping the regular expression node (for =~). */ + pm_call_node_t *call; + + /* The match write node being built, or NULL if no captures found yet. */ + pm_match_write_node_t *match; + + /* The list of capture names found so far (for deduplication). */ + pm_constant_id_list_t names; +} pm_regexp_name_data_t; + +/* + * Callback invoked by pm_regexp_parse() for each named capture group found. + */ +typedef void (*pm_regexp_name_callback_t)(pm_parser_t *parser, const pm_string_t *name, bool shared, pm_regexp_name_data_t *data); + +/* + * Parse a regular expression, validate its encoding, and optionally extract + * named capture groups. Returns the encoding flags to set on the node. + */ +PRISM_EXPORTED_FUNCTION pm_node_flags_t pm_regexp_parse(pm_parser_t *parser, pm_regular_expression_node_t *node, pm_regexp_name_callback_t name_callback, pm_regexp_name_data_t *name_data); + +/* + * Parse an interpolated regular expression for named capture groups only. + * No encoding validation is performed. + */ +void pm_regexp_parse_named_captures(pm_parser_t *parser, const uint8_t *source, size_t size, bool shared, bool extended_mode, pm_regexp_name_callback_t name_callback, pm_regexp_name_data_t *name_data); + +#endif diff --git a/include/prism/internal/serialize.h b/include/prism/internal/serialize.h new file mode 100644 index 0000000000..e611a0374b --- /dev/null +++ b/include/prism/internal/serialize.h @@ -0,0 +1,34 @@ +#ifndef PRISM_INTERNAL_SERIALIZE_H +#define PRISM_INTERNAL_SERIALIZE_H + +#include "prism/internal/encoding.h" +#include "prism/internal/list.h" + +#include "prism/ast.h" +#include "prism/buffer.h" +#include "prism/excludes.h" +#include "prism/parser.h" + +/* We optionally support serializing to a binary string. For systems that do not + * want or need this functionality, it can be turned off with the + * PRISM_EXCLUDE_SERIALIZATION define. */ +#ifndef PRISM_EXCLUDE_SERIALIZATION + +/* + * Serialize the given list of comments to the given buffer. + */ +void pm_serialize_comment_list(pm_list_t *list, pm_buffer_t *buffer); + +/* + * Serialize the name of the encoding to the buffer. + */ +void pm_serialize_encoding(const pm_encoding_t *encoding, pm_buffer_t *buffer); + +/* + * Serialize the encoding, metadata, nodes, and constant pool. + */ +void pm_serialize_content(pm_parser_t *parser, pm_node_t *node, pm_buffer_t *buffer); + +#endif + +#endif diff --git a/include/prism/internal/source.h b/include/prism/internal/source.h new file mode 100644 index 0000000000..b3c2b55be3 --- /dev/null +++ b/include/prism/internal/source.h @@ -0,0 +1,72 @@ +#ifndef PRISM_INTERNAL_SOURCE_H +#define PRISM_INTERNAL_SOURCE_H + +#include "prism/source.h" +#include "prism/buffer.h" + +#include + +/* + * The type of source, which determines cleanup behavior. + */ +typedef enum { + /* Wraps existing constant memory, no cleanup. */ + PM_SOURCE_CONSTANT, + + /* Wraps existing shared memory (non-owning slice), no cleanup. */ + PM_SOURCE_SHARED, + + /* Owns a heap-allocated buffer, freed on cleanup. */ + PM_SOURCE_OWNED, + + /* Memory-mapped file, unmapped on cleanup. */ + PM_SOURCE_MAPPED, + + /* Stream source backed by a pm_buffer_t. */ + PM_SOURCE_STREAM +} pm_source_type_t; + +/* + * The internal representation of a source. + */ +struct pm_source_t { + /* A pointer to the start of the source data. */ + const uint8_t *source; + + /* The length of the source data in bytes. */ + size_t length; + + /* The type of the source. */ + pm_source_type_t type; + + /* Stream-specific data, only used for PM_SOURCE_STREAM sources. */ + struct { + /* The buffer that holds the accumulated stream data. */ + pm_buffer_t *buffer; + + /* The stream object to read from. */ + void *stream; + + /* The function to use to read from the stream. */ + pm_source_stream_fgets_t *fgets; + + /* The function to use to check if the stream is at EOF. */ + pm_source_stream_feof_t *feof; + + /* Whether the stream has reached EOF. */ + bool eof; + } stream; +}; + +/* + * Read from a stream into the source's internal buffer. This is used by + * pm_parse_stream to incrementally read the source. + */ +bool pm_source_stream_read(pm_source_t *source); + +/* + * Returns whether the stream source has reached EOF. + */ +bool pm_source_stream_eof(const pm_source_t *source); + +#endif diff --git a/include/prism/static_literals.h b/include/prism/internal/static_literals.h similarity index 51% rename from include/prism/static_literals.h rename to include/prism/internal/static_literals.h index 0f8eb43bfa..d59002ac0a 100644 --- a/include/prism/static_literals.h +++ b/include/prism/internal/static_literals.h @@ -1,33 +1,25 @@ -/** - * @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 +#ifndef PRISM_INTERNAL_STATIC_LITERALS_H +#define PRISM_INTERNAL_STATIC_LITERALS_H -#include "prism/defines.h" #include "prism/ast.h" -#include "prism/util/pm_newline_list.h" - -#include -#include +#include "prism/buffer.h" +#include "prism/line_offset_list.h" -/** +/* * An internal hash table for a set of nodes. */ typedef struct { - /** The array of nodes in the hash table. */ + /* The array of nodes in the hash table. */ pm_node_t **nodes; - /** The size of the hash table. */ + /* The size of the hash table. */ uint32_t size; - /** The space that has been allocated in the hash table. */ + /* 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. @@ -36,88 +28,71 @@ typedef struct { * 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 The start of the source being parsed. - * @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, const uint8_t *start, int32_t start_line, pm_static_literals_t *literals, pm_node_t *node, bool replace); +pm_node_t * pm_static_literals_add(const pm_line_offset_list_t *line_offsets, const uint8_t *start, 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 The start of the source being parsed. - * @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, const uint8_t *start, int32_t start_line, const char *encoding_name, const pm_node_t *node); +void pm_static_literal_inspect(pm_buffer_t *buffer, const pm_line_offset_list_t *line_offsets, const uint8_t *start, int32_t start_line, const char *encoding_name, const pm_node_t *node); #endif diff --git a/include/prism/internal/stringy.h b/include/prism/internal/stringy.h new file mode 100644 index 0000000000..1aaa23ea75 --- /dev/null +++ b/include/prism/internal/stringy.h @@ -0,0 +1,30 @@ +#ifndef PRISM_INTERNAL_STRINGY_H +#define PRISM_INTERNAL_STRINGY_H + +#include "prism/stringy.h" + +/* + * 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. + */ +void pm_string_shared_init(pm_string_t *string, const uint8_t *start, const uint8_t *end); + +/* + * 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); + +/* + * Free the associated memory of the given string. + */ +void pm_string_cleanup(pm_string_t *string); + +#endif diff --git a/include/prism/util/pm_strncasecmp.h b/include/prism/internal/strncasecmp.h similarity index 51% rename from include/prism/util/pm_strncasecmp.h rename to include/prism/internal/strncasecmp.h index 5cb88cb5eb..775f6a993e 100644 --- a/include/prism/util/pm_strncasecmp.h +++ b/include/prism/internal/strncasecmp.h @@ -1,18 +1,10 @@ -/** - * @file pm_strncasecmp.h - * - * A custom strncasecmp implementation. - */ -#ifndef PRISM_STRNCASECMP_H -#define PRISM_STRNCASECMP_H +#ifndef PRISM_INTERNAL_STRNCASECMP_H +#define PRISM_INTERNAL_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. @@ -20,12 +12,6 @@ * 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); diff --git a/include/prism/util/pm_strpbrk.h b/include/prism/internal/strpbrk.h similarity index 66% rename from include/prism/util/pm_strpbrk.h rename to include/prism/internal/strpbrk.h index f387bd5782..d64156c002 100644 --- a/include/prism/util/pm_strpbrk.h +++ b/include/prism/internal/strpbrk.h @@ -1,19 +1,15 @@ -/** - * @file pm_strpbrk.h - * - * A custom strpbrk implementation. - */ -#ifndef PRISM_STRPBRK_H -#define PRISM_STRPBRK_H +#ifndef PRISM_INTERNAL_STRPBRK_H +#define PRISM_INTERNAL_STRPBRK_H -#include "prism/defines.h" -#include "prism/diagnostic.h" #include "prism/parser.h" +/* The maximum number of bytes in a strpbrk charset. */ +#define PM_STRPBRK_CACHE_SIZE 16 + #include -#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 @@ -31,15 +27,6 @@ * 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); diff --git a/include/prism/internal/tokens.h b/include/prism/internal/tokens.h new file mode 100644 index 0000000000..3a983e54ae --- /dev/null +++ b/include/prism/internal/tokens.h @@ -0,0 +1,11 @@ +#ifndef PRISM_INTERNAL_TOKENS_H +#define PRISM_INTERNAL_TOKENS_H + +#include "prism/ast.h" + +/* + * Returns the human name of the given token type. + */ +const char * pm_token_str(pm_token_type_t token_type); + +#endif diff --git a/include/prism/json.h b/include/prism/json.h new file mode 100644 index 0000000000..11039e7796 --- /dev/null +++ b/include/prism/json.h @@ -0,0 +1,32 @@ +/** + * @file json.h + */ +#ifndef PRISM_JSON_H +#define PRISM_JSON_H + +#include "prism/excludes.h" + +/* 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 + +#include "prism/compiler/exported.h" +#include "prism/compiler/nonnull.h" + +#include "prism/ast.h" +#include "prism/buffer.h" +#include "prism/parser.h" + +/** + * 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) PRISM_NONNULL(1, 2, 3); + +#endif + +#endif diff --git a/include/prism/line_offset_list.h b/include/prism/line_offset_list.h new file mode 100644 index 0000000000..848bc49139 --- /dev/null +++ b/include/prism/line_offset_list.h @@ -0,0 +1,61 @@ +/** + * @file line_offset_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_LINE_OFFSET_LIST_H +#define PRISM_LINE_OFFSET_LIST_H + +#include "prism/compiler/exported.h" +#include "prism/compiler/nonnull.h" + +#include +#include + +/** + * A list of offsets of the start of lines in a string. The offsets are assumed + * to be sorted/inserted in ascending order. + */ +typedef struct { + /** 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. */ + uint32_t *offsets; +} pm_line_offset_list_t; + +/** + * A line and column in a string. + */ +typedef struct { + /** The line number. */ + int32_t line; + + /** The column in bytes. */ + uint32_t column; +} pm_line_column_t; + +/** + * 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 The offset to search for. + * @param start_line The line to start counting from. + * @returns The line and column of the given offset. + */ +PRISM_EXPORTED_FUNCTION pm_line_column_t pm_line_offset_list_line_column(const pm_line_offset_list_t *list, uint32_t cursor, int32_t start_line) PRISM_NONNULL(1); + +#endif diff --git a/include/prism/magic_comments.h b/include/prism/magic_comments.h new file mode 100644 index 0000000000..c9d6b600e8 --- /dev/null +++ b/include/prism/magic_comments.h @@ -0,0 +1,35 @@ +/** + * @file magic_comments.h + * + * Types and functions related to magic comments found during parsing. + */ +#ifndef PRISM_MAGIC_COMMENTS_H +#define PRISM_MAGIC_COMMENTS_H + +#include "prism/compiler/exported.h" +#include "prism/compiler/nonnull.h" + +#include "prism/ast.h" + +#include + +/** An opaque pointer to a magic comment found while parsing. */ +typedef struct pm_magic_comment_t pm_magic_comment_t; + +/** + * Returns the location of the key associated with the given magic comment. + * + * @param magic_comment the magic comment whose key location we want to get + * @returns the location of the key associated with the given magic comment + */ +PRISM_EXPORTED_FUNCTION pm_location_t pm_magic_comment_key(const pm_magic_comment_t *magic_comment) PRISM_NONNULL(1); + +/** + * Returns the location of the value associated with the given magic comment. + * + * @param magic_comment the magic comment whose value location we want to get + * @returns the location of the value associated with the given magic comment + */ +PRISM_EXPORTED_FUNCTION pm_location_t pm_magic_comment_value(const pm_magic_comment_t *magic_comment) PRISM_NONNULL(1); + +#endif diff --git a/include/prism/node.h b/include/prism/node.h index e8686a327c..75bc3c9b2d 100644 --- a/include/prism/node.h +++ b/include/prism/node.h @@ -6,9 +6,10 @@ #ifndef PRISM_NODE_H #define PRISM_NODE_H -#include "prism/defines.h" -#include "prism/parser.h" -#include "prism/util/pm_buffer.h" +#include "prism/compiler/exported.h" +#include "prism/compiler/nonnull.h" + +#include "prism/ast.h" /** * Loop through each node in the node list, writing each node to the given @@ -17,52 +18,13 @@ #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. + * @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); +PRISM_EXPORTED_FUNCTION const char * pm_node_type(pm_node_type_t node_type); /** * Visit each of the nodes in this subtree using the given visitor callback. The @@ -80,7 +42,7 @@ PRISM_EXPORTED_FUNCTION const char * pm_node_type_to_str(pm_node_type_t node_typ * 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)); + * printf("%s\n", pm_node_type(node->type)); * * size_t next_indent = *indent + 1; * size_t *next_data = &next_indent; @@ -93,18 +55,21 @@ PRISM_EXPORTED_FUNCTION const char * pm_node_type_to_str(pm_node_type_t node_typ * 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); + * pm_arena_t *arena = pm_arena_new(); + * pm_options_t *options = pm_options_new(); + * + * pm_parser_t *parser = pm_parser_new(arena, (const uint8_t *) source, size, options); * * size_t indent = 0; - * pm_node_t *node = pm_parse(&parser); + * 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); + * pm_parser_free(parser); + * pm_options_free(options); + * pm_arena_free(arena); + * * return EXIT_SUCCESS; * } * ``` @@ -113,7 +78,7 @@ PRISM_EXPORTED_FUNCTION const char * pm_node_type_to_str(pm_node_type_t node_typ * @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); +PRISM_EXPORTED_FUNCTION void pm_visit_node(const pm_node_t *node, bool (*visitor)(const pm_node_t *node, void *data), void *data) PRISM_NONNULL(1); /** * Visit the children of the given node with the given callback. This is the @@ -124,6 +89,6 @@ PRISM_EXPORTED_FUNCTION void pm_visit_node(const pm_node_t *node, bool (*visitor * @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); +PRISM_EXPORTED_FUNCTION void pm_visit_child_nodes(const pm_node_t *node, bool (*visitor)(const pm_node_t *node, void *data), void *data) PRISM_NONNULL(1); #endif diff --git a/include/prism/options.h b/include/prism/options.h index 9a19a2aead..1b6ff4af1f 100644 --- a/include/prism/options.h +++ b/include/prism/options.h @@ -6,16 +6,27 @@ #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 "prism/compiler/exported.h" +#include "prism/compiler/nodiscard.h" +#include "prism/compiler/nonnull.h" + +#include "prism/stringy.h" #include #include -#include /** - * String literals should be made frozen. + * A scope of locals surrounding the code that is being parsed. + */ +typedef struct pm_options_scope_t pm_options_scope_t; + +/** + * The options that can be passed to the parser. + */ +typedef struct pm_options_t pm_options_t; + +/** + * String literals should not be frozen. */ #define PM_OPTIONS_FROZEN_STRING_LITERAL_DISABLED ((int8_t) -1) @@ -26,42 +37,25 @@ #define PM_OPTIONS_FROZEN_STRING_LITERAL_UNSET ((int8_t) 0) /** - * String literals should be made mutable. + * String literals should be made frozen. */ #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. */ +/** When the scope is forwarding with the * parameter. */ static const uint8_t PM_OPTIONS_SCOPE_FORWARDING_POSITIONALS = 0x1; -/** When the scope is fowarding with the ** parameter. */ +/** When the scope is forwarding with the ** parameter. */ static const uint8_t PM_OPTIONS_SCOPE_FORWARDING_KEYWORDS = 0x2; -/** When the scope is fowarding with the & parameter. */ +/** When the scope is forwarding with the & parameter. */ static const uint8_t PM_OPTIONS_SCOPE_FORWARDING_BLOCK = 0x4; -/** When the scope is fowarding with the ... parameter. */ +/** When the scope is forwarding 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. @@ -74,133 +68,7 @@ struct pm_options; * @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; +typedef void (*pm_options_shebang_callback_t)(pm_options_t *options, const uint8_t *source, size_t length, void *shebang_callback_data); /** * A bit representing whether or not the command line -a option was set. -a @@ -239,6 +107,22 @@ static const uint8_t PM_OPTIONS_COMMAND_LINE_P = 0x10; */ static const uint8_t PM_OPTIONS_COMMAND_LINE_X = 0x20; +/** + * Allocate a new options struct. If the options struct cannot be allocated, + * this function aborts the process. + * + * @returns A new options struct with default values. It is the responsibility + * of the caller to free this struct using pm_options_free(). + */ +PRISM_EXPORTED_FUNCTION PRISM_NODISCARD pm_options_t * pm_options_new(void); + +/** + * Free both the held memory of the given options struct and the struct itself. + * + * @param options The options struct to free. + */ +PRISM_EXPORTED_FUNCTION void pm_options_free(pm_options_t *options) PRISM_NONNULL(1); + /** * Set the shebang callback option on the given options struct. * @@ -246,70 +130,64 @@ static const uint8_t PM_OPTIONS_COMMAND_LINE_X = 0x20; * @param shebang_callback The shebang callback to set. * @param shebang_callback_data Any additional data that should be passed along * to the callback. + */ +PRISM_EXPORTED_FUNCTION void pm_options_shebang_callback_set(pm_options_t *options, pm_options_shebang_callback_t shebang_callback, void *shebang_callback_data) PRISM_NONNULL(1); + +/** + * Get the filepath option on the given options struct. * - * \public \memberof pm_options + * @param options The options struct to get the filepath from. + * @returns The filepath. */ -PRISM_EXPORTED_FUNCTION void pm_options_shebang_callback_set(pm_options_t *options, pm_options_shebang_callback_t shebang_callback, void *shebang_callback_data); +PRISM_EXPORTED_FUNCTION const pm_string_t * pm_options_filepath(const pm_options_t *options) PRISM_NONNULL(1); /** * 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); +PRISM_EXPORTED_FUNCTION void pm_options_filepath_set(pm_options_t *options, const char *filepath) PRISM_NONNULL(1); /** * 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); +PRISM_EXPORTED_FUNCTION void pm_options_line_set(pm_options_t *options, int32_t line) PRISM_NONNULL(1); /** * 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); +PRISM_EXPORTED_FUNCTION void pm_options_encoding_set(pm_options_t *options, const char *encoding) PRISM_NONNULL(1); /** * 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); +PRISM_EXPORTED_FUNCTION void pm_options_encoding_locked_set(pm_options_t *options, bool encoding_locked) PRISM_NONNULL(1); /** * 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); +PRISM_EXPORTED_FUNCTION void pm_options_frozen_string_literal_set(pm_options_t *options, bool frozen_string_literal) PRISM_NONNULL(1); /** * 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); +PRISM_EXPORTED_FUNCTION void pm_options_command_line_set(pm_options_t *options, uint8_t command_line) PRISM_NONNULL(1); /** * Set the version option on the given options struct by parsing the given @@ -319,176 +197,123 @@ PRISM_EXPORTED_FUNCTION void pm_options_command_line_set(pm_options_t *options, * @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. + * @returns Whether or not the version was parsed successfully. + */ +PRISM_EXPORTED_FUNCTION bool pm_options_version_set(pm_options_t *options, const char *version, size_t length) PRISM_NONNULL(1); + +/** + * Set the version option on the given options struct to the lowest version of + * Ruby that prism supports. + * + * @param options The options struct to set the version on. + */ +PRISM_EXPORTED_FUNCTION void pm_options_version_set_lowest(pm_options_t *options) PRISM_NONNULL(1); + +/** + * Set the version option on the given options struct to the highest version of + * Ruby that prism supports. * - * \public \memberof pm_options + * @param options The options struct to set the version on. */ -PRISM_EXPORTED_FUNCTION bool pm_options_version_set(pm_options_t *options, const char *version, size_t length); +PRISM_EXPORTED_FUNCTION void pm_options_version_set_highest(pm_options_t *options) PRISM_NONNULL(1); /** * 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); +PRISM_EXPORTED_FUNCTION void pm_options_main_script_set(pm_options_t *options, bool main_script) PRISM_NONNULL(1); /** * 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. + */ +PRISM_EXPORTED_FUNCTION void pm_options_partial_script_set(pm_options_t *options, bool partial_script) PRISM_NONNULL(1); + +/** + * Get the freeze option on the given options struct. * - * \public \memberof pm_options + * @param options The options struct to get the freeze value from. + * @returns The freeze value. */ -PRISM_EXPORTED_FUNCTION void pm_options_partial_script_set(pm_options_t *options, bool partial_script); +PRISM_EXPORTED_FUNCTION bool pm_options_freeze(const pm_options_t *options) PRISM_NONNULL(1); /** * 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); +PRISM_EXPORTED_FUNCTION void pm_options_freeze_set(pm_options_t *options, bool freeze) PRISM_NONNULL(1); /** * 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 + * @returns Whether or not the scopes array was initialized successfully. */ -PRISM_EXPORTED_FUNCTION bool pm_options_scopes_init(pm_options_t *options, size_t scopes_count); +PRISM_EXPORTED_FUNCTION bool pm_options_scopes_init(pm_options_t *options, size_t scopes_count) PRISM_NONNULL(1); /** - * Return a pointer to the scope at the given index within the given options. + * Return a constant 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. + * @returns A constant pointer to the scope at the given index. + */ +PRISM_EXPORTED_FUNCTION const pm_options_scope_t * pm_options_scope(const pm_options_t *options, size_t index) PRISM_NONNULL(1); + +/** + * Return a mutable pointer to the scope at the given index within the given + * options. * - * \public \memberof pm_options + * @param options The options struct to get the scope from. + * @param index The index of the scope to get. + * @returns A mutable pointer to the scope at the given index. */ -PRISM_EXPORTED_FUNCTION const pm_options_scope_t * pm_options_scope_get(const pm_options_t *options, size_t index); +PRISM_EXPORTED_FUNCTION pm_options_scope_t * pm_options_scope_mut(pm_options_t *options, size_t index) PRISM_NONNULL(1); /** * Create a new options scope struct. This will hold a set of locals that are in - * scope surrounding the code that is being parsed. + * scope surrounding the code that is being parsed. If the scope was unable to + * allocate its locals, this function will abort the process. * * @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); +PRISM_EXPORTED_FUNCTION void pm_options_scope_init(pm_options_scope_t *scope, size_t locals_count) PRISM_NONNULL(1); /** - * Return a pointer to the local at the given index within the given scope. + * Return a constant 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 + * @returns A constant pointer to the local at the given index. */ -PRISM_EXPORTED_FUNCTION const pm_string_t * pm_options_scope_local_get(const pm_options_scope_t *scope, size_t index); +PRISM_EXPORTED_FUNCTION const pm_string_t * pm_options_scope_local(const pm_options_scope_t *scope, size_t index) PRISM_NONNULL(1); /** - * Set the forwarding option on the given scope struct. + * Return a mutable pointer to the local at the given index within the given + * scope. * - * @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 + * @param scope The scope struct to get the local from. + * @param index The index of the local to get. + * @returns A mutable pointer to the local at the given index. */ -PRISM_EXPORTED_FUNCTION void pm_options_free(pm_options_t *options); +PRISM_EXPORTED_FUNCTION pm_string_t * pm_options_scope_local_mut(pm_options_scope_t *scope, size_t index) PRISM_NONNULL(1); /** - * 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 | - * | `2` | use the version of prism that is vendored in CRuby 3.4.0 | - * | `3` | use the version of prism that is vendored in CRuby 4.0.0 | - * | `4` | use the version of prism that is vendored in CRuby 4.1.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. + * Set the forwarding option on the given scope struct. * - * @param options The options struct to deserialize into. - * @param data The binary string to deserialize from. + * @param scope The scope struct to set the forwarding on. + * @param forwarding The forwarding value to set. */ -void pm_options_read(pm_options_t *options, const char *data); +PRISM_EXPORTED_FUNCTION void pm_options_scope_forwarding_set(pm_options_scope_t *scope, uint8_t forwarding) PRISM_NONNULL(1); #endif diff --git a/include/prism/pack.h b/include/prism/pack.h deleted file mode 100644 index 0b0b4b19cc..0000000000 --- a/include/prism/pack.h +++ /dev/null @@ -1,163 +0,0 @@ -/** - * @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/include/prism/parser.h b/include/prism/parser.h index a8d840d3bf..2c8c4b3a7a 100644 --- a/include/prism/parser.h +++ b/include/prism/parser.h @@ -6,925 +6,343 @@ #ifndef PRISM_PARSER_H #define PRISM_PARSER_H -#include "prism/defines.h" +#include "prism/compiler/nodiscard.h" +#include "prism/compiler/nonnull.h" + #include "prism/ast.h" -#include "prism/encoding.h" +#include "prism/comments.h" +#include "prism/diagnostic.h" +#include "prism/line_offset_list.h" +#include "prism/magic_comments.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; +typedef struct pm_parser_t 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. + * Allocate and initialize a parser with the given start and end pointers. + * + * @param arena The arena to use for all AST-lifetime allocations. It is caller- + * owned and must outlive the parser. + * @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. + * @returns The initialized parser. It is the responsibility of the caller to + * free the parser with `pm_parser_free()`. */ -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, +PRISM_EXPORTED_FUNCTION PRISM_NODISCARD pm_parser_t * pm_parser_new(pm_arena_t *arena, const uint8_t *source, size_t size, const pm_options_t *options) PRISM_NONNULL(1); - /** 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, +/** + * Free both the memory held by the given parser and the parser itself. + * + * @param parser The parser to free. + */ +PRISM_EXPORTED_FUNCTION void pm_parser_free(pm_parser_t *parser) PRISM_NONNULL(1); - /** a lambda expression with do..end */ - PM_CONTEXT_LAMBDA_DO_END, +/** + * 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); - /** an ensure statement within a lambda expression */ - PM_CONTEXT_LAMBDA_ENSURE, +/** + * 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. + */ +typedef void (*pm_lex_callback_t)(pm_parser_t *parser, pm_token_t *token, void *data); - /** a rescue else statement within a lambda expression */ - PM_CONTEXT_LAMBDA_ELSE, +/** + * Register a callback that will be called whenever prism changes the encoding + * it is using to parse based on the magic comment. + * + * @param parser The parser to register the callback with. + * @param callback The callback to register. + */ +PRISM_EXPORTED_FUNCTION void pm_parser_encoding_changed_callback_set(pm_parser_t *parser, pm_encoding_changed_callback_t callback) PRISM_NONNULL(1); - /** a rescue statement within a lambda expression */ - PM_CONTEXT_LAMBDA_RESCUE, +/** + * Register a callback that will be called whenever a token is lexed. + * + * @param parser The parser to register the callback with. + * @param data The opaque data to pass to the callback when it is called. + * @param callback The callback to register. + */ +PRISM_EXPORTED_FUNCTION void pm_parser_lex_callback_set(pm_parser_t *parser, pm_lex_callback_t callback, void *data) PRISM_NONNULL(1); - /** the predicate clause of a loop statement */ - PM_CONTEXT_LOOP_PREDICATE, +/** + * Returns the opaque data that is passed to the lex callback when it is called. + * + * @param parser The parser whose lex callback data we want to get. + * @returns The opaque data that is passed to the lex callback when it is called. + */ +PRISM_EXPORTED_FUNCTION void * pm_parser_lex_callback_data(const pm_parser_t *parser) PRISM_NONNULL(1); - /** the top level context */ - PM_CONTEXT_MAIN, +/** + * Returns the raw pointer to the start of the source that is being parsed. + * + * @param parser the parser whose start pointer we want to get + * @returns the raw pointer to the start of the source that is being parsed + */ +PRISM_EXPORTED_FUNCTION const uint8_t * pm_parser_start(const pm_parser_t *parser) PRISM_NONNULL(1); - /** a module declaration */ - PM_CONTEXT_MODULE, +/** + * Returns the raw pointer to the end of the source that is being parsed. + * + * @param parser the parser whose end pointer we want to get + * @returns the raw pointer to the end of the source that is being parsed + */ +PRISM_EXPORTED_FUNCTION const uint8_t * pm_parser_end(const pm_parser_t *parser) PRISM_NONNULL(1); - /** an ensure statement within a module statement */ - PM_CONTEXT_MODULE_ENSURE, +/** + * Returns the line that the parser was considered to have started on. + * + * @param parser the parser whose start line we want to get + * @returns the line that the parser was considered to have started on + */ +PRISM_EXPORTED_FUNCTION int32_t pm_parser_start_line(const pm_parser_t *parser) PRISM_NONNULL(1); - /** a rescue else statement within a module statement */ - PM_CONTEXT_MODULE_ELSE, +/** + * Returns the name of the encoding that is being used to parse the source. + * + * @param parser the parser whose encoding name we want to get + * @returns the name of the encoding that is being used to parse the source + */ +PRISM_EXPORTED_FUNCTION const char * pm_parser_encoding_name(const pm_parser_t *parser) PRISM_NONNULL(1); - /** a rescue statement within a module statement */ - PM_CONTEXT_MODULE_RESCUE, +/** + * Returns the width of the character at the given pointer in the encoding that + * is being used to parse the source. + * + * @param parser the parser whose encoding we want to use + * @param start a pointer to the start of the character + * @param remaining the number of bytes remaining in the source + * @returns the width of the character in bytes + */ +PRISM_EXPORTED_FUNCTION size_t pm_parser_encoding_char_width(const pm_parser_t *parser, const uint8_t *start, ptrdiff_t remaining) PRISM_NONNULL(1, 2); - /** a multiple target expression */ - PM_CONTEXT_MULTI_TARGET, +/** + * Returns whether or not the parser is using the US-ASCII encoding. + * + * @param parser the parser to check + * @returns true if the parser is using US-ASCII encoding, false otherwise + */ +PRISM_EXPORTED_FUNCTION bool pm_parser_encoding_us_ascii(const pm_parser_t *parser) PRISM_NONNULL(1); - /** a parenthesized expression */ - PM_CONTEXT_PARENS, +/** + * Returns the filepath that is being used to parse the source. + * + * @param parser the parser whose filepath we want to get + * @returns a pointer to the filepath string + */ +PRISM_EXPORTED_FUNCTION const pm_string_t * pm_parser_filepath(const pm_parser_t *parser) PRISM_NONNULL(1); - /** an END block */ - PM_CONTEXT_POSTEXE, +/** + * Find a constant in the parser's constant pool. Returns the id of the + * constant, or 0 if the constant is not found. + * + * @param parser the parser whose constant pool we want to search + * @param start a pointer to the start of the string to search for + * @param length the length of the string to search for + * @returns the id of the constant, or 0 if the constant is not found + */ +PRISM_EXPORTED_FUNCTION pm_constant_id_t pm_parser_constant_find(const pm_parser_t *parser, const uint8_t *start, size_t length) PRISM_NONNULL(1, 2); - /** a predicate inside an if/elsif/unless statement */ - PM_CONTEXT_PREDICATE, +/** + * Returns the frozen string literal value of the parser, as determined by the + * frozen_string_literal magic comment or the option set on the parser. + * + * @param parser the parser whose frozen string literal value we want to get + * @returns -1 if disabled, 0 if unset, 1 if enabled + */ +PRISM_EXPORTED_FUNCTION int8_t pm_parser_frozen_string_literal(const pm_parser_t *parser) PRISM_NONNULL(1); - /** a BEGIN block */ - PM_CONTEXT_PREEXE, +/** + * Returns the line offsets that are associated with the given parser. + * + * @param parser the parser whose line offsets we want to get + * @returns the line offsets that are associated with the given parser + */ +PRISM_EXPORTED_FUNCTION const pm_line_offset_list_t * pm_parser_line_offsets(const pm_parser_t *parser) PRISM_NONNULL(1); - /** a modifier rescue clause */ - PM_CONTEXT_RESCUE_MODIFIER, +/** + * Returns the location of the __DATA__ section that is associated with the + * given parser. + * + * @param parser the parser whose data location we want to get + * @returns the location of the __DATA__ section that is associated with the + * given parser. If it is unset, then the length will be set to 0. + */ +PRISM_EXPORTED_FUNCTION const pm_location_t * pm_parser_data_loc(const pm_parser_t *parser) PRISM_NONNULL(1); - /** a singleton class definition */ - PM_CONTEXT_SCLASS, +/** + * Returns whether the given parser is continuable, meaning that it could become + * valid if more input were appended, as opposed to being definitively invalid. + * + * @param parser the parser whose continuable status we want to get + * @returns whether the given parser is continuable + */ +PRISM_EXPORTED_FUNCTION bool pm_parser_continuable(const pm_parser_t *parser) PRISM_NONNULL(1); - /** an ensure statement with a singleton class */ - PM_CONTEXT_SCLASS_ENSURE, +/** + * Returns the lex state of the parser. Note that this is an internal detail, + * and we are purposefully not returning an instance of the internal enum that + * we use to track this. This is only exposed because we need it for some very + * niche use cases. Most consumers should avoid this function. + * + * @param parser the parser whose lex state we want to get + * @returns the lex state of the parser + */ +PRISM_EXPORTED_FUNCTION int pm_parser_lex_state(const pm_parser_t *parser) PRISM_NONNULL(1); - /** a rescue else statement with a singleton class */ - PM_CONTEXT_SCLASS_ELSE, +/** + * Returns the number of comments associated with the given parser. + * + * @param parser the parser whose comments we want to get the size of + * @returns the number of comments associated with the given parser + */ +PRISM_EXPORTED_FUNCTION size_t pm_parser_comments_size(const pm_parser_t *parser) PRISM_NONNULL(1); - /** a rescue statement with a singleton class */ - PM_CONTEXT_SCLASS_RESCUE, +/** + * A callback function that can be used to process comments found while parsing. + */ +typedef void (*pm_comment_callback_t)(const pm_comment_t *comment, void *data); - /** a ternary expression */ - PM_CONTEXT_TERNARY, +/** + * Iterates over the comments associated with the given parser and calls the + * given callback for each comment. + * + * @param parser the parser whose comments we want to iterate over + * @param callback the callback function to call for each comment. This function + * will be passed a pointer to the comment and the data parameter passed to + * this function. + * @param data the data to pass to the callback function for each comment. This + * can be NULL if no data needs to be passed to the callback function. + */ +PRISM_EXPORTED_FUNCTION void pm_parser_comments_each(const pm_parser_t *parser, pm_comment_callback_t callback, void *data) PRISM_NONNULL(1); - /** an unless statement */ - PM_CONTEXT_UNLESS, +/** + * Returns the number of magic comments associated with the given parser. + * + * @param parser the parser whose magic comments we want to get the size of + * @returns the number of magic comments associated with the given parser + */ +PRISM_EXPORTED_FUNCTION size_t pm_parser_magic_comments_size(const pm_parser_t *parser) PRISM_NONNULL(1); - /** an until statement */ - PM_CONTEXT_UNTIL, +/** + * A callback function that can be used to process magic comments found while parsing. + */ +typedef void (*pm_magic_comment_callback_t)(const pm_magic_comment_t *magic_comment, void *data); - /** a while statement */ - PM_CONTEXT_WHILE, -} pm_context_t; +/** + * Iterates over the magic comments associated with the given parser and calls the + * given callback for each magic comment. + * + * @param parser the parser whose magic comments we want to iterate over + * @param callback the callback function to call for each magic comment. This + * function will be passed a pointer to the magic comment and the data + * parameter passed to this function. + * @param data the data to pass to the callback function for each magic comment. + * This can be NULL if no data needs to be passed to the callback function. + */ +PRISM_EXPORTED_FUNCTION void pm_parser_magic_comments_each(const pm_parser_t *parser, pm_magic_comment_callback_t callback, void *data) PRISM_NONNULL(1); -/** 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; +/** + * Returns the number of errors associated with the given parser. + * + * @param parser the parser whose errors we want to get the size of + * @returns the number of errors associated with the given parser + */ +PRISM_EXPORTED_FUNCTION size_t pm_parser_errors_size(const pm_parser_t *parser) PRISM_NONNULL(1); - /** A pointer to the previous context in the linked list. */ - struct pm_context_node *prev; -} pm_context_node_t; +/** + * Returns the number of warnings associated with the given parser. + * + * @param parser the parser whose warnings we want to get the size of + * @returns the number of warnings associated with the given parser + */ +PRISM_EXPORTED_FUNCTION size_t pm_parser_warnings_size(const pm_parser_t *parser) PRISM_NONNULL(1); -/** 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; +/** + * A callback function that can be used to process diagnostics found while + * parsing. + */ +typedef void (*pm_diagnostic_callback_t)(const pm_diagnostic_t *diagnostic, void *data); /** - * This is a node in the linked list of comments that we've found while parsing. + * Iterates over the errors associated with the given parser and calls the + * given callback for each error. * - * @extends pm_list_node_t + * @param parser the parser whose errors we want to iterate over + * @param callback the callback function to call for each error. This function + * will be passed a pointer to the error and the data parameter passed to + * this function. + * @param data the data to pass to the callback function for each error. This + * can be NULL if no data needs to be passed to the callback function. */ -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; +PRISM_EXPORTED_FUNCTION void pm_parser_errors_each(const pm_parser_t *parser, pm_diagnostic_callback_t callback, void *data) PRISM_NONNULL(1); - /** The type of comment that we've found. */ - pm_comment_type_t type; -} pm_comment_t; +/** + * Iterates over the warnings associated with the given parser and calls the + * given callback for each warning. + * + * @param parser the parser whose warnings we want to iterate over + * @param callback the callback function to call for each warning. This function + * will be passed a pointer to the warning and the data parameter passed to + * this function. + * @param data the data to pass to the callback function for each warning. This + * can be NULL if no data needs to be passed to the callback function. + */ +PRISM_EXPORTED_FUNCTION void pm_parser_warnings_each(const pm_parser_t *parser, pm_diagnostic_callback_t callback, void *data) PRISM_NONNULL(1); /** - * This is a node in the linked list of magic comments that we've found while - * parsing. + * Returns the number of constants in the constant pool associated with the + * given parser. * - * @extends pm_list_node_t + * @param parser the parser whose constant pool constants we want to get the + * size of + * @returns the number of constants in the constant pool associated with the + * given parser */ -typedef struct { - /** The embedded base node. */ - pm_list_node_t node; +PRISM_EXPORTED_FUNCTION size_t pm_parser_constants_size(const pm_parser_t *parser) PRISM_NONNULL(1); - /** The key of the magic comment. */ - pm_location_t key; +/** + * A callback function that can be used to process constants found while + * parsing. + */ +typedef void (*pm_constant_callback_t)(const pm_constant_t *constant, void *data); - /** The value of the magic comment. */ - pm_location_t value; -} pm_magic_comment_t; +/** + * Iterates over the constants in the constant pool associated with the given + * parser and calls the given callback for each constant. + * + * @param parser the parser whose constants we want to iterate over + * @param callback the callback function to call for each constant. This function + * will be passed a pointer to the constant and the data parameter passed to + * this function. + * @param data the data to pass to the callback function for each constant. This + * can be NULL if no data needs to be passed to the callback function. + */ +PRISM_EXPORTED_FUNCTION void pm_parser_constants_each(const pm_parser_t *parser, pm_constant_callback_t callback, void *data) PRISM_NONNULL(1); /** - * 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. + * Returns a pointer to the constant at the given id in the constant pool + * associated with the given parser. + * + * @param parser the parser whose constant pool we want to look up from + * @param constant_id the id of the constant to look up (1-based) + * @returns a pointer to the constant at the given id */ -typedef void (*pm_encoding_changed_callback_t)(pm_parser_t *parser); +PRISM_EXPORTED_FUNCTION const pm_constant_t * pm_parser_constant(const pm_parser_t *parser, pm_constant_id_t constant_id) PRISM_NONNULL(1); /** - * 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; -}; + * Initiate the parser with the given parser. + * + * @param parser The parser to use. + * @returns The AST representing the source. + */ +PRISM_EXPORTED_FUNCTION pm_node_t * pm_parse(pm_parser_t *parser) PRISM_NONNULL(1); #endif diff --git a/include/prism/prettyprint.h b/include/prism/prettyprint.h index 5a52b2b6b8..0d8e416341 100644 --- a/include/prism/prettyprint.h +++ b/include/prism/prettyprint.h @@ -6,19 +6,16 @@ #ifndef PRISM_PRETTYPRINT_H #define PRISM_PRETTYPRINT_H -#include "prism/defines.h" +#include "prism/excludes.h" -#ifdef PRISM_EXCLUDE_PRETTYPRINT +#ifndef PRISM_EXCLUDE_PRETTYPRINT -void pm_prettyprint(void); - -#else - -#include +#include "prism/compiler/exported.h" +#include "prism/compiler/nonnull.h" #include "prism/ast.h" +#include "prism/buffer.h" #include "prism/parser.h" -#include "prism/util/pm_buffer.h" /** * Pretty-prints the AST represented by the given node to the given buffer. @@ -27,7 +24,7 @@ void pm_prettyprint(void); * @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); +PRISM_EXPORTED_FUNCTION void pm_prettyprint(pm_buffer_t *output_buffer, const pm_parser_t *parser, const pm_node_t *node) PRISM_NONNULL(1, 2, 3); #endif diff --git a/include/prism/regexp.h b/include/prism/regexp.h deleted file mode 100644 index 5366b5a5a0..0000000000 --- a/include/prism/regexp.h +++ /dev/null @@ -1,43 +0,0 @@ -/** - * @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/include/prism/serialize.h b/include/prism/serialize.h new file mode 100644 index 0000000000..786a1514bc --- /dev/null +++ b/include/prism/serialize.h @@ -0,0 +1,96 @@ +/** + * @file serialize.h + * + * The functions related to serializing the AST to a binary format. + */ +#ifndef PRISM_SERIALIZE_H +#define PRISM_SERIALIZE_H + +#include "prism/excludes.h" + +/* We optionally support serializing to a binary string. For systems that do not + * want or need this functionality, it can be turned off with the + * PRISM_EXCLUDE_SERIALIZATION define. */ +#ifndef PRISM_EXCLUDE_SERIALIZATION + +#include "prism/compiler/exported.h" +#include "prism/compiler/nonnull.h" + +#include "prism/buffer.h" +#include "prism/parser.h" +#include "prism/source.h" +#include "prism/stream.h" + +/** + * 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) PRISM_NONNULL(1, 2, 3); + +/** + * 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) PRISM_NONNULL(1, 2); + +/** + * Parse and serialize the AST represented by the given source into the given + * buffer. + * + * @param buffer The buffer to serialize to. + * @param source The source to parse. + * @param data The optional data to pass to the parser. + */ +PRISM_EXPORTED_FUNCTION void pm_serialize_parse_stream(pm_buffer_t *buffer, pm_source_t *source, const char *data) PRISM_NONNULL(1, 2); + +/** + * 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) PRISM_NONNULL(1, 2); + +/** + * 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) PRISM_NONNULL(1, 2); + +/** + * 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) PRISM_NONNULL(1, 2); + +/** + * 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. + * @returns True if the source parses without errors or warnings. + */ +PRISM_EXPORTED_FUNCTION bool pm_serialize_parse_success_p(const uint8_t *source, size_t size, const char *data) PRISM_NONNULL(1); + +#endif + +#endif diff --git a/include/prism/source.h b/include/prism/source.h new file mode 100644 index 0000000000..c79987d3fb --- /dev/null +++ b/include/prism/source.h @@ -0,0 +1,148 @@ +/** + * @file source.h + * + * An opaque type representing the source code being parsed, regardless of + * origin (constant memory, file, memory-mapped file, or stream). + */ +#ifndef PRISM_SOURCE_H +#define PRISM_SOURCE_H + +#include "prism/compiler/exported.h" +#include "prism/compiler/filesystem.h" +#include "prism/compiler/nodiscard.h" +#include "prism/compiler/nonnull.h" + +#include +#include + +/** + * An opaque type representing source code being parsed. + */ +typedef struct pm_source_t pm_source_t; + +/** + * This function is used 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_source_stream_fgets_t)(char *string, int size, void *stream); + +/** + * This function is used to check whether a stream is at EOF. It closely mirrors + * that of feof so that feof can be used as the default implementation. + */ +typedef int (pm_source_stream_feof_t)(void *stream); + +/** + * Represents the result of initializing a source from a file. + */ +typedef enum { + /** Indicates that the source was successfully initialized. */ + PM_SOURCE_INIT_SUCCESS = 0, + + /** + * Indicates a generic error from a source init function, where the type + * of error should be read from `errno` or `GetLastError()`. + */ + PM_SOURCE_INIT_ERROR_GENERIC = 1, + + /** + * Indicates that the file that was attempted to be opened was a directory. + */ + PM_SOURCE_INIT_ERROR_DIRECTORY = 2, + + /** + * Indicates that the file is not a regular file (e.g. a pipe or character + * device) and the caller should handle reading it. + */ + PM_SOURCE_INIT_ERROR_NON_REGULAR = 3 +} pm_source_init_result_t; + +/** + * Create a new source that wraps existing constant memory. The memory is not + * owned and will not be freed. + * + * @param data The pointer to the source data. + * @param length The length of the source data in bytes. + * @returns A new source. Aborts on allocation failure. + */ +PRISM_EXPORTED_FUNCTION PRISM_NODISCARD pm_source_t * pm_source_constant_new(const uint8_t *data, size_t length); + +/** + * Create a new source that wraps existing shared memory. The memory is not + * owned and will not be freed. Semantically a "slice" of another source. + * + * @param data The pointer to the source data. + * @param length The length of the source data in bytes. + * @returns A new source. Aborts on allocation failure. + */ +PRISM_EXPORTED_FUNCTION PRISM_NODISCARD pm_source_t * pm_source_shared_new(const uint8_t *data, size_t length); + +/** + * Create a new source that owns its memory. The memory will be freed with + * xfree when the source is freed. + * + * @param data The pointer to the heap-allocated source data. + * @param length The length of the source data in bytes. + * @returns A new source. Aborts on allocation failure. + */ +PRISM_EXPORTED_FUNCTION PRISM_NODISCARD pm_source_t * pm_source_owned_new(uint8_t *data, size_t length); + +/** + * Create a new source by reading a file into a heap-allocated buffer. + * + * @param filepath The path to the file to read. + * @param result Out parameter for the result of the initialization. + * @returns A new source, or NULL on error (with result written to out param). + */ +PRISM_EXPORTED_FUNCTION PRISM_NODISCARD pm_source_t * pm_source_file_new(const char *filepath, pm_source_init_result_t *result) PRISM_NONNULL(1, 2); + +/** + * Create a new source by memory-mapping a file. Falls back to file reading on + * platforms without mmap support. + * + * If the file is a non-regular file (e.g. a pipe or character device), + * PM_SOURCE_INIT_ERROR_NON_REGULAR is returned, allowing the caller to handle + * it appropriately (e.g. by reading it through their own I/O layer). + * + * @param filepath The path to the file to read. + * @param open_flags Additional flags to pass to open(2) (e.g. O_NONBLOCK). + * @param result Out parameter for the result of the initialization. + * @returns A new source, or NULL on error (with result written to out param). + */ +PRISM_EXPORTED_FUNCTION PRISM_NODISCARD pm_source_t * pm_source_mapped_new(const char *filepath, int open_flags, pm_source_init_result_t *result) PRISM_NONNULL(1, 3); + +/** + * Create a new source by reading from a stream using the provided callbacks. + * + * @param stream The stream to read from. + * @param fgets The function to use to read from the stream. + * @param feof The function to use to check if the stream is at EOF. + * @returns A new source. Aborts on allocation failure. + */ +PRISM_EXPORTED_FUNCTION PRISM_NODISCARD pm_source_t * pm_source_stream_new(void *stream, pm_source_stream_fgets_t *fgets, pm_source_stream_feof_t *feof); + +/** + * Free the given source and any memory it owns. + * + * @param source The source to free. + */ +PRISM_EXPORTED_FUNCTION void pm_source_free(pm_source_t *source) PRISM_NONNULL(1); + +/** + * Returns the length of the source data in bytes. + * + * @param source The source to get the length of. + * @returns The length of the source data. + */ +PRISM_EXPORTED_FUNCTION size_t pm_source_length(const pm_source_t *source) PRISM_NONNULL(1); + +/** + * Returns a pointer to the source data. + * + * @param source The source to get the data of. + * @returns A pointer to the source data. + */ +PRISM_EXPORTED_FUNCTION const uint8_t * pm_source_source(const pm_source_t *source) PRISM_NONNULL(1); + +#endif diff --git a/include/prism/stream.h b/include/prism/stream.h new file mode 100644 index 0000000000..678322b442 --- /dev/null +++ b/include/prism/stream.h @@ -0,0 +1,28 @@ +/** + * @file stream.h + * + * Functions for parsing streams. + */ +#ifndef PRISM_STREAM_H +#define PRISM_STREAM_H + +#include "prism/compiler/exported.h" +#include "prism/compiler/nonnull.h" + +#include "prism/arena.h" +#include "prism/options.h" +#include "prism/parser.h" +#include "prism/source.h" + +/** + * Parse a stream of Ruby source and return the tree. + * + * @param parser The out parameter to write the parser to. + * @param arena The arena to use for all AST-lifetime allocations. + * @param source The source to use, created via pm_source_stream_new. + * @param options The optional options to use when parsing. + * @returns The AST representing the source. + */ +PRISM_EXPORTED_FUNCTION pm_node_t * pm_parse_stream(pm_parser_t **parser, pm_arena_t *arena, pm_source_t *source, const pm_options_t *options) PRISM_NONNULL(1, 2, 3); + +#endif diff --git a/include/prism/string_query.h b/include/prism/string_query.h new file mode 100644 index 0000000000..6ee1a9d9b6 --- /dev/null +++ b/include/prism/string_query.h @@ -0,0 +1,63 @@ +/** + * @file string_query.h + * + * Functions for querying properties of strings, such as whether they are valid + * local variable names, constant names, or method names. + */ +#ifndef PRISM_STRING_QUERY_H +#define PRISM_STRING_QUERY_H + +#include "prism/compiler/exported.h" +#include "prism/compiler/nonnull.h" + +#include +#include + +/** + * 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. + * @returns 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) PRISM_NONNULL(1, 3); + +/** + * 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. + * @returns 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) PRISM_NONNULL(1, 3); + +/** + * 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. + * @returns 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) PRISM_NONNULL(1, 3); + +#endif diff --git a/include/prism/stringy.h b/include/prism/stringy.h new file mode 100644 index 0000000000..0d64387ac3 --- /dev/null +++ b/include/prism/stringy.h @@ -0,0 +1,72 @@ +/** + * @file stringy.h + * + * A generic string type that can have various ownership semantics. + */ +#ifndef PRISM_STRINGY_H +#define PRISM_STRINGY_H + +#include "prism/compiler/exported.h" +#include "prism/compiler/nonnull.h" + +#include +#include + +/** + * 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 internally. */ + PM_STRING_OWNED + } type; +} pm_string_t; + +/** + * 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. + */ +PRISM_EXPORTED_FUNCTION void pm_string_constant_init(pm_string_t *string, const char *source, size_t length) PRISM_NONNULL(1); + +/** + * 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. + */ +PRISM_EXPORTED_FUNCTION void pm_string_owned_init(pm_string_t *string, uint8_t *source, size_t length) PRISM_NONNULL(1, 2); + +/** + * Returns the length associated with the string. + * + * @param string The string to get the length of. + * @returns The length of the string. + */ +PRISM_EXPORTED_FUNCTION size_t pm_string_length(const pm_string_t *string) PRISM_NONNULL(1); + +/** + * Returns the start pointer associated with the string. + * + * @param string The string to get the start pointer of. + * @returns The start pointer of the string. + */ +PRISM_EXPORTED_FUNCTION const uint8_t * pm_string_source(const pm_string_t *string) PRISM_NONNULL(1); + +#endif diff --git a/include/prism/util/pm_buffer.h b/include/prism/util/pm_buffer.h deleted file mode 100644 index cb80f8b3ce..0000000000 --- a/include/prism/util/pm_buffer.h +++ /dev/null @@ -1,236 +0,0 @@ -/** - * @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/include/prism/util/pm_char.h b/include/prism/util/pm_char.h deleted file mode 100644 index b213e8edee..0000000000 --- a/include/prism/util/pm_char.h +++ /dev/null @@ -1,206 +0,0 @@ -/** - * @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. - * @param start_offset The offset at which the string occurs in the source, for - * the purpose of tracking newlines. - * @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, uint32_t start_offset); - -/** - * 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/include/prism/util/pm_constant_pool.h b/include/prism/util/pm_constant_pool.h deleted file mode 100644 index 6df23f8f50..0000000000 --- a/include/prism/util/pm_constant_pool.h +++ /dev/null @@ -1,218 +0,0 @@ -/** - * @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/include/prism/util/pm_integer.h b/include/prism/util/pm_integer.h deleted file mode 100644 index 304665e620..0000000000 --- a/include/prism/util/pm_integer.h +++ /dev/null @@ -1,130 +0,0 @@ -/** - * @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/include/prism/util/pm_memchr.h b/include/prism/util/pm_memchr.h deleted file mode 100644 index e0671eaed3..0000000000 --- a/include/prism/util/pm_memchr.h +++ /dev/null @@ -1,29 +0,0 @@ -/** - * @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/include/prism/util/pm_newline_list.h b/include/prism/util/pm_newline_list.h deleted file mode 100644 index dd3e625089..0000000000 --- a/include/prism/util/pm_newline_list.h +++ /dev/null @@ -1,108 +0,0 @@ -/** - * @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 { - /** 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. */ - uint32_t *offsets; -} pm_newline_list_t; - -/** - * A line and column in a string. - */ -typedef struct { - /** The line number. */ - int32_t line; - - /** The column in bytes. */ - 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 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, 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 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, uint32_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 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, uint32_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 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, uint32_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/include/prism/util/pm_string.h b/include/prism/util/pm_string.h deleted file mode 100644 index d8456ff294..0000000000 --- a/include/prism/util/pm_string.h +++ /dev/null @@ -1,200 +0,0 @@ -/** - * @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/include/prism/version.h b/include/prism/version.h index b95611f96c..181b398462 100644 --- a/include/prism/version.h +++ b/include/prism/version.h @@ -6,6 +6,8 @@ #ifndef PRISM_VERSION_H #define PRISM_VERSION_H +#include "prism/compiler/exported.h" + /** * The major version of the Prism library as an int. */ @@ -26,4 +28,11 @@ */ #define PRISM_VERSION "1.9.0" +/** + * The prism version and the serialization format. + * + * @returns The prism version as a constant string. + */ +PRISM_EXPORTED_FUNCTION const char * pm_version(void); + #endif diff --git a/java-wasm/src/main/java/org/prism/Prism.java b/java-wasm/src/main/java/org/prism/Prism.java deleted file mode 100644 index 9578a441a1..0000000000 --- a/java-wasm/src/main/java/org/prism/Prism.java +++ /dev/null @@ -1,91 +0,0 @@ -package org.prism; - -import com.dylibso.chicory.runtime.ByteArrayMemory; -import com.dylibso.chicory.annotations.WasmModuleInterface; -import com.dylibso.chicory.runtime.ImportValues; -import com.dylibso.chicory.runtime.Instance; -import com.dylibso.chicory.wasi.WasiOptions; -import com.dylibso.chicory.wasi.WasiPreview1; -import com.dylibso.chicory.wasm.types.MemoryLimits; - -import java.nio.charset.StandardCharsets; - -@WasmModuleInterface(WasmResource.absoluteFile) -public class Prism implements AutoCloseable { - private final WasiPreview1 wasi; - private final Instance instance; - private final Prism_ModuleExports exports; - - public Prism() { - this(WasiOptions.builder().build()); - } - public Prism(WasiOptions wasiOpts) { - wasi = WasiPreview1.builder().withOptions(wasiOpts).build(); - instance = Instance.builder(PrismModule.load()) - .withMemoryFactory(limits -> new ByteArrayMemory(new MemoryLimits(10, MemoryLimits.MAX_PAGES))) - .withMachineFactory(PrismModule::create) - .withImportValues(ImportValues.builder().addFunction(wasi.toHostFunctions()).build()) - .build(); - exports = new Prism_ModuleExports(instance); - } - - public Prism_ModuleExports exports() { - return exports; - } - - public byte[] serialize(byte[] packedOptions, byte[] source, int sourceLength) { - int sourcePointer = 0; - int optionsPointer = 0; - int bufferPointer = 0; - int resultPointer = 0; - byte[] result; - try { - sourcePointer = exports.calloc(1, sourceLength); - exports.memory().write(sourcePointer, source); - - optionsPointer = exports.calloc(1, packedOptions.length); - exports.memory().write(optionsPointer, packedOptions); - - bufferPointer = exports.calloc(exports.pmBufferSizeof(), 1); - exports.pmBufferInit(bufferPointer); - - exports.pmSerializeParse(bufferPointer, sourcePointer, sourceLength, optionsPointer); - - resultPointer = exports.pmBufferValue(bufferPointer); - - result = instance.memory().readBytes( - resultPointer, - exports.pmBufferLength(bufferPointer)); - } finally { - if (sourcePointer != 0) { - exports.free(sourcePointer); - } - if (optionsPointer != 0) { - exports.free(optionsPointer); - } - if (bufferPointer != 0) { - exports.free(bufferPointer); - } - if (resultPointer != 0) { - exports.free(resultPointer); - } - } - - return result; - } - - public ParseResult serializeParse(byte[] packedOptions, String source) { - var sourceBytes = source.getBytes(StandardCharsets.US_ASCII); - - byte[] result = serialize(packedOptions, sourceBytes, sourceBytes.length); - - return Loader.load(result, sourceBytes); - } - - @Override - public void close() { - if (wasi != null) { - wasi.close(); - } - } -} diff --git a/java-wasm/src/test/java/org/prism/DummyTest.java b/java-wasm/src/test/java/org/prism/DummyTest.java deleted file mode 100644 index 9e49f1afdd..0000000000 --- a/java-wasm/src/test/java/org/prism/DummyTest.java +++ /dev/null @@ -1,154 +0,0 @@ -package org.prism; - -import com.dylibso.chicory.runtime.ImportValues; -import com.dylibso.chicory.runtime.Instance; -import com.dylibso.chicory.wasi.WasiOptions; -import com.dylibso.chicory.wasi.WasiPreview1; -import com.dylibso.chicory.wasm.Parser; -import com.dylibso.chicory.wasm.types.Value; -import org.junit.jupiter.api.Test; - -import java.nio.charset.StandardCharsets; -import java.util.EnumSet; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertTrue; - -public class DummyTest { - - private static final byte[] packedOptions = ParsingOptions.serialize( - new byte[] {}, - 1, - new byte[] {}, - false, - EnumSet.noneOf(ParsingOptions.CommandLine.class), - ParsingOptions.SyntaxVersion.LATEST, - false, - false, - false, - new byte[][][] {} - ); - - @Test - public void test1() { - WasiOptions wasiOpts = WasiOptions.builder().build(); - WasiPreview1 wasi = WasiPreview1.builder().withOptions(wasiOpts).build(); - var wasmPrism = Instance.builder(Parser.parse(DummyTest.class.getResourceAsStream("/prism.wasm"))) - .withImportValues(ImportValues.builder().addFunction(wasi.toHostFunctions()).build()) - .build(); - var memory = wasmPrism.memory(); - var calloc = wasmPrism.export("calloc"); - var pmSerializeParse = wasmPrism.export("pm_serialize_parse"); - var pmBufferInit = wasmPrism.export("pm_buffer_init"); - var pmBufferSizeof = wasmPrism.export("pm_buffer_sizeof"); - var pmBufferValue = wasmPrism.export("pm_buffer_value"); - var pmBufferLength = wasmPrism.export("pm_buffer_length"); - - // The Ruby source code to be processed - var source = "1 + 1"; - var sourceBytes = source.getBytes(StandardCharsets.US_ASCII); - - var sourcePointer = calloc.apply(1, source.length()); - memory.writeString((int) sourcePointer[0], source); - - var optionsPointer = calloc.apply(1, packedOptions.length); - memory.write((int) optionsPointer[0], packedOptions); - - var bufferPointer = calloc.apply(pmBufferSizeof.apply()[0], 1); - pmBufferInit.apply(bufferPointer); - - pmSerializeParse.apply( - bufferPointer[0], sourcePointer[0], source.length(), optionsPointer[0]); - - var result = memory.readBytes( - (int) pmBufferValue.apply(bufferPointer[0])[0], - (int) pmBufferLength.apply(bufferPointer[0])[0]); - - System.out.println("RESULT: " + new String(result)); - - ParseResult pr = Loader.load(result, sourceBytes); - - assertEquals(1, pr.value.childNodes().length); - System.out.println("Nodes:"); - System.out.println(pr.value.childNodes()[0]); - assertTrue(pr.value.childNodes()[0].toString().contains("IntegerNode")); - } - - @Test - public void test1Aot() { - // The Ruby source code to be processed - var source = "1 + 1"; - - ParseResult pr = null; - try (Prism prism = new Prism()) { - pr = prism.serializeParse(packedOptions, source); - } - - assertEquals(1, pr.value.childNodes().length); - System.out.println("Nodes:"); - System.out.println(pr.value.childNodes()[0]); - assertTrue(pr.value.childNodes()[0].toString().contains("IntegerNode")); - } - - @Test - public void test2() { - WasiOptions wasiOpts = WasiOptions.builder().build(); - WasiPreview1 wasi = WasiPreview1.builder().withOptions(wasiOpts).build(); - var wasmPrism = Instance.builder(Parser.parse(DummyTest.class.getResourceAsStream("/prism.wasm"))) - .withImportValues(ImportValues.builder().addFunction(wasi.toHostFunctions()).build()) - .build(); - var memory = wasmPrism.memory(); - var calloc = wasmPrism.export("calloc"); - var pmSerializeParse = wasmPrism.export("pm_serialize_parse"); - var pmBufferInit = wasmPrism.export("pm_buffer_init"); - var pmBufferSizeof = wasmPrism.export("pm_buffer_sizeof"); - var pmBufferValue = wasmPrism.export("pm_buffer_value"); - var pmBufferLength = wasmPrism.export("pm_buffer_length"); - - // The Ruby source code to be processed - var source = "puts \"h\ne\nl\nl\no\n\""; - var sourceBytes = source.getBytes(StandardCharsets.US_ASCII); - - var sourcePointer = calloc.apply(1, source.length()); - memory.writeString((int) sourcePointer[0], source); - - var optionsPointer = calloc.apply(1, packedOptions.length); - memory.write((int) optionsPointer[0], packedOptions); - - var bufferPointer = calloc.apply(pmBufferSizeof.apply()[0], 1); - pmBufferInit.apply(bufferPointer); - - pmSerializeParse.apply( - bufferPointer[0], sourcePointer[0], source.length(), optionsPointer[0]); - - var result = memory.readBytes( - (int) pmBufferValue.apply(bufferPointer[0])[0], - (int) pmBufferLength.apply(bufferPointer[0])[0]); - - System.out.println("RESULT: " + new String(result)); - - ParseResult pr = Loader.load(result, sourceBytes); - - assertEquals(1, pr.value.childNodes().length); - - System.out.println("Nodes:"); - System.out.println(pr.value.childNodes()[0]); - assertTrue(pr.value.childNodes()[0].toString().contains("CallNode")); - } - - @Test - public void test2Aot() { - // The Ruby source code to be processed - var source = "puts \"h\ne\nl\nl\no\n\""; - - ParseResult pr = null; - try (Prism prism = new Prism()) { - pr = prism.serializeParse(packedOptions, source); - } - - assertEquals(1, pr.value.childNodes().length); - System.out.println("Nodes:"); - System.out.println(pr.value.childNodes()[0]); - assertTrue(pr.value.childNodes()[0].toString().contains("CallNode")); - } -} diff --git a/java/README.md b/java/README.md new file mode 100644 index 0000000000..b3062cebd2 --- /dev/null +++ b/java/README.md @@ -0,0 +1,25 @@ +# Prism Java API and bindings + +This is the top-level project for the Java API and backend bindings for the Prism Ruby language parser. + +* api/ contains the API +* native/ contains a native binding for the Prism shared library +* wasm/ contains a Chicory-based WASM build and binding + +## Updating versions + +Run the following command to update all module versions: + +``` +mvn versions:set -DnewVersion=1.2.3-SNAPSHOT +``` + +## Releasing + +Snapshots can be deployed with `mvn deploy` while the versions are `-SNAPSHOT`. + +When releasing to Maven Central, all projects should be released together using the `release` profile: + +``` +mvn clean deploy -Prelease +``` diff --git a/java/api/pom.xml b/java/api/pom.xml new file mode 100644 index 0000000000..6b3bd6d3a8 --- /dev/null +++ b/java/api/pom.xml @@ -0,0 +1,81 @@ + + + 4.0.0 + + + org.ruby-lang + prism-parser + 0.0.2-SNAPSHOT + + + prism-parser-api + Java Prism + Java API for the Prism Ruby language parser + https://github.com/ruby/prism + + + + org.ruby-lang + prism-parser-native + 0.0.2-SNAPSHOT + provided + + + + + + + org.codehaus.mojo + templating-maven-plugin + 3.1.0 + + + filtering-java-templates + + filter-sources + + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.15.0 + + 21 + + + com.dylibso.chicory + annotations-processor + ${chicory.version} + + + + + + org.apache.maven.plugins + maven-surefire-plugin + 3.5.5 + + + org.codehaus.mojo + build-helper-maven-plugin + 3.6.1 + + + generate-sources + + add-source + + + + ../java + + + + + + + + + diff --git a/java/api/src/main/java/org/ruby_lang/prism/AbstractNodeVisitor.java b/java/api/src/main/java/org/ruby_lang/prism/AbstractNodeVisitor.java new file mode 100644 index 0000000000..9c76b38d8e --- /dev/null +++ b/java/api/src/main/java/org/ruby_lang/prism/AbstractNodeVisitor.java @@ -0,0 +1,1538 @@ +/*----------------------------------------------------------------------------*/ +/* This file is generated by the templates/template.rb script and should not */ +/* be modified manually. See */ +/* templates/java/org/ruby_lang/prism/AbstractNodeVisitor.java.erb */ +/* if you are looking to modify the */ +/* template */ +/*----------------------------------------------------------------------------*/ + +package org.ruby_lang.prism; + +// GENERATED BY AbstractNodeVisitor.java.erb +// @formatter:off +public abstract class AbstractNodeVisitor { + + protected abstract T defaultVisit(Nodes.Node node); + + /** + * Visit a AliasGlobalVariableNode node. + * + * @param node The node to visit. + * @return The result of visiting the node. + */ + public T visitAliasGlobalVariableNode(Nodes.AliasGlobalVariableNode node) { + return defaultVisit(node); + } + + /** + * Visit a AliasMethodNode node. + * + * @param node The node to visit. + * @return The result of visiting the node. + */ + public T visitAliasMethodNode(Nodes.AliasMethodNode node) { + return defaultVisit(node); + } + + /** + * Visit a AlternationPatternNode node. + * + * @param node The node to visit. + * @return The result of visiting the node. + */ + public T visitAlternationPatternNode(Nodes.AlternationPatternNode node) { + return defaultVisit(node); + } + + /** + * Visit a AndNode node. + * + * @param node The node to visit. + * @return The result of visiting the node. + */ + public T visitAndNode(Nodes.AndNode node) { + return defaultVisit(node); + } + + /** + * Visit a ArgumentsNode node. + * + * @param node The node to visit. + * @return The result of visiting the node. + */ + public T visitArgumentsNode(Nodes.ArgumentsNode node) { + return defaultVisit(node); + } + + /** + * Visit a ArrayNode node. + * + * @param node The node to visit. + * @return The result of visiting the node. + */ + public T visitArrayNode(Nodes.ArrayNode node) { + return defaultVisit(node); + } + + /** + * Visit a ArrayPatternNode node. + * + * @param node The node to visit. + * @return The result of visiting the node. + */ + public T visitArrayPatternNode(Nodes.ArrayPatternNode node) { + return defaultVisit(node); + } + + /** + * Visit a AssocNode node. + * + * @param node The node to visit. + * @return The result of visiting the node. + */ + public T visitAssocNode(Nodes.AssocNode node) { + return defaultVisit(node); + } + + /** + * Visit a AssocSplatNode node. + * + * @param node The node to visit. + * @return The result of visiting the node. + */ + public T visitAssocSplatNode(Nodes.AssocSplatNode node) { + return defaultVisit(node); + } + + /** + * Visit a BackReferenceReadNode node. + * + * @param node The node to visit. + * @return The result of visiting the node. + */ + public T visitBackReferenceReadNode(Nodes.BackReferenceReadNode node) { + return defaultVisit(node); + } + + /** + * Visit a BeginNode node. + * + * @param node The node to visit. + * @return The result of visiting the node. + */ + public T visitBeginNode(Nodes.BeginNode node) { + return defaultVisit(node); + } + + /** + * Visit a BlockArgumentNode node. + * + * @param node The node to visit. + * @return The result of visiting the node. + */ + public T visitBlockArgumentNode(Nodes.BlockArgumentNode node) { + return defaultVisit(node); + } + + /** + * Visit a BlockLocalVariableNode node. + * + * @param node The node to visit. + * @return The result of visiting the node. + */ + public T visitBlockLocalVariableNode(Nodes.BlockLocalVariableNode node) { + return defaultVisit(node); + } + + /** + * Visit a BlockNode node. + * + * @param node The node to visit. + * @return The result of visiting the node. + */ + public T visitBlockNode(Nodes.BlockNode node) { + return defaultVisit(node); + } + + /** + * Visit a BlockParameterNode node. + * + * @param node The node to visit. + * @return The result of visiting the node. + */ + public T visitBlockParameterNode(Nodes.BlockParameterNode node) { + return defaultVisit(node); + } + + /** + * Visit a BlockParametersNode node. + * + * @param node The node to visit. + * @return The result of visiting the node. + */ + public T visitBlockParametersNode(Nodes.BlockParametersNode node) { + return defaultVisit(node); + } + + /** + * Visit a BreakNode node. + * + * @param node The node to visit. + * @return The result of visiting the node. + */ + public T visitBreakNode(Nodes.BreakNode node) { + return defaultVisit(node); + } + + /** + * Visit a CallAndWriteNode node. + * + * @param node The node to visit. + * @return The result of visiting the node. + */ + public T visitCallAndWriteNode(Nodes.CallAndWriteNode node) { + return defaultVisit(node); + } + + /** + * Visit a CallNode node. + * + * @param node The node to visit. + * @return The result of visiting the node. + */ + public T visitCallNode(Nodes.CallNode node) { + return defaultVisit(node); + } + + /** + * Visit a CallOperatorWriteNode node. + * + * @param node The node to visit. + * @return The result of visiting the node. + */ + public T visitCallOperatorWriteNode(Nodes.CallOperatorWriteNode node) { + return defaultVisit(node); + } + + /** + * Visit a CallOrWriteNode node. + * + * @param node The node to visit. + * @return The result of visiting the node. + */ + public T visitCallOrWriteNode(Nodes.CallOrWriteNode node) { + return defaultVisit(node); + } + + /** + * Visit a CallTargetNode node. + * + * @param node The node to visit. + * @return The result of visiting the node. + */ + public T visitCallTargetNode(Nodes.CallTargetNode node) { + return defaultVisit(node); + } + + /** + * Visit a CapturePatternNode node. + * + * @param node The node to visit. + * @return The result of visiting the node. + */ + public T visitCapturePatternNode(Nodes.CapturePatternNode node) { + return defaultVisit(node); + } + + /** + * Visit a CaseMatchNode node. + * + * @param node The node to visit. + * @return The result of visiting the node. + */ + public T visitCaseMatchNode(Nodes.CaseMatchNode node) { + return defaultVisit(node); + } + + /** + * Visit a CaseNode node. + * + * @param node The node to visit. + * @return The result of visiting the node. + */ + public T visitCaseNode(Nodes.CaseNode node) { + return defaultVisit(node); + } + + /** + * Visit a ClassNode node. + * + * @param node The node to visit. + * @return The result of visiting the node. + */ + public T visitClassNode(Nodes.ClassNode node) { + return defaultVisit(node); + } + + /** + * Visit a ClassVariableAndWriteNode node. + * + * @param node The node to visit. + * @return The result of visiting the node. + */ + public T visitClassVariableAndWriteNode(Nodes.ClassVariableAndWriteNode node) { + return defaultVisit(node); + } + + /** + * Visit a ClassVariableOperatorWriteNode node. + * + * @param node The node to visit. + * @return The result of visiting the node. + */ + public T visitClassVariableOperatorWriteNode(Nodes.ClassVariableOperatorWriteNode node) { + return defaultVisit(node); + } + + /** + * Visit a ClassVariableOrWriteNode node. + * + * @param node The node to visit. + * @return The result of visiting the node. + */ + public T visitClassVariableOrWriteNode(Nodes.ClassVariableOrWriteNode node) { + return defaultVisit(node); + } + + /** + * Visit a ClassVariableReadNode node. + * + * @param node The node to visit. + * @return The result of visiting the node. + */ + public T visitClassVariableReadNode(Nodes.ClassVariableReadNode node) { + return defaultVisit(node); + } + + /** + * Visit a ClassVariableTargetNode node. + * + * @param node The node to visit. + * @return The result of visiting the node. + */ + public T visitClassVariableTargetNode(Nodes.ClassVariableTargetNode node) { + return defaultVisit(node); + } + + /** + * Visit a ClassVariableWriteNode node. + * + * @param node The node to visit. + * @return The result of visiting the node. + */ + public T visitClassVariableWriteNode(Nodes.ClassVariableWriteNode node) { + return defaultVisit(node); + } + + /** + * Visit a ConstantAndWriteNode node. + * + * @param node The node to visit. + * @return The result of visiting the node. + */ + public T visitConstantAndWriteNode(Nodes.ConstantAndWriteNode node) { + return defaultVisit(node); + } + + /** + * Visit a ConstantOperatorWriteNode node. + * + * @param node The node to visit. + * @return The result of visiting the node. + */ + public T visitConstantOperatorWriteNode(Nodes.ConstantOperatorWriteNode node) { + return defaultVisit(node); + } + + /** + * Visit a ConstantOrWriteNode node. + * + * @param node The node to visit. + * @return The result of visiting the node. + */ + public T visitConstantOrWriteNode(Nodes.ConstantOrWriteNode node) { + return defaultVisit(node); + } + + /** + * Visit a ConstantPathAndWriteNode node. + * + * @param node The node to visit. + * @return The result of visiting the node. + */ + public T visitConstantPathAndWriteNode(Nodes.ConstantPathAndWriteNode node) { + return defaultVisit(node); + } + + /** + * Visit a ConstantPathNode node. + * + * @param node The node to visit. + * @return The result of visiting the node. + */ + public T visitConstantPathNode(Nodes.ConstantPathNode node) { + return defaultVisit(node); + } + + /** + * Visit a ConstantPathOperatorWriteNode node. + * + * @param node The node to visit. + * @return The result of visiting the node. + */ + public T visitConstantPathOperatorWriteNode(Nodes.ConstantPathOperatorWriteNode node) { + return defaultVisit(node); + } + + /** + * Visit a ConstantPathOrWriteNode node. + * + * @param node The node to visit. + * @return The result of visiting the node. + */ + public T visitConstantPathOrWriteNode(Nodes.ConstantPathOrWriteNode node) { + return defaultVisit(node); + } + + /** + * Visit a ConstantPathTargetNode node. + * + * @param node The node to visit. + * @return The result of visiting the node. + */ + public T visitConstantPathTargetNode(Nodes.ConstantPathTargetNode node) { + return defaultVisit(node); + } + + /** + * Visit a ConstantPathWriteNode node. + * + * @param node The node to visit. + * @return The result of visiting the node. + */ + public T visitConstantPathWriteNode(Nodes.ConstantPathWriteNode node) { + return defaultVisit(node); + } + + /** + * Visit a ConstantReadNode node. + * + * @param node The node to visit. + * @return The result of visiting the node. + */ + public T visitConstantReadNode(Nodes.ConstantReadNode node) { + return defaultVisit(node); + } + + /** + * Visit a ConstantTargetNode node. + * + * @param node The node to visit. + * @return The result of visiting the node. + */ + public T visitConstantTargetNode(Nodes.ConstantTargetNode node) { + return defaultVisit(node); + } + + /** + * Visit a ConstantWriteNode node. + * + * @param node The node to visit. + * @return The result of visiting the node. + */ + public T visitConstantWriteNode(Nodes.ConstantWriteNode node) { + return defaultVisit(node); + } + + /** + * Visit a DefNode node. + * + * @param node The node to visit. + * @return The result of visiting the node. + */ + public T visitDefNode(Nodes.DefNode node) { + return defaultVisit(node); + } + + /** + * Visit a DefinedNode node. + * + * @param node The node to visit. + * @return The result of visiting the node. + */ + public T visitDefinedNode(Nodes.DefinedNode node) { + return defaultVisit(node); + } + + /** + * Visit a ElseNode node. + * + * @param node The node to visit. + * @return The result of visiting the node. + */ + public T visitElseNode(Nodes.ElseNode node) { + return defaultVisit(node); + } + + /** + * Visit a EmbeddedStatementsNode node. + * + * @param node The node to visit. + * @return The result of visiting the node. + */ + public T visitEmbeddedStatementsNode(Nodes.EmbeddedStatementsNode node) { + return defaultVisit(node); + } + + /** + * Visit a EmbeddedVariableNode node. + * + * @param node The node to visit. + * @return The result of visiting the node. + */ + public T visitEmbeddedVariableNode(Nodes.EmbeddedVariableNode node) { + return defaultVisit(node); + } + + /** + * Visit a EnsureNode node. + * + * @param node The node to visit. + * @return The result of visiting the node. + */ + public T visitEnsureNode(Nodes.EnsureNode node) { + return defaultVisit(node); + } + + /** + * Visit a FalseNode node. + * + * @param node The node to visit. + * @return The result of visiting the node. + */ + public T visitFalseNode(Nodes.FalseNode node) { + return defaultVisit(node); + } + + /** + * Visit a FindPatternNode node. + * + * @param node The node to visit. + * @return The result of visiting the node. + */ + public T visitFindPatternNode(Nodes.FindPatternNode node) { + return defaultVisit(node); + } + + /** + * Visit a FlipFlopNode node. + * + * @param node The node to visit. + * @return The result of visiting the node. + */ + public T visitFlipFlopNode(Nodes.FlipFlopNode node) { + return defaultVisit(node); + } + + /** + * Visit a FloatNode node. + * + * @param node The node to visit. + * @return The result of visiting the node. + */ + public T visitFloatNode(Nodes.FloatNode node) { + return defaultVisit(node); + } + + /** + * Visit a ForNode node. + * + * @param node The node to visit. + * @return The result of visiting the node. + */ + public T visitForNode(Nodes.ForNode node) { + return defaultVisit(node); + } + + /** + * Visit a ForwardingArgumentsNode node. + * + * @param node The node to visit. + * @return The result of visiting the node. + */ + public T visitForwardingArgumentsNode(Nodes.ForwardingArgumentsNode node) { + return defaultVisit(node); + } + + /** + * Visit a ForwardingParameterNode node. + * + * @param node The node to visit. + * @return The result of visiting the node. + */ + public T visitForwardingParameterNode(Nodes.ForwardingParameterNode node) { + return defaultVisit(node); + } + + /** + * Visit a ForwardingSuperNode node. + * + * @param node The node to visit. + * @return The result of visiting the node. + */ + public T visitForwardingSuperNode(Nodes.ForwardingSuperNode node) { + return defaultVisit(node); + } + + /** + * Visit a GlobalVariableAndWriteNode node. + * + * @param node The node to visit. + * @return The result of visiting the node. + */ + public T visitGlobalVariableAndWriteNode(Nodes.GlobalVariableAndWriteNode node) { + return defaultVisit(node); + } + + /** + * Visit a GlobalVariableOperatorWriteNode node. + * + * @param node The node to visit. + * @return The result of visiting the node. + */ + public T visitGlobalVariableOperatorWriteNode(Nodes.GlobalVariableOperatorWriteNode node) { + return defaultVisit(node); + } + + /** + * Visit a GlobalVariableOrWriteNode node. + * + * @param node The node to visit. + * @return The result of visiting the node. + */ + public T visitGlobalVariableOrWriteNode(Nodes.GlobalVariableOrWriteNode node) { + return defaultVisit(node); + } + + /** + * Visit a GlobalVariableReadNode node. + * + * @param node The node to visit. + * @return The result of visiting the node. + */ + public T visitGlobalVariableReadNode(Nodes.GlobalVariableReadNode node) { + return defaultVisit(node); + } + + /** + * Visit a GlobalVariableTargetNode node. + * + * @param node The node to visit. + * @return The result of visiting the node. + */ + public T visitGlobalVariableTargetNode(Nodes.GlobalVariableTargetNode node) { + return defaultVisit(node); + } + + /** + * Visit a GlobalVariableWriteNode node. + * + * @param node The node to visit. + * @return The result of visiting the node. + */ + public T visitGlobalVariableWriteNode(Nodes.GlobalVariableWriteNode node) { + return defaultVisit(node); + } + + /** + * Visit a HashNode node. + * + * @param node The node to visit. + * @return The result of visiting the node. + */ + public T visitHashNode(Nodes.HashNode node) { + return defaultVisit(node); + } + + /** + * Visit a HashPatternNode node. + * + * @param node The node to visit. + * @return The result of visiting the node. + */ + public T visitHashPatternNode(Nodes.HashPatternNode node) { + return defaultVisit(node); + } + + /** + * Visit a IfNode node. + * + * @param node The node to visit. + * @return The result of visiting the node. + */ + public T visitIfNode(Nodes.IfNode node) { + return defaultVisit(node); + } + + /** + * Visit a ImaginaryNode node. + * + * @param node The node to visit. + * @return The result of visiting the node. + */ + public T visitImaginaryNode(Nodes.ImaginaryNode node) { + return defaultVisit(node); + } + + /** + * Visit a ImplicitNode node. + * + * @param node The node to visit. + * @return The result of visiting the node. + */ + public T visitImplicitNode(Nodes.ImplicitNode node) { + return defaultVisit(node); + } + + /** + * Visit a ImplicitRestNode node. + * + * @param node The node to visit. + * @return The result of visiting the node. + */ + public T visitImplicitRestNode(Nodes.ImplicitRestNode node) { + return defaultVisit(node); + } + + /** + * Visit a InNode node. + * + * @param node The node to visit. + * @return The result of visiting the node. + */ + public T visitInNode(Nodes.InNode node) { + return defaultVisit(node); + } + + /** + * Visit a IndexAndWriteNode node. + * + * @param node The node to visit. + * @return The result of visiting the node. + */ + public T visitIndexAndWriteNode(Nodes.IndexAndWriteNode node) { + return defaultVisit(node); + } + + /** + * Visit a IndexOperatorWriteNode node. + * + * @param node The node to visit. + * @return The result of visiting the node. + */ + public T visitIndexOperatorWriteNode(Nodes.IndexOperatorWriteNode node) { + return defaultVisit(node); + } + + /** + * Visit a IndexOrWriteNode node. + * + * @param node The node to visit. + * @return The result of visiting the node. + */ + public T visitIndexOrWriteNode(Nodes.IndexOrWriteNode node) { + return defaultVisit(node); + } + + /** + * Visit a IndexTargetNode node. + * + * @param node The node to visit. + * @return The result of visiting the node. + */ + public T visitIndexTargetNode(Nodes.IndexTargetNode node) { + return defaultVisit(node); + } + + /** + * Visit a InstanceVariableAndWriteNode node. + * + * @param node The node to visit. + * @return The result of visiting the node. + */ + public T visitInstanceVariableAndWriteNode(Nodes.InstanceVariableAndWriteNode node) { + return defaultVisit(node); + } + + /** + * Visit a InstanceVariableOperatorWriteNode node. + * + * @param node The node to visit. + * @return The result of visiting the node. + */ + public T visitInstanceVariableOperatorWriteNode(Nodes.InstanceVariableOperatorWriteNode node) { + return defaultVisit(node); + } + + /** + * Visit a InstanceVariableOrWriteNode node. + * + * @param node The node to visit. + * @return The result of visiting the node. + */ + public T visitInstanceVariableOrWriteNode(Nodes.InstanceVariableOrWriteNode node) { + return defaultVisit(node); + } + + /** + * Visit a InstanceVariableReadNode node. + * + * @param node The node to visit. + * @return The result of visiting the node. + */ + public T visitInstanceVariableReadNode(Nodes.InstanceVariableReadNode node) { + return defaultVisit(node); + } + + /** + * Visit a InstanceVariableTargetNode node. + * + * @param node The node to visit. + * @return The result of visiting the node. + */ + public T visitInstanceVariableTargetNode(Nodes.InstanceVariableTargetNode node) { + return defaultVisit(node); + } + + /** + * Visit a InstanceVariableWriteNode node. + * + * @param node The node to visit. + * @return The result of visiting the node. + */ + public T visitInstanceVariableWriteNode(Nodes.InstanceVariableWriteNode node) { + return defaultVisit(node); + } + + /** + * Visit a IntegerNode node. + * + * @param node The node to visit. + * @return The result of visiting the node. + */ + public T visitIntegerNode(Nodes.IntegerNode node) { + return defaultVisit(node); + } + + /** + * Visit a InterpolatedMatchLastLineNode node. + * + * @param node The node to visit. + * @return The result of visiting the node. + */ + public T visitInterpolatedMatchLastLineNode(Nodes.InterpolatedMatchLastLineNode node) { + return defaultVisit(node); + } + + /** + * Visit a InterpolatedRegularExpressionNode node. + * + * @param node The node to visit. + * @return The result of visiting the node. + */ + public T visitInterpolatedRegularExpressionNode(Nodes.InterpolatedRegularExpressionNode node) { + return defaultVisit(node); + } + + /** + * Visit a InterpolatedStringNode node. + * + * @param node The node to visit. + * @return The result of visiting the node. + */ + public T visitInterpolatedStringNode(Nodes.InterpolatedStringNode node) { + return defaultVisit(node); + } + + /** + * Visit a InterpolatedSymbolNode node. + * + * @param node The node to visit. + * @return The result of visiting the node. + */ + public T visitInterpolatedSymbolNode(Nodes.InterpolatedSymbolNode node) { + return defaultVisit(node); + } + + /** + * Visit a InterpolatedXStringNode node. + * + * @param node The node to visit. + * @return The result of visiting the node. + */ + public T visitInterpolatedXStringNode(Nodes.InterpolatedXStringNode node) { + return defaultVisit(node); + } + + /** + * Visit a ItLocalVariableReadNode node. + * + * @param node The node to visit. + * @return The result of visiting the node. + */ + public T visitItLocalVariableReadNode(Nodes.ItLocalVariableReadNode node) { + return defaultVisit(node); + } + + /** + * Visit a ItParametersNode node. + * + * @param node The node to visit. + * @return The result of visiting the node. + */ + public T visitItParametersNode(Nodes.ItParametersNode node) { + return defaultVisit(node); + } + + /** + * Visit a KeywordHashNode node. + * + * @param node The node to visit. + * @return The result of visiting the node. + */ + public T visitKeywordHashNode(Nodes.KeywordHashNode node) { + return defaultVisit(node); + } + + /** + * Visit a KeywordRestParameterNode node. + * + * @param node The node to visit. + * @return The result of visiting the node. + */ + public T visitKeywordRestParameterNode(Nodes.KeywordRestParameterNode node) { + return defaultVisit(node); + } + + /** + * Visit a LambdaNode node. + * + * @param node The node to visit. + * @return The result of visiting the node. + */ + public T visitLambdaNode(Nodes.LambdaNode node) { + return defaultVisit(node); + } + + /** + * Visit a LocalVariableAndWriteNode node. + * + * @param node The node to visit. + * @return The result of visiting the node. + */ + public T visitLocalVariableAndWriteNode(Nodes.LocalVariableAndWriteNode node) { + return defaultVisit(node); + } + + /** + * Visit a LocalVariableOperatorWriteNode node. + * + * @param node The node to visit. + * @return The result of visiting the node. + */ + public T visitLocalVariableOperatorWriteNode(Nodes.LocalVariableOperatorWriteNode node) { + return defaultVisit(node); + } + + /** + * Visit a LocalVariableOrWriteNode node. + * + * @param node The node to visit. + * @return The result of visiting the node. + */ + public T visitLocalVariableOrWriteNode(Nodes.LocalVariableOrWriteNode node) { + return defaultVisit(node); + } + + /** + * Visit a LocalVariableReadNode node. + * + * @param node The node to visit. + * @return The result of visiting the node. + */ + public T visitLocalVariableReadNode(Nodes.LocalVariableReadNode node) { + return defaultVisit(node); + } + + /** + * Visit a LocalVariableTargetNode node. + * + * @param node The node to visit. + * @return The result of visiting the node. + */ + public T visitLocalVariableTargetNode(Nodes.LocalVariableTargetNode node) { + return defaultVisit(node); + } + + /** + * Visit a LocalVariableWriteNode node. + * + * @param node The node to visit. + * @return The result of visiting the node. + */ + public T visitLocalVariableWriteNode(Nodes.LocalVariableWriteNode node) { + return defaultVisit(node); + } + + /** + * Visit a MatchLastLineNode node. + * + * @param node The node to visit. + * @return The result of visiting the node. + */ + public T visitMatchLastLineNode(Nodes.MatchLastLineNode node) { + return defaultVisit(node); + } + + /** + * Visit a MatchPredicateNode node. + * + * @param node The node to visit. + * @return The result of visiting the node. + */ + public T visitMatchPredicateNode(Nodes.MatchPredicateNode node) { + return defaultVisit(node); + } + + /** + * Visit a MatchRequiredNode node. + * + * @param node The node to visit. + * @return The result of visiting the node. + */ + public T visitMatchRequiredNode(Nodes.MatchRequiredNode node) { + return defaultVisit(node); + } + + /** + * Visit a MatchWriteNode node. + * + * @param node The node to visit. + * @return The result of visiting the node. + */ + public T visitMatchWriteNode(Nodes.MatchWriteNode node) { + return defaultVisit(node); + } + + /** + * Visit a MissingNode node. + * + * @param node The node to visit. + * @return The result of visiting the node. + */ + public T visitMissingNode(Nodes.MissingNode node) { + return defaultVisit(node); + } + + /** + * Visit a ModuleNode node. + * + * @param node The node to visit. + * @return The result of visiting the node. + */ + public T visitModuleNode(Nodes.ModuleNode node) { + return defaultVisit(node); + } + + /** + * Visit a MultiTargetNode node. + * + * @param node The node to visit. + * @return The result of visiting the node. + */ + public T visitMultiTargetNode(Nodes.MultiTargetNode node) { + return defaultVisit(node); + } + + /** + * Visit a MultiWriteNode node. + * + * @param node The node to visit. + * @return The result of visiting the node. + */ + public T visitMultiWriteNode(Nodes.MultiWriteNode node) { + return defaultVisit(node); + } + + /** + * Visit a NextNode node. + * + * @param node The node to visit. + * @return The result of visiting the node. + */ + public T visitNextNode(Nodes.NextNode node) { + return defaultVisit(node); + } + + /** + * Visit a NilNode node. + * + * @param node The node to visit. + * @return The result of visiting the node. + */ + public T visitNilNode(Nodes.NilNode node) { + return defaultVisit(node); + } + + /** + * Visit a NoBlockParameterNode node. + * + * @param node The node to visit. + * @return The result of visiting the node. + */ + public T visitNoBlockParameterNode(Nodes.NoBlockParameterNode node) { + return defaultVisit(node); + } + + /** + * Visit a NoKeywordsParameterNode node. + * + * @param node The node to visit. + * @return The result of visiting the node. + */ + public T visitNoKeywordsParameterNode(Nodes.NoKeywordsParameterNode node) { + return defaultVisit(node); + } + + /** + * Visit a NumberedParametersNode node. + * + * @param node The node to visit. + * @return The result of visiting the node. + */ + public T visitNumberedParametersNode(Nodes.NumberedParametersNode node) { + return defaultVisit(node); + } + + /** + * Visit a NumberedReferenceReadNode node. + * + * @param node The node to visit. + * @return The result of visiting the node. + */ + public T visitNumberedReferenceReadNode(Nodes.NumberedReferenceReadNode node) { + return defaultVisit(node); + } + + /** + * Visit a OptionalKeywordParameterNode node. + * + * @param node The node to visit. + * @return The result of visiting the node. + */ + public T visitOptionalKeywordParameterNode(Nodes.OptionalKeywordParameterNode node) { + return defaultVisit(node); + } + + /** + * Visit a OptionalParameterNode node. + * + * @param node The node to visit. + * @return The result of visiting the node. + */ + public T visitOptionalParameterNode(Nodes.OptionalParameterNode node) { + return defaultVisit(node); + } + + /** + * Visit a OrNode node. + * + * @param node The node to visit. + * @return The result of visiting the node. + */ + public T visitOrNode(Nodes.OrNode node) { + return defaultVisit(node); + } + + /** + * Visit a ParametersNode node. + * + * @param node The node to visit. + * @return The result of visiting the node. + */ + public T visitParametersNode(Nodes.ParametersNode node) { + return defaultVisit(node); + } + + /** + * Visit a ParenthesesNode node. + * + * @param node The node to visit. + * @return The result of visiting the node. + */ + public T visitParenthesesNode(Nodes.ParenthesesNode node) { + return defaultVisit(node); + } + + /** + * Visit a PinnedExpressionNode node. + * + * @param node The node to visit. + * @return The result of visiting the node. + */ + public T visitPinnedExpressionNode(Nodes.PinnedExpressionNode node) { + return defaultVisit(node); + } + + /** + * Visit a PinnedVariableNode node. + * + * @param node The node to visit. + * @return The result of visiting the node. + */ + public T visitPinnedVariableNode(Nodes.PinnedVariableNode node) { + return defaultVisit(node); + } + + /** + * Visit a PostExecutionNode node. + * + * @param node The node to visit. + * @return The result of visiting the node. + */ + public T visitPostExecutionNode(Nodes.PostExecutionNode node) { + return defaultVisit(node); + } + + /** + * Visit a PreExecutionNode node. + * + * @param node The node to visit. + * @return The result of visiting the node. + */ + public T visitPreExecutionNode(Nodes.PreExecutionNode node) { + return defaultVisit(node); + } + + /** + * Visit a ProgramNode node. + * + * @param node The node to visit. + * @return The result of visiting the node. + */ + public T visitProgramNode(Nodes.ProgramNode node) { + return defaultVisit(node); + } + + /** + * Visit a RangeNode node. + * + * @param node The node to visit. + * @return The result of visiting the node. + */ + public T visitRangeNode(Nodes.RangeNode node) { + return defaultVisit(node); + } + + /** + * Visit a RationalNode node. + * + * @param node The node to visit. + * @return The result of visiting the node. + */ + public T visitRationalNode(Nodes.RationalNode node) { + return defaultVisit(node); + } + + /** + * Visit a RedoNode node. + * + * @param node The node to visit. + * @return The result of visiting the node. + */ + public T visitRedoNode(Nodes.RedoNode node) { + return defaultVisit(node); + } + + /** + * Visit a RegularExpressionNode node. + * + * @param node The node to visit. + * @return The result of visiting the node. + */ + public T visitRegularExpressionNode(Nodes.RegularExpressionNode node) { + return defaultVisit(node); + } + + /** + * Visit a RequiredKeywordParameterNode node. + * + * @param node The node to visit. + * @return The result of visiting the node. + */ + public T visitRequiredKeywordParameterNode(Nodes.RequiredKeywordParameterNode node) { + return defaultVisit(node); + } + + /** + * Visit a RequiredParameterNode node. + * + * @param node The node to visit. + * @return The result of visiting the node. + */ + public T visitRequiredParameterNode(Nodes.RequiredParameterNode node) { + return defaultVisit(node); + } + + /** + * Visit a RescueModifierNode node. + * + * @param node The node to visit. + * @return The result of visiting the node. + */ + public T visitRescueModifierNode(Nodes.RescueModifierNode node) { + return defaultVisit(node); + } + + /** + * Visit a RescueNode node. + * + * @param node The node to visit. + * @return The result of visiting the node. + */ + public T visitRescueNode(Nodes.RescueNode node) { + return defaultVisit(node); + } + + /** + * Visit a RestParameterNode node. + * + * @param node The node to visit. + * @return The result of visiting the node. + */ + public T visitRestParameterNode(Nodes.RestParameterNode node) { + return defaultVisit(node); + } + + /** + * Visit a RetryNode node. + * + * @param node The node to visit. + * @return The result of visiting the node. + */ + public T visitRetryNode(Nodes.RetryNode node) { + return defaultVisit(node); + } + + /** + * Visit a ReturnNode node. + * + * @param node The node to visit. + * @return The result of visiting the node. + */ + public T visitReturnNode(Nodes.ReturnNode node) { + return defaultVisit(node); + } + + /** + * Visit a SelfNode node. + * + * @param node The node to visit. + * @return The result of visiting the node. + */ + public T visitSelfNode(Nodes.SelfNode node) { + return defaultVisit(node); + } + + /** + * Visit a ShareableConstantNode node. + * + * @param node The node to visit. + * @return The result of visiting the node. + */ + public T visitShareableConstantNode(Nodes.ShareableConstantNode node) { + return defaultVisit(node); + } + + /** + * Visit a SingletonClassNode node. + * + * @param node The node to visit. + * @return The result of visiting the node. + */ + public T visitSingletonClassNode(Nodes.SingletonClassNode node) { + return defaultVisit(node); + } + + /** + * Visit a SourceEncodingNode node. + * + * @param node The node to visit. + * @return The result of visiting the node. + */ + public T visitSourceEncodingNode(Nodes.SourceEncodingNode node) { + return defaultVisit(node); + } + + /** + * Visit a SourceFileNode node. + * + * @param node The node to visit. + * @return The result of visiting the node. + */ + public T visitSourceFileNode(Nodes.SourceFileNode node) { + return defaultVisit(node); + } + + /** + * Visit a SourceLineNode node. + * + * @param node The node to visit. + * @return The result of visiting the node. + */ + public T visitSourceLineNode(Nodes.SourceLineNode node) { + return defaultVisit(node); + } + + /** + * Visit a SplatNode node. + * + * @param node The node to visit. + * @return The result of visiting the node. + */ + public T visitSplatNode(Nodes.SplatNode node) { + return defaultVisit(node); + } + + /** + * Visit a StatementsNode node. + * + * @param node The node to visit. + * @return The result of visiting the node. + */ + public T visitStatementsNode(Nodes.StatementsNode node) { + return defaultVisit(node); + } + + /** + * Visit a StringNode node. + * + * @param node The node to visit. + * @return The result of visiting the node. + */ + public T visitStringNode(Nodes.StringNode node) { + return defaultVisit(node); + } + + /** + * Visit a SuperNode node. + * + * @param node The node to visit. + * @return The result of visiting the node. + */ + public T visitSuperNode(Nodes.SuperNode node) { + return defaultVisit(node); + } + + /** + * Visit a SymbolNode node. + * + * @param node The node to visit. + * @return The result of visiting the node. + */ + public T visitSymbolNode(Nodes.SymbolNode node) { + return defaultVisit(node); + } + + /** + * Visit a TrueNode node. + * + * @param node The node to visit. + * @return The result of visiting the node. + */ + public T visitTrueNode(Nodes.TrueNode node) { + return defaultVisit(node); + } + + /** + * Visit a UndefNode node. + * + * @param node The node to visit. + * @return The result of visiting the node. + */ + public T visitUndefNode(Nodes.UndefNode node) { + return defaultVisit(node); + } + + /** + * Visit a UnlessNode node. + * + * @param node The node to visit. + * @return The result of visiting the node. + */ + public T visitUnlessNode(Nodes.UnlessNode node) { + return defaultVisit(node); + } + + /** + * Visit a UntilNode node. + * + * @param node The node to visit. + * @return The result of visiting the node. + */ + public T visitUntilNode(Nodes.UntilNode node) { + return defaultVisit(node); + } + + /** + * Visit a WhenNode node. + * + * @param node The node to visit. + * @return The result of visiting the node. + */ + public T visitWhenNode(Nodes.WhenNode node) { + return defaultVisit(node); + } + + /** + * Visit a WhileNode node. + * + * @param node The node to visit. + * @return The result of visiting the node. + */ + public T visitWhileNode(Nodes.WhileNode node) { + return defaultVisit(node); + } + + /** + * Visit a XStringNode node. + * + * @param node The node to visit. + * @return The result of visiting the node. + */ + public T visitXStringNode(Nodes.XStringNode node) { + return defaultVisit(node); + } + + /** + * Visit a YieldNode node. + * + * @param node The node to visit. + * @return The result of visiting the node. + */ + public T visitYieldNode(Nodes.YieldNode node) { + return defaultVisit(node); + } + +} +// @formatter:on diff --git a/java/api/src/main/java/org/ruby_lang/prism/Loader.java b/java/api/src/main/java/org/ruby_lang/prism/Loader.java new file mode 100644 index 0000000000..f084d169ad --- /dev/null +++ b/java/api/src/main/java/org/ruby_lang/prism/Loader.java @@ -0,0 +1,775 @@ +/*----------------------------------------------------------------------------*/ +/* This file is generated by the templates/template.rb script and should not */ +/* be modified manually. See */ +/* templates/java/org/ruby_lang/prism/Loader.java.erb */ +/* if you are looking to modify the */ +/* template */ +/*----------------------------------------------------------------------------*/ + +package org.ruby_lang.prism; + +import java.lang.Short; +import java.math.BigInteger; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; +import java.util.Locale; + +// GENERATED BY Loader.java.erb +// @formatter:off +public class Loader { + + public static ParseResult load(byte[] serialized) { + return new Loader(serialized).load(); + } + + // Overridable methods + + public byte[] bytesToName(byte[] bytes) { + return bytes; + } + + private static final class ConstantPool { + + private final Loader loader; + private final int bufferOffset; + private final byte[][] cache; + + ConstantPool(Loader loader, int bufferOffset, int length) { + this.loader = loader; + this.bufferOffset = bufferOffset; + cache = new byte[length][]; + } + + byte[] get(ByteBuffer buffer, int oneBasedIndex) { + int index = oneBasedIndex - 1; + byte[] constant = cache[index]; + + if (constant == null) { + int offset = bufferOffset + index * 8; + int start = buffer.getInt(offset); + int length = buffer.getInt(offset + 4); + + byte[] bytes = new byte[length]; + buffer.get(start, bytes); + + constant = loader.bytesToName(bytes); + cache[index] = constant; + } + + return constant; + } + + } + + private final ByteBuffer buffer; + protected String encodingName; + private ConstantPool constantPool; + private Nodes.Source source = null; + + protected Loader(byte[] serialized) { + this.buffer = ByteBuffer.wrap(serialized).order(ByteOrder.nativeOrder()); + } + + protected ParseResult load() { + this.source = new Nodes.Source(); + + expect((byte) 'P', "incorrect prism header"); + expect((byte) 'R', "incorrect prism header"); + expect((byte) 'I', "incorrect prism header"); + expect((byte) 'S', "incorrect prism header"); + expect((byte) 'M', "incorrect prism header"); + + expect((byte) 1, "prism major version does not match"); + expect((byte) 9, "prism minor version does not match"); + expect((byte) 0, "prism patch version does not match"); + + expect((byte) 1, "Loader.java requires no location fields in the serialized output"); + + // This loads the name of the encoding. + int encodingLength = loadVarUInt(); + byte[] encodingNameBytes = new byte[encodingLength]; + buffer.get(encodingNameBytes); + this.encodingName = new String(encodingNameBytes, StandardCharsets.US_ASCII); + + source.setStartLine(loadVarSInt()); + source.setLineOffsets(loadLineOffsets()); + + ParseResult.MagicComment[] magicComments = loadMagicComments(); + Nodes.Location dataLocation = loadOptionalLocation(); + ParseResult.Error[] errors = loadErrors(); + ParseResult.Warning[] warnings = loadWarnings(); + boolean continuable = buffer.get() != 0; + + int constantPoolBufferOffset = buffer.getInt(); + int constantPoolLength = loadVarUInt(); + this.constantPool = new ConstantPool(this, constantPoolBufferOffset, constantPoolLength); + + Nodes.Node node; + if (errors.length == 0) { + node = loadNode(); + + int left = constantPoolBufferOffset - buffer.position(); + if (left != 0) { + throw new Error("Expected to consume all bytes while deserializing but there were " + left + " bytes left"); + } + + MarkNewlinesVisitor visitor = new MarkNewlinesVisitor(source); + node.accept(visitor); + } else { + node = null; + } + + return new ParseResult(node, magicComments, dataLocation, errors, warnings, continuable, source); + } + + private byte[] loadString() { + int length = loadVarUInt(); + byte[] bytes = new byte[length]; + buffer.get(bytes); + return bytes; + } + + private int[] loadLineOffsets() { + int count = loadVarUInt(); + int[] lineOffsets = new int[count]; + for (int i = 0; i < count; i++) { + lineOffsets[i] = loadVarUInt(); + } + return lineOffsets; + } + + private ParseResult.MagicComment[] loadMagicComments() { + int count = loadVarUInt(); + ParseResult.MagicComment[] magicComments = new ParseResult.MagicComment[count]; + + for (int i = 0; i < count; i++) { + Nodes.Location keyLocation = loadLocation(); + Nodes.Location valueLocation = loadLocation(); + + ParseResult.MagicComment magicComment = new ParseResult.MagicComment(keyLocation, valueLocation); + magicComments[i] = magicComment; + } + + return magicComments; + } + + private ParseResult.Error[] loadErrors() { + int count = loadVarUInt(); + ParseResult.Error[] errors = new ParseResult.Error[count]; + + // error messages only contain ASCII characters + for (int i = 0; i < count; i++) { + Nodes.ErrorType type = Nodes.ERROR_TYPES[loadVarUInt()]; + byte[] bytes = loadString(); + String message = new String(bytes, StandardCharsets.US_ASCII); + Nodes.Location location = loadLocation(); + ParseResult.ErrorLevel level = ParseResult.ERROR_LEVELS[buffer.get()]; + + ParseResult.Error error = new ParseResult.Error(type, message, location, level); + errors[i] = error; + } + + return errors; + } + + private ParseResult.Warning[] loadWarnings() { + int count = loadVarUInt(); + ParseResult.Warning[] warnings = new ParseResult.Warning[count]; + + // warning messages only contain ASCII characters + for (int i = 0; i < count; i++) { + Nodes.WarningType type = Nodes.WARNING_TYPES[loadVarUInt() - 299]; + byte[] bytes = loadString(); + String message = new String(bytes, StandardCharsets.US_ASCII); + Nodes.Location location = loadLocation(); + ParseResult.WarningLevel level = ParseResult.WARNING_LEVELS[buffer.get()]; + + ParseResult.Warning warning = new ParseResult.Warning(type, message, location, level); + warnings[i] = warning; + } + + return warnings; + } + + private Nodes.Node loadOptionalNode() { + if (buffer.get(buffer.position()) != 0) { + return loadNode(); + } else { + buffer.position(buffer.position() + 1); // continue after the 0 byte + return null; + } + } + + private byte[] loadConstant() { + return constantPool.get(buffer, loadVarUInt()); + } + + private byte[] loadOptionalConstant() { + if (buffer.get(buffer.position()) != 0) { + return loadConstant(); + } else { + buffer.position(buffer.position() + 1); // continue after the 0 byte + return null; + } + } + + private byte[][] loadConstants() { + int length = loadVarUInt(); + if (length == 0) { + return Nodes.EMPTY_IDENTIFIER_ARRAY; + } + byte[][] constants = new byte[length][]; + for (int i = 0; i < length; i++) { + constants[i] = constantPool.get(buffer, loadVarUInt()); + } + return constants; + } + + private Nodes.Location loadLocation() { + return new Nodes.Location(loadVarUInt(), loadVarUInt()); + } + + private Nodes.Location loadOptionalLocation() { + if (buffer.get() != 0) { + return loadLocation(); + } else { + return null; + } + } + + // From https://github.com/protocolbuffers/protobuf/blob/v23.1/java/core/src/main/java/com/google/protobuf/BinaryReader.java#L1507 + private int loadVarUInt() { + int x; + if ((x = buffer.get()) >= 0) { + return x; + } else if ((x ^= (buffer.get() << 7)) < 0) { + x ^= (~0 << 7); + } else if ((x ^= (buffer.get() << 14)) >= 0) { + x ^= (~0 << 7) ^ (~0 << 14); + } else if ((x ^= (buffer.get() << 21)) < 0) { + x ^= (~0 << 7) ^ (~0 << 14) ^ (~0 << 21); + } else { + x ^= buffer.get() << 28; + x ^= (~0 << 7) ^ (~0 << 14) ^ (~0 << 21) ^ (~0 << 28); + } + return x; + } + + // From https://github.com/protocolbuffers/protobuf/blob/v25.1/java/core/src/main/java/com/google/protobuf/CodedInputStream.java#L508-L510 + private int loadVarSInt() { + int x = loadVarUInt(); + return (x >>> 1) ^ (-(x & 1)); + } + + private short loadFlags() { + int flags = loadVarUInt(); + assert flags >= 0 && flags <= Short.MAX_VALUE; + return (short) flags; + } + + private static final BigInteger UNSIGNED_LONG_MASK = BigInteger.ONE.shiftLeft(Long.SIZE).subtract(BigInteger.ONE); + + private Object loadInteger() { + boolean negative = buffer.get() != 0; + + int wordsLength = loadVarUInt(); + assert wordsLength > 0; + + // Load the first word. If it's the only word, then return an int if it + // fits into one and a long otherwise. + int firstWord = loadVarUInt(); + if (wordsLength == 1) { + if (firstWord < 0) { + if (negative && firstWord == Integer.MIN_VALUE) { + return Integer.MIN_VALUE; + } + + long words = Integer.toUnsignedLong(firstWord); + return negative ? -words : words; + } + return negative ? -firstWord : firstWord; + } + + // Load the second word. If there are only two words, then return a long + // if it fits into one and a BigInteger otherwise. + int secondWord = loadVarUInt(); + if (wordsLength == 2) { + long words = (((long) secondWord) << 32L) | Integer.toUnsignedLong(firstWord); + if (words < 0L) { + if (negative && words == Long.MIN_VALUE) { + return Long.MIN_VALUE; + } + + BigInteger result = BigInteger.valueOf(words).and(UNSIGNED_LONG_MASK); + return negative ? result.negate() : result; + } + return negative ? -words : words; + } + + // Otherwise, load the remaining words and return a BigInt. + BigInteger result = BigInteger.valueOf(Integer.toUnsignedLong(firstWord)); + result = result.or(BigInteger.valueOf(Integer.toUnsignedLong(secondWord)).shiftLeft(32)); + + for (int wordsIndex = 2; wordsIndex < wordsLength; wordsIndex++) { + result = result.or(BigInteger.valueOf(Integer.toUnsignedLong(loadVarUInt())).shiftLeft(wordsIndex * 32)); + } + + return negative ? result.negate() : result; + } + + private Nodes.Node loadNode() { + int type = buffer.get() & 0xFF; + int startOffset = loadVarUInt(); + int length = loadVarUInt(); + + switch (type) { + case 1: + return new Nodes.AliasGlobalVariableNode(startOffset, length, loadNode(), loadNode()); + case 2: + return new Nodes.AliasMethodNode(startOffset, length, loadNode(), loadNode()); + case 3: + return new Nodes.AlternationPatternNode(startOffset, length, loadNode(), loadNode()); + case 4: + return new Nodes.AndNode(startOffset, length, loadNode(), loadNode()); + case 5: + return new Nodes.ArgumentsNode(startOffset, length, loadFlags(), loadNodes()); + case 6: + return new Nodes.ArrayNode(startOffset, length, loadFlags(), loadNodes()); + case 7: + return new Nodes.ArrayPatternNode(startOffset, length, loadOptionalNode(), loadNodes(), loadOptionalNode(), loadNodes()); + case 8: + return new Nodes.AssocNode(startOffset, length, loadNode(), loadNode()); + case 9: + return new Nodes.AssocSplatNode(startOffset, length, loadOptionalNode()); + case 10: + return new Nodes.BackReferenceReadNode(startOffset, length, loadConstant()); + case 11: + return new Nodes.BeginNode(startOffset, length, (Nodes.StatementsNode) loadOptionalNode(), (Nodes.RescueNode) loadOptionalNode(), (Nodes.ElseNode) loadOptionalNode(), (Nodes.EnsureNode) loadOptionalNode()); + case 12: + return new Nodes.BlockArgumentNode(startOffset, length, loadOptionalNode()); + case 13: + return new Nodes.BlockLocalVariableNode(startOffset, length, loadFlags(), loadConstant()); + case 14: + return new Nodes.BlockNode(startOffset, length, loadConstants(), loadOptionalNode(), loadOptionalNode()); + case 15: + return new Nodes.BlockParameterNode(startOffset, length, loadFlags(), loadOptionalConstant()); + case 16: + return new Nodes.BlockParametersNode(startOffset, length, (Nodes.ParametersNode) loadOptionalNode(), loadBlockLocalVariableNodes()); + case 17: + return new Nodes.BreakNode(startOffset, length, (Nodes.ArgumentsNode) loadOptionalNode()); + case 18: + return new Nodes.CallAndWriteNode(startOffset, length, loadFlags(), loadOptionalNode(), loadConstant(), loadConstant(), loadNode()); + case 19: + return new Nodes.CallNode(startOffset, length, loadFlags(), loadOptionalNode(), loadConstant(), (Nodes.ArgumentsNode) loadOptionalNode(), loadOptionalNode()); + case 20: + return new Nodes.CallOperatorWriteNode(startOffset, length, loadFlags(), loadOptionalNode(), loadConstant(), loadConstant(), loadConstant(), loadNode()); + case 21: + return new Nodes.CallOrWriteNode(startOffset, length, loadFlags(), loadOptionalNode(), loadConstant(), loadConstant(), loadNode()); + case 22: + return new Nodes.CallTargetNode(startOffset, length, loadFlags(), loadNode(), loadConstant()); + case 23: + return new Nodes.CapturePatternNode(startOffset, length, loadNode(), (Nodes.LocalVariableTargetNode) loadNode()); + case 24: + return new Nodes.CaseMatchNode(startOffset, length, loadOptionalNode(), loadInNodes(), (Nodes.ElseNode) loadOptionalNode()); + case 25: + return new Nodes.CaseNode(startOffset, length, loadOptionalNode(), loadWhenNodes(), (Nodes.ElseNode) loadOptionalNode()); + case 26: + return new Nodes.ClassNode(startOffset, length, loadConstants(), loadNode(), loadOptionalNode(), loadOptionalNode(), loadConstant()); + case 27: + return new Nodes.ClassVariableAndWriteNode(startOffset, length, loadConstant(), loadNode()); + case 28: + return new Nodes.ClassVariableOperatorWriteNode(startOffset, length, loadConstant(), loadNode(), loadConstant()); + case 29: + return new Nodes.ClassVariableOrWriteNode(startOffset, length, loadConstant(), loadNode()); + case 30: + return new Nodes.ClassVariableReadNode(startOffset, length, loadConstant()); + case 31: + return new Nodes.ClassVariableTargetNode(startOffset, length, loadConstant()); + case 32: + return new Nodes.ClassVariableWriteNode(startOffset, length, loadConstant(), loadNode()); + case 33: + return new Nodes.ConstantAndWriteNode(startOffset, length, loadConstant(), loadNode()); + case 34: + return new Nodes.ConstantOperatorWriteNode(startOffset, length, loadConstant(), loadNode(), loadConstant()); + case 35: + return new Nodes.ConstantOrWriteNode(startOffset, length, loadConstant(), loadNode()); + case 36: + return new Nodes.ConstantPathAndWriteNode(startOffset, length, (Nodes.ConstantPathNode) loadNode(), loadNode()); + case 37: + return new Nodes.ConstantPathNode(startOffset, length, loadOptionalNode(), loadOptionalConstant()); + case 38: + return new Nodes.ConstantPathOperatorWriteNode(startOffset, length, (Nodes.ConstantPathNode) loadNode(), loadNode(), loadConstant()); + case 39: + return new Nodes.ConstantPathOrWriteNode(startOffset, length, (Nodes.ConstantPathNode) loadNode(), loadNode()); + case 40: + return new Nodes.ConstantPathTargetNode(startOffset, length, loadOptionalNode(), loadOptionalConstant()); + case 41: + return new Nodes.ConstantPathWriteNode(startOffset, length, (Nodes.ConstantPathNode) loadNode(), loadNode()); + case 42: + return new Nodes.ConstantReadNode(startOffset, length, loadConstant()); + case 43: + return new Nodes.ConstantTargetNode(startOffset, length, loadConstant()); + case 44: + return new Nodes.ConstantWriteNode(startOffset, length, loadConstant(), loadNode()); + case 45: + return loadDefNode(startOffset, length); + case 46: + return new Nodes.DefinedNode(startOffset, length, loadNode()); + case 47: + return new Nodes.ElseNode(startOffset, length, (Nodes.StatementsNode) loadOptionalNode()); + case 48: + return new Nodes.EmbeddedStatementsNode(startOffset, length, (Nodes.StatementsNode) loadOptionalNode()); + case 49: + return new Nodes.EmbeddedVariableNode(startOffset, length, loadNode()); + case 50: + return new Nodes.EnsureNode(startOffset, length, (Nodes.StatementsNode) loadOptionalNode()); + case 51: + return new Nodes.FalseNode(startOffset, length); + case 52: + return new Nodes.FindPatternNode(startOffset, length, loadOptionalNode(), (Nodes.SplatNode) loadNode(), loadNodes(), (Nodes.SplatNode) loadNode()); + case 53: + return new Nodes.FlipFlopNode(startOffset, length, loadFlags(), loadOptionalNode(), loadOptionalNode()); + case 54: + return new Nodes.FloatNode(startOffset, length, buffer.getDouble()); + case 55: + return new Nodes.ForNode(startOffset, length, loadNode(), loadNode(), (Nodes.StatementsNode) loadOptionalNode()); + case 56: + return new Nodes.ForwardingArgumentsNode(startOffset, length); + case 57: + return new Nodes.ForwardingParameterNode(startOffset, length); + case 58: + return new Nodes.ForwardingSuperNode(startOffset, length, (Nodes.BlockNode) loadOptionalNode()); + case 59: + return new Nodes.GlobalVariableAndWriteNode(startOffset, length, loadConstant(), loadNode()); + case 60: + return new Nodes.GlobalVariableOperatorWriteNode(startOffset, length, loadConstant(), loadNode(), loadConstant()); + case 61: + return new Nodes.GlobalVariableOrWriteNode(startOffset, length, loadConstant(), loadNode()); + case 62: + return new Nodes.GlobalVariableReadNode(startOffset, length, loadConstant()); + case 63: + return new Nodes.GlobalVariableTargetNode(startOffset, length, loadConstant()); + case 64: + return new Nodes.GlobalVariableWriteNode(startOffset, length, loadConstant(), loadNode()); + case 65: + return new Nodes.HashNode(startOffset, length, loadNodes()); + case 66: + return new Nodes.HashPatternNode(startOffset, length, loadOptionalNode(), loadAssocNodes(), loadOptionalNode()); + case 67: + return new Nodes.IfNode(startOffset, length, loadNode(), (Nodes.StatementsNode) loadOptionalNode(), loadOptionalNode()); + case 68: + return new Nodes.ImaginaryNode(startOffset, length, loadNode()); + case 69: + return new Nodes.ImplicitNode(startOffset, length, loadNode()); + case 70: + return new Nodes.ImplicitRestNode(startOffset, length); + case 71: + return new Nodes.InNode(startOffset, length, loadNode(), (Nodes.StatementsNode) loadOptionalNode()); + case 72: + return new Nodes.IndexAndWriteNode(startOffset, length, loadFlags(), loadOptionalNode(), (Nodes.ArgumentsNode) loadOptionalNode(), (Nodes.BlockArgumentNode) loadOptionalNode(), loadNode()); + case 73: + return new Nodes.IndexOperatorWriteNode(startOffset, length, loadFlags(), loadOptionalNode(), (Nodes.ArgumentsNode) loadOptionalNode(), (Nodes.BlockArgumentNode) loadOptionalNode(), loadConstant(), loadNode()); + case 74: + return new Nodes.IndexOrWriteNode(startOffset, length, loadFlags(), loadOptionalNode(), (Nodes.ArgumentsNode) loadOptionalNode(), (Nodes.BlockArgumentNode) loadOptionalNode(), loadNode()); + case 75: + return new Nodes.IndexTargetNode(startOffset, length, loadFlags(), loadNode(), (Nodes.ArgumentsNode) loadOptionalNode(), (Nodes.BlockArgumentNode) loadOptionalNode()); + case 76: + return new Nodes.InstanceVariableAndWriteNode(startOffset, length, loadConstant(), loadNode()); + case 77: + return new Nodes.InstanceVariableOperatorWriteNode(startOffset, length, loadConstant(), loadNode(), loadConstant()); + case 78: + return new Nodes.InstanceVariableOrWriteNode(startOffset, length, loadConstant(), loadNode()); + case 79: + return new Nodes.InstanceVariableReadNode(startOffset, length, loadConstant()); + case 80: + return new Nodes.InstanceVariableTargetNode(startOffset, length, loadConstant()); + case 81: + return new Nodes.InstanceVariableWriteNode(startOffset, length, loadConstant(), loadNode()); + case 82: + return new Nodes.IntegerNode(startOffset, length, loadFlags(), loadInteger()); + case 83: + return new Nodes.InterpolatedMatchLastLineNode(startOffset, length, loadFlags(), loadNodes()); + case 84: + return new Nodes.InterpolatedRegularExpressionNode(startOffset, length, loadFlags(), loadNodes()); + case 85: + return new Nodes.InterpolatedStringNode(startOffset, length, loadFlags(), loadNodes()); + case 86: + return new Nodes.InterpolatedSymbolNode(startOffset, length, loadNodes()); + case 87: + return new Nodes.InterpolatedXStringNode(startOffset, length, loadNodes()); + case 88: + return new Nodes.ItLocalVariableReadNode(startOffset, length); + case 89: + return new Nodes.ItParametersNode(startOffset, length); + case 90: + return new Nodes.KeywordHashNode(startOffset, length, loadFlags(), loadNodes()); + case 91: + return new Nodes.KeywordRestParameterNode(startOffset, length, loadFlags(), loadOptionalConstant()); + case 92: + return new Nodes.LambdaNode(startOffset, length, loadConstants(), loadOptionalNode(), loadOptionalNode()); + case 93: + return new Nodes.LocalVariableAndWriteNode(startOffset, length, loadNode(), loadConstant(), loadVarUInt()); + case 94: + return new Nodes.LocalVariableOperatorWriteNode(startOffset, length, loadNode(), loadConstant(), loadConstant(), loadVarUInt()); + case 95: + return new Nodes.LocalVariableOrWriteNode(startOffset, length, loadNode(), loadConstant(), loadVarUInt()); + case 96: + return new Nodes.LocalVariableReadNode(startOffset, length, loadConstant(), loadVarUInt()); + case 97: + return new Nodes.LocalVariableTargetNode(startOffset, length, loadConstant(), loadVarUInt()); + case 98: + return new Nodes.LocalVariableWriteNode(startOffset, length, loadConstant(), loadVarUInt(), loadNode()); + case 99: + return new Nodes.MatchLastLineNode(startOffset, length, loadFlags(), loadString()); + case 100: + return new Nodes.MatchPredicateNode(startOffset, length, loadNode(), loadNode()); + case 101: + return new Nodes.MatchRequiredNode(startOffset, length, loadNode(), loadNode()); + case 102: + return new Nodes.MatchWriteNode(startOffset, length, (Nodes.CallNode) loadNode(), loadLocalVariableTargetNodes()); + case 103: + return new Nodes.MissingNode(startOffset, length); + case 104: + return new Nodes.ModuleNode(startOffset, length, loadConstants(), loadNode(), loadOptionalNode(), loadConstant()); + case 105: + return new Nodes.MultiTargetNode(startOffset, length, loadNodes(), loadOptionalNode(), loadNodes()); + case 106: + return new Nodes.MultiWriteNode(startOffset, length, loadNodes(), loadOptionalNode(), loadNodes(), loadNode()); + case 107: + return new Nodes.NextNode(startOffset, length, (Nodes.ArgumentsNode) loadOptionalNode()); + case 108: + return new Nodes.NilNode(startOffset, length); + case 109: + return new Nodes.NoBlockParameterNode(startOffset, length); + case 110: + return new Nodes.NoKeywordsParameterNode(startOffset, length); + case 111: + return new Nodes.NumberedParametersNode(startOffset, length, buffer.get()); + case 112: + return new Nodes.NumberedReferenceReadNode(startOffset, length, loadVarUInt()); + case 113: + return new Nodes.OptionalKeywordParameterNode(startOffset, length, loadFlags(), loadConstant(), loadNode()); + case 114: + return new Nodes.OptionalParameterNode(startOffset, length, loadFlags(), loadConstant(), loadNode()); + case 115: + return new Nodes.OrNode(startOffset, length, loadNode(), loadNode()); + case 116: + return new Nodes.ParametersNode(startOffset, length, loadNodes(), loadOptionalParameterNodes(), loadOptionalNode(), loadNodes(), loadNodes(), loadOptionalNode(), loadOptionalNode()); + case 117: + return new Nodes.ParenthesesNode(startOffset, length, loadFlags(), loadOptionalNode()); + case 118: + return new Nodes.PinnedExpressionNode(startOffset, length, loadNode()); + case 119: + return new Nodes.PinnedVariableNode(startOffset, length, loadNode()); + case 120: + return new Nodes.PostExecutionNode(startOffset, length, (Nodes.StatementsNode) loadOptionalNode()); + case 121: + return new Nodes.PreExecutionNode(startOffset, length, (Nodes.StatementsNode) loadOptionalNode()); + case 122: + return new Nodes.ProgramNode(startOffset, length, loadConstants(), (Nodes.StatementsNode) loadNode()); + case 123: + return new Nodes.RangeNode(startOffset, length, loadFlags(), loadOptionalNode(), loadOptionalNode()); + case 124: + return new Nodes.RationalNode(startOffset, length, loadFlags(), loadInteger(), loadInteger()); + case 125: + return new Nodes.RedoNode(startOffset, length); + case 126: + return new Nodes.RegularExpressionNode(startOffset, length, loadFlags(), loadString()); + case 127: + return new Nodes.RequiredKeywordParameterNode(startOffset, length, loadFlags(), loadConstant()); + case 128: + return new Nodes.RequiredParameterNode(startOffset, length, loadFlags(), loadConstant()); + case 129: + return new Nodes.RescueModifierNode(startOffset, length, loadNode(), loadNode()); + case 130: + return new Nodes.RescueNode(startOffset, length, loadNodes(), loadOptionalNode(), (Nodes.StatementsNode) loadOptionalNode(), (Nodes.RescueNode) loadOptionalNode()); + case 131: + return new Nodes.RestParameterNode(startOffset, length, loadFlags(), loadOptionalConstant()); + case 132: + return new Nodes.RetryNode(startOffset, length); + case 133: + return new Nodes.ReturnNode(startOffset, length, (Nodes.ArgumentsNode) loadOptionalNode()); + case 134: + return new Nodes.SelfNode(startOffset, length); + case 135: + return new Nodes.ShareableConstantNode(startOffset, length, loadFlags(), loadNode()); + case 136: + return new Nodes.SingletonClassNode(startOffset, length, loadConstants(), loadNode(), loadOptionalNode()); + case 137: + return new Nodes.SourceEncodingNode(startOffset, length); + case 138: + return new Nodes.SourceFileNode(startOffset, length, loadFlags(), loadString()); + case 139: + return new Nodes.SourceLineNode(startOffset, length); + case 140: + return new Nodes.SplatNode(startOffset, length, loadOptionalNode()); + case 141: + return new Nodes.StatementsNode(startOffset, length, loadNodes()); + case 142: + return new Nodes.StringNode(startOffset, length, loadFlags(), loadString()); + case 143: + return new Nodes.SuperNode(startOffset, length, (Nodes.ArgumentsNode) loadOptionalNode(), loadOptionalNode()); + case 144: + return new Nodes.SymbolNode(startOffset, length, loadFlags(), loadString()); + case 145: + return new Nodes.TrueNode(startOffset, length); + case 146: + return new Nodes.UndefNode(startOffset, length, loadNodes()); + case 147: + return new Nodes.UnlessNode(startOffset, length, loadNode(), (Nodes.StatementsNode) loadOptionalNode(), (Nodes.ElseNode) loadOptionalNode()); + case 148: + return new Nodes.UntilNode(startOffset, length, loadFlags(), loadNode(), (Nodes.StatementsNode) loadOptionalNode()); + case 149: + return new Nodes.WhenNode(startOffset, length, loadNodes(), (Nodes.StatementsNode) loadOptionalNode()); + case 150: + return new Nodes.WhileNode(startOffset, length, loadFlags(), loadNode(), (Nodes.StatementsNode) loadOptionalNode()); + case 151: + return new Nodes.XStringNode(startOffset, length, loadFlags(), loadString()); + case 152: + return new Nodes.YieldNode(startOffset, length, (Nodes.ArgumentsNode) loadOptionalNode()); + default: + throw new Error("Unknown node type: " + type); + } + } + + // Can be overridden to use createLazyDefNode instead + protected Nodes.DefNode loadDefNode(int startOffset, int length) { + return createDefNode(startOffset, length); + } + + protected Nodes.DefNode createLazyDefNode(int startOffset, int length) { + int bufferPosition = buffer.position(); + int serializedLength = buffer.getInt(); + // Load everything except the body and locals, because the name, receiver, parameters are still needed for lazily defining the method + Nodes.DefNode lazyDefNode = new Nodes.DefNode(startOffset, length, -bufferPosition, this, loadConstant(), loadOptionalNode(), (Nodes.ParametersNode) loadOptionalNode(), null, Nodes.EMPTY_IDENTIFIER_ARRAY); + buffer.position(bufferPosition + serializedLength); // skip past the serialized DefNode + return lazyDefNode; + } + + protected Nodes.DefNode createDefNode(int startOffset, int length) { + return new Nodes.DefNode(startOffset, length, buffer.getInt(), null, loadConstant(), loadOptionalNode(), (Nodes.ParametersNode) loadOptionalNode(), loadOptionalNode(), loadConstants()); + } + + Nodes.DefNode createDefNodeFromSavedPosition(int startOffset, int length, int bufferPosition) { + Nodes.DefNode node; + // This method mutates the buffer position and may be called from different threads so we must synchronize + synchronized (this) { + buffer.position(bufferPosition); + node = createDefNode(startOffset, length); + } + + MarkNewlinesVisitor visitor = new MarkNewlinesVisitor(source); + node.accept(visitor); + + return node; + } + + private static final Nodes.Node[] EMPTY_Node_ARRAY = {}; + + private Nodes.Node[] loadNodes() { + int length = loadVarUInt(); + if (length == 0) { + return EMPTY_Node_ARRAY; + } + Nodes.Node[] nodes = new Nodes.Node[length]; + for (int i = 0; i < length; i++) { + nodes[i] = loadNode(); + } + return nodes; + } + + private static final Nodes.BlockLocalVariableNode[] EMPTY_BlockLocalVariableNode_ARRAY = {}; + + private Nodes.BlockLocalVariableNode[] loadBlockLocalVariableNodes() { + int length = loadVarUInt(); + if (length == 0) { + return EMPTY_BlockLocalVariableNode_ARRAY; + } + Nodes.BlockLocalVariableNode[] nodes = new Nodes.BlockLocalVariableNode[length]; + for (int i = 0; i < length; i++) { + nodes[i] = (Nodes.BlockLocalVariableNode) loadNode(); + } + return nodes; + } + + private static final Nodes.InNode[] EMPTY_InNode_ARRAY = {}; + + private Nodes.InNode[] loadInNodes() { + int length = loadVarUInt(); + if (length == 0) { + return EMPTY_InNode_ARRAY; + } + Nodes.InNode[] nodes = new Nodes.InNode[length]; + for (int i = 0; i < length; i++) { + nodes[i] = (Nodes.InNode) loadNode(); + } + return nodes; + } + + private static final Nodes.WhenNode[] EMPTY_WhenNode_ARRAY = {}; + + private Nodes.WhenNode[] loadWhenNodes() { + int length = loadVarUInt(); + if (length == 0) { + return EMPTY_WhenNode_ARRAY; + } + Nodes.WhenNode[] nodes = new Nodes.WhenNode[length]; + for (int i = 0; i < length; i++) { + nodes[i] = (Nodes.WhenNode) loadNode(); + } + return nodes; + } + + private static final Nodes.AssocNode[] EMPTY_AssocNode_ARRAY = {}; + + private Nodes.AssocNode[] loadAssocNodes() { + int length = loadVarUInt(); + if (length == 0) { + return EMPTY_AssocNode_ARRAY; + } + Nodes.AssocNode[] nodes = new Nodes.AssocNode[length]; + for (int i = 0; i < length; i++) { + nodes[i] = (Nodes.AssocNode) loadNode(); + } + return nodes; + } + + private static final Nodes.LocalVariableTargetNode[] EMPTY_LocalVariableTargetNode_ARRAY = {}; + + private Nodes.LocalVariableTargetNode[] loadLocalVariableTargetNodes() { + int length = loadVarUInt(); + if (length == 0) { + return EMPTY_LocalVariableTargetNode_ARRAY; + } + Nodes.LocalVariableTargetNode[] nodes = new Nodes.LocalVariableTargetNode[length]; + for (int i = 0; i < length; i++) { + nodes[i] = (Nodes.LocalVariableTargetNode) loadNode(); + } + return nodes; + } + + private static final Nodes.OptionalParameterNode[] EMPTY_OptionalParameterNode_ARRAY = {}; + + private Nodes.OptionalParameterNode[] loadOptionalParameterNodes() { + int length = loadVarUInt(); + if (length == 0) { + return EMPTY_OptionalParameterNode_ARRAY; + } + Nodes.OptionalParameterNode[] nodes = new Nodes.OptionalParameterNode[length]; + for (int i = 0; i < length; i++) { + nodes[i] = (Nodes.OptionalParameterNode) loadNode(); + } + return nodes; + } + + private void expect(byte value, String error) { + byte b = buffer.get(); + if (b != value) { + throw new Error("Deserialization error: " + error + " (expected " + value + " but was " + b + " at position " + buffer.position() + ")"); + } + } + +} +// @formatter:on diff --git a/java/org/prism/MarkNewlinesVisitor.java b/java/api/src/main/java/org/ruby_lang/prism/MarkNewlinesVisitor.java similarity index 91% rename from java/org/prism/MarkNewlinesVisitor.java rename to java/api/src/main/java/org/ruby_lang/prism/MarkNewlinesVisitor.java index 27ab8643e7..148b0d1a2e 100644 --- a/java/org/prism/MarkNewlinesVisitor.java +++ b/java/api/src/main/java/org/ruby_lang/prism/MarkNewlinesVisitor.java @@ -1,4 +1,4 @@ -package org.prism; +package org.ruby_lang.prism; // Keep in sync with Ruby MarkNewlinesVisitor final class MarkNewlinesVisitor extends AbstractNodeVisitor { @@ -6,9 +6,9 @@ final class MarkNewlinesVisitor extends AbstractNodeVisitor { private final Nodes.Source source; private boolean[] newlineMarked; - MarkNewlinesVisitor(Nodes.Source source, boolean[] newlineMarked) { + MarkNewlinesVisitor(Nodes.Source source) { this.source = source; - this.newlineMarked = newlineMarked; + this.newlineMarked = new boolean[1 + source.getLineCount()]; } @Override diff --git a/java/api/src/main/java/org/ruby_lang/prism/Nodes.java b/java/api/src/main/java/org/ruby_lang/prism/Nodes.java new file mode 100644 index 0000000000..8f032d5fa0 --- /dev/null +++ b/java/api/src/main/java/org/ruby_lang/prism/Nodes.java @@ -0,0 +1,11628 @@ +/*----------------------------------------------------------------------------*/ +/* This file is generated by the templates/template.rb script and should not */ +/* be modified manually. See */ +/* templates/java/org/ruby_lang/prism/Nodes.java.erb */ +/* if you are looking to modify the */ +/* template */ +/*----------------------------------------------------------------------------*/ + +package org.ruby_lang.prism; + +import java.lang.Override; +import java.lang.String; +import java.lang.StringBuilder; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.Arrays; + +// GENERATED BY Nodes.java.erb +// @formatter:off +public abstract class Nodes { + + public static final byte[][] EMPTY_IDENTIFIER_ARRAY = {}; + + @Target(ElementType.FIELD) + @Retention(RetentionPolicy.SOURCE) + public @interface Nullable { + } + + @Target(ElementType.FIELD) + @Retention(RetentionPolicy.SOURCE) + public @interface UnionType { + Class[] value(); + } + + public static final class Location { + + public static final Location[] EMPTY_ARRAY = {}; + + public final int startOffset; + public final int length; + + public Location(int startOffset, int length) { + this.startOffset = startOffset; + this.length = length; + } + + public int endOffset() { + return startOffset + length; + } + } + + public static final class Source { + private int startLine = 1; + private int[] lineOffsets = null; + + Source() { + } + + void setStartLine(int startLine) { + this.startLine = startLine; + } + + void setLineOffsets(int[] lineOffsets) { + this.lineOffsets = lineOffsets; + } + + // 1-based + public int line(int byteOffset) { + return startLine + findLine(byteOffset); + } + + // 0-based + public int findLine(int byteOffset) { + assert byteOffset >= 0 : byteOffset; + int index = Arrays.binarySearch(lineOffsets, byteOffset); + int line; + if (index < 0) { + line = -index - 2; + } else { + line = index; + } + assert line >= 0 && line <= getLineCount() : line; + return line; + } + + public int getLineCount() { + return lineOffsets.length; + } + } + + public static abstract class Node { + + public static final Node[] EMPTY_ARRAY = {}; + + public final int startOffset; + public final int length; + private boolean newLineFlag = false; + + public Node(int startOffset, int length) { + this.startOffset = startOffset; + this.length = length; + } + + public final int endOffset() { + return startOffset + length; + } + + public final boolean hasNewLineFlag() { + return newLineFlag; + } + + public void setNewLineFlag(Source source, boolean[] newlineMarked) { + int line = source.findLine(this.startOffset); + if (!newlineMarked[line]) { + newlineMarked[line] = true; + this.newLineFlag = true; + } + } + + public void setNewLineFlag(boolean newLineFlag) { + this.newLineFlag = newLineFlag; + } + + public abstract T accept(AbstractNodeVisitor visitor); + + public abstract void visitChildNodes(AbstractNodeVisitor visitor); + + public abstract Node[] childNodes(); + + @Override + public String toString() { + return toString(""); + } + + protected abstract String toString(String indent); + } + + protected static String asString(Object value) { + return value.toString(); + } + + protected static String asString(byte[] value) { + StringBuilder buf = new StringBuilder(value.length); + for (byte b : value) { + if (b >= 0x20 && b <= 0x7e) { + buf.append((char) b); + } else { + buf.append(String.format("\\x%02x", Byte.toUnsignedInt(b))); + } + } + return buf.toString(); + } + + /** + * Flags for arguments nodes. + */ + public static final class ArgumentsNodeFlags implements Comparable { + + // if the arguments contain forwarding + public static final short CONTAINS_FORWARDING = 1 << 2; + + // if the arguments contain keywords + public static final short CONTAINS_KEYWORDS = 1 << 3; + + // if the arguments contain a keyword splat + public static final short CONTAINS_KEYWORD_SPLAT = 1 << 4; + + // if the arguments contain a splat + public static final short CONTAINS_SPLAT = 1 << 5; + + // if the arguments contain multiple splats + public static final short CONTAINS_MULTIPLE_SPLATS = 1 << 6; + + public static boolean isContainsForwarding(short flags) { + return (flags & CONTAINS_FORWARDING) != 0; + } + + public static boolean isContainsKeywords(short flags) { + return (flags & CONTAINS_KEYWORDS) != 0; + } + + public static boolean isContainsKeywordSplat(short flags) { + return (flags & CONTAINS_KEYWORD_SPLAT) != 0; + } + + public static boolean isContainsSplat(short flags) { + return (flags & CONTAINS_SPLAT) != 0; + } + + public static boolean isContainsMultipleSplats(short flags) { + return (flags & CONTAINS_MULTIPLE_SPLATS) != 0; + } + + private final short flags; + + public ArgumentsNodeFlags(short flags) { + this.flags = flags; + } + + @Override + public int hashCode() { + return flags; + } + + @Override + public boolean equals(Object other) { + if (!(other instanceof ArgumentsNodeFlags)) { + return false; + } + + return flags == ((ArgumentsNodeFlags) other).flags; + } + + @Override + public int compareTo(ArgumentsNodeFlags other) { + return flags - other.flags; + } + + public boolean isContainsForwarding() { + return (flags & CONTAINS_FORWARDING) != 0; + } + + public boolean isContainsKeywords() { + return (flags & CONTAINS_KEYWORDS) != 0; + } + + public boolean isContainsKeywordSplat() { + return (flags & CONTAINS_KEYWORD_SPLAT) != 0; + } + + public boolean isContainsSplat() { + return (flags & CONTAINS_SPLAT) != 0; + } + + public boolean isContainsMultipleSplats() { + return (flags & CONTAINS_MULTIPLE_SPLATS) != 0; + } + + } + + /** + * Flags for array nodes. + */ + public static final class ArrayNodeFlags implements Comparable { + + // if array contains splat nodes + public static final short CONTAINS_SPLAT = 1 << 2; + + public static boolean isContainsSplat(short flags) { + return (flags & CONTAINS_SPLAT) != 0; + } + + private final short flags; + + public ArrayNodeFlags(short flags) { + this.flags = flags; + } + + @Override + public int hashCode() { + return flags; + } + + @Override + public boolean equals(Object other) { + if (!(other instanceof ArrayNodeFlags)) { + return false; + } + + return flags == ((ArrayNodeFlags) other).flags; + } + + @Override + public int compareTo(ArrayNodeFlags other) { + return flags - other.flags; + } + + public boolean isContainsSplat() { + return (flags & CONTAINS_SPLAT) != 0; + } + + } + + /** + * Flags for call nodes. + */ + public static final class CallNodeFlags implements Comparable { + + // &. operator + public static final short SAFE_NAVIGATION = 1 << 2; + + // a call that could have been a local variable + public static final short VARIABLE_CALL = 1 << 3; + + // a call that is an attribute write, so the value being written should be returned + public static final short ATTRIBUTE_WRITE = 1 << 4; + + // a call that ignores method visibility + public static final short IGNORE_VISIBILITY = 1 << 5; + + public static boolean isSafeNavigation(short flags) { + return (flags & SAFE_NAVIGATION) != 0; + } + + public static boolean isVariableCall(short flags) { + return (flags & VARIABLE_CALL) != 0; + } + + public static boolean isAttributeWrite(short flags) { + return (flags & ATTRIBUTE_WRITE) != 0; + } + + public static boolean isIgnoreVisibility(short flags) { + return (flags & IGNORE_VISIBILITY) != 0; + } + + private final short flags; + + public CallNodeFlags(short flags) { + this.flags = flags; + } + + @Override + public int hashCode() { + return flags; + } + + @Override + public boolean equals(Object other) { + if (!(other instanceof CallNodeFlags)) { + return false; + } + + return flags == ((CallNodeFlags) other).flags; + } + + @Override + public int compareTo(CallNodeFlags other) { + return flags - other.flags; + } + + public boolean isSafeNavigation() { + return (flags & SAFE_NAVIGATION) != 0; + } + + public boolean isVariableCall() { + return (flags & VARIABLE_CALL) != 0; + } + + public boolean isAttributeWrite() { + return (flags & ATTRIBUTE_WRITE) != 0; + } + + public boolean isIgnoreVisibility() { + return (flags & IGNORE_VISIBILITY) != 0; + } + + } + + /** + * Flags for nodes that have unescaped content. + */ + public static final class EncodingFlags implements Comparable { + + // internal bytes forced the encoding to UTF-8 + public static final short FORCED_UTF8_ENCODING = 1 << 2; + + // internal bytes forced the encoding to binary + public static final short FORCED_BINARY_ENCODING = 1 << 3; + + public static boolean isForcedUtf8Encoding(short flags) { + return (flags & FORCED_UTF8_ENCODING) != 0; + } + + public static boolean isForcedBinaryEncoding(short flags) { + return (flags & FORCED_BINARY_ENCODING) != 0; + } + + private final short flags; + + public EncodingFlags(short flags) { + this.flags = flags; + } + + @Override + public int hashCode() { + return flags; + } + + @Override + public boolean equals(Object other) { + if (!(other instanceof EncodingFlags)) { + return false; + } + + return flags == ((EncodingFlags) other).flags; + } + + @Override + public int compareTo(EncodingFlags other) { + return flags - other.flags; + } + + public boolean isForcedUtf8Encoding() { + return (flags & FORCED_UTF8_ENCODING) != 0; + } + + public boolean isForcedBinaryEncoding() { + return (flags & FORCED_BINARY_ENCODING) != 0; + } + + } + + /** + * Flags for integer nodes that correspond to the base of the integer. + */ + public static final class IntegerBaseFlags implements Comparable { + + // 0b prefix + public static final short BINARY = 1 << 2; + + // 0d or no prefix + public static final short DECIMAL = 1 << 3; + + // 0o or 0 prefix + public static final short OCTAL = 1 << 4; + + // 0x prefix + public static final short HEXADECIMAL = 1 << 5; + + public static boolean isBinary(short flags) { + return (flags & BINARY) != 0; + } + + public static boolean isDecimal(short flags) { + return (flags & DECIMAL) != 0; + } + + public static boolean isOctal(short flags) { + return (flags & OCTAL) != 0; + } + + public static boolean isHexadecimal(short flags) { + return (flags & HEXADECIMAL) != 0; + } + + private final short flags; + + public IntegerBaseFlags(short flags) { + this.flags = flags; + } + + @Override + public int hashCode() { + return flags; + } + + @Override + public boolean equals(Object other) { + if (!(other instanceof IntegerBaseFlags)) { + return false; + } + + return flags == ((IntegerBaseFlags) other).flags; + } + + @Override + public int compareTo(IntegerBaseFlags other) { + return flags - other.flags; + } + + public boolean isBinary() { + return (flags & BINARY) != 0; + } + + public boolean isDecimal() { + return (flags & DECIMAL) != 0; + } + + public boolean isOctal() { + return (flags & OCTAL) != 0; + } + + public boolean isHexadecimal() { + return (flags & HEXADECIMAL) != 0; + } + + } + + /** + * Flags for interpolated string nodes that indicated mutability if they are also marked as literals. + */ + public static final class InterpolatedStringNodeFlags implements Comparable { + + // frozen by virtue of a `frozen_string_literal: true` comment or `--enable-frozen-string-literal`; only for adjacent string literals like `'a' 'b'` + public static final short 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'` + public static final short MUTABLE = 1 << 3; + + public static boolean isFrozen(short flags) { + return (flags & FROZEN) != 0; + } + + public static boolean isMutable(short flags) { + return (flags & MUTABLE) != 0; + } + + private final short flags; + + public InterpolatedStringNodeFlags(short flags) { + this.flags = flags; + } + + @Override + public int hashCode() { + return flags; + } + + @Override + public boolean equals(Object other) { + if (!(other instanceof InterpolatedStringNodeFlags)) { + return false; + } + + return flags == ((InterpolatedStringNodeFlags) other).flags; + } + + @Override + public int compareTo(InterpolatedStringNodeFlags other) { + return flags - other.flags; + } + + public boolean isFrozen() { + return (flags & FROZEN) != 0; + } + + public boolean isMutable() { + return (flags & MUTABLE) != 0; + } + + } + + /** + * Flags for keyword hash nodes. + */ + public static final class KeywordHashNodeFlags implements Comparable { + + // a keyword hash which only has `AssocNode` elements all with symbol keys, which means the elements can be treated as keyword arguments + public static final short SYMBOL_KEYS = 1 << 2; + + public static boolean isSymbolKeys(short flags) { + return (flags & SYMBOL_KEYS) != 0; + } + + private final short flags; + + public KeywordHashNodeFlags(short flags) { + this.flags = flags; + } + + @Override + public int hashCode() { + return flags; + } + + @Override + public boolean equals(Object other) { + if (!(other instanceof KeywordHashNodeFlags)) { + return false; + } + + return flags == ((KeywordHashNodeFlags) other).flags; + } + + @Override + public int compareTo(KeywordHashNodeFlags other) { + return flags - other.flags; + } + + public boolean isSymbolKeys() { + return (flags & SYMBOL_KEYS) != 0; + } + + } + + /** + * Flags for while and until loop nodes. + */ + public static final class LoopFlags implements Comparable { + + // a loop after a begin statement, so the body is executed first before the condition + public static final short BEGIN_MODIFIER = 1 << 2; + + public static boolean isBeginModifier(short flags) { + return (flags & BEGIN_MODIFIER) != 0; + } + + private final short flags; + + public LoopFlags(short flags) { + this.flags = flags; + } + + @Override + public int hashCode() { + return flags; + } + + @Override + public boolean equals(Object other) { + if (!(other instanceof LoopFlags)) { + return false; + } + + return flags == ((LoopFlags) other).flags; + } + + @Override + public int compareTo(LoopFlags other) { + return flags - other.flags; + } + + public boolean isBeginModifier() { + return (flags & BEGIN_MODIFIER) != 0; + } + + } + + /** + * Flags for parameter nodes. + */ + public static final class ParameterFlags implements Comparable { + + // a parameter name that has been repeated in the method signature + public static final short REPEATED_PARAMETER = 1 << 2; + + public static boolean isRepeatedParameter(short flags) { + return (flags & REPEATED_PARAMETER) != 0; + } + + private final short flags; + + public ParameterFlags(short flags) { + this.flags = flags; + } + + @Override + public int hashCode() { + return flags; + } + + @Override + public boolean equals(Object other) { + if (!(other instanceof ParameterFlags)) { + return false; + } + + return flags == ((ParameterFlags) other).flags; + } + + @Override + public int compareTo(ParameterFlags other) { + return flags - other.flags; + } + + public boolean isRepeatedParameter() { + return (flags & REPEATED_PARAMETER) != 0; + } + + } + + /** + * Flags for parentheses nodes. + */ + public static final class ParenthesesNodeFlags implements Comparable { + + // parentheses that contain multiple potentially void statements + public static final short MULTIPLE_STATEMENTS = 1 << 2; + + public static boolean isMultipleStatements(short flags) { + return (flags & MULTIPLE_STATEMENTS) != 0; + } + + private final short flags; + + public ParenthesesNodeFlags(short flags) { + this.flags = flags; + } + + @Override + public int hashCode() { + return flags; + } + + @Override + public boolean equals(Object other) { + if (!(other instanceof ParenthesesNodeFlags)) { + return false; + } + + return flags == ((ParenthesesNodeFlags) other).flags; + } + + @Override + public int compareTo(ParenthesesNodeFlags other) { + return flags - other.flags; + } + + public boolean isMultipleStatements() { + return (flags & MULTIPLE_STATEMENTS) != 0; + } + + } + + /** + * Flags for range and flip-flop nodes. + */ + public static final class RangeFlags implements Comparable { + + // ... operator + public static final short EXCLUDE_END = 1 << 2; + + public static boolean isExcludeEnd(short flags) { + return (flags & EXCLUDE_END) != 0; + } + + private final short flags; + + public RangeFlags(short flags) { + this.flags = flags; + } + + @Override + public int hashCode() { + return flags; + } + + @Override + public boolean equals(Object other) { + if (!(other instanceof RangeFlags)) { + return false; + } + + return flags == ((RangeFlags) other).flags; + } + + @Override + public int compareTo(RangeFlags other) { + return flags - other.flags; + } + + public boolean isExcludeEnd() { + return (flags & EXCLUDE_END) != 0; + } + + } + + /** + * Flags for regular expression and match last line nodes. + */ + public static final class RegularExpressionFlags implements Comparable { + + // i - ignores the case of characters when matching + public static final short IGNORE_CASE = 1 << 2; + + // x - ignores whitespace and allows comments in regular expressions + public static final short EXTENDED = 1 << 3; + + // m - allows $ to match the end of lines within strings + public static final short MULTI_LINE = 1 << 4; + + // o - only interpolates values into the regular expression once + public static final short ONCE = 1 << 5; + + // e - forces the EUC-JP encoding + public static final short EUC_JP = 1 << 6; + + // n - forces the ASCII-8BIT encoding + public static final short ASCII_8BIT = 1 << 7; + + // s - forces the Windows-31J encoding + public static final short WINDOWS_31J = 1 << 8; + + // u - forces the UTF-8 encoding + public static final short UTF_8 = 1 << 9; + + // internal bytes forced the encoding to UTF-8 + public static final short FORCED_UTF8_ENCODING = 1 << 10; + + // internal bytes forced the encoding to binary + public static final short FORCED_BINARY_ENCODING = 1 << 11; + + // internal bytes forced the encoding to US-ASCII + public static final short FORCED_US_ASCII_ENCODING = 1 << 12; + + public static boolean isIgnoreCase(short flags) { + return (flags & IGNORE_CASE) != 0; + } + + public static boolean isExtended(short flags) { + return (flags & EXTENDED) != 0; + } + + public static boolean isMultiLine(short flags) { + return (flags & MULTI_LINE) != 0; + } + + public static boolean isOnce(short flags) { + return (flags & ONCE) != 0; + } + + public static boolean isEucJp(short flags) { + return (flags & EUC_JP) != 0; + } + + public static boolean isAscii8bit(short flags) { + return (flags & ASCII_8BIT) != 0; + } + + public static boolean isWindows31j(short flags) { + return (flags & WINDOWS_31J) != 0; + } + + public static boolean isUtf8(short flags) { + return (flags & UTF_8) != 0; + } + + public static boolean isForcedUtf8Encoding(short flags) { + return (flags & FORCED_UTF8_ENCODING) != 0; + } + + public static boolean isForcedBinaryEncoding(short flags) { + return (flags & FORCED_BINARY_ENCODING) != 0; + } + + public static boolean isForcedUsAsciiEncoding(short flags) { + return (flags & FORCED_US_ASCII_ENCODING) != 0; + } + + private final short flags; + + public RegularExpressionFlags(short flags) { + this.flags = flags; + } + + @Override + public int hashCode() { + return flags; + } + + @Override + public boolean equals(Object other) { + if (!(other instanceof RegularExpressionFlags)) { + return false; + } + + return flags == ((RegularExpressionFlags) other).flags; + } + + @Override + public int compareTo(RegularExpressionFlags other) { + return flags - other.flags; + } + + public boolean isIgnoreCase() { + return (flags & IGNORE_CASE) != 0; + } + + public boolean isExtended() { + return (flags & EXTENDED) != 0; + } + + public boolean isMultiLine() { + return (flags & MULTI_LINE) != 0; + } + + public boolean isOnce() { + return (flags & ONCE) != 0; + } + + public boolean isEucJp() { + return (flags & EUC_JP) != 0; + } + + public boolean isAscii8bit() { + return (flags & ASCII_8BIT) != 0; + } + + public boolean isWindows31j() { + return (flags & WINDOWS_31J) != 0; + } + + public boolean isUtf8() { + return (flags & UTF_8) != 0; + } + + public boolean isForcedUtf8Encoding() { + return (flags & FORCED_UTF8_ENCODING) != 0; + } + + public boolean isForcedBinaryEncoding() { + return (flags & FORCED_BINARY_ENCODING) != 0; + } + + public boolean isForcedUsAsciiEncoding() { + return (flags & FORCED_US_ASCII_ENCODING) != 0; + } + + } + + /** + * Flags for shareable constant nodes. + */ + public static final class ShareableConstantNodeFlags implements Comparable { + + // constant writes that should be modified with shareable constant value literal + public static final short LITERAL = 1 << 2; + + // constant writes that should be modified with shareable constant value experimental everything + public static final short EXPERIMENTAL_EVERYTHING = 1 << 3; + + // constant writes that should be modified with shareable constant value experimental copy + public static final short EXPERIMENTAL_COPY = 1 << 4; + + public static boolean isLiteral(short flags) { + return (flags & LITERAL) != 0; + } + + public static boolean isExperimentalEverything(short flags) { + return (flags & EXPERIMENTAL_EVERYTHING) != 0; + } + + public static boolean isExperimentalCopy(short flags) { + return (flags & EXPERIMENTAL_COPY) != 0; + } + + private final short flags; + + public ShareableConstantNodeFlags(short flags) { + this.flags = flags; + } + + @Override + public int hashCode() { + return flags; + } + + @Override + public boolean equals(Object other) { + if (!(other instanceof ShareableConstantNodeFlags)) { + return false; + } + + return flags == ((ShareableConstantNodeFlags) other).flags; + } + + @Override + public int compareTo(ShareableConstantNodeFlags other) { + return flags - other.flags; + } + + public boolean isLiteral() { + return (flags & LITERAL) != 0; + } + + public boolean isExperimentalEverything() { + return (flags & EXPERIMENTAL_EVERYTHING) != 0; + } + + public boolean isExperimentalCopy() { + return (flags & EXPERIMENTAL_COPY) != 0; + } + + } + + /** + * Flags for string nodes. + */ + public static final class StringFlags implements Comparable { + + // internal bytes forced the encoding to UTF-8 + public static final short FORCED_UTF8_ENCODING = 1 << 2; + + // internal bytes forced the encoding to binary + public static final short FORCED_BINARY_ENCODING = 1 << 3; + + // frozen by virtue of a `frozen_string_literal: true` comment or `--enable-frozen-string-literal` + public static final short FROZEN = 1 << 4; + + // mutable by virtue of a `frozen_string_literal: false` comment or `--disable-frozen-string-literal` + public static final short MUTABLE = 1 << 5; + + public static boolean isForcedUtf8Encoding(short flags) { + return (flags & FORCED_UTF8_ENCODING) != 0; + } + + public static boolean isForcedBinaryEncoding(short flags) { + return (flags & FORCED_BINARY_ENCODING) != 0; + } + + public static boolean isFrozen(short flags) { + return (flags & FROZEN) != 0; + } + + public static boolean isMutable(short flags) { + return (flags & MUTABLE) != 0; + } + + private final short flags; + + public StringFlags(short flags) { + this.flags = flags; + } + + @Override + public int hashCode() { + return flags; + } + + @Override + public boolean equals(Object other) { + if (!(other instanceof StringFlags)) { + return false; + } + + return flags == ((StringFlags) other).flags; + } + + @Override + public int compareTo(StringFlags other) { + return flags - other.flags; + } + + public boolean isForcedUtf8Encoding() { + return (flags & FORCED_UTF8_ENCODING) != 0; + } + + public boolean isForcedBinaryEncoding() { + return (flags & FORCED_BINARY_ENCODING) != 0; + } + + public boolean isFrozen() { + return (flags & FROZEN) != 0; + } + + public boolean isMutable() { + return (flags & MUTABLE) != 0; + } + + } + + /** + * Flags for symbol nodes. + */ + public static final class SymbolFlags implements Comparable { + + // internal bytes forced the encoding to UTF-8 + public static final short FORCED_UTF8_ENCODING = 1 << 2; + + // internal bytes forced the encoding to binary + public static final short FORCED_BINARY_ENCODING = 1 << 3; + + // internal bytes forced the encoding to US-ASCII + public static final short FORCED_US_ASCII_ENCODING = 1 << 4; + + public static boolean isForcedUtf8Encoding(short flags) { + return (flags & FORCED_UTF8_ENCODING) != 0; + } + + public static boolean isForcedBinaryEncoding(short flags) { + return (flags & FORCED_BINARY_ENCODING) != 0; + } + + public static boolean isForcedUsAsciiEncoding(short flags) { + return (flags & FORCED_US_ASCII_ENCODING) != 0; + } + + private final short flags; + + public SymbolFlags(short flags) { + this.flags = flags; + } + + @Override + public int hashCode() { + return flags; + } + + @Override + public boolean equals(Object other) { + if (!(other instanceof SymbolFlags)) { + return false; + } + + return flags == ((SymbolFlags) other).flags; + } + + @Override + public int compareTo(SymbolFlags other) { + return flags - other.flags; + } + + public boolean isForcedUtf8Encoding() { + return (flags & FORCED_UTF8_ENCODING) != 0; + } + + public boolean isForcedBinaryEncoding() { + return (flags & FORCED_BINARY_ENCODING) != 0; + } + + public boolean isForcedUsAsciiEncoding() { + return (flags & FORCED_US_ASCII_ENCODING) != 0; + } + + } + + /** + *
+     * Represents the use of the `alias` keyword to alias a global variable.
+     *
+     *     alias $foo $bar
+     *     ^^^^^^^^^^^^^^^
+     * 
+ */ + public static final class AliasGlobalVariableNode extends Node { + /** + *
+         * Represents the new name of the global variable that can be used after aliasing.
+         *
+         *     alias $foo $bar
+         *           ^^^^
+         * 
+ */ + @UnionType({ GlobalVariableReadNode.class, BackReferenceReadNode.class, NumberedReferenceReadNode.class }) + public final Node new_name; + /** + *
+         * Represents the old name of the global variable that can be used before aliasing.
+         *
+         *     alias $foo $bar
+         *                ^^^^
+         * 
+ */ + @UnionType({ GlobalVariableReadNode.class, BackReferenceReadNode.class, NumberedReferenceReadNode.class }) + public final Node old_name; + + public AliasGlobalVariableNode(int startOffset, int length, Node new_name, Node old_name) { + super(startOffset, length); + this.new_name = new_name; + this.old_name = old_name; + } + + public void visitChildNodes(AbstractNodeVisitor visitor) { + this.new_name.accept(visitor); + this.old_name.accept(visitor); + } + + public Node[] childNodes() { + return new Node[] { this.new_name, this.old_name }; + } + + public T accept(AbstractNodeVisitor visitor) { + return visitor.visitAliasGlobalVariableNode(this); + } + + @Override + protected String toString(String indent) { + StringBuilder builder = new StringBuilder(); + builder.append(this.getClass().getSimpleName()); + if (hasNewLineFlag()) { + builder.append("[Li]"); + } + builder.append('\n'); + String nextIndent = indent + " "; + builder.append(nextIndent); + builder.append("new_name: "); + builder.append(this.new_name.toString(nextIndent)); + builder.append(nextIndent); + builder.append("old_name: "); + builder.append(this.old_name.toString(nextIndent)); + return builder.toString(); + } + } + + /** + *
+     * Represents the use of the `alias` keyword to alias a method.
+     *
+     *     alias foo bar
+     *     ^^^^^^^^^^^^^
+     * 
+ */ + public static final class AliasMethodNode extends Node { + /** + *
+         * Represents the new name of the method that will be aliased.
+         *
+         *     alias foo bar
+         *           ^^^
+         *
+         *     alias :foo :bar
+         *           ^^^^
+         *
+         *     alias :"#{foo}" :"#{bar}"
+         *           ^^^^^^^^^
+         * 
+ */ + @UnionType({ SymbolNode.class, InterpolatedSymbolNode.class }) + public final Node new_name; + /** + *
+         * Represents the old name of the method that will be aliased.
+         *
+         *     alias foo bar
+         *               ^^^
+         *
+         *     alias :foo :bar
+         *                ^^^^
+         *
+         *     alias :"#{foo}" :"#{bar}"
+         *                     ^^^^^^^^^
+         * 
+ */ + @UnionType({ SymbolNode.class, InterpolatedSymbolNode.class }) + public final Node old_name; + + public AliasMethodNode(int startOffset, int length, Node new_name, Node old_name) { + super(startOffset, length); + this.new_name = new_name; + this.old_name = old_name; + } + + public void visitChildNodes(AbstractNodeVisitor visitor) { + this.new_name.accept(visitor); + this.old_name.accept(visitor); + } + + public Node[] childNodes() { + return new Node[] { this.new_name, this.old_name }; + } + + public T accept(AbstractNodeVisitor visitor) { + return visitor.visitAliasMethodNode(this); + } + + @Override + protected String toString(String indent) { + StringBuilder builder = new StringBuilder(); + builder.append(this.getClass().getSimpleName()); + if (hasNewLineFlag()) { + builder.append("[Li]"); + } + builder.append('\n'); + String nextIndent = indent + " "; + builder.append(nextIndent); + builder.append("new_name: "); + builder.append(this.new_name.toString(nextIndent)); + builder.append(nextIndent); + builder.append("old_name: "); + builder.append(this.old_name.toString(nextIndent)); + return builder.toString(); + } + } + + /** + *
+     * Represents an alternation pattern in pattern matching.
+     *
+     *     foo => bar | baz
+     *            ^^^^^^^^^
+     * 
+ */ + public static final class AlternationPatternNode extends Node { + /** + *
+         * Represents the left side of the expression.
+         *
+         *     foo => bar | baz
+         *            ^^^
+         * 
+ */ + public final Node left; + /** + *
+         * Represents the right side of the expression.
+         *
+         *     foo => bar | baz
+         *                  ^^^
+         * 
+ */ + public final Node right; + + public AlternationPatternNode(int startOffset, int length, Node left, Node right) { + super(startOffset, length); + this.left = left; + this.right = right; + } + + public void visitChildNodes(AbstractNodeVisitor visitor) { + this.left.accept(visitor); + this.right.accept(visitor); + } + + public Node[] childNodes() { + return new Node[] { this.left, this.right }; + } + + public T accept(AbstractNodeVisitor visitor) { + return visitor.visitAlternationPatternNode(this); + } + + @Override + protected String toString(String indent) { + StringBuilder builder = new StringBuilder(); + builder.append(this.getClass().getSimpleName()); + if (hasNewLineFlag()) { + builder.append("[Li]"); + } + builder.append('\n'); + String nextIndent = indent + " "; + builder.append(nextIndent); + builder.append("left: "); + builder.append(this.left.toString(nextIndent)); + builder.append(nextIndent); + builder.append("right: "); + builder.append(this.right.toString(nextIndent)); + return builder.toString(); + } + } + + /** + *
+     * Represents the use of the `&&` operator or the `and` keyword.
+     *
+     *     left and right
+     *     ^^^^^^^^^^^^^^
+     * 
+ */ + public static final class AndNode extends Node { + /** + *
+         * 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
+         *     ^
+         * 
+ */ + public final Node left; + /** + *
+         * Represents the right side of the expression.
+         *
+         *     left && right
+         *             ^^^^^
+         *
+         *     1 and 2
+         *           ^
+         * 
+ */ + public final Node right; + + public AndNode(int startOffset, int length, Node left, Node right) { + super(startOffset, length); + this.left = left; + this.right = right; + } + + public void visitChildNodes(AbstractNodeVisitor visitor) { + this.left.accept(visitor); + this.right.accept(visitor); + } + + public Node[] childNodes() { + return new Node[] { this.left, this.right }; + } + + public T accept(AbstractNodeVisitor visitor) { + return visitor.visitAndNode(this); + } + + @Override + protected String toString(String indent) { + StringBuilder builder = new StringBuilder(); + builder.append(this.getClass().getSimpleName()); + if (hasNewLineFlag()) { + builder.append("[Li]"); + } + builder.append('\n'); + String nextIndent = indent + " "; + builder.append(nextIndent); + builder.append("left: "); + builder.append(this.left.toString(nextIndent)); + builder.append(nextIndent); + builder.append("right: "); + builder.append(this.right.toString(nextIndent)); + return builder.toString(); + } + } + + /** + *
+     * Represents a set of arguments to a method or a keyword.
+     *
+     *     return foo, bar, baz
+     *            ^^^^^^^^^^^^^
+     * 
+ */ + public static final class ArgumentsNode extends Node { + public final short flags; + /** + *
+         * 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)
+         *         ^^^^^^^^
+         * 
+ */ + public final Node[] arguments; + + public ArgumentsNode(int startOffset, int length, short flags, Node[] arguments) { + super(startOffset, length); + this.flags = flags; + this.arguments = arguments; + } + + public boolean isContainsForwarding() { + return ArgumentsNodeFlags.isContainsForwarding(flags); + } + + public boolean isContainsKeywords() { + return ArgumentsNodeFlags.isContainsKeywords(flags); + } + + public boolean isContainsKeywordSplat() { + return ArgumentsNodeFlags.isContainsKeywordSplat(flags); + } + + public boolean isContainsSplat() { + return ArgumentsNodeFlags.isContainsSplat(flags); + } + + public boolean isContainsMultipleSplats() { + return ArgumentsNodeFlags.isContainsMultipleSplats(flags); + } + + public void visitChildNodes(AbstractNodeVisitor visitor) { + for (Nodes.Node child : this.arguments) { + child.accept(visitor); + } + } + + public Node[] childNodes() { + ArrayList childNodes = new ArrayList<>(); + childNodes.addAll(Arrays.asList(this.arguments)); + return childNodes.toArray(EMPTY_ARRAY); + } + + public T accept(AbstractNodeVisitor visitor) { + return visitor.visitArgumentsNode(this); + } + + @Override + protected String toString(String indent) { + StringBuilder builder = new StringBuilder(); + builder.append(this.getClass().getSimpleName()); + if (hasNewLineFlag()) { + builder.append("[Li]"); + } + builder.append('\n'); + String nextIndent = indent + " "; + String nextNextIndent = nextIndent + " "; + builder.append(nextIndent); + builder.append("ArgumentsNodeFlags: "); + builder.append(flags); + builder.append('\n'); + builder.append(nextIndent); + builder.append("arguments: "); + builder.append('\n'); + for (Node child : this.arguments) { + builder.append(nextNextIndent).append(child.toString(nextNextIndent)); + } + return builder.toString(); + } + } + + /** + *
+     * Represents an array literal. This can be a regular array using brackets or a special array using % like %w or %i.
+     *
+     *     [1, 2, 3]
+     *     ^^^^^^^^^
+     * 
+ */ + public static final class ArrayNode extends Node { + public final short flags; + /** + *
+         * 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.
+         * 
+ */ + public final Node[] elements; + + public ArrayNode(int startOffset, int length, short flags, Node[] elements) { + super(startOffset, length); + this.flags = flags; + this.elements = elements; + } + + public boolean isContainsSplat() { + return ArrayNodeFlags.isContainsSplat(flags); + } + + public void visitChildNodes(AbstractNodeVisitor visitor) { + for (Nodes.Node child : this.elements) { + child.accept(visitor); + } + } + + public Node[] childNodes() { + ArrayList childNodes = new ArrayList<>(); + childNodes.addAll(Arrays.asList(this.elements)); + return childNodes.toArray(EMPTY_ARRAY); + } + + public T accept(AbstractNodeVisitor visitor) { + return visitor.visitArrayNode(this); + } + + @Override + protected String toString(String indent) { + StringBuilder builder = new StringBuilder(); + builder.append(this.getClass().getSimpleName()); + if (hasNewLineFlag()) { + builder.append("[Li]"); + } + builder.append('\n'); + String nextIndent = indent + " "; + String nextNextIndent = nextIndent + " "; + builder.append(nextIndent); + builder.append("ArrayNodeFlags: "); + builder.append(flags); + builder.append('\n'); + builder.append(nextIndent); + builder.append("elements: "); + builder.append('\n'); + for (Node child : this.elements) { + builder.append(nextNextIndent).append(child.toString(nextNextIndent)); + } + return builder.toString(); + } + } + + /** + *
+     * 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]
+     *            ^^^^^^^^^^^^
+     * 
+ */ + public static final class ArrayPatternNode extends Node { + /** + *
+         * Represents the optional constant preceding the Array
+         *
+         *     foo in Bar[]
+         *            ^^^
+         *
+         *     foo in Bar[1, 2, 3]
+         *            ^^^
+         *
+         *     foo in Bar::Baz[1, 2, 3]
+         *            ^^^^^^^^
+         * 
+ */ + @Nullable + @UnionType({ ConstantPathNode.class, ConstantReadNode.class }) + public final Node constant; + /** + *
+         * Represents the required elements of the array pattern.
+         *
+         *     foo in [1, 2]
+         *             ^  ^
+         * 
+ */ + public final Node[] requireds; + /** + *
+         * Represents the rest element of the array pattern.
+         *
+         *     foo in *bar
+         *            ^^^^
+         * 
+ */ + @Nullable + public final Node rest; + /** + *
+         * Represents the elements after the rest element of the array pattern.
+         *
+         *     foo in *bar, baz
+         *                  ^^^
+         * 
+ */ + public final Node[] posts; + + public ArrayPatternNode(int startOffset, int length, Node constant, Node[] requireds, Node rest, Node[] posts) { + super(startOffset, length); + this.constant = constant; + this.requireds = requireds; + this.rest = rest; + this.posts = posts; + } + + public void visitChildNodes(AbstractNodeVisitor visitor) { + if (this.constant != null) { + this.constant.accept(visitor); + } + for (Nodes.Node child : this.requireds) { + child.accept(visitor); + } + if (this.rest != null) { + this.rest.accept(visitor); + } + for (Nodes.Node child : this.posts) { + child.accept(visitor); + } + } + + public Node[] childNodes() { + ArrayList childNodes = new ArrayList<>(); + childNodes.add(this.constant); + childNodes.addAll(Arrays.asList(this.requireds)); + childNodes.add(this.rest); + childNodes.addAll(Arrays.asList(this.posts)); + return childNodes.toArray(EMPTY_ARRAY); + } + + public T accept(AbstractNodeVisitor visitor) { + return visitor.visitArrayPatternNode(this); + } + + @Override + protected String toString(String indent) { + StringBuilder builder = new StringBuilder(); + builder.append(this.getClass().getSimpleName()); + if (hasNewLineFlag()) { + builder.append("[Li]"); + } + builder.append('\n'); + String nextIndent = indent + " "; + String nextNextIndent = nextIndent + " "; + builder.append(nextIndent); + builder.append("constant: "); + builder.append(this.constant == null ? "null\n" : this.constant.toString(nextIndent)); + builder.append(nextIndent); + builder.append("requireds: "); + builder.append('\n'); + for (Node child : this.requireds) { + builder.append(nextNextIndent).append(child.toString(nextNextIndent)); + } + builder.append(nextIndent); + builder.append("rest: "); + builder.append(this.rest == null ? "null\n" : this.rest.toString(nextIndent)); + builder.append(nextIndent); + builder.append("posts: "); + builder.append('\n'); + for (Node child : this.posts) { + builder.append(nextNextIndent).append(child.toString(nextNextIndent)); + } + return builder.toString(); + } + } + + /** + *
+     * Represents a hash key/value pair.
+     *
+     *     { a => b }
+     *       ^^^^^^
+     * 
+ */ + public static final class AssocNode extends Node { + /** + *
+         * 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 }
+         *       ^^^^^^^^^^
+         * 
+ */ + public final Node 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 }
+         *          ^
+         * 
+ */ + public final Node value; + + public AssocNode(int startOffset, int length, Node key, Node value) { + super(startOffset, length); + this.key = key; + this.value = value; + } + + public void visitChildNodes(AbstractNodeVisitor visitor) { + this.key.accept(visitor); + this.value.accept(visitor); + } + + public Node[] childNodes() { + return new Node[] { this.key, this.value }; + } + + public T accept(AbstractNodeVisitor visitor) { + return visitor.visitAssocNode(this); + } + + @Override + protected String toString(String indent) { + StringBuilder builder = new StringBuilder(); + builder.append(this.getClass().getSimpleName()); + if (hasNewLineFlag()) { + builder.append("[Li]"); + } + builder.append('\n'); + String nextIndent = indent + " "; + builder.append(nextIndent); + builder.append("key: "); + builder.append(this.key.toString(nextIndent)); + builder.append(nextIndent); + builder.append("value: "); + builder.append(this.value.toString(nextIndent)); + return builder.toString(); + } + } + + /** + *
+     * Represents a splat in a hash literal.
+     *
+     *     { **foo }
+     *       ^^^^^
+     * 
+ */ + public static final class AssocSplatNode extends Node { + /** + *
+         * The value to be splatted, if present. Will be missing when keyword rest argument forwarding is used.
+         *
+         *     { **foo }
+         *         ^^^
+         * 
+ */ + @Nullable + public final Node value; + + public AssocSplatNode(int startOffset, int length, Node value) { + super(startOffset, length); + this.value = value; + } + + public void visitChildNodes(AbstractNodeVisitor visitor) { + if (this.value != null) { + this.value.accept(visitor); + } + } + + public Node[] childNodes() { + return new Node[] { this.value }; + } + + public T accept(AbstractNodeVisitor visitor) { + return visitor.visitAssocSplatNode(this); + } + + @Override + protected String toString(String indent) { + StringBuilder builder = new StringBuilder(); + builder.append(this.getClass().getSimpleName()); + if (hasNewLineFlag()) { + builder.append("[Li]"); + } + builder.append('\n'); + String nextIndent = indent + " "; + builder.append(nextIndent); + builder.append("value: "); + builder.append(this.value == null ? "null\n" : this.value.toString(nextIndent)); + return builder.toString(); + } + } + + /** + *
+     * Represents reading a reference to a field in the previous match.
+     *
+     *     $'
+     *     ^^
+     * 
+ */ + public static final class BackReferenceReadNode extends Node { + /** + *
+         * The name of the back-reference variable, including the leading `$`.
+         *
+         *     $& # name `:$&`
+         *
+         *     $+ # name `:$+`
+         * 
+ */ + public final byte[] name; + + public BackReferenceReadNode(int startOffset, int length, byte[] name) { + super(startOffset, length); + this.name = name; + } + + public void visitChildNodes(AbstractNodeVisitor visitor) { + } + + public Node[] childNodes() { + return EMPTY_ARRAY; + } + + public T accept(AbstractNodeVisitor visitor) { + return visitor.visitBackReferenceReadNode(this); + } + + @Override + protected String toString(String indent) { + StringBuilder builder = new StringBuilder(); + builder.append(this.getClass().getSimpleName()); + if (hasNewLineFlag()) { + builder.append("[Li]"); + } + builder.append('\n'); + String nextIndent = indent + " "; + builder.append(nextIndent); + builder.append("name: "); + builder.append('"').append(asString(this.name)).append('"'); + builder.append('\n'); + return builder.toString(); + } + } + + /** + *
+     * Represents a begin statement.
+     *
+     *     begin
+     *       foo
+     *     end
+     *     ^^^^^
+     * 
+ */ + public static final class BeginNode extends Node { + /** + *
+         * Represents the statements within the begin block.
+         *
+         *     begin x end
+         *           ^
+         * 
+ */ + @Nullable + public final StatementsNode statements; + /** + *
+         * Represents the rescue clause within the begin block.
+         *
+         *     begin x; rescue y; end
+         *              ^^^^^^^^
+         * 
+ */ + @Nullable + public final RescueNode rescue_clause; + /** + *
+         * Represents the else clause within the begin block.
+         *
+         *     begin x; rescue y; else z; end
+         *                        ^^^^^^^^^^^
+         * 
+ */ + @Nullable + public final ElseNode else_clause; + /** + *
+         * Represents the ensure clause within the begin block.
+         *
+         *     begin x; ensure y; end
+         *              ^^^^^^^^
+         * 
+ */ + @Nullable + public final EnsureNode ensure_clause; + + public BeginNode(int startOffset, int length, StatementsNode statements, RescueNode rescue_clause, ElseNode else_clause, EnsureNode ensure_clause) { + super(startOffset, length); + this.statements = statements; + this.rescue_clause = rescue_clause; + this.else_clause = else_clause; + this.ensure_clause = ensure_clause; + } + + @Override + public void setNewLineFlag(Source source, boolean[] newlineMarked) { + // Never mark BeginNode with a newline flag, mark children instead + } + + public void visitChildNodes(AbstractNodeVisitor visitor) { + if (this.statements != null) { + this.statements.accept(visitor); + } + if (this.rescue_clause != null) { + this.rescue_clause.accept(visitor); + } + if (this.else_clause != null) { + this.else_clause.accept(visitor); + } + if (this.ensure_clause != null) { + this.ensure_clause.accept(visitor); + } + } + + public Node[] childNodes() { + return new Node[] { this.statements, this.rescue_clause, this.else_clause, this.ensure_clause }; + } + + public T accept(AbstractNodeVisitor visitor) { + return visitor.visitBeginNode(this); + } + + @Override + protected String toString(String indent) { + StringBuilder builder = new StringBuilder(); + builder.append(this.getClass().getSimpleName()); + if (hasNewLineFlag()) { + builder.append("[Li]"); + } + builder.append('\n'); + String nextIndent = indent + " "; + builder.append(nextIndent); + builder.append("statements: "); + builder.append(this.statements == null ? "null\n" : this.statements.toString(nextIndent)); + builder.append(nextIndent); + builder.append("rescue_clause: "); + builder.append(this.rescue_clause == null ? "null\n" : this.rescue_clause.toString(nextIndent)); + builder.append(nextIndent); + builder.append("else_clause: "); + builder.append(this.else_clause == null ? "null\n" : this.else_clause.toString(nextIndent)); + builder.append(nextIndent); + builder.append("ensure_clause: "); + builder.append(this.ensure_clause == null ? "null\n" : this.ensure_clause.toString(nextIndent)); + return builder.toString(); + } + } + + /** + *
+     * Represents a block argument using `&`.
+     *
+     *     bar(&args)
+     *         ^^^^^
+     * 
+ */ + public static final class BlockArgumentNode extends Node { + /** + *
+         * 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)
+         *          ^^^^
+         * 
+ */ + @Nullable + public final Node expression; + + public BlockArgumentNode(int startOffset, int length, Node expression) { + super(startOffset, length); + this.expression = expression; + } + + public void visitChildNodes(AbstractNodeVisitor visitor) { + if (this.expression != null) { + this.expression.accept(visitor); + } + } + + public Node[] childNodes() { + return new Node[] { this.expression }; + } + + public T accept(AbstractNodeVisitor visitor) { + return visitor.visitBlockArgumentNode(this); + } + + @Override + protected String toString(String indent) { + StringBuilder builder = new StringBuilder(); + builder.append(this.getClass().getSimpleName()); + if (hasNewLineFlag()) { + builder.append("[Li]"); + } + builder.append('\n'); + String nextIndent = indent + " "; + builder.append(nextIndent); + builder.append("expression: "); + builder.append(this.expression == null ? "null\n" : this.expression.toString(nextIndent)); + return builder.toString(); + } + } + + /** + *
+     * Represents a block local variable.
+     *
+     *     a { |; b| }
+     *            ^
+     * 
+ */ + public static final class BlockLocalVariableNode extends Node { + public final short flags; + /** + *
+         * The name of the block local variable.
+         *
+         *     a { |; b| } # name `:b`
+         *            ^
+         * 
+ */ + public final byte[] name; + + public BlockLocalVariableNode(int startOffset, int length, short flags, byte[] name) { + super(startOffset, length); + this.flags = flags; + this.name = name; + } + + public boolean isRepeatedParameter() { + return ParameterFlags.isRepeatedParameter(flags); + } + + public void visitChildNodes(AbstractNodeVisitor visitor) { + } + + public Node[] childNodes() { + return EMPTY_ARRAY; + } + + public T accept(AbstractNodeVisitor visitor) { + return visitor.visitBlockLocalVariableNode(this); + } + + @Override + protected String toString(String indent) { + StringBuilder builder = new StringBuilder(); + builder.append(this.getClass().getSimpleName()); + if (hasNewLineFlag()) { + builder.append("[Li]"); + } + builder.append('\n'); + String nextIndent = indent + " "; + builder.append(nextIndent); + builder.append("ParameterFlags: "); + builder.append(flags); + builder.append('\n'); + builder.append(nextIndent); + builder.append("name: "); + builder.append('"').append(asString(this.name)).append('"'); + builder.append('\n'); + return builder.toString(); + } + } + + /** + *
+     * Represents a block of ruby code.
+     *
+     *     [1, 2, 3].each { |i| puts x }
+     *                    ^^^^^^^^^^^^^^
+     * 
+ */ + public static final class BlockNode extends Node { + /** + *
+         * The local variables declared in the block.
+         *
+         *     [1, 2, 3].each { |i| puts x } # locals: [:i]
+         *                       ^
+         * 
+ */ + public final byte[][] 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 }
+         *                    ^^^^^^^^^^^
+         * 
+ */ + @Nullable + @UnionType({ BlockParametersNode.class, NumberedParametersNode.class, ItParametersNode.class }) + public final Node parameters; + /** + *
+         * The body of the block.
+         *
+         *     [1, 2, 3].each { |i| puts x }
+         *                          ^^^^^^
+         * 
+ */ + @Nullable + @UnionType({ StatementsNode.class, BeginNode.class }) + public final Node body; + + public BlockNode(int startOffset, int length, byte[][] locals, Node parameters, Node body) { + super(startOffset, length); + this.locals = locals; + this.parameters = parameters; + this.body = body; + } + + public void visitChildNodes(AbstractNodeVisitor visitor) { + if (this.parameters != null) { + this.parameters.accept(visitor); + } + if (this.body != null) { + this.body.accept(visitor); + } + } + + public Node[] childNodes() { + return new Node[] { this.parameters, this.body }; + } + + public T accept(AbstractNodeVisitor visitor) { + return visitor.visitBlockNode(this); + } + + @Override + protected String toString(String indent) { + StringBuilder builder = new StringBuilder(); + builder.append(this.getClass().getSimpleName()); + if (hasNewLineFlag()) { + builder.append("[Li]"); + } + builder.append('\n'); + String nextIndent = indent + " "; + String nextNextIndent = nextIndent + " "; + builder.append(nextIndent); + builder.append("locals: "); + builder.append('\n'); + for (byte[] constant : this.locals) { + builder.append(nextNextIndent).append('"').append(asString(constant)).append('"').append('\n'); + } + builder.append(nextIndent); + builder.append("parameters: "); + builder.append(this.parameters == null ? "null\n" : this.parameters.toString(nextIndent)); + builder.append(nextIndent); + builder.append("body: "); + builder.append(this.body == null ? "null\n" : this.body.toString(nextIndent)); + return builder.toString(); + } + } + + /** + *
+     * Represents a block parameter of a method, block, or lambda definition.
+     *
+     *     def a(&b)
+     *           ^^
+     *     end
+     * 
+ */ + public static final class BlockParameterNode extends Node { + public final short flags; + /** + *
+         * The name of the block parameter.
+         *
+         *     def a(&b) # name `:b`
+         *            ^
+         *     end
+         * 
+ */ + @Nullable + public final byte[] name; + + public BlockParameterNode(int startOffset, int length, short flags, byte[] name) { + super(startOffset, length); + this.flags = flags; + this.name = name; + } + + public boolean isRepeatedParameter() { + return ParameterFlags.isRepeatedParameter(flags); + } + + public void visitChildNodes(AbstractNodeVisitor visitor) { + } + + public Node[] childNodes() { + return EMPTY_ARRAY; + } + + public T accept(AbstractNodeVisitor visitor) { + return visitor.visitBlockParameterNode(this); + } + + @Override + protected String toString(String indent) { + StringBuilder builder = new StringBuilder(); + builder.append(this.getClass().getSimpleName()); + if (hasNewLineFlag()) { + builder.append("[Li]"); + } + builder.append('\n'); + String nextIndent = indent + " "; + builder.append(nextIndent); + builder.append("ParameterFlags: "); + builder.append(flags); + builder.append('\n'); + builder.append(nextIndent); + builder.append("name: "); + builder.append(this.name == null ? "null" : "\"" + asString(this.name) + "\""); + builder.append('\n'); + return builder.toString(); + } + } + + /** + *
+     * Represents a block's parameters declaration.
+     *
+     *     -> (a, b = 1; local) { }
+     *        ^^^^^^^^^^^^^^^^^
+     *
+     *     foo do |a, b = 1; local|
+     *            ^^^^^^^^^^^^^^^^^
+     *     end
+     * 
+ */ + public static final class BlockParametersNode extends Node { + /** + *
+         * Represents the parameters of the block.
+         *
+         *     -> (a, b = 1; local) { }
+         *         ^^^^^^^^
+         *
+         *     foo do |a, b = 1; local|
+         *             ^^^^^^^^
+         *     end
+         * 
+ */ + @Nullable + public final ParametersNode parameters; + /** + *
+         * Represents the local variables of the block.
+         *
+         *     -> (a, b = 1; local) { }
+         *                   ^^^^^
+         *
+         *     foo do |a, b = 1; local|
+         *                       ^^^^^
+         *     end
+         * 
+ */ + public final BlockLocalVariableNode[] locals; + + public BlockParametersNode(int startOffset, int length, ParametersNode parameters, BlockLocalVariableNode[] locals) { + super(startOffset, length); + this.parameters = parameters; + this.locals = locals; + } + + public void visitChildNodes(AbstractNodeVisitor visitor) { + if (this.parameters != null) { + this.parameters.accept(visitor); + } + for (Nodes.Node child : this.locals) { + child.accept(visitor); + } + } + + public Node[] childNodes() { + ArrayList childNodes = new ArrayList<>(); + childNodes.add(this.parameters); + childNodes.addAll(Arrays.asList(this.locals)); + return childNodes.toArray(EMPTY_ARRAY); + } + + public T accept(AbstractNodeVisitor visitor) { + return visitor.visitBlockParametersNode(this); + } + + @Override + protected String toString(String indent) { + StringBuilder builder = new StringBuilder(); + builder.append(this.getClass().getSimpleName()); + if (hasNewLineFlag()) { + builder.append("[Li]"); + } + builder.append('\n'); + String nextIndent = indent + " "; + String nextNextIndent = nextIndent + " "; + builder.append(nextIndent); + builder.append("parameters: "); + builder.append(this.parameters == null ? "null\n" : this.parameters.toString(nextIndent)); + builder.append(nextIndent); + builder.append("locals: "); + builder.append('\n'); + for (Node child : this.locals) { + builder.append(nextNextIndent).append(child.toString(nextNextIndent)); + } + return builder.toString(); + } + } + + /** + *
+     * Represents the use of the `break` keyword.
+     *
+     *     break foo
+     *     ^^^^^^^^^
+     * 
+ */ + public static final class BreakNode extends Node { + /** + *
+         * 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
+         *           ^^^
+         * 
+ */ + @Nullable + public final ArgumentsNode arguments; + + public BreakNode(int startOffset, int length, ArgumentsNode arguments) { + super(startOffset, length); + this.arguments = arguments; + } + + public void visitChildNodes(AbstractNodeVisitor visitor) { + if (this.arguments != null) { + this.arguments.accept(visitor); + } + } + + public Node[] childNodes() { + return new Node[] { this.arguments }; + } + + public T accept(AbstractNodeVisitor visitor) { + return visitor.visitBreakNode(this); + } + + @Override + protected String toString(String indent) { + StringBuilder builder = new StringBuilder(); + builder.append(this.getClass().getSimpleName()); + if (hasNewLineFlag()) { + builder.append("[Li]"); + } + builder.append('\n'); + String nextIndent = indent + " "; + builder.append(nextIndent); + builder.append("arguments: "); + builder.append(this.arguments == null ? "null\n" : this.arguments.toString(nextIndent)); + return builder.toString(); + } + } + + /** + *
+     * Represents the use of the `&&=` operator on a call.
+     *
+     *     foo.bar &&= value
+     *     ^^^^^^^^^^^^^^^^^
+     * 
+ */ + public static final class CallAndWriteNode extends Node { + public final short flags; + /** + *
+         * 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
+         *     ^^^
+         * 
+ */ + @Nullable + public final Node receiver; + /** + *
+         * Represents the name of the method being called.
+         *
+         *     foo.bar &&= value # read_name `:bar`
+         *         ^^^
+         * 
+ */ + public final byte[] read_name; + /** + *
+         * Represents the name of the method being written to.
+         *
+         *     foo.bar &&= value # write_name `:bar=`
+         *         ^^^
+         * 
+ */ + public final byte[] write_name; + /** + *
+         * Represents the value being assigned.
+         *
+         *     foo.bar &&= value
+         *                 ^^^^^
+         * 
+ */ + public final Node value; + + public CallAndWriteNode(int startOffset, int length, short flags, Node receiver, byte[] read_name, byte[] write_name, Node value) { + super(startOffset, length); + this.flags = flags; + this.receiver = receiver; + this.read_name = read_name; + this.write_name = write_name; + this.value = value; + } + + public boolean isSafeNavigation() { + return CallNodeFlags.isSafeNavigation(flags); + } + + public boolean isVariableCall() { + return CallNodeFlags.isVariableCall(flags); + } + + public boolean isAttributeWrite() { + return CallNodeFlags.isAttributeWrite(flags); + } + + public boolean isIgnoreVisibility() { + return CallNodeFlags.isIgnoreVisibility(flags); + } + + public void visitChildNodes(AbstractNodeVisitor visitor) { + if (this.receiver != null) { + this.receiver.accept(visitor); + } + this.value.accept(visitor); + } + + public Node[] childNodes() { + return new Node[] { this.receiver, this.value }; + } + + public T accept(AbstractNodeVisitor visitor) { + return visitor.visitCallAndWriteNode(this); + } + + @Override + protected String toString(String indent) { + StringBuilder builder = new StringBuilder(); + builder.append(this.getClass().getSimpleName()); + if (hasNewLineFlag()) { + builder.append("[Li]"); + } + builder.append('\n'); + String nextIndent = indent + " "; + builder.append(nextIndent); + builder.append("CallNodeFlags: "); + builder.append(flags); + builder.append('\n'); + builder.append(nextIndent); + builder.append("receiver: "); + builder.append(this.receiver == null ? "null\n" : this.receiver.toString(nextIndent)); + builder.append(nextIndent); + builder.append("read_name: "); + builder.append('"').append(asString(this.read_name)).append('"'); + builder.append('\n'); + builder.append(nextIndent); + builder.append("write_name: "); + builder.append('"').append(asString(this.write_name)).append('"'); + builder.append('\n'); + builder.append(nextIndent); + builder.append("value: "); + builder.append(this.value.toString(nextIndent)); + return builder.toString(); + } + } + + /** + *
+     * Represents a method call, in all of the various forms that can take.
+     *
+     *     foo
+     *     ^^^
+     *
+     *     foo()
+     *     ^^^^^
+     *
+     *     +foo
+     *     ^^^^
+     *
+     *     foo + bar
+     *     ^^^^^^^^^
+     *
+     *     foo.bar
+     *     ^^^^^^^
+     *
+     *     foo&.bar
+     *     ^^^^^^^^
+     * 
+ */ + public static final class CallNode extends Node { + public final short flags; + /** + *
+         * 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
+         *     ^^^
+         * 
+ */ + @Nullable + public final Node receiver; + /** + *
+         * Represents the name of the method being called.
+         *
+         *     foo.bar # name `:foo`
+         *     ^^^
+         * 
+ */ + public final byte[] name; + /** + *
+         * 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)
+         *         ^^^
+         * 
+ */ + @Nullable + public final ArgumentsNode arguments; + /** + *
+         * Represents the block that is being passed to the method.
+         *
+         *     foo { |a| a }
+         *         ^^^^^^^^^
+         * 
+ */ + @Nullable + @UnionType({ BlockNode.class, BlockArgumentNode.class }) + public final Node block; + + public CallNode(int startOffset, int length, short flags, Node receiver, byte[] name, ArgumentsNode arguments, Node block) { + super(startOffset, length); + this.flags = flags; + this.receiver = receiver; + this.name = name; + this.arguments = arguments; + this.block = block; + } + + public boolean isSafeNavigation() { + return CallNodeFlags.isSafeNavigation(flags); + } + + public boolean isVariableCall() { + return CallNodeFlags.isVariableCall(flags); + } + + public boolean isAttributeWrite() { + return CallNodeFlags.isAttributeWrite(flags); + } + + public boolean isIgnoreVisibility() { + return CallNodeFlags.isIgnoreVisibility(flags); + } + + public void visitChildNodes(AbstractNodeVisitor visitor) { + if (this.receiver != null) { + this.receiver.accept(visitor); + } + if (this.arguments != null) { + this.arguments.accept(visitor); + } + if (this.block != null) { + this.block.accept(visitor); + } + } + + public Node[] childNodes() { + return new Node[] { this.receiver, this.arguments, this.block }; + } + + public T accept(AbstractNodeVisitor visitor) { + return visitor.visitCallNode(this); + } + + @Override + protected String toString(String indent) { + StringBuilder builder = new StringBuilder(); + builder.append(this.getClass().getSimpleName()); + if (hasNewLineFlag()) { + builder.append("[Li]"); + } + builder.append('\n'); + String nextIndent = indent + " "; + builder.append(nextIndent); + builder.append("CallNodeFlags: "); + builder.append(flags); + builder.append('\n'); + builder.append(nextIndent); + builder.append("receiver: "); + builder.append(this.receiver == null ? "null\n" : this.receiver.toString(nextIndent)); + builder.append(nextIndent); + builder.append("name: "); + builder.append('"').append(asString(this.name)).append('"'); + builder.append('\n'); + builder.append(nextIndent); + builder.append("arguments: "); + builder.append(this.arguments == null ? "null\n" : this.arguments.toString(nextIndent)); + builder.append(nextIndent); + builder.append("block: "); + builder.append(this.block == null ? "null\n" : this.block.toString(nextIndent)); + return builder.toString(); + } + } + + /** + *
+     * Represents the use of an assignment operator on a call.
+     *
+     *     foo.bar += baz
+     *     ^^^^^^^^^^^^^^
+     * 
+ */ + public static final class CallOperatorWriteNode extends Node { + public final short flags; + /** + *
+         * 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
+         *     ^^^
+         * 
+ */ + @Nullable + public final Node receiver; + /** + *
+         * Represents the name of the method being called.
+         *
+         *     foo.bar += value # read_name `:bar`
+         *         ^^^
+         * 
+ */ + public final byte[] read_name; + /** + *
+         * Represents the name of the method being written to.
+         *
+         *     foo.bar += value # write_name `:bar=`
+         *         ^^^
+         * 
+ */ + public final byte[] write_name; + /** + *
+         * Represents the binary operator being used.
+         *
+         *     foo.bar += value # binary_operator `:+`
+         *             ^
+         * 
+ */ + public final byte[] binary_operator; + /** + *
+         * Represents the value being assigned.
+         *
+         *     foo.bar += value
+         *                ^^^^^
+         * 
+ */ + public final Node value; + + public CallOperatorWriteNode(int startOffset, int length, short flags, Node receiver, byte[] read_name, byte[] write_name, byte[] binary_operator, Node value) { + super(startOffset, length); + this.flags = flags; + this.receiver = receiver; + this.read_name = read_name; + this.write_name = write_name; + this.binary_operator = binary_operator; + this.value = value; + } + + public boolean isSafeNavigation() { + return CallNodeFlags.isSafeNavigation(flags); + } + + public boolean isVariableCall() { + return CallNodeFlags.isVariableCall(flags); + } + + public boolean isAttributeWrite() { + return CallNodeFlags.isAttributeWrite(flags); + } + + public boolean isIgnoreVisibility() { + return CallNodeFlags.isIgnoreVisibility(flags); + } + + public void visitChildNodes(AbstractNodeVisitor visitor) { + if (this.receiver != null) { + this.receiver.accept(visitor); + } + this.value.accept(visitor); + } + + public Node[] childNodes() { + return new Node[] { this.receiver, this.value }; + } + + public T accept(AbstractNodeVisitor visitor) { + return visitor.visitCallOperatorWriteNode(this); + } + + @Override + protected String toString(String indent) { + StringBuilder builder = new StringBuilder(); + builder.append(this.getClass().getSimpleName()); + if (hasNewLineFlag()) { + builder.append("[Li]"); + } + builder.append('\n'); + String nextIndent = indent + " "; + builder.append(nextIndent); + builder.append("CallNodeFlags: "); + builder.append(flags); + builder.append('\n'); + builder.append(nextIndent); + builder.append("receiver: "); + builder.append(this.receiver == null ? "null\n" : this.receiver.toString(nextIndent)); + builder.append(nextIndent); + builder.append("read_name: "); + builder.append('"').append(asString(this.read_name)).append('"'); + builder.append('\n'); + builder.append(nextIndent); + builder.append("write_name: "); + builder.append('"').append(asString(this.write_name)).append('"'); + builder.append('\n'); + builder.append(nextIndent); + builder.append("binary_operator: "); + builder.append('"').append(asString(this.binary_operator)).append('"'); + builder.append('\n'); + builder.append(nextIndent); + builder.append("value: "); + builder.append(this.value.toString(nextIndent)); + return builder.toString(); + } + } + + /** + *
+     * Represents the use of the `||=` operator on a call.
+     *
+     *     foo.bar ||= value
+     *     ^^^^^^^^^^^^^^^^^
+     * 
+ */ + public static final class CallOrWriteNode extends Node { + public final short flags; + /** + *
+         * 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
+         *     ^^^
+         * 
+ */ + @Nullable + public final Node receiver; + /** + *
+         * Represents the name of the method being called.
+         *
+         *     foo.bar ||= value # read_name `:bar`
+         *         ^^^
+         * 
+ */ + public final byte[] read_name; + /** + *
+         * Represents the name of the method being written to.
+         *
+         *     foo.bar ||= value # write_name `:bar=`
+         *         ^^^
+         * 
+ */ + public final byte[] write_name; + /** + *
+         * Represents the value being assigned.
+         *
+         *     foo.bar ||= value
+         *                 ^^^^^
+         * 
+ */ + public final Node value; + + public CallOrWriteNode(int startOffset, int length, short flags, Node receiver, byte[] read_name, byte[] write_name, Node value) { + super(startOffset, length); + this.flags = flags; + this.receiver = receiver; + this.read_name = read_name; + this.write_name = write_name; + this.value = value; + } + + public boolean isSafeNavigation() { + return CallNodeFlags.isSafeNavigation(flags); + } + + public boolean isVariableCall() { + return CallNodeFlags.isVariableCall(flags); + } + + public boolean isAttributeWrite() { + return CallNodeFlags.isAttributeWrite(flags); + } + + public boolean isIgnoreVisibility() { + return CallNodeFlags.isIgnoreVisibility(flags); + } + + public void visitChildNodes(AbstractNodeVisitor visitor) { + if (this.receiver != null) { + this.receiver.accept(visitor); + } + this.value.accept(visitor); + } + + public Node[] childNodes() { + return new Node[] { this.receiver, this.value }; + } + + public T accept(AbstractNodeVisitor visitor) { + return visitor.visitCallOrWriteNode(this); + } + + @Override + protected String toString(String indent) { + StringBuilder builder = new StringBuilder(); + builder.append(this.getClass().getSimpleName()); + if (hasNewLineFlag()) { + builder.append("[Li]"); + } + builder.append('\n'); + String nextIndent = indent + " "; + builder.append(nextIndent); + builder.append("CallNodeFlags: "); + builder.append(flags); + builder.append('\n'); + builder.append(nextIndent); + builder.append("receiver: "); + builder.append(this.receiver == null ? "null\n" : this.receiver.toString(nextIndent)); + builder.append(nextIndent); + builder.append("read_name: "); + builder.append('"').append(asString(this.read_name)).append('"'); + builder.append('\n'); + builder.append(nextIndent); + builder.append("write_name: "); + builder.append('"').append(asString(this.write_name)).append('"'); + builder.append('\n'); + builder.append(nextIndent); + builder.append("value: "); + builder.append(this.value.toString(nextIndent)); + return builder.toString(); + } + } + + /** + *
+     * Represents assigning to a method call.
+     *
+     *     foo.bar, = 1
+     *     ^^^^^^^
+     *
+     *     begin
+     *     rescue => foo.bar
+     *               ^^^^^^^
+     *     end
+     *
+     *     for foo.bar in baz do end
+     *         ^^^^^^^
+     * 
+ */ + public static final class CallTargetNode extends Node { + public final short flags; + /** + *
+         * 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
+         *     ^^^
+         * 
+ */ + public final Node receiver; + /** + *
+         * Represents the name of the method being called.
+         *
+         *     foo.bar = 1 # name `:foo`
+         *     ^^^
+         * 
+ */ + public final byte[] name; + + public CallTargetNode(int startOffset, int length, short flags, Node receiver, byte[] name) { + super(startOffset, length); + this.flags = flags; + this.receiver = receiver; + this.name = name; + } + + public boolean isSafeNavigation() { + return CallNodeFlags.isSafeNavigation(flags); + } + + public boolean isVariableCall() { + return CallNodeFlags.isVariableCall(flags); + } + + public boolean isAttributeWrite() { + return CallNodeFlags.isAttributeWrite(flags); + } + + public boolean isIgnoreVisibility() { + return CallNodeFlags.isIgnoreVisibility(flags); + } + + public void visitChildNodes(AbstractNodeVisitor visitor) { + this.receiver.accept(visitor); + } + + public Node[] childNodes() { + return new Node[] { this.receiver }; + } + + public T accept(AbstractNodeVisitor visitor) { + return visitor.visitCallTargetNode(this); + } + + @Override + protected String toString(String indent) { + StringBuilder builder = new StringBuilder(); + builder.append(this.getClass().getSimpleName()); + if (hasNewLineFlag()) { + builder.append("[Li]"); + } + builder.append('\n'); + String nextIndent = indent + " "; + builder.append(nextIndent); + builder.append("CallNodeFlags: "); + builder.append(flags); + builder.append('\n'); + builder.append(nextIndent); + builder.append("receiver: "); + builder.append(this.receiver.toString(nextIndent)); + builder.append(nextIndent); + builder.append("name: "); + builder.append('"').append(asString(this.name)).append('"'); + builder.append('\n'); + return builder.toString(); + } + } + + /** + *
+     * Represents assigning to a local variable in pattern matching.
+     *
+     *     foo => [bar => baz]
+     *             ^^^^^^^^^^
+     * 
+ */ + public static final class CapturePatternNode extends Node { + /** + *
+         * Represents the value to capture.
+         *
+         *     foo => bar
+         *            ^^^
+         * 
+ */ + public final Node value; + /** + *
+         * Represents the target of the capture.
+         *
+         *     foo => bar
+         *     ^^^
+         * 
+ */ + public final LocalVariableTargetNode target; + + public CapturePatternNode(int startOffset, int length, Node value, LocalVariableTargetNode target) { + super(startOffset, length); + this.value = value; + this.target = target; + } + + public void visitChildNodes(AbstractNodeVisitor visitor) { + this.value.accept(visitor); + this.target.accept(visitor); + } + + public Node[] childNodes() { + return new Node[] { this.value, this.target }; + } + + public T accept(AbstractNodeVisitor visitor) { + return visitor.visitCapturePatternNode(this); + } + + @Override + protected String toString(String indent) { + StringBuilder builder = new StringBuilder(); + builder.append(this.getClass().getSimpleName()); + if (hasNewLineFlag()) { + builder.append("[Li]"); + } + builder.append('\n'); + String nextIndent = indent + " "; + builder.append(nextIndent); + builder.append("value: "); + builder.append(this.value.toString(nextIndent)); + builder.append(nextIndent); + builder.append("target: "); + builder.append(this.target.toString(nextIndent)); + return builder.toString(); + } + } + + /** + *
+     * Represents the use of a case statement for pattern matching.
+     *
+     *     case true
+     *     in false
+     *     end
+     *     ^^^^^^^^^
+     * 
+ */ + public static final class CaseMatchNode extends Node { + /** + *
+         * 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
+         *          ^^^^
+         * 
+ */ + @Nullable + public final Node predicate; + /** + *
+         * Represents the conditions of the case match.
+         *
+         *     case true; in false; end
+         *                ^^^^^^^^
+         * 
+ */ + public final InNode[] conditions; + /** + *
+         * Represents the else clause of the case match.
+         *
+         *     case true; in false; else; end
+         *                          ^^^^^^^^^
+         * 
+ */ + @Nullable + public final ElseNode else_clause; + + public CaseMatchNode(int startOffset, int length, Node predicate, InNode[] conditions, ElseNode else_clause) { + super(startOffset, length); + this.predicate = predicate; + this.conditions = conditions; + this.else_clause = else_clause; + } + + public void visitChildNodes(AbstractNodeVisitor visitor) { + if (this.predicate != null) { + this.predicate.accept(visitor); + } + for (Nodes.Node child : this.conditions) { + child.accept(visitor); + } + if (this.else_clause != null) { + this.else_clause.accept(visitor); + } + } + + public Node[] childNodes() { + ArrayList childNodes = new ArrayList<>(); + childNodes.add(this.predicate); + childNodes.addAll(Arrays.asList(this.conditions)); + childNodes.add(this.else_clause); + return childNodes.toArray(EMPTY_ARRAY); + } + + public T accept(AbstractNodeVisitor visitor) { + return visitor.visitCaseMatchNode(this); + } + + @Override + protected String toString(String indent) { + StringBuilder builder = new StringBuilder(); + builder.append(this.getClass().getSimpleName()); + if (hasNewLineFlag()) { + builder.append("[Li]"); + } + builder.append('\n'); + String nextIndent = indent + " "; + String nextNextIndent = nextIndent + " "; + builder.append(nextIndent); + builder.append("predicate: "); + builder.append(this.predicate == null ? "null\n" : this.predicate.toString(nextIndent)); + builder.append(nextIndent); + builder.append("conditions: "); + builder.append('\n'); + for (Node child : this.conditions) { + builder.append(nextNextIndent).append(child.toString(nextNextIndent)); + } + builder.append(nextIndent); + builder.append("else_clause: "); + builder.append(this.else_clause == null ? "null\n" : this.else_clause.toString(nextIndent)); + return builder.toString(); + } + } + + /** + *
+     * Represents the use of a case statement.
+     *
+     *     case true
+     *     when false
+     *     end
+     *     ^^^^^^^^^^
+     * 
+ */ + public static final class CaseNode extends Node { + /** + *
+         * 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
+         *          ^^^^
+         * 
+ */ + @Nullable + public final Node predicate; + /** + *
+         * Represents the conditions of the case statement.
+         *
+         *     case true; when false; end
+         *                ^^^^^^^^^^
+         * 
+ */ + public final WhenNode[] conditions; + /** + *
+         * Represents the else clause of the case statement.
+         *
+         *     case true; when false; else; end
+         *                            ^^^^^^^^^
+         * 
+ */ + @Nullable + public final ElseNode else_clause; + + public CaseNode(int startOffset, int length, Node predicate, WhenNode[] conditions, ElseNode else_clause) { + super(startOffset, length); + this.predicate = predicate; + this.conditions = conditions; + this.else_clause = else_clause; + } + + public void visitChildNodes(AbstractNodeVisitor visitor) { + if (this.predicate != null) { + this.predicate.accept(visitor); + } + for (Nodes.Node child : this.conditions) { + child.accept(visitor); + } + if (this.else_clause != null) { + this.else_clause.accept(visitor); + } + } + + public Node[] childNodes() { + ArrayList childNodes = new ArrayList<>(); + childNodes.add(this.predicate); + childNodes.addAll(Arrays.asList(this.conditions)); + childNodes.add(this.else_clause); + return childNodes.toArray(EMPTY_ARRAY); + } + + public T accept(AbstractNodeVisitor visitor) { + return visitor.visitCaseNode(this); + } + + @Override + protected String toString(String indent) { + StringBuilder builder = new StringBuilder(); + builder.append(this.getClass().getSimpleName()); + if (hasNewLineFlag()) { + builder.append("[Li]"); + } + builder.append('\n'); + String nextIndent = indent + " "; + String nextNextIndent = nextIndent + " "; + builder.append(nextIndent); + builder.append("predicate: "); + builder.append(this.predicate == null ? "null\n" : this.predicate.toString(nextIndent)); + builder.append(nextIndent); + builder.append("conditions: "); + builder.append('\n'); + for (Node child : this.conditions) { + builder.append(nextNextIndent).append(child.toString(nextNextIndent)); + } + builder.append(nextIndent); + builder.append("else_clause: "); + builder.append(this.else_clause == null ? "null\n" : this.else_clause.toString(nextIndent)); + return builder.toString(); + } + } + + /** + *
+     * Represents a class declaration involving the `class` keyword.
+     *
+     *     class Foo end
+     *     ^^^^^^^^^^^^^
+     * 
+ */ + public static final class ClassNode extends Node { + public final byte[][] locals; + @UnionType({ ConstantReadNode.class, ConstantPathNode.class }) + public final Node constant_path; + /** + *
+         * Represents the superclass of the class.
+         *
+         *     class Foo < Bar
+         *                 ^^^
+         * 
+ */ + @Nullable + public final Node superclass; + /** + *
+         * Represents the body of the class.
+         *
+         *     class Foo; bar; end
+         *                ^^^
+         * 
+ */ + @Nullable + @UnionType({ StatementsNode.class, BeginNode.class }) + public final Node body; + /** + *
+         * The name of the class.
+         *
+         *     class Foo end # name `:Foo`
+         * 
+ */ + public final byte[] name; + + public ClassNode(int startOffset, int length, byte[][] locals, Node constant_path, Node superclass, Node body, byte[] name) { + super(startOffset, length); + this.locals = locals; + this.constant_path = constant_path; + this.superclass = superclass; + this.body = body; + this.name = name; + } + + public void visitChildNodes(AbstractNodeVisitor visitor) { + this.constant_path.accept(visitor); + if (this.superclass != null) { + this.superclass.accept(visitor); + } + if (this.body != null) { + this.body.accept(visitor); + } + } + + public Node[] childNodes() { + return new Node[] { this.constant_path, this.superclass, this.body }; + } + + public T accept(AbstractNodeVisitor visitor) { + return visitor.visitClassNode(this); + } + + @Override + protected String toString(String indent) { + StringBuilder builder = new StringBuilder(); + builder.append(this.getClass().getSimpleName()); + if (hasNewLineFlag()) { + builder.append("[Li]"); + } + builder.append('\n'); + String nextIndent = indent + " "; + String nextNextIndent = nextIndent + " "; + builder.append(nextIndent); + builder.append("locals: "); + builder.append('\n'); + for (byte[] constant : this.locals) { + builder.append(nextNextIndent).append('"').append(asString(constant)).append('"').append('\n'); + } + builder.append(nextIndent); + builder.append("constant_path: "); + builder.append(this.constant_path.toString(nextIndent)); + builder.append(nextIndent); + builder.append("superclass: "); + builder.append(this.superclass == null ? "null\n" : this.superclass.toString(nextIndent)); + builder.append(nextIndent); + builder.append("body: "); + builder.append(this.body == null ? "null\n" : this.body.toString(nextIndent)); + builder.append(nextIndent); + builder.append("name: "); + builder.append('"').append(asString(this.name)).append('"'); + builder.append('\n'); + return builder.toString(); + } + } + + /** + *
+     * Represents the use of the `&&=` operator for assignment to a class variable.
+     *
+     *     @@target &&= value
+     *     ^^^^^^^^^^^^^^^^^^
+     * 
+ */ + public static final class ClassVariableAndWriteNode extends Node { + /** + *
+         * 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`
+         *     ^^^^^^^^
+         * 
+ */ + public final byte[] name; + /** + *
+         * 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
+         *                  ^^^^^
+         * 
+ */ + public final Node value; + + public ClassVariableAndWriteNode(int startOffset, int length, byte[] name, Node value) { + super(startOffset, length); + this.name = name; + this.value = value; + } + + public void visitChildNodes(AbstractNodeVisitor visitor) { + this.value.accept(visitor); + } + + public Node[] childNodes() { + return new Node[] { this.value }; + } + + public T accept(AbstractNodeVisitor visitor) { + return visitor.visitClassVariableAndWriteNode(this); + } + + @Override + protected String toString(String indent) { + StringBuilder builder = new StringBuilder(); + builder.append(this.getClass().getSimpleName()); + if (hasNewLineFlag()) { + builder.append("[Li]"); + } + builder.append('\n'); + String nextIndent = indent + " "; + builder.append(nextIndent); + builder.append("name: "); + builder.append('"').append(asString(this.name)).append('"'); + builder.append('\n'); + builder.append(nextIndent); + builder.append("value: "); + builder.append(this.value.toString(nextIndent)); + return builder.toString(); + } + } + + /** + *
+     * Represents assigning to a class variable using an operator that isn't `=`.
+     *
+     *     @@target += value
+     *     ^^^^^^^^^^^^^^^^^
+     * 
+ */ + public static final class ClassVariableOperatorWriteNode extends Node { + public final byte[] name; + public final Node value; + public final byte[] binary_operator; + + public ClassVariableOperatorWriteNode(int startOffset, int length, byte[] name, Node value, byte[] binary_operator) { + super(startOffset, length); + this.name = name; + this.value = value; + this.binary_operator = binary_operator; + } + + public void visitChildNodes(AbstractNodeVisitor visitor) { + this.value.accept(visitor); + } + + public Node[] childNodes() { + return new Node[] { this.value }; + } + + public T accept(AbstractNodeVisitor visitor) { + return visitor.visitClassVariableOperatorWriteNode(this); + } + + @Override + protected String toString(String indent) { + StringBuilder builder = new StringBuilder(); + builder.append(this.getClass().getSimpleName()); + if (hasNewLineFlag()) { + builder.append("[Li]"); + } + builder.append('\n'); + String nextIndent = indent + " "; + builder.append(nextIndent); + builder.append("name: "); + builder.append('"').append(asString(this.name)).append('"'); + builder.append('\n'); + builder.append(nextIndent); + builder.append("value: "); + builder.append(this.value.toString(nextIndent)); + builder.append(nextIndent); + builder.append("binary_operator: "); + builder.append('"').append(asString(this.binary_operator)).append('"'); + builder.append('\n'); + return builder.toString(); + } + } + + /** + *
+     * Represents the use of the `||=` operator for assignment to a class variable.
+     *
+     *     @@target ||= value
+     *     ^^^^^^^^^^^^^^^^^^
+     * 
+ */ + public static final class ClassVariableOrWriteNode extends Node { + public final byte[] name; + public final Node value; + + public ClassVariableOrWriteNode(int startOffset, int length, byte[] name, Node value) { + super(startOffset, length); + this.name = name; + this.value = value; + } + + public void visitChildNodes(AbstractNodeVisitor visitor) { + this.value.accept(visitor); + } + + public Node[] childNodes() { + return new Node[] { this.value }; + } + + public T accept(AbstractNodeVisitor visitor) { + return visitor.visitClassVariableOrWriteNode(this); + } + + @Override + protected String toString(String indent) { + StringBuilder builder = new StringBuilder(); + builder.append(this.getClass().getSimpleName()); + if (hasNewLineFlag()) { + builder.append("[Li]"); + } + builder.append('\n'); + String nextIndent = indent + " "; + builder.append(nextIndent); + builder.append("name: "); + builder.append('"').append(asString(this.name)).append('"'); + builder.append('\n'); + builder.append(nextIndent); + builder.append("value: "); + builder.append(this.value.toString(nextIndent)); + return builder.toString(); + } + } + + /** + *
+     * Represents referencing a class variable.
+     *
+     *     @@foo
+     *     ^^^^^
+     * 
+ */ + public static final class ClassVariableReadNode extends Node { + /** + *
+         * 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`
+         * 
+ */ + public final byte[] name; + + public ClassVariableReadNode(int startOffset, int length, byte[] name) { + super(startOffset, length); + this.name = name; + } + + public void visitChildNodes(AbstractNodeVisitor visitor) { + } + + public Node[] childNodes() { + return EMPTY_ARRAY; + } + + public T accept(AbstractNodeVisitor visitor) { + return visitor.visitClassVariableReadNode(this); + } + + @Override + protected String toString(String indent) { + StringBuilder builder = new StringBuilder(); + builder.append(this.getClass().getSimpleName()); + if (hasNewLineFlag()) { + builder.append("[Li]"); + } + builder.append('\n'); + String nextIndent = indent + " "; + builder.append(nextIndent); + builder.append("name: "); + builder.append('"').append(asString(this.name)).append('"'); + builder.append('\n'); + return builder.toString(); + } + } + + /** + *
+     * Represents writing to a class variable in a context that doesn't have an explicit value.
+     *
+     *     @@foo, @@bar = baz
+     *     ^^^^^  ^^^^^
+     * 
+ */ + public static final class ClassVariableTargetNode extends Node { + public final byte[] name; + + public ClassVariableTargetNode(int startOffset, int length, byte[] name) { + super(startOffset, length); + this.name = name; + } + + public void visitChildNodes(AbstractNodeVisitor visitor) { + } + + public Node[] childNodes() { + return EMPTY_ARRAY; + } + + public T accept(AbstractNodeVisitor visitor) { + return visitor.visitClassVariableTargetNode(this); + } + + @Override + protected String toString(String indent) { + StringBuilder builder = new StringBuilder(); + builder.append(this.getClass().getSimpleName()); + if (hasNewLineFlag()) { + builder.append("[Li]"); + } + builder.append('\n'); + String nextIndent = indent + " "; + builder.append(nextIndent); + builder.append("name: "); + builder.append('"').append(asString(this.name)).append('"'); + builder.append('\n'); + return builder.toString(); + } + } + + /** + *
+     * Represents writing to a class variable.
+     *
+     *     @@foo = 1
+     *     ^^^^^^^^^
+     * 
+ */ + public static final class ClassVariableWriteNode extends Node { + /** + *
+         * 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`
+         * 
+ */ + public final byte[] name; + /** + *
+         * 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
+         *              ^^^
+         * 
+ */ + public final Node value; + + public ClassVariableWriteNode(int startOffset, int length, byte[] name, Node value) { + super(startOffset, length); + this.name = name; + this.value = value; + } + + public void visitChildNodes(AbstractNodeVisitor visitor) { + this.value.accept(visitor); + } + + public Node[] childNodes() { + return new Node[] { this.value }; + } + + public T accept(AbstractNodeVisitor visitor) { + return visitor.visitClassVariableWriteNode(this); + } + + @Override + protected String toString(String indent) { + StringBuilder builder = new StringBuilder(); + builder.append(this.getClass().getSimpleName()); + if (hasNewLineFlag()) { + builder.append("[Li]"); + } + builder.append('\n'); + String nextIndent = indent + " "; + builder.append(nextIndent); + builder.append("name: "); + builder.append('"').append(asString(this.name)).append('"'); + builder.append('\n'); + builder.append(nextIndent); + builder.append("value: "); + builder.append(this.value.toString(nextIndent)); + return builder.toString(); + } + } + + /** + *
+     * Represents the use of the `&&=` operator for assignment to a constant.
+     *
+     *     Target &&= value
+     *     ^^^^^^^^^^^^^^^^
+     * 
+ */ + public static final class ConstantAndWriteNode extends Node { + public final byte[] name; + public final Node value; + + public ConstantAndWriteNode(int startOffset, int length, byte[] name, Node value) { + super(startOffset, length); + this.name = name; + this.value = value; + } + + public void visitChildNodes(AbstractNodeVisitor visitor) { + this.value.accept(visitor); + } + + public Node[] childNodes() { + return new Node[] { this.value }; + } + + public T accept(AbstractNodeVisitor visitor) { + return visitor.visitConstantAndWriteNode(this); + } + + @Override + protected String toString(String indent) { + StringBuilder builder = new StringBuilder(); + builder.append(this.getClass().getSimpleName()); + if (hasNewLineFlag()) { + builder.append("[Li]"); + } + builder.append('\n'); + String nextIndent = indent + " "; + builder.append(nextIndent); + builder.append("name: "); + builder.append('"').append(asString(this.name)).append('"'); + builder.append('\n'); + builder.append(nextIndent); + builder.append("value: "); + builder.append(this.value.toString(nextIndent)); + return builder.toString(); + } + } + + /** + *
+     * Represents assigning to a constant using an operator that isn't `=`.
+     *
+     *     Target += value
+     *     ^^^^^^^^^^^^^^^
+     * 
+ */ + public static final class ConstantOperatorWriteNode extends Node { + public final byte[] name; + public final Node value; + public final byte[] binary_operator; + + public ConstantOperatorWriteNode(int startOffset, int length, byte[] name, Node value, byte[] binary_operator) { + super(startOffset, length); + this.name = name; + this.value = value; + this.binary_operator = binary_operator; + } + + public void visitChildNodes(AbstractNodeVisitor visitor) { + this.value.accept(visitor); + } + + public Node[] childNodes() { + return new Node[] { this.value }; + } + + public T accept(AbstractNodeVisitor visitor) { + return visitor.visitConstantOperatorWriteNode(this); + } + + @Override + protected String toString(String indent) { + StringBuilder builder = new StringBuilder(); + builder.append(this.getClass().getSimpleName()); + if (hasNewLineFlag()) { + builder.append("[Li]"); + } + builder.append('\n'); + String nextIndent = indent + " "; + builder.append(nextIndent); + builder.append("name: "); + builder.append('"').append(asString(this.name)).append('"'); + builder.append('\n'); + builder.append(nextIndent); + builder.append("value: "); + builder.append(this.value.toString(nextIndent)); + builder.append(nextIndent); + builder.append("binary_operator: "); + builder.append('"').append(asString(this.binary_operator)).append('"'); + builder.append('\n'); + return builder.toString(); + } + } + + /** + *
+     * Represents the use of the `||=` operator for assignment to a constant.
+     *
+     *     Target ||= value
+     *     ^^^^^^^^^^^^^^^^
+     * 
+ */ + public static final class ConstantOrWriteNode extends Node { + public final byte[] name; + public final Node value; + + public ConstantOrWriteNode(int startOffset, int length, byte[] name, Node value) { + super(startOffset, length); + this.name = name; + this.value = value; + } + + public void visitChildNodes(AbstractNodeVisitor visitor) { + this.value.accept(visitor); + } + + public Node[] childNodes() { + return new Node[] { this.value }; + } + + public T accept(AbstractNodeVisitor visitor) { + return visitor.visitConstantOrWriteNode(this); + } + + @Override + protected String toString(String indent) { + StringBuilder builder = new StringBuilder(); + builder.append(this.getClass().getSimpleName()); + if (hasNewLineFlag()) { + builder.append("[Li]"); + } + builder.append('\n'); + String nextIndent = indent + " "; + builder.append(nextIndent); + builder.append("name: "); + builder.append('"').append(asString(this.name)).append('"'); + builder.append('\n'); + builder.append(nextIndent); + builder.append("value: "); + builder.append(this.value.toString(nextIndent)); + return builder.toString(); + } + } + + /** + *
+     * Represents the use of the `&&=` operator for assignment to a constant path.
+     *
+     *     Parent::Child &&= value
+     *     ^^^^^^^^^^^^^^^^^^^^^^^
+     * 
+ */ + public static final class ConstantPathAndWriteNode extends Node { + public final ConstantPathNode target; + public final Node value; + + public ConstantPathAndWriteNode(int startOffset, int length, ConstantPathNode target, Node value) { + super(startOffset, length); + this.target = target; + this.value = value; + } + + public void visitChildNodes(AbstractNodeVisitor visitor) { + this.target.accept(visitor); + this.value.accept(visitor); + } + + public Node[] childNodes() { + return new Node[] { this.target, this.value }; + } + + public T accept(AbstractNodeVisitor visitor) { + return visitor.visitConstantPathAndWriteNode(this); + } + + @Override + protected String toString(String indent) { + StringBuilder builder = new StringBuilder(); + builder.append(this.getClass().getSimpleName()); + if (hasNewLineFlag()) { + builder.append("[Li]"); + } + builder.append('\n'); + String nextIndent = indent + " "; + builder.append(nextIndent); + builder.append("target: "); + builder.append(this.target.toString(nextIndent)); + builder.append(nextIndent); + builder.append("value: "); + builder.append(this.value.toString(nextIndent)); + return builder.toString(); + } + } + + /** + *
+     * Represents accessing a constant through a path of `::` operators.
+     *
+     *     Foo::Bar
+     *     ^^^^^^^^
+     * 
+ */ + public static final class ConstantPathNode extends Node { + /** + *
+         * 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
+         *     ^^^
+         * 
+ */ + @Nullable + public final Node parent; + /** + *
+         * The name of the constant being accessed. This could be `nil` in the event of a syntax error.
+         * 
+ */ + @Nullable + public final byte[] name; + + public ConstantPathNode(int startOffset, int length, Node parent, byte[] name) { + super(startOffset, length); + this.parent = parent; + this.name = name; + } + + public void visitChildNodes(AbstractNodeVisitor visitor) { + if (this.parent != null) { + this.parent.accept(visitor); + } + } + + public Node[] childNodes() { + return new Node[] { this.parent }; + } + + public T accept(AbstractNodeVisitor visitor) { + return visitor.visitConstantPathNode(this); + } + + @Override + protected String toString(String indent) { + StringBuilder builder = new StringBuilder(); + builder.append(this.getClass().getSimpleName()); + if (hasNewLineFlag()) { + builder.append("[Li]"); + } + builder.append('\n'); + String nextIndent = indent + " "; + builder.append(nextIndent); + builder.append("parent: "); + builder.append(this.parent == null ? "null\n" : this.parent.toString(nextIndent)); + builder.append(nextIndent); + builder.append("name: "); + builder.append(this.name == null ? "null" : "\"" + asString(this.name) + "\""); + builder.append('\n'); + return builder.toString(); + } + } + + /** + *
+     * Represents assigning to a constant path using an operator that isn't `=`.
+     *
+     *     Parent::Child += value
+     *     ^^^^^^^^^^^^^^^^^^^^^^
+     * 
+ */ + public static final class ConstantPathOperatorWriteNode extends Node { + public final ConstantPathNode target; + public final Node value; + public final byte[] binary_operator; + + public ConstantPathOperatorWriteNode(int startOffset, int length, ConstantPathNode target, Node value, byte[] binary_operator) { + super(startOffset, length); + this.target = target; + this.value = value; + this.binary_operator = binary_operator; + } + + public void visitChildNodes(AbstractNodeVisitor visitor) { + this.target.accept(visitor); + this.value.accept(visitor); + } + + public Node[] childNodes() { + return new Node[] { this.target, this.value }; + } + + public T accept(AbstractNodeVisitor visitor) { + return visitor.visitConstantPathOperatorWriteNode(this); + } + + @Override + protected String toString(String indent) { + StringBuilder builder = new StringBuilder(); + builder.append(this.getClass().getSimpleName()); + if (hasNewLineFlag()) { + builder.append("[Li]"); + } + builder.append('\n'); + String nextIndent = indent + " "; + builder.append(nextIndent); + builder.append("target: "); + builder.append(this.target.toString(nextIndent)); + builder.append(nextIndent); + builder.append("value: "); + builder.append(this.value.toString(nextIndent)); + builder.append(nextIndent); + builder.append("binary_operator: "); + builder.append('"').append(asString(this.binary_operator)).append('"'); + builder.append('\n'); + return builder.toString(); + } + } + + /** + *
+     * Represents the use of the `||=` operator for assignment to a constant path.
+     *
+     *     Parent::Child ||= value
+     *     ^^^^^^^^^^^^^^^^^^^^^^^
+     * 
+ */ + public static final class ConstantPathOrWriteNode extends Node { + public final ConstantPathNode target; + public final Node value; + + public ConstantPathOrWriteNode(int startOffset, int length, ConstantPathNode target, Node value) { + super(startOffset, length); + this.target = target; + this.value = value; + } + + public void visitChildNodes(AbstractNodeVisitor visitor) { + this.target.accept(visitor); + this.value.accept(visitor); + } + + public Node[] childNodes() { + return new Node[] { this.target, this.value }; + } + + public T accept(AbstractNodeVisitor visitor) { + return visitor.visitConstantPathOrWriteNode(this); + } + + @Override + protected String toString(String indent) { + StringBuilder builder = new StringBuilder(); + builder.append(this.getClass().getSimpleName()); + if (hasNewLineFlag()) { + builder.append("[Li]"); + } + builder.append('\n'); + String nextIndent = indent + " "; + builder.append(nextIndent); + builder.append("target: "); + builder.append(this.target.toString(nextIndent)); + builder.append(nextIndent); + builder.append("value: "); + builder.append(this.value.toString(nextIndent)); + return builder.toString(); + } + } + + /** + *
+     * Represents writing to a constant path in a context that doesn't have an explicit value.
+     *
+     *     Foo::Foo, Bar::Bar = baz
+     *     ^^^^^^^^  ^^^^^^^^
+     * 
+ */ + public static final class ConstantPathTargetNode extends Node { + @Nullable + public final Node parent; + @Nullable + public final byte[] name; + + public ConstantPathTargetNode(int startOffset, int length, Node parent, byte[] name) { + super(startOffset, length); + this.parent = parent; + this.name = name; + } + + public void visitChildNodes(AbstractNodeVisitor visitor) { + if (this.parent != null) { + this.parent.accept(visitor); + } + } + + public Node[] childNodes() { + return new Node[] { this.parent }; + } + + public T accept(AbstractNodeVisitor visitor) { + return visitor.visitConstantPathTargetNode(this); + } + + @Override + protected String toString(String indent) { + StringBuilder builder = new StringBuilder(); + builder.append(this.getClass().getSimpleName()); + if (hasNewLineFlag()) { + builder.append("[Li]"); + } + builder.append('\n'); + String nextIndent = indent + " "; + builder.append(nextIndent); + builder.append("parent: "); + builder.append(this.parent == null ? "null\n" : this.parent.toString(nextIndent)); + builder.append(nextIndent); + builder.append("name: "); + builder.append(this.name == null ? "null" : "\"" + asString(this.name) + "\""); + builder.append('\n'); + return builder.toString(); + } + } + + /** + *
+     * Represents writing to a constant path.
+     *
+     *     ::Foo = 1
+     *     ^^^^^^^^^
+     *
+     *     Foo::Bar = 1
+     *     ^^^^^^^^^^^^
+     *
+     *     ::Foo::Bar = 1
+     *     ^^^^^^^^^^^^^^
+     * 
+ */ + public static final class ConstantPathWriteNode extends Node { + /** + *
+         * A node representing the constant path being written to.
+         *
+         *     Foo::Bar = 1
+         *     ^^^^^^^^
+         *
+         *     ::Foo = :abc
+         *     ^^^^^
+         * 
+ */ + public final ConstantPathNode target; + /** + *
+         * 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
+         *                ^^^^
+         * 
+ */ + public final Node value; + + public ConstantPathWriteNode(int startOffset, int length, ConstantPathNode target, Node value) { + super(startOffset, length); + this.target = target; + this.value = value; + } + + public void visitChildNodes(AbstractNodeVisitor visitor) { + this.target.accept(visitor); + this.value.accept(visitor); + } + + public Node[] childNodes() { + return new Node[] { this.target, this.value }; + } + + public T accept(AbstractNodeVisitor visitor) { + return visitor.visitConstantPathWriteNode(this); + } + + @Override + protected String toString(String indent) { + StringBuilder builder = new StringBuilder(); + builder.append(this.getClass().getSimpleName()); + if (hasNewLineFlag()) { + builder.append("[Li]"); + } + builder.append('\n'); + String nextIndent = indent + " "; + builder.append(nextIndent); + builder.append("target: "); + builder.append(this.target.toString(nextIndent)); + builder.append(nextIndent); + builder.append("value: "); + builder.append(this.value.toString(nextIndent)); + return builder.toString(); + } + } + + /** + *
+     * Represents referencing a constant.
+     *
+     *     Foo
+     *     ^^^
+     * 
+ */ + public static final class ConstantReadNode extends Node { + /** + *
+         * 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`
+         * 
+ */ + public final byte[] name; + + public ConstantReadNode(int startOffset, int length, byte[] name) { + super(startOffset, length); + this.name = name; + } + + public void visitChildNodes(AbstractNodeVisitor visitor) { + } + + public Node[] childNodes() { + return EMPTY_ARRAY; + } + + public T accept(AbstractNodeVisitor visitor) { + return visitor.visitConstantReadNode(this); + } + + @Override + protected String toString(String indent) { + StringBuilder builder = new StringBuilder(); + builder.append(this.getClass().getSimpleName()); + if (hasNewLineFlag()) { + builder.append("[Li]"); + } + builder.append('\n'); + String nextIndent = indent + " "; + builder.append(nextIndent); + builder.append("name: "); + builder.append('"').append(asString(this.name)).append('"'); + builder.append('\n'); + return builder.toString(); + } + } + + /** + *
+     * Represents writing to a constant in a context that doesn't have an explicit value.
+     *
+     *     Foo, Bar = baz
+     *     ^^^  ^^^
+     * 
+ */ + public static final class ConstantTargetNode extends Node { + public final byte[] name; + + public ConstantTargetNode(int startOffset, int length, byte[] name) { + super(startOffset, length); + this.name = name; + } + + public void visitChildNodes(AbstractNodeVisitor visitor) { + } + + public Node[] childNodes() { + return EMPTY_ARRAY; + } + + public T accept(AbstractNodeVisitor visitor) { + return visitor.visitConstantTargetNode(this); + } + + @Override + protected String toString(String indent) { + StringBuilder builder = new StringBuilder(); + builder.append(this.getClass().getSimpleName()); + if (hasNewLineFlag()) { + builder.append("[Li]"); + } + builder.append('\n'); + String nextIndent = indent + " "; + builder.append(nextIndent); + builder.append("name: "); + builder.append('"').append(asString(this.name)).append('"'); + builder.append('\n'); + return builder.toString(); + } + } + + /** + *
+     * Represents writing to a constant.
+     *
+     *     Foo = 1
+     *     ^^^^^^^
+     * 
+ */ + public static final class ConstantWriteNode extends Node { + /** + *
+         * 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`
+         * 
+ */ + public final byte[] name; + /** + *
+         * 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
+         *               ^^^^^^^^^
+         * 
+ */ + public final Node value; + + public ConstantWriteNode(int startOffset, int length, byte[] name, Node value) { + super(startOffset, length); + this.name = name; + this.value = value; + } + + public void visitChildNodes(AbstractNodeVisitor visitor) { + this.value.accept(visitor); + } + + public Node[] childNodes() { + return new Node[] { this.value }; + } + + public T accept(AbstractNodeVisitor visitor) { + return visitor.visitConstantWriteNode(this); + } + + @Override + protected String toString(String indent) { + StringBuilder builder = new StringBuilder(); + builder.append(this.getClass().getSimpleName()); + if (hasNewLineFlag()) { + builder.append("[Li]"); + } + builder.append('\n'); + String nextIndent = indent + " "; + builder.append(nextIndent); + builder.append("name: "); + builder.append('"').append(asString(this.name)).append('"'); + builder.append('\n'); + builder.append(nextIndent); + builder.append("value: "); + builder.append(this.value.toString(nextIndent)); + return builder.toString(); + } + } + + /** + *
+     * Represents a method definition.
+     *
+     *     def method
+     *     end
+     *     ^^^^^^^^^^
+     * 
+ */ + public static final class DefNode extends Node { + public final int serializedLength; + public final Loader loader; + public final byte[] name; + @Nullable + public final Node receiver; + @Nullable + public final ParametersNode parameters; + @Nullable + @UnionType({ StatementsNode.class, BeginNode.class }) + public final Node body; + public final byte[][] locals; + + public DefNode(int startOffset, int length, int serializedLength, Loader loader, byte[] name, Node receiver, ParametersNode parameters, Node body, byte[][] locals) { + super(startOffset, length); + this.serializedLength = serializedLength; + this.loader = loader; + this.name = name; + this.receiver = receiver; + this.parameters = parameters; + this.body = body; + this.locals = locals; + } + + public boolean isLazy() { + return serializedLength < 0; + } + + public DefNode getNonLazy() { + if (isLazy()) { + return loader.createDefNodeFromSavedPosition(startOffset, length, -serializedLength); + } else { + return this; + } + } + + public void visitChildNodes(AbstractNodeVisitor visitor) { + if (this.receiver != null) { + this.receiver.accept(visitor); + } + if (this.parameters != null) { + this.parameters.accept(visitor); + } + if (this.body != null) { + this.body.accept(visitor); + } + } + + public Node[] childNodes() { + return new Node[] { this.receiver, this.parameters, this.body }; + } + + public T accept(AbstractNodeVisitor visitor) { + return visitor.visitDefNode(this); + } + + @Override + protected String toString(String indent) { + StringBuilder builder = new StringBuilder(); + builder.append(this.getClass().getSimpleName()); + if (hasNewLineFlag()) { + builder.append("[Li]"); + } + builder.append('\n'); + String nextIndent = indent + " "; + String nextNextIndent = nextIndent + " "; + builder.append(nextIndent); + builder.append("name: "); + builder.append('"').append(asString(this.name)).append('"'); + builder.append('\n'); + builder.append(nextIndent); + builder.append("receiver: "); + builder.append(this.receiver == null ? "null\n" : this.receiver.toString(nextIndent)); + builder.append(nextIndent); + builder.append("parameters: "); + builder.append(this.parameters == null ? "null\n" : this.parameters.toString(nextIndent)); + builder.append(nextIndent); + builder.append("body: "); + builder.append(this.body == null ? "null\n" : this.body.toString(nextIndent)); + builder.append(nextIndent); + builder.append("locals: "); + builder.append('\n'); + for (byte[] constant : this.locals) { + builder.append(nextNextIndent).append('"').append(asString(constant)).append('"').append('\n'); + } + return builder.toString(); + } + } + + /** + *
+     * Represents the use of the `defined?` keyword.
+     *
+     *     defined?(a)
+     *     ^^^^^^^^^^^
+     * 
+ */ + public static final class DefinedNode extends Node { + public final Node value; + + public DefinedNode(int startOffset, int length, Node value) { + super(startOffset, length); + this.value = value; + } + + public void visitChildNodes(AbstractNodeVisitor visitor) { + this.value.accept(visitor); + } + + public Node[] childNodes() { + return new Node[] { this.value }; + } + + public T accept(AbstractNodeVisitor visitor) { + return visitor.visitDefinedNode(this); + } + + @Override + protected String toString(String indent) { + StringBuilder builder = new StringBuilder(); + builder.append(this.getClass().getSimpleName()); + if (hasNewLineFlag()) { + builder.append("[Li]"); + } + builder.append('\n'); + String nextIndent = indent + " "; + builder.append(nextIndent); + builder.append("value: "); + builder.append(this.value.toString(nextIndent)); + return builder.toString(); + } + } + + /** + *
+     * Represents an `else` clause in a `case`, `if`, or `unless` statement.
+     *
+     *     if a then b else c end
+     *                 ^^^^^^^^^^
+     * 
+ */ + public static final class ElseNode extends Node { + @Nullable + public final StatementsNode statements; + + public ElseNode(int startOffset, int length, StatementsNode statements) { + super(startOffset, length); + this.statements = statements; + } + + public void visitChildNodes(AbstractNodeVisitor visitor) { + if (this.statements != null) { + this.statements.accept(visitor); + } + } + + public Node[] childNodes() { + return new Node[] { this.statements }; + } + + public T accept(AbstractNodeVisitor visitor) { + return visitor.visitElseNode(this); + } + + @Override + protected String toString(String indent) { + StringBuilder builder = new StringBuilder(); + builder.append(this.getClass().getSimpleName()); + if (hasNewLineFlag()) { + builder.append("[Li]"); + } + builder.append('\n'); + String nextIndent = indent + " "; + builder.append(nextIndent); + builder.append("statements: "); + builder.append(this.statements == null ? "null\n" : this.statements.toString(nextIndent)); + return builder.toString(); + } + } + + /** + *
+     * Represents an interpolated set of statements.
+     *
+     *     "foo #{bar}"
+     *          ^^^^^^
+     * 
+ */ + public static final class EmbeddedStatementsNode extends Node { + @Nullable + public final StatementsNode statements; + + public EmbeddedStatementsNode(int startOffset, int length, StatementsNode statements) { + super(startOffset, length); + this.statements = statements; + } + + public void visitChildNodes(AbstractNodeVisitor visitor) { + if (this.statements != null) { + this.statements.accept(visitor); + } + } + + public Node[] childNodes() { + return new Node[] { this.statements }; + } + + public T accept(AbstractNodeVisitor visitor) { + return visitor.visitEmbeddedStatementsNode(this); + } + + @Override + protected String toString(String indent) { + StringBuilder builder = new StringBuilder(); + builder.append(this.getClass().getSimpleName()); + if (hasNewLineFlag()) { + builder.append("[Li]"); + } + builder.append('\n'); + String nextIndent = indent + " "; + builder.append(nextIndent); + builder.append("statements: "); + builder.append(this.statements == null ? "null\n" : this.statements.toString(nextIndent)); + return builder.toString(); + } + } + + /** + *
+     * Represents an interpolated variable.
+     *
+     *     "foo #@bar"
+     *          ^^^^^
+     * 
+ */ + public static final class EmbeddedVariableNode extends Node { + @UnionType({ InstanceVariableReadNode.class, ClassVariableReadNode.class, GlobalVariableReadNode.class, BackReferenceReadNode.class, NumberedReferenceReadNode.class }) + public final Node variable; + + public EmbeddedVariableNode(int startOffset, int length, Node variable) { + super(startOffset, length); + this.variable = variable; + } + + public void visitChildNodes(AbstractNodeVisitor visitor) { + this.variable.accept(visitor); + } + + public Node[] childNodes() { + return new Node[] { this.variable }; + } + + public T accept(AbstractNodeVisitor visitor) { + return visitor.visitEmbeddedVariableNode(this); + } + + @Override + protected String toString(String indent) { + StringBuilder builder = new StringBuilder(); + builder.append(this.getClass().getSimpleName()); + if (hasNewLineFlag()) { + builder.append("[Li]"); + } + builder.append('\n'); + String nextIndent = indent + " "; + builder.append(nextIndent); + builder.append("variable: "); + builder.append(this.variable.toString(nextIndent)); + return builder.toString(); + } + } + + /** + *
+     * Represents an `ensure` clause in a `begin` statement.
+     *
+     *     begin
+     *       foo
+     *     ensure
+     *     ^^^^^^
+     *       bar
+     *     end
+     * 
+ */ + public static final class EnsureNode extends Node { + @Nullable + public final StatementsNode statements; + + public EnsureNode(int startOffset, int length, StatementsNode statements) { + super(startOffset, length); + this.statements = statements; + } + + public void visitChildNodes(AbstractNodeVisitor visitor) { + if (this.statements != null) { + this.statements.accept(visitor); + } + } + + public Node[] childNodes() { + return new Node[] { this.statements }; + } + + public T accept(AbstractNodeVisitor visitor) { + return visitor.visitEnsureNode(this); + } + + @Override + protected String toString(String indent) { + StringBuilder builder = new StringBuilder(); + builder.append(this.getClass().getSimpleName()); + if (hasNewLineFlag()) { + builder.append("[Li]"); + } + builder.append('\n'); + String nextIndent = indent + " "; + builder.append(nextIndent); + builder.append("statements: "); + builder.append(this.statements == null ? "null\n" : this.statements.toString(nextIndent)); + return builder.toString(); + } + } + + /** + *
+     * Represents the use of the literal `false` keyword.
+     *
+     *     false
+     *     ^^^^^
+     * 
+ */ + public static final class FalseNode extends Node { + + public FalseNode(int startOffset, int length) { + super(startOffset, length); + } + + public void visitChildNodes(AbstractNodeVisitor visitor) { + } + + public Node[] childNodes() { + return EMPTY_ARRAY; + } + + public T accept(AbstractNodeVisitor visitor) { + return visitor.visitFalseNode(this); + } + + @Override + protected String toString(String indent) { + StringBuilder builder = new StringBuilder(); + builder.append(this.getClass().getSimpleName()); + if (hasNewLineFlag()) { + builder.append("[Li]"); + } + builder.append('\n'); + String nextIndent = indent + " "; + return builder.toString(); + } + } + + /** + *
+     * 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
+     *            ^^^^^^^^^^^^^^^
+     * 
+ */ + public static final class FindPatternNode extends Node { + /** + *
+         * Represents the optional constant preceding the pattern
+         *
+         *     foo in Foo(*bar, baz, *qux)
+         *            ^^^
+         * 
+ */ + @Nullable + @UnionType({ ConstantPathNode.class, ConstantReadNode.class }) + public final Node constant; + /** + *
+         * Represents the first wildcard node in the pattern.
+         *
+         *     foo in *bar, baz, *qux
+         *            ^^^^
+         *
+         *     foo in Foo(*bar, baz, *qux)
+         *                ^^^^
+         * 
+ */ + public final SplatNode left; + /** + *
+         * Represents the nodes in between the wildcards.
+         *
+         *     foo in *bar, baz, *qux
+         *                  ^^^
+         *
+         *     foo in Foo(*bar, baz, 1, *qux)
+         *                      ^^^^^^
+         * 
+ */ + public final Node[] requireds; + /** + *
+         * Represents the second wildcard node in the pattern.
+         *
+         *     foo in *bar, baz, *qux
+         *                       ^^^^
+         *
+         *     foo in Foo(*bar, baz, *qux)
+         *                           ^^^^
+         * 
+ */ + public final SplatNode right; + + public FindPatternNode(int startOffset, int length, Node constant, SplatNode left, Node[] requireds, SplatNode right) { + super(startOffset, length); + this.constant = constant; + this.left = left; + this.requireds = requireds; + this.right = right; + } + + public void visitChildNodes(AbstractNodeVisitor visitor) { + if (this.constant != null) { + this.constant.accept(visitor); + } + this.left.accept(visitor); + for (Nodes.Node child : this.requireds) { + child.accept(visitor); + } + this.right.accept(visitor); + } + + public Node[] childNodes() { + ArrayList childNodes = new ArrayList<>(); + childNodes.add(this.constant); + childNodes.add(this.left); + childNodes.addAll(Arrays.asList(this.requireds)); + childNodes.add(this.right); + return childNodes.toArray(EMPTY_ARRAY); + } + + public T accept(AbstractNodeVisitor visitor) { + return visitor.visitFindPatternNode(this); + } + + @Override + protected String toString(String indent) { + StringBuilder builder = new StringBuilder(); + builder.append(this.getClass().getSimpleName()); + if (hasNewLineFlag()) { + builder.append("[Li]"); + } + builder.append('\n'); + String nextIndent = indent + " "; + String nextNextIndent = nextIndent + " "; + builder.append(nextIndent); + builder.append("constant: "); + builder.append(this.constant == null ? "null\n" : this.constant.toString(nextIndent)); + builder.append(nextIndent); + builder.append("left: "); + builder.append(this.left.toString(nextIndent)); + builder.append(nextIndent); + builder.append("requireds: "); + builder.append('\n'); + for (Node child : this.requireds) { + builder.append(nextNextIndent).append(child.toString(nextNextIndent)); + } + builder.append(nextIndent); + builder.append("right: "); + builder.append(this.right.toString(nextIndent)); + return builder.toString(); + } + } + + /** + *
+     * Represents the use of the `..` or `...` operators to create flip flops.
+     *
+     *     baz if foo .. bar
+     *            ^^^^^^^^^^
+     * 
+ */ + public static final class FlipFlopNode extends Node { + public final short flags; + @Nullable + public final Node left; + @Nullable + public final Node right; + + public FlipFlopNode(int startOffset, int length, short flags, Node left, Node right) { + super(startOffset, length); + this.flags = flags; + this.left = left; + this.right = right; + } + + public boolean isExcludeEnd() { + return RangeFlags.isExcludeEnd(flags); + } + + public void visitChildNodes(AbstractNodeVisitor visitor) { + if (this.left != null) { + this.left.accept(visitor); + } + if (this.right != null) { + this.right.accept(visitor); + } + } + + public Node[] childNodes() { + return new Node[] { this.left, this.right }; + } + + public T accept(AbstractNodeVisitor visitor) { + return visitor.visitFlipFlopNode(this); + } + + @Override + protected String toString(String indent) { + StringBuilder builder = new StringBuilder(); + builder.append(this.getClass().getSimpleName()); + if (hasNewLineFlag()) { + builder.append("[Li]"); + } + builder.append('\n'); + String nextIndent = indent + " "; + builder.append(nextIndent); + builder.append("RangeFlags: "); + builder.append(flags); + builder.append('\n'); + builder.append(nextIndent); + builder.append("left: "); + builder.append(this.left == null ? "null\n" : this.left.toString(nextIndent)); + builder.append(nextIndent); + builder.append("right: "); + builder.append(this.right == null ? "null\n" : this.right.toString(nextIndent)); + return builder.toString(); + } + } + + /** + *
+     * Represents a floating point number literal.
+     *
+     *     1.0
+     *     ^^^
+     * 
+ */ + public static final class FloatNode extends Node { + /** + *
+         * The value of the floating point number as a Float.
+         * 
+ */ + public final double value; + + public FloatNode(int startOffset, int length, double value) { + super(startOffset, length); + this.value = value; + } + + public void visitChildNodes(AbstractNodeVisitor visitor) { + } + + public Node[] childNodes() { + return EMPTY_ARRAY; + } + + public T accept(AbstractNodeVisitor visitor) { + return visitor.visitFloatNode(this); + } + + @Override + protected String toString(String indent) { + StringBuilder builder = new StringBuilder(); + builder.append(this.getClass().getSimpleName()); + if (hasNewLineFlag()) { + builder.append("[Li]"); + } + builder.append('\n'); + String nextIndent = indent + " "; + builder.append(nextIndent); + builder.append("value: "); + builder.append(this.value); + builder.append('\n'); + return builder.toString(); + } + } + + /** + *
+     * Represents the use of the `for` keyword.
+     *
+     *     for i in a end
+     *     ^^^^^^^^^^^^^^
+     * 
+ */ + public static final class ForNode extends Node { + /** + *
+         * The index expression for `for` loops.
+         *
+         *     for i in a end
+         *         ^
+         * 
+ */ + @UnionType({ LocalVariableTargetNode.class, InstanceVariableTargetNode.class, ClassVariableTargetNode.class, GlobalVariableTargetNode.class, ConstantTargetNode.class, ConstantPathTargetNode.class, CallTargetNode.class, IndexTargetNode.class, MultiTargetNode.class }) + public final Node index; + /** + *
+         * The collection to iterate over.
+         *
+         *     for i in a end
+         *              ^
+         * 
+ */ + public final Node collection; + /** + *
+         * Represents the body of statements to execute for each iteration of the loop.
+         *
+         *     for i in a
+         *       foo(i)
+         *       ^^^^^^
+         *     end
+         * 
+ */ + @Nullable + public final StatementsNode statements; + + public ForNode(int startOffset, int length, Node index, Node collection, StatementsNode statements) { + super(startOffset, length); + this.index = index; + this.collection = collection; + this.statements = statements; + } + + public void visitChildNodes(AbstractNodeVisitor visitor) { + this.index.accept(visitor); + this.collection.accept(visitor); + if (this.statements != null) { + this.statements.accept(visitor); + } + } + + public Node[] childNodes() { + return new Node[] { this.index, this.collection, this.statements }; + } + + public T accept(AbstractNodeVisitor visitor) { + return visitor.visitForNode(this); + } + + @Override + protected String toString(String indent) { + StringBuilder builder = new StringBuilder(); + builder.append(this.getClass().getSimpleName()); + if (hasNewLineFlag()) { + builder.append("[Li]"); + } + builder.append('\n'); + String nextIndent = indent + " "; + builder.append(nextIndent); + builder.append("index: "); + builder.append(this.index.toString(nextIndent)); + builder.append(nextIndent); + builder.append("collection: "); + builder.append(this.collection.toString(nextIndent)); + builder.append(nextIndent); + builder.append("statements: "); + builder.append(this.statements == null ? "null\n" : this.statements.toString(nextIndent)); + return builder.toString(); + } + } + + /** + *
+     * Represents forwarding all arguments to this method to another method.
+     *
+     *     def foo(...)
+     *       bar(...)
+     *           ^^^
+     *     end
+     * 
+ */ + public static final class ForwardingArgumentsNode extends Node { + + public ForwardingArgumentsNode(int startOffset, int length) { + super(startOffset, length); + } + + public void visitChildNodes(AbstractNodeVisitor visitor) { + } + + public Node[] childNodes() { + return EMPTY_ARRAY; + } + + public T accept(AbstractNodeVisitor visitor) { + return visitor.visitForwardingArgumentsNode(this); + } + + @Override + protected String toString(String indent) { + StringBuilder builder = new StringBuilder(); + builder.append(this.getClass().getSimpleName()); + if (hasNewLineFlag()) { + builder.append("[Li]"); + } + builder.append('\n'); + String nextIndent = indent + " "; + return builder.toString(); + } + } + + /** + *
+     * Represents the use of the forwarding parameter in a method, block, or lambda declaration.
+     *
+     *     def foo(...)
+     *             ^^^
+     *     end
+     * 
+ */ + public static final class ForwardingParameterNode extends Node { + + public ForwardingParameterNode(int startOffset, int length) { + super(startOffset, length); + } + + public void visitChildNodes(AbstractNodeVisitor visitor) { + } + + public Node[] childNodes() { + return EMPTY_ARRAY; + } + + public T accept(AbstractNodeVisitor visitor) { + return visitor.visitForwardingParameterNode(this); + } + + @Override + protected String toString(String indent) { + StringBuilder builder = new StringBuilder(); + builder.append(this.getClass().getSimpleName()); + if (hasNewLineFlag()) { + builder.append("[Li]"); + } + builder.append('\n'); + String nextIndent = indent + " "; + return builder.toString(); + } + } + + /** + *
+     * 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.
+     * 
+ */ + public static final class ForwardingSuperNode extends Node { + /** + *
+         * All other arguments are forwarded as normal, except the original block is replaced with the new block.
+         * 
+ */ + @Nullable + public final BlockNode block; + + public ForwardingSuperNode(int startOffset, int length, BlockNode block) { + super(startOffset, length); + this.block = block; + } + + public void visitChildNodes(AbstractNodeVisitor visitor) { + if (this.block != null) { + this.block.accept(visitor); + } + } + + public Node[] childNodes() { + return new Node[] { this.block }; + } + + public T accept(AbstractNodeVisitor visitor) { + return visitor.visitForwardingSuperNode(this); + } + + @Override + protected String toString(String indent) { + StringBuilder builder = new StringBuilder(); + builder.append(this.getClass().getSimpleName()); + if (hasNewLineFlag()) { + builder.append("[Li]"); + } + builder.append('\n'); + String nextIndent = indent + " "; + builder.append(nextIndent); + builder.append("block: "); + builder.append(this.block == null ? "null\n" : this.block.toString(nextIndent)); + return builder.toString(); + } + } + + /** + *
+     * Represents the use of the `&&=` operator for assignment to a global variable.
+     *
+     *     $target &&= value
+     *     ^^^^^^^^^^^^^^^^^
+     * 
+ */ + public static final class GlobalVariableAndWriteNode extends Node { + public final byte[] name; + public final Node value; + + public GlobalVariableAndWriteNode(int startOffset, int length, byte[] name, Node value) { + super(startOffset, length); + this.name = name; + this.value = value; + } + + public void visitChildNodes(AbstractNodeVisitor visitor) { + this.value.accept(visitor); + } + + public Node[] childNodes() { + return new Node[] { this.value }; + } + + public T accept(AbstractNodeVisitor visitor) { + return visitor.visitGlobalVariableAndWriteNode(this); + } + + @Override + protected String toString(String indent) { + StringBuilder builder = new StringBuilder(); + builder.append(this.getClass().getSimpleName()); + if (hasNewLineFlag()) { + builder.append("[Li]"); + } + builder.append('\n'); + String nextIndent = indent + " "; + builder.append(nextIndent); + builder.append("name: "); + builder.append('"').append(asString(this.name)).append('"'); + builder.append('\n'); + builder.append(nextIndent); + builder.append("value: "); + builder.append(this.value.toString(nextIndent)); + return builder.toString(); + } + } + + /** + *
+     * Represents assigning to a global variable using an operator that isn't `=`.
+     *
+     *     $target += value
+     *     ^^^^^^^^^^^^^^^^
+     * 
+ */ + public static final class GlobalVariableOperatorWriteNode extends Node { + public final byte[] name; + public final Node value; + public final byte[] binary_operator; + + public GlobalVariableOperatorWriteNode(int startOffset, int length, byte[] name, Node value, byte[] binary_operator) { + super(startOffset, length); + this.name = name; + this.value = value; + this.binary_operator = binary_operator; + } + + public void visitChildNodes(AbstractNodeVisitor visitor) { + this.value.accept(visitor); + } + + public Node[] childNodes() { + return new Node[] { this.value }; + } + + public T accept(AbstractNodeVisitor visitor) { + return visitor.visitGlobalVariableOperatorWriteNode(this); + } + + @Override + protected String toString(String indent) { + StringBuilder builder = new StringBuilder(); + builder.append(this.getClass().getSimpleName()); + if (hasNewLineFlag()) { + builder.append("[Li]"); + } + builder.append('\n'); + String nextIndent = indent + " "; + builder.append(nextIndent); + builder.append("name: "); + builder.append('"').append(asString(this.name)).append('"'); + builder.append('\n'); + builder.append(nextIndent); + builder.append("value: "); + builder.append(this.value.toString(nextIndent)); + builder.append(nextIndent); + builder.append("binary_operator: "); + builder.append('"').append(asString(this.binary_operator)).append('"'); + builder.append('\n'); + return builder.toString(); + } + } + + /** + *
+     * Represents the use of the `||=` operator for assignment to a global variable.
+     *
+     *     $target ||= value
+     *     ^^^^^^^^^^^^^^^^^
+     * 
+ */ + public static final class GlobalVariableOrWriteNode extends Node { + public final byte[] name; + public final Node value; + + public GlobalVariableOrWriteNode(int startOffset, int length, byte[] name, Node value) { + super(startOffset, length); + this.name = name; + this.value = value; + } + + public void visitChildNodes(AbstractNodeVisitor visitor) { + this.value.accept(visitor); + } + + public Node[] childNodes() { + return new Node[] { this.value }; + } + + public T accept(AbstractNodeVisitor visitor) { + return visitor.visitGlobalVariableOrWriteNode(this); + } + + @Override + protected String toString(String indent) { + StringBuilder builder = new StringBuilder(); + builder.append(this.getClass().getSimpleName()); + if (hasNewLineFlag()) { + builder.append("[Li]"); + } + builder.append('\n'); + String nextIndent = indent + " "; + builder.append(nextIndent); + builder.append("name: "); + builder.append('"').append(asString(this.name)).append('"'); + builder.append('\n'); + builder.append(nextIndent); + builder.append("value: "); + builder.append(this.value.toString(nextIndent)); + return builder.toString(); + } + } + + /** + *
+     * Represents referencing a global variable.
+     *
+     *     $foo
+     *     ^^^^
+     * 
+ */ + public static final class GlobalVariableReadNode extends Node { + /** + *
+         * 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`
+         * 
+ */ + public final byte[] name; + + public GlobalVariableReadNode(int startOffset, int length, byte[] name) { + super(startOffset, length); + this.name = name; + } + + public void visitChildNodes(AbstractNodeVisitor visitor) { + } + + public Node[] childNodes() { + return EMPTY_ARRAY; + } + + public T accept(AbstractNodeVisitor visitor) { + return visitor.visitGlobalVariableReadNode(this); + } + + @Override + protected String toString(String indent) { + StringBuilder builder = new StringBuilder(); + builder.append(this.getClass().getSimpleName()); + if (hasNewLineFlag()) { + builder.append("[Li]"); + } + builder.append('\n'); + String nextIndent = indent + " "; + builder.append(nextIndent); + builder.append("name: "); + builder.append('"').append(asString(this.name)).append('"'); + builder.append('\n'); + return builder.toString(); + } + } + + /** + *
+     * Represents writing to a global variable in a context that doesn't have an explicit value.
+     *
+     *     $foo, $bar = baz
+     *     ^^^^  ^^^^
+     * 
+ */ + public static final class GlobalVariableTargetNode extends Node { + public final byte[] name; + + public GlobalVariableTargetNode(int startOffset, int length, byte[] name) { + super(startOffset, length); + this.name = name; + } + + public void visitChildNodes(AbstractNodeVisitor visitor) { + } + + public Node[] childNodes() { + return EMPTY_ARRAY; + } + + public T accept(AbstractNodeVisitor visitor) { + return visitor.visitGlobalVariableTargetNode(this); + } + + @Override + protected String toString(String indent) { + StringBuilder builder = new StringBuilder(); + builder.append(this.getClass().getSimpleName()); + if (hasNewLineFlag()) { + builder.append("[Li]"); + } + builder.append('\n'); + String nextIndent = indent + " "; + builder.append(nextIndent); + builder.append("name: "); + builder.append('"').append(asString(this.name)).append('"'); + builder.append('\n'); + return builder.toString(); + } + } + + /** + *
+     * Represents writing to a global variable.
+     *
+     *     $foo = 1
+     *     ^^^^^^^^
+     * 
+ */ + public static final class GlobalVariableWriteNode extends Node { + /** + *
+         * 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`
+         * 
+ */ + public final byte[] name; + /** + *
+         * 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
+         *             ^^^
+         * 
+ */ + public final Node value; + + public GlobalVariableWriteNode(int startOffset, int length, byte[] name, Node value) { + super(startOffset, length); + this.name = name; + this.value = value; + } + + public void visitChildNodes(AbstractNodeVisitor visitor) { + this.value.accept(visitor); + } + + public Node[] childNodes() { + return new Node[] { this.value }; + } + + public T accept(AbstractNodeVisitor visitor) { + return visitor.visitGlobalVariableWriteNode(this); + } + + @Override + protected String toString(String indent) { + StringBuilder builder = new StringBuilder(); + builder.append(this.getClass().getSimpleName()); + if (hasNewLineFlag()) { + builder.append("[Li]"); + } + builder.append('\n'); + String nextIndent = indent + " "; + builder.append(nextIndent); + builder.append("name: "); + builder.append('"').append(asString(this.name)).append('"'); + builder.append('\n'); + builder.append(nextIndent); + builder.append("value: "); + builder.append(this.value.toString(nextIndent)); + return builder.toString(); + } + } + + /** + *
+     * Represents a hash literal.
+     *
+     *     { a => b }
+     *     ^^^^^^^^^^
+     * 
+ */ + public static final class HashNode extends Node { + /** + *
+         * The elements of the hash. These can be either `AssocNode`s or `AssocSplatNode`s.
+         *
+         *     { a: b }
+         *       ^^^^
+         *
+         *     { **foo }
+         *       ^^^^^
+         * 
+ */ + @UnionType({ AssocNode.class, AssocSplatNode.class }) + public final Node[] elements; + + public HashNode(int startOffset, int length, Node[] elements) { + super(startOffset, length); + this.elements = elements; + } + + public void visitChildNodes(AbstractNodeVisitor visitor) { + for (Nodes.Node child : this.elements) { + child.accept(visitor); + } + } + + public Node[] childNodes() { + ArrayList childNodes = new ArrayList<>(); + childNodes.addAll(Arrays.asList(this.elements)); + return childNodes.toArray(EMPTY_ARRAY); + } + + public T accept(AbstractNodeVisitor visitor) { + return visitor.visitHashNode(this); + } + + @Override + protected String toString(String indent) { + StringBuilder builder = new StringBuilder(); + builder.append(this.getClass().getSimpleName()); + if (hasNewLineFlag()) { + builder.append("[Li]"); + } + builder.append('\n'); + String nextIndent = indent + " "; + String nextNextIndent = nextIndent + " "; + builder.append(nextIndent); + builder.append("elements: "); + builder.append('\n'); + for (Node child : this.elements) { + builder.append(nextNextIndent).append(child.toString(nextNextIndent)); + } + return builder.toString(); + } + } + + /** + *
+     * 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 }
+     *            ^^^^^^^^^^^^^^
+     * 
+ */ + public static final class HashPatternNode extends Node { + /** + *
+         * Represents the optional constant preceding the Hash.
+         *
+         *     foo => Bar[a: 1, b: 2]
+         *          ^^^
+         *
+         *     foo => Bar::Baz[a: 1, b: 2]
+         *          ^^^^^^^^
+         * 
+ */ + @Nullable + @UnionType({ ConstantPathNode.class, ConstantReadNode.class }) + public final Node constant; + /** + *
+         * Represents the explicit named hash keys and values.
+         *
+         *     foo => { a: 1, b:, ** }
+         *              ^^^^^^^^
+         * 
+ */ + public final AssocNode[] 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 }
+         *                        ^^^^^
+         * 
+ */ + @Nullable + @UnionType({ AssocSplatNode.class, NoKeywordsParameterNode.class }) + public final Node rest; + + public HashPatternNode(int startOffset, int length, Node constant, AssocNode[] elements, Node rest) { + super(startOffset, length); + this.constant = constant; + this.elements = elements; + this.rest = rest; + } + + public void visitChildNodes(AbstractNodeVisitor visitor) { + if (this.constant != null) { + this.constant.accept(visitor); + } + for (Nodes.Node child : this.elements) { + child.accept(visitor); + } + if (this.rest != null) { + this.rest.accept(visitor); + } + } + + public Node[] childNodes() { + ArrayList childNodes = new ArrayList<>(); + childNodes.add(this.constant); + childNodes.addAll(Arrays.asList(this.elements)); + childNodes.add(this.rest); + return childNodes.toArray(EMPTY_ARRAY); + } + + public T accept(AbstractNodeVisitor visitor) { + return visitor.visitHashPatternNode(this); + } + + @Override + protected String toString(String indent) { + StringBuilder builder = new StringBuilder(); + builder.append(this.getClass().getSimpleName()); + if (hasNewLineFlag()) { + builder.append("[Li]"); + } + builder.append('\n'); + String nextIndent = indent + " "; + String nextNextIndent = nextIndent + " "; + builder.append(nextIndent); + builder.append("constant: "); + builder.append(this.constant == null ? "null\n" : this.constant.toString(nextIndent)); + builder.append(nextIndent); + builder.append("elements: "); + builder.append('\n'); + for (Node child : this.elements) { + builder.append(nextNextIndent).append(child.toString(nextNextIndent)); + } + builder.append(nextIndent); + builder.append("rest: "); + builder.append(this.rest == null ? "null\n" : this.rest.toString(nextIndent)); + return builder.toString(); + } + } + + /** + *
+     * 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
+     *     ^^^^^^^^^^^^^^^
+     * 
+ */ + public static final class IfNode extends Node { + /** + *
+         * The node for the condition the `IfNode` is testing.
+         *
+         *     if foo
+         *        ^^^
+         *       bar
+         *     end
+         *
+         *     bar if foo
+         *            ^^^
+         *
+         *     foo ? bar : baz
+         *     ^^^
+         * 
+ */ + public final Node predicate; + /** + *
+         * 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
+         * 
+ */ + @Nullable + public final StatementsNode 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
+         *                     ^^^^^^^^^^^^
+         * 
+ */ + @Nullable + @UnionType({ ElseNode.class, IfNode.class }) + public final Node subsequent; + + public IfNode(int startOffset, int length, Node predicate, StatementsNode statements, Node subsequent) { + super(startOffset, length); + this.predicate = predicate; + this.statements = statements; + this.subsequent = subsequent; + } + + @Override + public void setNewLineFlag(Source source, boolean[] newlineMarked) { + this.predicate.setNewLineFlag(source, newlineMarked); + } + + public void visitChildNodes(AbstractNodeVisitor visitor) { + this.predicate.accept(visitor); + if (this.statements != null) { + this.statements.accept(visitor); + } + if (this.subsequent != null) { + this.subsequent.accept(visitor); + } + } + + public Node[] childNodes() { + return new Node[] { this.predicate, this.statements, this.subsequent }; + } + + public T accept(AbstractNodeVisitor visitor) { + return visitor.visitIfNode(this); + } + + @Override + protected String toString(String indent) { + StringBuilder builder = new StringBuilder(); + builder.append(this.getClass().getSimpleName()); + if (hasNewLineFlag()) { + builder.append("[Li]"); + } + builder.append('\n'); + String nextIndent = indent + " "; + builder.append(nextIndent); + builder.append("predicate: "); + builder.append(this.predicate.toString(nextIndent)); + builder.append(nextIndent); + builder.append("statements: "); + builder.append(this.statements == null ? "null\n" : this.statements.toString(nextIndent)); + builder.append(nextIndent); + builder.append("subsequent: "); + builder.append(this.subsequent == null ? "null\n" : this.subsequent.toString(nextIndent)); + return builder.toString(); + } + } + + /** + *
+     * Represents an imaginary number literal.
+     *
+     *     1.0i
+     *     ^^^^
+     * 
+ */ + public static final class ImaginaryNode extends Node { + @UnionType({ FloatNode.class, IntegerNode.class, RationalNode.class }) + public final Node numeric; + + public ImaginaryNode(int startOffset, int length, Node numeric) { + super(startOffset, length); + this.numeric = numeric; + } + + public void visitChildNodes(AbstractNodeVisitor visitor) { + this.numeric.accept(visitor); + } + + public Node[] childNodes() { + return new Node[] { this.numeric }; + } + + public T accept(AbstractNodeVisitor visitor) { + return visitor.visitImaginaryNode(this); + } + + @Override + protected String toString(String indent) { + StringBuilder builder = new StringBuilder(); + builder.append(this.getClass().getSimpleName()); + if (hasNewLineFlag()) { + builder.append("[Li]"); + } + builder.append('\n'); + String nextIndent = indent + " "; + builder.append(nextIndent); + builder.append("numeric: "); + builder.append(this.numeric.toString(nextIndent)); + return builder.toString(); + } + } + + /** + *
+     * 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: }
+     *              ^^^^
+     * 
+ */ + public static final class ImplicitNode extends Node { + @UnionType({ LocalVariableReadNode.class, CallNode.class, ConstantReadNode.class, LocalVariableTargetNode.class }) + public final Node value; + + public ImplicitNode(int startOffset, int length, Node value) { + super(startOffset, length); + this.value = value; + } + + public void visitChildNodes(AbstractNodeVisitor visitor) { + this.value.accept(visitor); + } + + public Node[] childNodes() { + return new Node[] { this.value }; + } + + public T accept(AbstractNodeVisitor visitor) { + return visitor.visitImplicitNode(this); + } + + @Override + protected String toString(String indent) { + StringBuilder builder = new StringBuilder(); + builder.append(this.getClass().getSimpleName()); + if (hasNewLineFlag()) { + builder.append("[Li]"); + } + builder.append('\n'); + String nextIndent = indent + " "; + builder.append(nextIndent); + builder.append("value: "); + builder.append(this.value.toString(nextIndent)); + return builder.toString(); + } + } + + /** + *
+     * Represents using a trailing comma to indicate an implicit rest parameter.
+     *
+     *     foo { |bar,| }
+     *               ^
+     *
+     *     foo in [bar,]
+     *                ^
+     *
+     *     for foo, in bar do end
+     *            ^
+     *
+     *     foo, = bar
+     *        ^
+     * 
+ */ + public static final class ImplicitRestNode extends Node { + + public ImplicitRestNode(int startOffset, int length) { + super(startOffset, length); + } + + public void visitChildNodes(AbstractNodeVisitor visitor) { + } + + public Node[] childNodes() { + return EMPTY_ARRAY; + } + + public T accept(AbstractNodeVisitor visitor) { + return visitor.visitImplicitRestNode(this); + } + + @Override + protected String toString(String indent) { + StringBuilder builder = new StringBuilder(); + builder.append(this.getClass().getSimpleName()); + if (hasNewLineFlag()) { + builder.append("[Li]"); + } + builder.append('\n'); + String nextIndent = indent + " "; + return builder.toString(); + } + } + + /** + *
+     * Represents the use of the `in` keyword in a case statement.
+     *
+     *     case a; in b then c end
+     *             ^^^^^^^^^^^
+     * 
+ */ + public static final class InNode extends Node { + public final Node pattern; + @Nullable + public final StatementsNode statements; + + public InNode(int startOffset, int length, Node pattern, StatementsNode statements) { + super(startOffset, length); + this.pattern = pattern; + this.statements = statements; + } + + public void visitChildNodes(AbstractNodeVisitor visitor) { + this.pattern.accept(visitor); + if (this.statements != null) { + this.statements.accept(visitor); + } + } + + public Node[] childNodes() { + return new Node[] { this.pattern, this.statements }; + } + + public T accept(AbstractNodeVisitor visitor) { + return visitor.visitInNode(this); + } + + @Override + protected String toString(String indent) { + StringBuilder builder = new StringBuilder(); + builder.append(this.getClass().getSimpleName()); + if (hasNewLineFlag()) { + builder.append("[Li]"); + } + builder.append('\n'); + String nextIndent = indent + " "; + builder.append(nextIndent); + builder.append("pattern: "); + builder.append(this.pattern.toString(nextIndent)); + builder.append(nextIndent); + builder.append("statements: "); + builder.append(this.statements == null ? "null\n" : this.statements.toString(nextIndent)); + return builder.toString(); + } + } + + /** + *
+     * Represents the use of the `&&=` operator on a call to the `[]` method.
+     *
+     *     foo.bar[baz] &&= value
+     *     ^^^^^^^^^^^^^^^^^^^^^^
+     * 
+ */ + public static final class IndexAndWriteNode extends Node { + public final short flags; + @Nullable + public final Node receiver; + @Nullable + public final ArgumentsNode arguments; + @Nullable + public final BlockArgumentNode block; + public final Node value; + + public IndexAndWriteNode(int startOffset, int length, short flags, Node receiver, ArgumentsNode arguments, BlockArgumentNode block, Node value) { + super(startOffset, length); + this.flags = flags; + this.receiver = receiver; + this.arguments = arguments; + this.block = block; + this.value = value; + } + + public boolean isSafeNavigation() { + return CallNodeFlags.isSafeNavigation(flags); + } + + public boolean isVariableCall() { + return CallNodeFlags.isVariableCall(flags); + } + + public boolean isAttributeWrite() { + return CallNodeFlags.isAttributeWrite(flags); + } + + public boolean isIgnoreVisibility() { + return CallNodeFlags.isIgnoreVisibility(flags); + } + + public void visitChildNodes(AbstractNodeVisitor visitor) { + if (this.receiver != null) { + this.receiver.accept(visitor); + } + if (this.arguments != null) { + this.arguments.accept(visitor); + } + if (this.block != null) { + this.block.accept(visitor); + } + this.value.accept(visitor); + } + + public Node[] childNodes() { + return new Node[] { this.receiver, this.arguments, this.block, this.value }; + } + + public T accept(AbstractNodeVisitor visitor) { + return visitor.visitIndexAndWriteNode(this); + } + + @Override + protected String toString(String indent) { + StringBuilder builder = new StringBuilder(); + builder.append(this.getClass().getSimpleName()); + if (hasNewLineFlag()) { + builder.append("[Li]"); + } + builder.append('\n'); + String nextIndent = indent + " "; + builder.append(nextIndent); + builder.append("CallNodeFlags: "); + builder.append(flags); + builder.append('\n'); + builder.append(nextIndent); + builder.append("receiver: "); + builder.append(this.receiver == null ? "null\n" : this.receiver.toString(nextIndent)); + builder.append(nextIndent); + builder.append("arguments: "); + builder.append(this.arguments == null ? "null\n" : this.arguments.toString(nextIndent)); + builder.append(nextIndent); + builder.append("block: "); + builder.append(this.block == null ? "null\n" : this.block.toString(nextIndent)); + builder.append(nextIndent); + builder.append("value: "); + builder.append(this.value.toString(nextIndent)); + return builder.toString(); + } + } + + /** + *
+     * Represents the use of an assignment operator on a call to `[]`.
+     *
+     *     foo.bar[baz] += value
+     *     ^^^^^^^^^^^^^^^^^^^^^
+     * 
+ */ + public static final class IndexOperatorWriteNode extends Node { + public final short flags; + @Nullable + public final Node receiver; + @Nullable + public final ArgumentsNode arguments; + @Nullable + public final BlockArgumentNode block; + public final byte[] binary_operator; + public final Node value; + + public IndexOperatorWriteNode(int startOffset, int length, short flags, Node receiver, ArgumentsNode arguments, BlockArgumentNode block, byte[] binary_operator, Node value) { + super(startOffset, length); + this.flags = flags; + this.receiver = receiver; + this.arguments = arguments; + this.block = block; + this.binary_operator = binary_operator; + this.value = value; + } + + public boolean isSafeNavigation() { + return CallNodeFlags.isSafeNavigation(flags); + } + + public boolean isVariableCall() { + return CallNodeFlags.isVariableCall(flags); + } + + public boolean isAttributeWrite() { + return CallNodeFlags.isAttributeWrite(flags); + } + + public boolean isIgnoreVisibility() { + return CallNodeFlags.isIgnoreVisibility(flags); + } + + public void visitChildNodes(AbstractNodeVisitor visitor) { + if (this.receiver != null) { + this.receiver.accept(visitor); + } + if (this.arguments != null) { + this.arguments.accept(visitor); + } + if (this.block != null) { + this.block.accept(visitor); + } + this.value.accept(visitor); + } + + public Node[] childNodes() { + return new Node[] { this.receiver, this.arguments, this.block, this.value }; + } + + public T accept(AbstractNodeVisitor visitor) { + return visitor.visitIndexOperatorWriteNode(this); + } + + @Override + protected String toString(String indent) { + StringBuilder builder = new StringBuilder(); + builder.append(this.getClass().getSimpleName()); + if (hasNewLineFlag()) { + builder.append("[Li]"); + } + builder.append('\n'); + String nextIndent = indent + " "; + builder.append(nextIndent); + builder.append("CallNodeFlags: "); + builder.append(flags); + builder.append('\n'); + builder.append(nextIndent); + builder.append("receiver: "); + builder.append(this.receiver == null ? "null\n" : this.receiver.toString(nextIndent)); + builder.append(nextIndent); + builder.append("arguments: "); + builder.append(this.arguments == null ? "null\n" : this.arguments.toString(nextIndent)); + builder.append(nextIndent); + builder.append("block: "); + builder.append(this.block == null ? "null\n" : this.block.toString(nextIndent)); + builder.append(nextIndent); + builder.append("binary_operator: "); + builder.append('"').append(asString(this.binary_operator)).append('"'); + builder.append('\n'); + builder.append(nextIndent); + builder.append("value: "); + builder.append(this.value.toString(nextIndent)); + return builder.toString(); + } + } + + /** + *
+     * Represents the use of the `||=` operator on a call to `[]`.
+     *
+     *     foo.bar[baz] ||= value
+     *     ^^^^^^^^^^^^^^^^^^^^^^
+     * 
+ */ + public static final class IndexOrWriteNode extends Node { + public final short flags; + @Nullable + public final Node receiver; + @Nullable + public final ArgumentsNode arguments; + @Nullable + public final BlockArgumentNode block; + public final Node value; + + public IndexOrWriteNode(int startOffset, int length, short flags, Node receiver, ArgumentsNode arguments, BlockArgumentNode block, Node value) { + super(startOffset, length); + this.flags = flags; + this.receiver = receiver; + this.arguments = arguments; + this.block = block; + this.value = value; + } + + public boolean isSafeNavigation() { + return CallNodeFlags.isSafeNavigation(flags); + } + + public boolean isVariableCall() { + return CallNodeFlags.isVariableCall(flags); + } + + public boolean isAttributeWrite() { + return CallNodeFlags.isAttributeWrite(flags); + } + + public boolean isIgnoreVisibility() { + return CallNodeFlags.isIgnoreVisibility(flags); + } + + public void visitChildNodes(AbstractNodeVisitor visitor) { + if (this.receiver != null) { + this.receiver.accept(visitor); + } + if (this.arguments != null) { + this.arguments.accept(visitor); + } + if (this.block != null) { + this.block.accept(visitor); + } + this.value.accept(visitor); + } + + public Node[] childNodes() { + return new Node[] { this.receiver, this.arguments, this.block, this.value }; + } + + public T accept(AbstractNodeVisitor visitor) { + return visitor.visitIndexOrWriteNode(this); + } + + @Override + protected String toString(String indent) { + StringBuilder builder = new StringBuilder(); + builder.append(this.getClass().getSimpleName()); + if (hasNewLineFlag()) { + builder.append("[Li]"); + } + builder.append('\n'); + String nextIndent = indent + " "; + builder.append(nextIndent); + builder.append("CallNodeFlags: "); + builder.append(flags); + builder.append('\n'); + builder.append(nextIndent); + builder.append("receiver: "); + builder.append(this.receiver == null ? "null\n" : this.receiver.toString(nextIndent)); + builder.append(nextIndent); + builder.append("arguments: "); + builder.append(this.arguments == null ? "null\n" : this.arguments.toString(nextIndent)); + builder.append(nextIndent); + builder.append("block: "); + builder.append(this.block == null ? "null\n" : this.block.toString(nextIndent)); + builder.append(nextIndent); + builder.append("value: "); + builder.append(this.value.toString(nextIndent)); + return builder.toString(); + } + } + + /** + *
+     * Represents assigning to an index.
+     *
+     *     foo[bar], = 1
+     *     ^^^^^^^^
+     *
+     *     begin
+     *     rescue => foo[bar]
+     *               ^^^^^^^^
+     *     end
+     *
+     *     for foo[bar] in baz do end
+     *         ^^^^^^^^
+     * 
+ */ + public static final class IndexTargetNode extends Node { + public final short flags; + public final Node receiver; + @Nullable + public final ArgumentsNode arguments; + @Nullable + public final BlockArgumentNode block; + + public IndexTargetNode(int startOffset, int length, short flags, Node receiver, ArgumentsNode arguments, BlockArgumentNode block) { + super(startOffset, length); + this.flags = flags; + this.receiver = receiver; + this.arguments = arguments; + this.block = block; + } + + public boolean isSafeNavigation() { + return CallNodeFlags.isSafeNavigation(flags); + } + + public boolean isVariableCall() { + return CallNodeFlags.isVariableCall(flags); + } + + public boolean isAttributeWrite() { + return CallNodeFlags.isAttributeWrite(flags); + } + + public boolean isIgnoreVisibility() { + return CallNodeFlags.isIgnoreVisibility(flags); + } + + public void visitChildNodes(AbstractNodeVisitor visitor) { + this.receiver.accept(visitor); + if (this.arguments != null) { + this.arguments.accept(visitor); + } + if (this.block != null) { + this.block.accept(visitor); + } + } + + public Node[] childNodes() { + return new Node[] { this.receiver, this.arguments, this.block }; + } + + public T accept(AbstractNodeVisitor visitor) { + return visitor.visitIndexTargetNode(this); + } + + @Override + protected String toString(String indent) { + StringBuilder builder = new StringBuilder(); + builder.append(this.getClass().getSimpleName()); + if (hasNewLineFlag()) { + builder.append("[Li]"); + } + builder.append('\n'); + String nextIndent = indent + " "; + builder.append(nextIndent); + builder.append("CallNodeFlags: "); + builder.append(flags); + builder.append('\n'); + builder.append(nextIndent); + builder.append("receiver: "); + builder.append(this.receiver.toString(nextIndent)); + builder.append(nextIndent); + builder.append("arguments: "); + builder.append(this.arguments == null ? "null\n" : this.arguments.toString(nextIndent)); + builder.append(nextIndent); + builder.append("block: "); + builder.append(this.block == null ? "null\n" : this.block.toString(nextIndent)); + return builder.toString(); + } + } + + /** + *
+     * Represents the use of the `&&=` operator for assignment to an instance variable.
+     *
+     *     @target &&= value
+     *     ^^^^^^^^^^^^^^^^^
+     * 
+ */ + public static final class InstanceVariableAndWriteNode extends Node { + public final byte[] name; + public final Node value; + + public InstanceVariableAndWriteNode(int startOffset, int length, byte[] name, Node value) { + super(startOffset, length); + this.name = name; + this.value = value; + } + + public void visitChildNodes(AbstractNodeVisitor visitor) { + this.value.accept(visitor); + } + + public Node[] childNodes() { + return new Node[] { this.value }; + } + + public T accept(AbstractNodeVisitor visitor) { + return visitor.visitInstanceVariableAndWriteNode(this); + } + + @Override + protected String toString(String indent) { + StringBuilder builder = new StringBuilder(); + builder.append(this.getClass().getSimpleName()); + if (hasNewLineFlag()) { + builder.append("[Li]"); + } + builder.append('\n'); + String nextIndent = indent + " "; + builder.append(nextIndent); + builder.append("name: "); + builder.append('"').append(asString(this.name)).append('"'); + builder.append('\n'); + builder.append(nextIndent); + builder.append("value: "); + builder.append(this.value.toString(nextIndent)); + return builder.toString(); + } + } + + /** + *
+     * Represents assigning to an instance variable using an operator that isn't `=`.
+     *
+     *     @target += value
+     *     ^^^^^^^^^^^^^^^^
+     * 
+ */ + public static final class InstanceVariableOperatorWriteNode extends Node { + public final byte[] name; + public final Node value; + public final byte[] binary_operator; + + public InstanceVariableOperatorWriteNode(int startOffset, int length, byte[] name, Node value, byte[] binary_operator) { + super(startOffset, length); + this.name = name; + this.value = value; + this.binary_operator = binary_operator; + } + + public void visitChildNodes(AbstractNodeVisitor visitor) { + this.value.accept(visitor); + } + + public Node[] childNodes() { + return new Node[] { this.value }; + } + + public T accept(AbstractNodeVisitor visitor) { + return visitor.visitInstanceVariableOperatorWriteNode(this); + } + + @Override + protected String toString(String indent) { + StringBuilder builder = new StringBuilder(); + builder.append(this.getClass().getSimpleName()); + if (hasNewLineFlag()) { + builder.append("[Li]"); + } + builder.append('\n'); + String nextIndent = indent + " "; + builder.append(nextIndent); + builder.append("name: "); + builder.append('"').append(asString(this.name)).append('"'); + builder.append('\n'); + builder.append(nextIndent); + builder.append("value: "); + builder.append(this.value.toString(nextIndent)); + builder.append(nextIndent); + builder.append("binary_operator: "); + builder.append('"').append(asString(this.binary_operator)).append('"'); + builder.append('\n'); + return builder.toString(); + } + } + + /** + *
+     * Represents the use of the `||=` operator for assignment to an instance variable.
+     *
+     *     @target ||= value
+     *     ^^^^^^^^^^^^^^^^^
+     * 
+ */ + public static final class InstanceVariableOrWriteNode extends Node { + public final byte[] name; + public final Node value; + + public InstanceVariableOrWriteNode(int startOffset, int length, byte[] name, Node value) { + super(startOffset, length); + this.name = name; + this.value = value; + } + + public void visitChildNodes(AbstractNodeVisitor visitor) { + this.value.accept(visitor); + } + + public Node[] childNodes() { + return new Node[] { this.value }; + } + + public T accept(AbstractNodeVisitor visitor) { + return visitor.visitInstanceVariableOrWriteNode(this); + } + + @Override + protected String toString(String indent) { + StringBuilder builder = new StringBuilder(); + builder.append(this.getClass().getSimpleName()); + if (hasNewLineFlag()) { + builder.append("[Li]"); + } + builder.append('\n'); + String nextIndent = indent + " "; + builder.append(nextIndent); + builder.append("name: "); + builder.append('"').append(asString(this.name)).append('"'); + builder.append('\n'); + builder.append(nextIndent); + builder.append("value: "); + builder.append(this.value.toString(nextIndent)); + return builder.toString(); + } + } + + /** + *
+     * Represents referencing an instance variable.
+     *
+     *     @foo
+     *     ^^^^
+     * 
+ */ + public static final class InstanceVariableReadNode extends Node { + /** + *
+         * 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`
+         * 
+ */ + public final byte[] name; + + public InstanceVariableReadNode(int startOffset, int length, byte[] name) { + super(startOffset, length); + this.name = name; + } + + public void visitChildNodes(AbstractNodeVisitor visitor) { + } + + public Node[] childNodes() { + return EMPTY_ARRAY; + } + + public T accept(AbstractNodeVisitor visitor) { + return visitor.visitInstanceVariableReadNode(this); + } + + @Override + protected String toString(String indent) { + StringBuilder builder = new StringBuilder(); + builder.append(this.getClass().getSimpleName()); + if (hasNewLineFlag()) { + builder.append("[Li]"); + } + builder.append('\n'); + String nextIndent = indent + " "; + builder.append(nextIndent); + builder.append("name: "); + builder.append('"').append(asString(this.name)).append('"'); + builder.append('\n'); + return builder.toString(); + } + } + + /** + *
+     * Represents writing to an instance variable in a context that doesn't have an explicit value.
+     *
+     *     @foo, @bar = baz
+     *     ^^^^  ^^^^
+     * 
+ */ + public static final class InstanceVariableTargetNode extends Node { + public final byte[] name; + + public InstanceVariableTargetNode(int startOffset, int length, byte[] name) { + super(startOffset, length); + this.name = name; + } + + public void visitChildNodes(AbstractNodeVisitor visitor) { + } + + public Node[] childNodes() { + return EMPTY_ARRAY; + } + + public T accept(AbstractNodeVisitor visitor) { + return visitor.visitInstanceVariableTargetNode(this); + } + + @Override + protected String toString(String indent) { + StringBuilder builder = new StringBuilder(); + builder.append(this.getClass().getSimpleName()); + if (hasNewLineFlag()) { + builder.append("[Li]"); + } + builder.append('\n'); + String nextIndent = indent + " "; + builder.append(nextIndent); + builder.append("name: "); + builder.append('"').append(asString(this.name)).append('"'); + builder.append('\n'); + return builder.toString(); + } + } + + /** + *
+     * Represents writing to an instance variable.
+     *
+     *     @foo = 1
+     *     ^^^^^^^^
+     * 
+ */ + public static final class InstanceVariableWriteNode extends Node { + /** + *
+         * 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`
+         * 
+ */ + public final byte[] name; + /** + *
+         * 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
+         *           ^^^^
+         * 
+ */ + public final Node value; + + public InstanceVariableWriteNode(int startOffset, int length, byte[] name, Node value) { + super(startOffset, length); + this.name = name; + this.value = value; + } + + public void visitChildNodes(AbstractNodeVisitor visitor) { + this.value.accept(visitor); + } + + public Node[] childNodes() { + return new Node[] { this.value }; + } + + public T accept(AbstractNodeVisitor visitor) { + return visitor.visitInstanceVariableWriteNode(this); + } + + @Override + protected String toString(String indent) { + StringBuilder builder = new StringBuilder(); + builder.append(this.getClass().getSimpleName()); + if (hasNewLineFlag()) { + builder.append("[Li]"); + } + builder.append('\n'); + String nextIndent = indent + " "; + builder.append(nextIndent); + builder.append("name: "); + builder.append('"').append(asString(this.name)).append('"'); + builder.append('\n'); + builder.append(nextIndent); + builder.append("value: "); + builder.append(this.value.toString(nextIndent)); + return builder.toString(); + } + } + + /** + *
+     * Represents an integer number literal.
+     *
+     *     1
+     *     ^
+     * 
+ */ + public static final class IntegerNode extends Node { + public final short flags; + /** + *
+         * The value of the integer literal as a number.
+         * 
+ */ + public final Object value; + + public IntegerNode(int startOffset, int length, short flags, Object value) { + super(startOffset, length); + this.flags = flags; + this.value = value; + } + + public boolean isBinary() { + return IntegerBaseFlags.isBinary(flags); + } + + public boolean isDecimal() { + return IntegerBaseFlags.isDecimal(flags); + } + + public boolean isOctal() { + return IntegerBaseFlags.isOctal(flags); + } + + public boolean isHexadecimal() { + return IntegerBaseFlags.isHexadecimal(flags); + } + + public void visitChildNodes(AbstractNodeVisitor visitor) { + } + + public Node[] childNodes() { + return EMPTY_ARRAY; + } + + public T accept(AbstractNodeVisitor visitor) { + return visitor.visitIntegerNode(this); + } + + @Override + protected String toString(String indent) { + StringBuilder builder = new StringBuilder(); + builder.append(this.getClass().getSimpleName()); + if (hasNewLineFlag()) { + builder.append("[Li]"); + } + builder.append('\n'); + String nextIndent = indent + " "; + builder.append(nextIndent); + builder.append("IntegerBaseFlags: "); + builder.append(flags); + builder.append('\n'); + builder.append(nextIndent); + builder.append("value: "); + builder.append(this.value); + builder.append('\n'); + return builder.toString(); + } + } + + /** + *
+     * 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
+     *        ^^^^^^^^^^^^^^^^
+     * 
+ */ + public static final class InterpolatedMatchLastLineNode extends Node { + public final short flags; + @UnionType({ StringNode.class, EmbeddedStatementsNode.class, EmbeddedVariableNode.class }) + public final Node[] parts; + + public InterpolatedMatchLastLineNode(int startOffset, int length, short flags, Node[] parts) { + super(startOffset, length); + this.flags = flags; + this.parts = parts; + } + + public boolean isIgnoreCase() { + return RegularExpressionFlags.isIgnoreCase(flags); + } + + public boolean isExtended() { + return RegularExpressionFlags.isExtended(flags); + } + + public boolean isMultiLine() { + return RegularExpressionFlags.isMultiLine(flags); + } + + public boolean isOnce() { + return RegularExpressionFlags.isOnce(flags); + } + + public boolean isEucJp() { + return RegularExpressionFlags.isEucJp(flags); + } + + public boolean isAscii8bit() { + return RegularExpressionFlags.isAscii8bit(flags); + } + + public boolean isWindows31j() { + return RegularExpressionFlags.isWindows31j(flags); + } + + public boolean isUtf8() { + return RegularExpressionFlags.isUtf8(flags); + } + + public boolean isForcedUtf8Encoding() { + return RegularExpressionFlags.isForcedUtf8Encoding(flags); + } + + public boolean isForcedBinaryEncoding() { + return RegularExpressionFlags.isForcedBinaryEncoding(flags); + } + + public boolean isForcedUsAsciiEncoding() { + return RegularExpressionFlags.isForcedUsAsciiEncoding(flags); + } + + @Override + public void setNewLineFlag(Source source, boolean[] newlineMarked) { + Node first = this.parts.length > 0 ? this.parts[0] : null; + if (first != null) { + first.setNewLineFlag(source, newlineMarked); + } + } + + public void visitChildNodes(AbstractNodeVisitor visitor) { + for (Nodes.Node child : this.parts) { + child.accept(visitor); + } + } + + public Node[] childNodes() { + ArrayList childNodes = new ArrayList<>(); + childNodes.addAll(Arrays.asList(this.parts)); + return childNodes.toArray(EMPTY_ARRAY); + } + + public T accept(AbstractNodeVisitor visitor) { + return visitor.visitInterpolatedMatchLastLineNode(this); + } + + @Override + protected String toString(String indent) { + StringBuilder builder = new StringBuilder(); + builder.append(this.getClass().getSimpleName()); + if (hasNewLineFlag()) { + builder.append("[Li]"); + } + builder.append('\n'); + String nextIndent = indent + " "; + String nextNextIndent = nextIndent + " "; + builder.append(nextIndent); + builder.append("RegularExpressionFlags: "); + builder.append(flags); + builder.append('\n'); + builder.append(nextIndent); + builder.append("parts: "); + builder.append('\n'); + for (Node child : this.parts) { + builder.append(nextNextIndent).append(child.toString(nextNextIndent)); + } + return builder.toString(); + } + } + + /** + *
+     * Represents a regular expression literal that contains interpolation.
+     *
+     *     /foo #{bar} baz/
+     *     ^^^^^^^^^^^^^^^^
+     * 
+ */ + public static final class InterpolatedRegularExpressionNode extends Node { + public final short flags; + @UnionType({ StringNode.class, EmbeddedStatementsNode.class, EmbeddedVariableNode.class }) + public final Node[] parts; + + public InterpolatedRegularExpressionNode(int startOffset, int length, short flags, Node[] parts) { + super(startOffset, length); + this.flags = flags; + this.parts = parts; + } + + public boolean isIgnoreCase() { + return RegularExpressionFlags.isIgnoreCase(flags); + } + + public boolean isExtended() { + return RegularExpressionFlags.isExtended(flags); + } + + public boolean isMultiLine() { + return RegularExpressionFlags.isMultiLine(flags); + } + + public boolean isOnce() { + return RegularExpressionFlags.isOnce(flags); + } + + public boolean isEucJp() { + return RegularExpressionFlags.isEucJp(flags); + } + + public boolean isAscii8bit() { + return RegularExpressionFlags.isAscii8bit(flags); + } + + public boolean isWindows31j() { + return RegularExpressionFlags.isWindows31j(flags); + } + + public boolean isUtf8() { + return RegularExpressionFlags.isUtf8(flags); + } + + public boolean isForcedUtf8Encoding() { + return RegularExpressionFlags.isForcedUtf8Encoding(flags); + } + + public boolean isForcedBinaryEncoding() { + return RegularExpressionFlags.isForcedBinaryEncoding(flags); + } + + public boolean isForcedUsAsciiEncoding() { + return RegularExpressionFlags.isForcedUsAsciiEncoding(flags); + } + + @Override + public void setNewLineFlag(Source source, boolean[] newlineMarked) { + Node first = this.parts.length > 0 ? this.parts[0] : null; + if (first != null) { + first.setNewLineFlag(source, newlineMarked); + } + } + + public void visitChildNodes(AbstractNodeVisitor visitor) { + for (Nodes.Node child : this.parts) { + child.accept(visitor); + } + } + + public Node[] childNodes() { + ArrayList childNodes = new ArrayList<>(); + childNodes.addAll(Arrays.asList(this.parts)); + return childNodes.toArray(EMPTY_ARRAY); + } + + public T accept(AbstractNodeVisitor visitor) { + return visitor.visitInterpolatedRegularExpressionNode(this); + } + + @Override + protected String toString(String indent) { + StringBuilder builder = new StringBuilder(); + builder.append(this.getClass().getSimpleName()); + if (hasNewLineFlag()) { + builder.append("[Li]"); + } + builder.append('\n'); + String nextIndent = indent + " "; + String nextNextIndent = nextIndent + " "; + builder.append(nextIndent); + builder.append("RegularExpressionFlags: "); + builder.append(flags); + builder.append('\n'); + builder.append(nextIndent); + builder.append("parts: "); + builder.append('\n'); + for (Node child : this.parts) { + builder.append(nextNextIndent).append(child.toString(nextNextIndent)); + } + return builder.toString(); + } + } + + /** + *
+     * Represents a string literal that contains interpolation.
+     *
+     *     "foo #{bar} baz"
+     *     ^^^^^^^^^^^^^^^^
+     * 
+ */ + public static final class InterpolatedStringNode extends Node { + public final short flags; + @UnionType({ StringNode.class, EmbeddedStatementsNode.class, EmbeddedVariableNode.class, InterpolatedStringNode.class }) + public final Node[] parts; + + public InterpolatedStringNode(int startOffset, int length, short flags, Node[] parts) { + super(startOffset, length); + this.flags = flags; + this.parts = parts; + } + + public boolean isFrozen() { + return InterpolatedStringNodeFlags.isFrozen(flags); + } + + public boolean isMutable() { + return InterpolatedStringNodeFlags.isMutable(flags); + } + + @Override + public void setNewLineFlag(Source source, boolean[] newlineMarked) { + Node first = this.parts.length > 0 ? this.parts[0] : null; + if (first != null) { + first.setNewLineFlag(source, newlineMarked); + } + } + + public void visitChildNodes(AbstractNodeVisitor visitor) { + for (Nodes.Node child : this.parts) { + child.accept(visitor); + } + } + + public Node[] childNodes() { + ArrayList childNodes = new ArrayList<>(); + childNodes.addAll(Arrays.asList(this.parts)); + return childNodes.toArray(EMPTY_ARRAY); + } + + public T accept(AbstractNodeVisitor visitor) { + return visitor.visitInterpolatedStringNode(this); + } + + @Override + protected String toString(String indent) { + StringBuilder builder = new StringBuilder(); + builder.append(this.getClass().getSimpleName()); + if (hasNewLineFlag()) { + builder.append("[Li]"); + } + builder.append('\n'); + String nextIndent = indent + " "; + String nextNextIndent = nextIndent + " "; + builder.append(nextIndent); + builder.append("InterpolatedStringNodeFlags: "); + builder.append(flags); + builder.append('\n'); + builder.append(nextIndent); + builder.append("parts: "); + builder.append('\n'); + for (Node child : this.parts) { + builder.append(nextNextIndent).append(child.toString(nextNextIndent)); + } + return builder.toString(); + } + } + + /** + *
+     * Represents a symbol literal that contains interpolation.
+     *
+     *     :"foo #{bar} baz"
+     *     ^^^^^^^^^^^^^^^^^
+     * 
+ */ + public static final class InterpolatedSymbolNode extends Node { + @UnionType({ StringNode.class, EmbeddedStatementsNode.class, EmbeddedVariableNode.class }) + public final Node[] parts; + + public InterpolatedSymbolNode(int startOffset, int length, Node[] parts) { + super(startOffset, length); + this.parts = parts; + } + + @Override + public void setNewLineFlag(Source source, boolean[] newlineMarked) { + Node first = this.parts.length > 0 ? this.parts[0] : null; + if (first != null) { + first.setNewLineFlag(source, newlineMarked); + } + } + + public void visitChildNodes(AbstractNodeVisitor visitor) { + for (Nodes.Node child : this.parts) { + child.accept(visitor); + } + } + + public Node[] childNodes() { + ArrayList childNodes = new ArrayList<>(); + childNodes.addAll(Arrays.asList(this.parts)); + return childNodes.toArray(EMPTY_ARRAY); + } + + public T accept(AbstractNodeVisitor visitor) { + return visitor.visitInterpolatedSymbolNode(this); + } + + @Override + protected String toString(String indent) { + StringBuilder builder = new StringBuilder(); + builder.append(this.getClass().getSimpleName()); + if (hasNewLineFlag()) { + builder.append("[Li]"); + } + builder.append('\n'); + String nextIndent = indent + " "; + String nextNextIndent = nextIndent + " "; + builder.append(nextIndent); + builder.append("parts: "); + builder.append('\n'); + for (Node child : this.parts) { + builder.append(nextNextIndent).append(child.toString(nextNextIndent)); + } + return builder.toString(); + } + } + + /** + *
+     * Represents an xstring literal that contains interpolation.
+     *
+     *     `foo #{bar} baz`
+     *     ^^^^^^^^^^^^^^^^
+     * 
+ */ + public static final class InterpolatedXStringNode extends Node { + @UnionType({ StringNode.class, EmbeddedStatementsNode.class, EmbeddedVariableNode.class }) + public final Node[] parts; + + public InterpolatedXStringNode(int startOffset, int length, Node[] parts) { + super(startOffset, length); + this.parts = parts; + } + + @Override + public void setNewLineFlag(Source source, boolean[] newlineMarked) { + Node first = this.parts.length > 0 ? this.parts[0] : null; + if (first != null) { + first.setNewLineFlag(source, newlineMarked); + } + } + + public void visitChildNodes(AbstractNodeVisitor visitor) { + for (Nodes.Node child : this.parts) { + child.accept(visitor); + } + } + + public Node[] childNodes() { + ArrayList childNodes = new ArrayList<>(); + childNodes.addAll(Arrays.asList(this.parts)); + return childNodes.toArray(EMPTY_ARRAY); + } + + public T accept(AbstractNodeVisitor visitor) { + return visitor.visitInterpolatedXStringNode(this); + } + + @Override + protected String toString(String indent) { + StringBuilder builder = new StringBuilder(); + builder.append(this.getClass().getSimpleName()); + if (hasNewLineFlag()) { + builder.append("[Li]"); + } + builder.append('\n'); + String nextIndent = indent + " "; + String nextNextIndent = nextIndent + " "; + builder.append(nextIndent); + builder.append("parts: "); + builder.append('\n'); + for (Node child : this.parts) { + builder.append(nextNextIndent).append(child.toString(nextNextIndent)); + } + return builder.toString(); + } + } + + /** + *
+     * Represents reading from the implicit `it` local variable.
+     *
+     *     -> { it }
+     *          ^^
+     * 
+ */ + public static final class ItLocalVariableReadNode extends Node { + + public ItLocalVariableReadNode(int startOffset, int length) { + super(startOffset, length); + } + + public void visitChildNodes(AbstractNodeVisitor visitor) { + } + + public Node[] childNodes() { + return EMPTY_ARRAY; + } + + public T accept(AbstractNodeVisitor visitor) { + return visitor.visitItLocalVariableReadNode(this); + } + + @Override + protected String toString(String indent) { + StringBuilder builder = new StringBuilder(); + builder.append(this.getClass().getSimpleName()); + if (hasNewLineFlag()) { + builder.append("[Li]"); + } + builder.append('\n'); + String nextIndent = indent + " "; + return builder.toString(); + } + } + + /** + *
+     * Represents an implicit set of parameters through the use of the `it` keyword within a block or lambda.
+     *
+     *     -> { it + it }
+     *     ^^^^^^^^^^^^^^
+     * 
+ */ + public static final class ItParametersNode extends Node { + + public ItParametersNode(int startOffset, int length) { + super(startOffset, length); + } + + public void visitChildNodes(AbstractNodeVisitor visitor) { + } + + public Node[] childNodes() { + return EMPTY_ARRAY; + } + + public T accept(AbstractNodeVisitor visitor) { + return visitor.visitItParametersNode(this); + } + + @Override + protected String toString(String indent) { + StringBuilder builder = new StringBuilder(); + builder.append(this.getClass().getSimpleName()); + if (hasNewLineFlag()) { + builder.append("[Li]"); + } + builder.append('\n'); + String nextIndent = indent + " "; + return builder.toString(); + } + } + + /** + *
+     * Represents a hash literal without opening and closing braces.
+     *
+     *     foo(a: b)
+     *         ^^^^
+     * 
+ */ + public static final class KeywordHashNode extends Node { + public final short flags; + @UnionType({ AssocNode.class, AssocSplatNode.class }) + public final Node[] elements; + + public KeywordHashNode(int startOffset, int length, short flags, Node[] elements) { + super(startOffset, length); + this.flags = flags; + this.elements = elements; + } + + public boolean isSymbolKeys() { + return KeywordHashNodeFlags.isSymbolKeys(flags); + } + + public void visitChildNodes(AbstractNodeVisitor visitor) { + for (Nodes.Node child : this.elements) { + child.accept(visitor); + } + } + + public Node[] childNodes() { + ArrayList childNodes = new ArrayList<>(); + childNodes.addAll(Arrays.asList(this.elements)); + return childNodes.toArray(EMPTY_ARRAY); + } + + public T accept(AbstractNodeVisitor visitor) { + return visitor.visitKeywordHashNode(this); + } + + @Override + protected String toString(String indent) { + StringBuilder builder = new StringBuilder(); + builder.append(this.getClass().getSimpleName()); + if (hasNewLineFlag()) { + builder.append("[Li]"); + } + builder.append('\n'); + String nextIndent = indent + " "; + String nextNextIndent = nextIndent + " "; + builder.append(nextIndent); + builder.append("KeywordHashNodeFlags: "); + builder.append(flags); + builder.append('\n'); + builder.append(nextIndent); + builder.append("elements: "); + builder.append('\n'); + for (Node child : this.elements) { + builder.append(nextNextIndent).append(child.toString(nextNextIndent)); + } + return builder.toString(); + } + } + + /** + *
+     * Represents a keyword rest parameter to a method, block, or lambda definition.
+     *
+     *     def a(**b)
+     *           ^^^
+     *     end
+     * 
+ */ + public static final class KeywordRestParameterNode extends Node { + public final short flags; + @Nullable + public final byte[] name; + + public KeywordRestParameterNode(int startOffset, int length, short flags, byte[] name) { + super(startOffset, length); + this.flags = flags; + this.name = name; + } + + public boolean isRepeatedParameter() { + return ParameterFlags.isRepeatedParameter(flags); + } + + public void visitChildNodes(AbstractNodeVisitor visitor) { + } + + public Node[] childNodes() { + return EMPTY_ARRAY; + } + + public T accept(AbstractNodeVisitor visitor) { + return visitor.visitKeywordRestParameterNode(this); + } + + @Override + protected String toString(String indent) { + StringBuilder builder = new StringBuilder(); + builder.append(this.getClass().getSimpleName()); + if (hasNewLineFlag()) { + builder.append("[Li]"); + } + builder.append('\n'); + String nextIndent = indent + " "; + builder.append(nextIndent); + builder.append("ParameterFlags: "); + builder.append(flags); + builder.append('\n'); + builder.append(nextIndent); + builder.append("name: "); + builder.append(this.name == null ? "null" : "\"" + asString(this.name) + "\""); + builder.append('\n'); + return builder.toString(); + } + } + + /** + *
+     * Represents using a lambda literal (not the lambda method call).
+     *
+     *     ->(value) { value * 2 }
+     *     ^^^^^^^^^^^^^^^^^^^^^^^
+     * 
+ */ + public static final class LambdaNode extends Node { + public final byte[][] locals; + @Nullable + @UnionType({ BlockParametersNode.class, NumberedParametersNode.class, ItParametersNode.class }) + public final Node parameters; + @Nullable + @UnionType({ StatementsNode.class, BeginNode.class }) + public final Node body; + + public LambdaNode(int startOffset, int length, byte[][] locals, Node parameters, Node body) { + super(startOffset, length); + this.locals = locals; + this.parameters = parameters; + this.body = body; + } + + public void visitChildNodes(AbstractNodeVisitor visitor) { + if (this.parameters != null) { + this.parameters.accept(visitor); + } + if (this.body != null) { + this.body.accept(visitor); + } + } + + public Node[] childNodes() { + return new Node[] { this.parameters, this.body }; + } + + public T accept(AbstractNodeVisitor visitor) { + return visitor.visitLambdaNode(this); + } + + @Override + protected String toString(String indent) { + StringBuilder builder = new StringBuilder(); + builder.append(this.getClass().getSimpleName()); + if (hasNewLineFlag()) { + builder.append("[Li]"); + } + builder.append('\n'); + String nextIndent = indent + " "; + String nextNextIndent = nextIndent + " "; + builder.append(nextIndent); + builder.append("locals: "); + builder.append('\n'); + for (byte[] constant : this.locals) { + builder.append(nextNextIndent).append('"').append(asString(constant)).append('"').append('\n'); + } + builder.append(nextIndent); + builder.append("parameters: "); + builder.append(this.parameters == null ? "null\n" : this.parameters.toString(nextIndent)); + builder.append(nextIndent); + builder.append("body: "); + builder.append(this.body == null ? "null\n" : this.body.toString(nextIndent)); + return builder.toString(); + } + } + + /** + *
+     * Represents the use of the `&&=` operator for assignment to a local variable.
+     *
+     *     target &&= value
+     *     ^^^^^^^^^^^^^^^^
+     * 
+ */ + public static final class LocalVariableAndWriteNode extends Node { + public final Node value; + public final byte[] name; + public final int depth; + + public LocalVariableAndWriteNode(int startOffset, int length, Node value, byte[] name, int depth) { + super(startOffset, length); + this.value = value; + this.name = name; + this.depth = depth; + } + + public void visitChildNodes(AbstractNodeVisitor visitor) { + this.value.accept(visitor); + } + + public Node[] childNodes() { + return new Node[] { this.value }; + } + + public T accept(AbstractNodeVisitor visitor) { + return visitor.visitLocalVariableAndWriteNode(this); + } + + @Override + protected String toString(String indent) { + StringBuilder builder = new StringBuilder(); + builder.append(this.getClass().getSimpleName()); + if (hasNewLineFlag()) { + builder.append("[Li]"); + } + builder.append('\n'); + String nextIndent = indent + " "; + builder.append(nextIndent); + builder.append("value: "); + builder.append(this.value.toString(nextIndent)); + builder.append(nextIndent); + builder.append("name: "); + builder.append('"').append(asString(this.name)).append('"'); + builder.append('\n'); + builder.append(nextIndent); + builder.append("depth: "); + builder.append(this.depth); + builder.append('\n'); + return builder.toString(); + } + } + + /** + *
+     * Represents assigning to a local variable using an operator that isn't `=`.
+     *
+     *     target += value
+     *     ^^^^^^^^^^^^^^^
+     * 
+ */ + public static final class LocalVariableOperatorWriteNode extends Node { + public final Node value; + public final byte[] name; + public final byte[] binary_operator; + public final int depth; + + public LocalVariableOperatorWriteNode(int startOffset, int length, Node value, byte[] name, byte[] binary_operator, int depth) { + super(startOffset, length); + this.value = value; + this.name = name; + this.binary_operator = binary_operator; + this.depth = depth; + } + + public void visitChildNodes(AbstractNodeVisitor visitor) { + this.value.accept(visitor); + } + + public Node[] childNodes() { + return new Node[] { this.value }; + } + + public T accept(AbstractNodeVisitor visitor) { + return visitor.visitLocalVariableOperatorWriteNode(this); + } + + @Override + protected String toString(String indent) { + StringBuilder builder = new StringBuilder(); + builder.append(this.getClass().getSimpleName()); + if (hasNewLineFlag()) { + builder.append("[Li]"); + } + builder.append('\n'); + String nextIndent = indent + " "; + builder.append(nextIndent); + builder.append("value: "); + builder.append(this.value.toString(nextIndent)); + builder.append(nextIndent); + builder.append("name: "); + builder.append('"').append(asString(this.name)).append('"'); + builder.append('\n'); + builder.append(nextIndent); + builder.append("binary_operator: "); + builder.append('"').append(asString(this.binary_operator)).append('"'); + builder.append('\n'); + builder.append(nextIndent); + builder.append("depth: "); + builder.append(this.depth); + builder.append('\n'); + return builder.toString(); + } + } + + /** + *
+     * Represents the use of the `||=` operator for assignment to a local variable.
+     *
+     *     target ||= value
+     *     ^^^^^^^^^^^^^^^^
+     * 
+ */ + public static final class LocalVariableOrWriteNode extends Node { + public final Node value; + public final byte[] name; + public final int depth; + + public LocalVariableOrWriteNode(int startOffset, int length, Node value, byte[] name, int depth) { + super(startOffset, length); + this.value = value; + this.name = name; + this.depth = depth; + } + + public void visitChildNodes(AbstractNodeVisitor visitor) { + this.value.accept(visitor); + } + + public Node[] childNodes() { + return new Node[] { this.value }; + } + + public T accept(AbstractNodeVisitor visitor) { + return visitor.visitLocalVariableOrWriteNode(this); + } + + @Override + protected String toString(String indent) { + StringBuilder builder = new StringBuilder(); + builder.append(this.getClass().getSimpleName()); + if (hasNewLineFlag()) { + builder.append("[Li]"); + } + builder.append('\n'); + String nextIndent = indent + " "; + builder.append(nextIndent); + builder.append("value: "); + builder.append(this.value.toString(nextIndent)); + builder.append(nextIndent); + builder.append("name: "); + builder.append('"').append(asString(this.name)).append('"'); + builder.append('\n'); + builder.append(nextIndent); + builder.append("depth: "); + builder.append(this.depth); + builder.append('\n'); + return builder.toString(); + } + } + + /** + *
+     * 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
+     *     ^^^
+     * 
+ */ + public static final class LocalVariableReadNode extends Node { + /** + *
+         * 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`
+         * 
+ */ + public final byte[] 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).
+         * 
+ */ + public final int depth; + + public LocalVariableReadNode(int startOffset, int length, byte[] name, int depth) { + super(startOffset, length); + this.name = name; + this.depth = depth; + } + + public void visitChildNodes(AbstractNodeVisitor visitor) { + } + + public Node[] childNodes() { + return EMPTY_ARRAY; + } + + public T accept(AbstractNodeVisitor visitor) { + return visitor.visitLocalVariableReadNode(this); + } + + @Override + protected String toString(String indent) { + StringBuilder builder = new StringBuilder(); + builder.append(this.getClass().getSimpleName()); + if (hasNewLineFlag()) { + builder.append("[Li]"); + } + builder.append('\n'); + String nextIndent = indent + " "; + builder.append(nextIndent); + builder.append("name: "); + builder.append('"').append(asString(this.name)).append('"'); + builder.append('\n'); + builder.append(nextIndent); + builder.append("depth: "); + builder.append(this.depth); + builder.append('\n'); + return builder.toString(); + } + } + + /** + *
+     * Represents writing to a local variable in a context that doesn't have an explicit value.
+     *
+     *     foo, bar = baz
+     *     ^^^  ^^^
+     *
+     *     foo => baz
+     *            ^^^
+     * 
+ */ + public static final class LocalVariableTargetNode extends Node { + public final byte[] name; + public final int depth; + + public LocalVariableTargetNode(int startOffset, int length, byte[] name, int depth) { + super(startOffset, length); + this.name = name; + this.depth = depth; + } + + public void visitChildNodes(AbstractNodeVisitor visitor) { + } + + public Node[] childNodes() { + return EMPTY_ARRAY; + } + + public T accept(AbstractNodeVisitor visitor) { + return visitor.visitLocalVariableTargetNode(this); + } + + @Override + protected String toString(String indent) { + StringBuilder builder = new StringBuilder(); + builder.append(this.getClass().getSimpleName()); + if (hasNewLineFlag()) { + builder.append("[Li]"); + } + builder.append('\n'); + String nextIndent = indent + " "; + builder.append(nextIndent); + builder.append("name: "); + builder.append('"').append(asString(this.name)).append('"'); + builder.append('\n'); + builder.append(nextIndent); + builder.append("depth: "); + builder.append(this.depth); + builder.append('\n'); + return builder.toString(); + } + } + + /** + *
+     * Represents writing to a local variable.
+     *
+     *     foo = 1
+     *     ^^^^^^^
+     * 
+ */ + public static final class LocalVariableWriteNode extends Node { + /** + *
+         * 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`
+         * 
+ */ + public final byte[] 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).
+         * 
+ */ + public final int depth; + /** + *
+         * 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
+         * 
+ */ + public final Node value; + + public LocalVariableWriteNode(int startOffset, int length, byte[] name, int depth, Node value) { + super(startOffset, length); + this.name = name; + this.depth = depth; + this.value = value; + } + + public void visitChildNodes(AbstractNodeVisitor visitor) { + this.value.accept(visitor); + } + + public Node[] childNodes() { + return new Node[] { this.value }; + } + + public T accept(AbstractNodeVisitor visitor) { + return visitor.visitLocalVariableWriteNode(this); + } + + @Override + protected String toString(String indent) { + StringBuilder builder = new StringBuilder(); + builder.append(this.getClass().getSimpleName()); + if (hasNewLineFlag()) { + builder.append("[Li]"); + } + builder.append('\n'); + String nextIndent = indent + " "; + builder.append(nextIndent); + builder.append("name: "); + builder.append('"').append(asString(this.name)).append('"'); + builder.append('\n'); + builder.append(nextIndent); + builder.append("depth: "); + builder.append(this.depth); + builder.append('\n'); + builder.append(nextIndent); + builder.append("value: "); + builder.append(this.value.toString(nextIndent)); + return builder.toString(); + } + } + + /** + *
+     * 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
+     *        ^^^^^^
+     * 
+ */ + public static final class MatchLastLineNode extends Node { + public final short flags; + public final byte[] unescaped; + + public MatchLastLineNode(int startOffset, int length, short flags, byte[] unescaped) { + super(startOffset, length); + this.flags = flags; + this.unescaped = unescaped; + } + + public boolean isIgnoreCase() { + return RegularExpressionFlags.isIgnoreCase(flags); + } + + public boolean isExtended() { + return RegularExpressionFlags.isExtended(flags); + } + + public boolean isMultiLine() { + return RegularExpressionFlags.isMultiLine(flags); + } + + public boolean isOnce() { + return RegularExpressionFlags.isOnce(flags); + } + + public boolean isEucJp() { + return RegularExpressionFlags.isEucJp(flags); + } + + public boolean isAscii8bit() { + return RegularExpressionFlags.isAscii8bit(flags); + } + + public boolean isWindows31j() { + return RegularExpressionFlags.isWindows31j(flags); + } + + public boolean isUtf8() { + return RegularExpressionFlags.isUtf8(flags); + } + + public boolean isForcedUtf8Encoding() { + return RegularExpressionFlags.isForcedUtf8Encoding(flags); + } + + public boolean isForcedBinaryEncoding() { + return RegularExpressionFlags.isForcedBinaryEncoding(flags); + } + + public boolean isForcedUsAsciiEncoding() { + return RegularExpressionFlags.isForcedUsAsciiEncoding(flags); + } + + public void visitChildNodes(AbstractNodeVisitor visitor) { + } + + public Node[] childNodes() { + return EMPTY_ARRAY; + } + + public T accept(AbstractNodeVisitor visitor) { + return visitor.visitMatchLastLineNode(this); + } + + @Override + protected String toString(String indent) { + StringBuilder builder = new StringBuilder(); + builder.append(this.getClass().getSimpleName()); + if (hasNewLineFlag()) { + builder.append("[Li]"); + } + builder.append('\n'); + String nextIndent = indent + " "; + builder.append(nextIndent); + builder.append("RegularExpressionFlags: "); + builder.append(flags); + builder.append('\n'); + builder.append(nextIndent); + builder.append("unescaped: "); + builder.append('"' + asString(this.unescaped) + '"'); + builder.append('\n'); + return builder.toString(); + } + } + + /** + *
+     * Represents the use of the modifier `in` operator.
+     *
+     *     foo in bar
+     *     ^^^^^^^^^^
+     * 
+ */ + public static final class MatchPredicateNode extends Node { + public final Node value; + public final Node pattern; + + public MatchPredicateNode(int startOffset, int length, Node value, Node pattern) { + super(startOffset, length); + this.value = value; + this.pattern = pattern; + } + + public void visitChildNodes(AbstractNodeVisitor visitor) { + this.value.accept(visitor); + this.pattern.accept(visitor); + } + + public Node[] childNodes() { + return new Node[] { this.value, this.pattern }; + } + + public T accept(AbstractNodeVisitor visitor) { + return visitor.visitMatchPredicateNode(this); + } + + @Override + protected String toString(String indent) { + StringBuilder builder = new StringBuilder(); + builder.append(this.getClass().getSimpleName()); + if (hasNewLineFlag()) { + builder.append("[Li]"); + } + builder.append('\n'); + String nextIndent = indent + " "; + builder.append(nextIndent); + builder.append("value: "); + builder.append(this.value.toString(nextIndent)); + builder.append(nextIndent); + builder.append("pattern: "); + builder.append(this.pattern.toString(nextIndent)); + return builder.toString(); + } + } + + /** + *
+     * Represents the use of the `=>` operator.
+     *
+     *     foo => bar
+     *     ^^^^^^^^^^
+     * 
+ */ + public static final class MatchRequiredNode extends Node { + /** + *
+         * Represents the left-hand side of the operator.
+         *
+         *     foo => bar
+         *     ^^^
+         * 
+ */ + public final Node 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
+         * 
+ */ + public final Node pattern; + + public MatchRequiredNode(int startOffset, int length, Node value, Node pattern) { + super(startOffset, length); + this.value = value; + this.pattern = pattern; + } + + public void visitChildNodes(AbstractNodeVisitor visitor) { + this.value.accept(visitor); + this.pattern.accept(visitor); + } + + public Node[] childNodes() { + return new Node[] { this.value, this.pattern }; + } + + public T accept(AbstractNodeVisitor visitor) { + return visitor.visitMatchRequiredNode(this); + } + + @Override + protected String toString(String indent) { + StringBuilder builder = new StringBuilder(); + builder.append(this.getClass().getSimpleName()); + if (hasNewLineFlag()) { + builder.append("[Li]"); + } + builder.append('\n'); + String nextIndent = indent + " "; + builder.append(nextIndent); + builder.append("value: "); + builder.append(this.value.toString(nextIndent)); + builder.append(nextIndent); + builder.append("pattern: "); + builder.append(this.pattern.toString(nextIndent)); + return builder.toString(); + } + } + + /** + *
+     * Represents writing local variables using a regular expression match with named capture groups.
+     *
+     *     /(?<foo>bar)/ =~ baz
+     *     ^^^^^^^^^^^^^^^^^^^^
+     * 
+ */ + public static final class MatchWriteNode extends Node { + public final CallNode call; + public final LocalVariableTargetNode[] targets; + + public MatchWriteNode(int startOffset, int length, CallNode call, LocalVariableTargetNode[] targets) { + super(startOffset, length); + this.call = call; + this.targets = targets; + } + + public void visitChildNodes(AbstractNodeVisitor visitor) { + this.call.accept(visitor); + for (Nodes.Node child : this.targets) { + child.accept(visitor); + } + } + + public Node[] childNodes() { + ArrayList childNodes = new ArrayList<>(); + childNodes.add(this.call); + childNodes.addAll(Arrays.asList(this.targets)); + return childNodes.toArray(EMPTY_ARRAY); + } + + public T accept(AbstractNodeVisitor visitor) { + return visitor.visitMatchWriteNode(this); + } + + @Override + protected String toString(String indent) { + StringBuilder builder = new StringBuilder(); + builder.append(this.getClass().getSimpleName()); + if (hasNewLineFlag()) { + builder.append("[Li]"); + } + builder.append('\n'); + String nextIndent = indent + " "; + String nextNextIndent = nextIndent + " "; + builder.append(nextIndent); + builder.append("call: "); + builder.append(this.call.toString(nextIndent)); + builder.append(nextIndent); + builder.append("targets: "); + builder.append('\n'); + for (Node child : this.targets) { + builder.append(nextNextIndent).append(child.toString(nextNextIndent)); + } + return builder.toString(); + } + } + + /** + *
+     * Represents a node that is missing from the source and results in a syntax error.
+     * 
+ */ + public static final class MissingNode extends Node { + + public MissingNode(int startOffset, int length) { + super(startOffset, length); + } + + public void visitChildNodes(AbstractNodeVisitor visitor) { + } + + public Node[] childNodes() { + return EMPTY_ARRAY; + } + + public T accept(AbstractNodeVisitor visitor) { + return visitor.visitMissingNode(this); + } + + @Override + protected String toString(String indent) { + StringBuilder builder = new StringBuilder(); + builder.append(this.getClass().getSimpleName()); + if (hasNewLineFlag()) { + builder.append("[Li]"); + } + builder.append('\n'); + String nextIndent = indent + " "; + return builder.toString(); + } + } + + /** + *
+     * Represents a module declaration involving the `module` keyword.
+     *
+     *     module Foo end
+     *     ^^^^^^^^^^^^^^
+     * 
+ */ + public static final class ModuleNode extends Node { + public final byte[][] locals; + @UnionType({ ConstantReadNode.class, ConstantPathNode.class }) + public final Node constant_path; + @Nullable + @UnionType({ StatementsNode.class, BeginNode.class }) + public final Node body; + public final byte[] name; + + public ModuleNode(int startOffset, int length, byte[][] locals, Node constant_path, Node body, byte[] name) { + super(startOffset, length); + this.locals = locals; + this.constant_path = constant_path; + this.body = body; + this.name = name; + } + + public void visitChildNodes(AbstractNodeVisitor visitor) { + this.constant_path.accept(visitor); + if (this.body != null) { + this.body.accept(visitor); + } + } + + public Node[] childNodes() { + return new Node[] { this.constant_path, this.body }; + } + + public T accept(AbstractNodeVisitor visitor) { + return visitor.visitModuleNode(this); + } + + @Override + protected String toString(String indent) { + StringBuilder builder = new StringBuilder(); + builder.append(this.getClass().getSimpleName()); + if (hasNewLineFlag()) { + builder.append("[Li]"); + } + builder.append('\n'); + String nextIndent = indent + " "; + String nextNextIndent = nextIndent + " "; + builder.append(nextIndent); + builder.append("locals: "); + builder.append('\n'); + for (byte[] constant : this.locals) { + builder.append(nextNextIndent).append('"').append(asString(constant)).append('"').append('\n'); + } + builder.append(nextIndent); + builder.append("constant_path: "); + builder.append(this.constant_path.toString(nextIndent)); + builder.append(nextIndent); + builder.append("body: "); + builder.append(this.body == null ? "null\n" : this.body.toString(nextIndent)); + builder.append(nextIndent); + builder.append("name: "); + builder.append('"').append(asString(this.name)).append('"'); + builder.append('\n'); + return builder.toString(); + } + } + + /** + *
+     * 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]]
+     *         ^^^^
+     * 
+ */ + public static final class MultiTargetNode extends Node { + /** + *
+         * 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
+         *         ^^^^
+         * 
+ */ + @UnionType({ LocalVariableTargetNode.class, InstanceVariableTargetNode.class, ClassVariableTargetNode.class, GlobalVariableTargetNode.class, ConstantTargetNode.class, ConstantPathTargetNode.class, CallTargetNode.class, IndexTargetNode.class, MultiTargetNode.class, RequiredParameterNode.class }) + public final Node[] 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
+         *          ^
+         * 
+ */ + @Nullable + @UnionType({ ImplicitRestNode.class, SplatNode.class }) + public final Node rest; + /** + *
+         * Represents the targets expressions after a splat node.
+         *
+         *     a, (*, b, c) = 1, 2, 3, 4, 5
+         *            ^^^^
+         * 
+ */ + @UnionType({ LocalVariableTargetNode.class, InstanceVariableTargetNode.class, ClassVariableTargetNode.class, GlobalVariableTargetNode.class, ConstantTargetNode.class, ConstantPathTargetNode.class, CallTargetNode.class, IndexTargetNode.class, MultiTargetNode.class, RequiredParameterNode.class }) + public final Node[] rights; + + public MultiTargetNode(int startOffset, int length, Node[] lefts, Node rest, Node[] rights) { + super(startOffset, length); + this.lefts = lefts; + this.rest = rest; + this.rights = rights; + } + + public void visitChildNodes(AbstractNodeVisitor visitor) { + for (Nodes.Node child : this.lefts) { + child.accept(visitor); + } + if (this.rest != null) { + this.rest.accept(visitor); + } + for (Nodes.Node child : this.rights) { + child.accept(visitor); + } + } + + public Node[] childNodes() { + ArrayList childNodes = new ArrayList<>(); + childNodes.addAll(Arrays.asList(this.lefts)); + childNodes.add(this.rest); + childNodes.addAll(Arrays.asList(this.rights)); + return childNodes.toArray(EMPTY_ARRAY); + } + + public T accept(AbstractNodeVisitor visitor) { + return visitor.visitMultiTargetNode(this); + } + + @Override + protected String toString(String indent) { + StringBuilder builder = new StringBuilder(); + builder.append(this.getClass().getSimpleName()); + if (hasNewLineFlag()) { + builder.append("[Li]"); + } + builder.append('\n'); + String nextIndent = indent + " "; + String nextNextIndent = nextIndent + " "; + builder.append(nextIndent); + builder.append("lefts: "); + builder.append('\n'); + for (Node child : this.lefts) { + builder.append(nextNextIndent).append(child.toString(nextNextIndent)); + } + builder.append(nextIndent); + builder.append("rest: "); + builder.append(this.rest == null ? "null\n" : this.rest.toString(nextIndent)); + builder.append(nextIndent); + builder.append("rights: "); + builder.append('\n'); + for (Node child : this.rights) { + builder.append(nextNextIndent).append(child.toString(nextNextIndent)); + } + return builder.toString(); + } + } + + /** + *
+     * Represents a write to a multi-target expression.
+     *
+     *     a, b, c = 1, 2, 3
+     *     ^^^^^^^^^^^^^^^^^
+     * 
+ */ + public static final class MultiWriteNode extends Node { + /** + *
+         * 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
+         *     ^^^^^^^
+         * 
+ */ + @UnionType({ LocalVariableTargetNode.class, InstanceVariableTargetNode.class, ClassVariableTargetNode.class, GlobalVariableTargetNode.class, ConstantTargetNode.class, ConstantPathTargetNode.class, CallTargetNode.class, IndexTargetNode.class, MultiTargetNode.class }) + public final Node[] 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
+         *         ^
+         * 
+ */ + @Nullable + @UnionType({ ImplicitRestNode.class, SplatNode.class }) + public final Node rest; + /** + *
+         * Represents the targets expressions after a splat node.
+         *
+         *     a, *, b, c = 1, 2, 3, 4, 5
+         *           ^^^^
+         * 
+ */ + @UnionType({ LocalVariableTargetNode.class, InstanceVariableTargetNode.class, ClassVariableTargetNode.class, GlobalVariableTargetNode.class, ConstantTargetNode.class, ConstantPathTargetNode.class, CallTargetNode.class, IndexTargetNode.class, MultiTargetNode.class }) + public final Node[] rights; + /** + *
+         * 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
+         *               ^^^^^^^
+         * 
+ */ + public final Node value; + + public MultiWriteNode(int startOffset, int length, Node[] lefts, Node rest, Node[] rights, Node value) { + super(startOffset, length); + this.lefts = lefts; + this.rest = rest; + this.rights = rights; + this.value = value; + } + + public void visitChildNodes(AbstractNodeVisitor visitor) { + for (Nodes.Node child : this.lefts) { + child.accept(visitor); + } + if (this.rest != null) { + this.rest.accept(visitor); + } + for (Nodes.Node child : this.rights) { + child.accept(visitor); + } + this.value.accept(visitor); + } + + public Node[] childNodes() { + ArrayList childNodes = new ArrayList<>(); + childNodes.addAll(Arrays.asList(this.lefts)); + childNodes.add(this.rest); + childNodes.addAll(Arrays.asList(this.rights)); + childNodes.add(this.value); + return childNodes.toArray(EMPTY_ARRAY); + } + + public T accept(AbstractNodeVisitor visitor) { + return visitor.visitMultiWriteNode(this); + } + + @Override + protected String toString(String indent) { + StringBuilder builder = new StringBuilder(); + builder.append(this.getClass().getSimpleName()); + if (hasNewLineFlag()) { + builder.append("[Li]"); + } + builder.append('\n'); + String nextIndent = indent + " "; + String nextNextIndent = nextIndent + " "; + builder.append(nextIndent); + builder.append("lefts: "); + builder.append('\n'); + for (Node child : this.lefts) { + builder.append(nextNextIndent).append(child.toString(nextNextIndent)); + } + builder.append(nextIndent); + builder.append("rest: "); + builder.append(this.rest == null ? "null\n" : this.rest.toString(nextIndent)); + builder.append(nextIndent); + builder.append("rights: "); + builder.append('\n'); + for (Node child : this.rights) { + builder.append(nextNextIndent).append(child.toString(nextNextIndent)); + } + builder.append(nextIndent); + builder.append("value: "); + builder.append(this.value.toString(nextIndent)); + return builder.toString(); + } + } + + /** + *
+     * Represents the use of the `next` keyword.
+     *
+     *     next 1
+     *     ^^^^^^
+     * 
+ */ + public static final class NextNode extends Node { + @Nullable + public final ArgumentsNode arguments; + + public NextNode(int startOffset, int length, ArgumentsNode arguments) { + super(startOffset, length); + this.arguments = arguments; + } + + public void visitChildNodes(AbstractNodeVisitor visitor) { + if (this.arguments != null) { + this.arguments.accept(visitor); + } + } + + public Node[] childNodes() { + return new Node[] { this.arguments }; + } + + public T accept(AbstractNodeVisitor visitor) { + return visitor.visitNextNode(this); + } + + @Override + protected String toString(String indent) { + StringBuilder builder = new StringBuilder(); + builder.append(this.getClass().getSimpleName()); + if (hasNewLineFlag()) { + builder.append("[Li]"); + } + builder.append('\n'); + String nextIndent = indent + " "; + builder.append(nextIndent); + builder.append("arguments: "); + builder.append(this.arguments == null ? "null\n" : this.arguments.toString(nextIndent)); + return builder.toString(); + } + } + + /** + *
+     * Represents the use of the `nil` keyword.
+     *
+     *     nil
+     *     ^^^
+     * 
+ */ + public static final class NilNode extends Node { + + public NilNode(int startOffset, int length) { + super(startOffset, length); + } + + public void visitChildNodes(AbstractNodeVisitor visitor) { + } + + public Node[] childNodes() { + return EMPTY_ARRAY; + } + + public T accept(AbstractNodeVisitor visitor) { + return visitor.visitNilNode(this); + } + + @Override + protected String toString(String indent) { + StringBuilder builder = new StringBuilder(); + builder.append(this.getClass().getSimpleName()); + if (hasNewLineFlag()) { + builder.append("[Li]"); + } + builder.append('\n'); + String nextIndent = indent + " "; + return builder.toString(); + } + } + + /** + *
+     * Represents the use of `&nil` inside method arguments.
+     *
+     *     def a(&nil)
+     *           ^^^^
+     *     end
+     * 
+ */ + public static final class NoBlockParameterNode extends Node { + + public NoBlockParameterNode(int startOffset, int length) { + super(startOffset, length); + } + + public void visitChildNodes(AbstractNodeVisitor visitor) { + } + + public Node[] childNodes() { + return EMPTY_ARRAY; + } + + public T accept(AbstractNodeVisitor visitor) { + return visitor.visitNoBlockParameterNode(this); + } + + @Override + protected String toString(String indent) { + StringBuilder builder = new StringBuilder(); + builder.append(this.getClass().getSimpleName()); + if (hasNewLineFlag()) { + builder.append("[Li]"); + } + builder.append('\n'); + String nextIndent = indent + " "; + return builder.toString(); + } + } + + /** + *
+     * Represents the use of `**nil` inside method arguments.
+     *
+     *     def a(**nil)
+     *           ^^^^^
+     *     end
+     * 
+ */ + public static final class NoKeywordsParameterNode extends Node { + + public NoKeywordsParameterNode(int startOffset, int length) { + super(startOffset, length); + } + + public void visitChildNodes(AbstractNodeVisitor visitor) { + } + + public Node[] childNodes() { + return EMPTY_ARRAY; + } + + public T accept(AbstractNodeVisitor visitor) { + return visitor.visitNoKeywordsParameterNode(this); + } + + @Override + protected String toString(String indent) { + StringBuilder builder = new StringBuilder(); + builder.append(this.getClass().getSimpleName()); + if (hasNewLineFlag()) { + builder.append("[Li]"); + } + builder.append('\n'); + String nextIndent = indent + " "; + return builder.toString(); + } + } + + /** + *
+     * Represents an implicit set of parameters through the use of numbered parameters within a block or lambda.
+     *
+     *     -> { _1 + _2 }
+     *     ^^^^^^^^^^^^^^
+     * 
+ */ + public static final class NumberedParametersNode extends Node { + public final int maximum; + + public NumberedParametersNode(int startOffset, int length, int maximum) { + super(startOffset, length); + this.maximum = maximum; + } + + public void visitChildNodes(AbstractNodeVisitor visitor) { + } + + public Node[] childNodes() { + return EMPTY_ARRAY; + } + + public T accept(AbstractNodeVisitor visitor) { + return visitor.visitNumberedParametersNode(this); + } + + @Override + protected String toString(String indent) { + StringBuilder builder = new StringBuilder(); + builder.append(this.getClass().getSimpleName()); + if (hasNewLineFlag()) { + builder.append("[Li]"); + } + builder.append('\n'); + String nextIndent = indent + " "; + builder.append(nextIndent); + builder.append("maximum: "); + builder.append(this.maximum); + builder.append('\n'); + return builder.toString(); + } + } + + /** + *
+     * Represents reading a numbered reference to a capture in the previous match.
+     *
+     *     $1
+     *     ^^
+     * 
+ */ + public static final class NumberedReferenceReadNode extends Node { + /** + *
+         * 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`
+         * 
+ */ + public final int number; + + public NumberedReferenceReadNode(int startOffset, int length, int number) { + super(startOffset, length); + this.number = number; + } + + public void visitChildNodes(AbstractNodeVisitor visitor) { + } + + public Node[] childNodes() { + return EMPTY_ARRAY; + } + + public T accept(AbstractNodeVisitor visitor) { + return visitor.visitNumberedReferenceReadNode(this); + } + + @Override + protected String toString(String indent) { + StringBuilder builder = new StringBuilder(); + builder.append(this.getClass().getSimpleName()); + if (hasNewLineFlag()) { + builder.append("[Li]"); + } + builder.append('\n'); + String nextIndent = indent + " "; + builder.append(nextIndent); + builder.append("number: "); + builder.append(this.number); + builder.append('\n'); + return builder.toString(); + } + } + + /** + *
+     * Represents an optional keyword parameter to a method, block, or lambda definition.
+     *
+     *     def a(b: 1)
+     *           ^^^^
+     *     end
+     * 
+ */ + public static final class OptionalKeywordParameterNode extends Node { + public final short flags; + public final byte[] name; + public final Node value; + + public OptionalKeywordParameterNode(int startOffset, int length, short flags, byte[] name, Node value) { + super(startOffset, length); + this.flags = flags; + this.name = name; + this.value = value; + } + + public boolean isRepeatedParameter() { + return ParameterFlags.isRepeatedParameter(flags); + } + + public void visitChildNodes(AbstractNodeVisitor visitor) { + this.value.accept(visitor); + } + + public Node[] childNodes() { + return new Node[] { this.value }; + } + + public T accept(AbstractNodeVisitor visitor) { + return visitor.visitOptionalKeywordParameterNode(this); + } + + @Override + protected String toString(String indent) { + StringBuilder builder = new StringBuilder(); + builder.append(this.getClass().getSimpleName()); + if (hasNewLineFlag()) { + builder.append("[Li]"); + } + builder.append('\n'); + String nextIndent = indent + " "; + builder.append(nextIndent); + builder.append("ParameterFlags: "); + builder.append(flags); + builder.append('\n'); + builder.append(nextIndent); + builder.append("name: "); + builder.append('"').append(asString(this.name)).append('"'); + builder.append('\n'); + builder.append(nextIndent); + builder.append("value: "); + builder.append(this.value.toString(nextIndent)); + return builder.toString(); + } + } + + /** + *
+     * Represents an optional parameter to a method, block, or lambda definition.
+     *
+     *     def a(b = 1)
+     *           ^^^^^
+     *     end
+     * 
+ */ + public static final class OptionalParameterNode extends Node { + public final short flags; + public final byte[] name; + public final Node value; + + public OptionalParameterNode(int startOffset, int length, short flags, byte[] name, Node value) { + super(startOffset, length); + this.flags = flags; + this.name = name; + this.value = value; + } + + public boolean isRepeatedParameter() { + return ParameterFlags.isRepeatedParameter(flags); + } + + public void visitChildNodes(AbstractNodeVisitor visitor) { + this.value.accept(visitor); + } + + public Node[] childNodes() { + return new Node[] { this.value }; + } + + public T accept(AbstractNodeVisitor visitor) { + return visitor.visitOptionalParameterNode(this); + } + + @Override + protected String toString(String indent) { + StringBuilder builder = new StringBuilder(); + builder.append(this.getClass().getSimpleName()); + if (hasNewLineFlag()) { + builder.append("[Li]"); + } + builder.append('\n'); + String nextIndent = indent + " "; + builder.append(nextIndent); + builder.append("ParameterFlags: "); + builder.append(flags); + builder.append('\n'); + builder.append(nextIndent); + builder.append("name: "); + builder.append('"').append(asString(this.name)).append('"'); + builder.append('\n'); + builder.append(nextIndent); + builder.append("value: "); + builder.append(this.value.toString(nextIndent)); + return builder.toString(); + } + } + + /** + *
+     * Represents the use of the `||` operator or the `or` keyword.
+     *
+     *     left or right
+     *     ^^^^^^^^^^^^^
+     * 
+ */ + public static final class OrNode extends Node { + /** + *
+         * 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
+         *     ^
+         * 
+ */ + public final Node left; + /** + *
+         * Represents the right side of the expression.
+         *
+         *     left || right
+         *             ^^^^^
+         *
+         *     1 or 2
+         *          ^
+         * 
+ */ + public final Node right; + + public OrNode(int startOffset, int length, Node left, Node right) { + super(startOffset, length); + this.left = left; + this.right = right; + } + + public void visitChildNodes(AbstractNodeVisitor visitor) { + this.left.accept(visitor); + this.right.accept(visitor); + } + + public Node[] childNodes() { + return new Node[] { this.left, this.right }; + } + + public T accept(AbstractNodeVisitor visitor) { + return visitor.visitOrNode(this); + } + + @Override + protected String toString(String indent) { + StringBuilder builder = new StringBuilder(); + builder.append(this.getClass().getSimpleName()); + if (hasNewLineFlag()) { + builder.append("[Li]"); + } + builder.append('\n'); + String nextIndent = indent + " "; + builder.append(nextIndent); + builder.append("left: "); + builder.append(this.left.toString(nextIndent)); + builder.append(nextIndent); + builder.append("right: "); + builder.append(this.right.toString(nextIndent)); + return builder.toString(); + } + } + + /** + *
+     * Represents the list of parameters on a method, block, or lambda definition.
+     *
+     *     def a(b, c, d)
+     *           ^^^^^^^
+     *     end
+     * 
+ */ + public static final class ParametersNode extends Node { + @UnionType({ RequiredParameterNode.class, MultiTargetNode.class }) + public final Node[] requireds; + public final OptionalParameterNode[] optionals; + @Nullable + @UnionType({ RestParameterNode.class, ImplicitRestNode.class }) + public final Node rest; + @UnionType({ RequiredParameterNode.class, MultiTargetNode.class }) + public final Node[] posts; + @UnionType({ RequiredKeywordParameterNode.class, OptionalKeywordParameterNode.class }) + public final Node[] keywords; + @Nullable + @UnionType({ KeywordRestParameterNode.class, ForwardingParameterNode.class, NoKeywordsParameterNode.class }) + public final Node keyword_rest; + @Nullable + @UnionType({ BlockParameterNode.class, NoBlockParameterNode.class }) + public final Node block; + + public ParametersNode(int startOffset, int length, Node[] requireds, OptionalParameterNode[] optionals, Node rest, Node[] posts, Node[] keywords, Node keyword_rest, Node block) { + super(startOffset, length); + this.requireds = requireds; + this.optionals = optionals; + this.rest = rest; + this.posts = posts; + this.keywords = keywords; + this.keyword_rest = keyword_rest; + this.block = block; + } + + public void visitChildNodes(AbstractNodeVisitor visitor) { + for (Nodes.Node child : this.requireds) { + child.accept(visitor); + } + for (Nodes.Node child : this.optionals) { + child.accept(visitor); + } + if (this.rest != null) { + this.rest.accept(visitor); + } + for (Nodes.Node child : this.posts) { + child.accept(visitor); + } + for (Nodes.Node child : this.keywords) { + child.accept(visitor); + } + if (this.keyword_rest != null) { + this.keyword_rest.accept(visitor); + } + if (this.block != null) { + this.block.accept(visitor); + } + } + + public Node[] childNodes() { + ArrayList childNodes = new ArrayList<>(); + childNodes.addAll(Arrays.asList(this.requireds)); + childNodes.addAll(Arrays.asList(this.optionals)); + childNodes.add(this.rest); + childNodes.addAll(Arrays.asList(this.posts)); + childNodes.addAll(Arrays.asList(this.keywords)); + childNodes.add(this.keyword_rest); + childNodes.add(this.block); + return childNodes.toArray(EMPTY_ARRAY); + } + + public T accept(AbstractNodeVisitor visitor) { + return visitor.visitParametersNode(this); + } + + @Override + protected String toString(String indent) { + StringBuilder builder = new StringBuilder(); + builder.append(this.getClass().getSimpleName()); + if (hasNewLineFlag()) { + builder.append("[Li]"); + } + builder.append('\n'); + String nextIndent = indent + " "; + String nextNextIndent = nextIndent + " "; + builder.append(nextIndent); + builder.append("requireds: "); + builder.append('\n'); + for (Node child : this.requireds) { + builder.append(nextNextIndent).append(child.toString(nextNextIndent)); + } + builder.append(nextIndent); + builder.append("optionals: "); + builder.append('\n'); + for (Node child : this.optionals) { + builder.append(nextNextIndent).append(child.toString(nextNextIndent)); + } + builder.append(nextIndent); + builder.append("rest: "); + builder.append(this.rest == null ? "null\n" : this.rest.toString(nextIndent)); + builder.append(nextIndent); + builder.append("posts: "); + builder.append('\n'); + for (Node child : this.posts) { + builder.append(nextNextIndent).append(child.toString(nextNextIndent)); + } + builder.append(nextIndent); + builder.append("keywords: "); + builder.append('\n'); + for (Node child : this.keywords) { + builder.append(nextNextIndent).append(child.toString(nextNextIndent)); + } + builder.append(nextIndent); + builder.append("keyword_rest: "); + builder.append(this.keyword_rest == null ? "null\n" : this.keyword_rest.toString(nextIndent)); + builder.append(nextIndent); + builder.append("block: "); + builder.append(this.block == null ? "null\n" : this.block.toString(nextIndent)); + return builder.toString(); + } + } + + /** + *
+     * Represents a parenthesized expression
+     *
+     *     (10 + 34)
+     *     ^^^^^^^^^
+     * 
+ */ + public static final class ParenthesesNode extends Node { + public final short flags; + @Nullable + public final Node body; + + public ParenthesesNode(int startOffset, int length, short flags, Node body) { + super(startOffset, length); + this.flags = flags; + this.body = body; + } + + public boolean isMultipleStatements() { + return ParenthesesNodeFlags.isMultipleStatements(flags); + } + + @Override + public void setNewLineFlag(Source source, boolean[] newlineMarked) { + // Never mark ParenthesesNode with a newline flag, mark children instead + } + + public void visitChildNodes(AbstractNodeVisitor visitor) { + if (this.body != null) { + this.body.accept(visitor); + } + } + + public Node[] childNodes() { + return new Node[] { this.body }; + } + + public T accept(AbstractNodeVisitor visitor) { + return visitor.visitParenthesesNode(this); + } + + @Override + protected String toString(String indent) { + StringBuilder builder = new StringBuilder(); + builder.append(this.getClass().getSimpleName()); + if (hasNewLineFlag()) { + builder.append("[Li]"); + } + builder.append('\n'); + String nextIndent = indent + " "; + builder.append(nextIndent); + builder.append("ParenthesesNodeFlags: "); + builder.append(flags); + builder.append('\n'); + builder.append(nextIndent); + builder.append("body: "); + builder.append(this.body == null ? "null\n" : this.body.toString(nextIndent)); + return builder.toString(); + } + } + + /** + *
+     * Represents the use of the `^` operator for pinning an expression in a pattern matching expression.
+     *
+     *     foo in ^(bar)
+     *            ^^^^^^
+     * 
+ */ + public static final class PinnedExpressionNode extends Node { + /** + *
+         * The expression used in the pinned expression
+         *
+         *     foo in ^(bar)
+         *              ^^^
+         * 
+ */ + public final Node expression; + + public PinnedExpressionNode(int startOffset, int length, Node expression) { + super(startOffset, length); + this.expression = expression; + } + + public void visitChildNodes(AbstractNodeVisitor visitor) { + this.expression.accept(visitor); + } + + public Node[] childNodes() { + return new Node[] { this.expression }; + } + + public T accept(AbstractNodeVisitor visitor) { + return visitor.visitPinnedExpressionNode(this); + } + + @Override + protected String toString(String indent) { + StringBuilder builder = new StringBuilder(); + builder.append(this.getClass().getSimpleName()); + if (hasNewLineFlag()) { + builder.append("[Li]"); + } + builder.append('\n'); + String nextIndent = indent + " "; + builder.append(nextIndent); + builder.append("expression: "); + builder.append(this.expression.toString(nextIndent)); + return builder.toString(); + } + } + + /** + *
+     * Represents the use of the `^` operator for pinning a variable in a pattern matching expression.
+     *
+     *     foo in ^bar
+     *            ^^^^
+     * 
+ */ + public static final class PinnedVariableNode extends Node { + /** + *
+         * The variable used in the pinned expression
+         *
+         *     foo in ^bar
+         *             ^^^
+         * 
+ */ + @UnionType({ LocalVariableReadNode.class, InstanceVariableReadNode.class, ClassVariableReadNode.class, GlobalVariableReadNode.class, BackReferenceReadNode.class, NumberedReferenceReadNode.class, ItLocalVariableReadNode.class }) + public final Node variable; + + public PinnedVariableNode(int startOffset, int length, Node variable) { + super(startOffset, length); + this.variable = variable; + } + + public void visitChildNodes(AbstractNodeVisitor visitor) { + this.variable.accept(visitor); + } + + public Node[] childNodes() { + return new Node[] { this.variable }; + } + + public T accept(AbstractNodeVisitor visitor) { + return visitor.visitPinnedVariableNode(this); + } + + @Override + protected String toString(String indent) { + StringBuilder builder = new StringBuilder(); + builder.append(this.getClass().getSimpleName()); + if (hasNewLineFlag()) { + builder.append("[Li]"); + } + builder.append('\n'); + String nextIndent = indent + " "; + builder.append(nextIndent); + builder.append("variable: "); + builder.append(this.variable.toString(nextIndent)); + return builder.toString(); + } + } + + /** + *
+     * Represents the use of the `END` keyword.
+     *
+     *     END { foo }
+     *     ^^^^^^^^^^^
+     * 
+ */ + public static final class PostExecutionNode extends Node { + @Nullable + public final StatementsNode statements; + + public PostExecutionNode(int startOffset, int length, StatementsNode statements) { + super(startOffset, length); + this.statements = statements; + } + + public void visitChildNodes(AbstractNodeVisitor visitor) { + if (this.statements != null) { + this.statements.accept(visitor); + } + } + + public Node[] childNodes() { + return new Node[] { this.statements }; + } + + public T accept(AbstractNodeVisitor visitor) { + return visitor.visitPostExecutionNode(this); + } + + @Override + protected String toString(String indent) { + StringBuilder builder = new StringBuilder(); + builder.append(this.getClass().getSimpleName()); + if (hasNewLineFlag()) { + builder.append("[Li]"); + } + builder.append('\n'); + String nextIndent = indent + " "; + builder.append(nextIndent); + builder.append("statements: "); + builder.append(this.statements == null ? "null\n" : this.statements.toString(nextIndent)); + return builder.toString(); + } + } + + /** + *
+     * Represents the use of the `BEGIN` keyword.
+     *
+     *     BEGIN { foo }
+     *     ^^^^^^^^^^^^^
+     * 
+ */ + public static final class PreExecutionNode extends Node { + @Nullable + public final StatementsNode statements; + + public PreExecutionNode(int startOffset, int length, StatementsNode statements) { + super(startOffset, length); + this.statements = statements; + } + + public void visitChildNodes(AbstractNodeVisitor visitor) { + if (this.statements != null) { + this.statements.accept(visitor); + } + } + + public Node[] childNodes() { + return new Node[] { this.statements }; + } + + public T accept(AbstractNodeVisitor visitor) { + return visitor.visitPreExecutionNode(this); + } + + @Override + protected String toString(String indent) { + StringBuilder builder = new StringBuilder(); + builder.append(this.getClass().getSimpleName()); + if (hasNewLineFlag()) { + builder.append("[Li]"); + } + builder.append('\n'); + String nextIndent = indent + " "; + builder.append(nextIndent); + builder.append("statements: "); + builder.append(this.statements == null ? "null\n" : this.statements.toString(nextIndent)); + return builder.toString(); + } + } + + /** + *
+     * The top level node of any parse tree.
+     * 
+ */ + public static final class ProgramNode extends Node { + public final byte[][] locals; + public final StatementsNode statements; + + public ProgramNode(int startOffset, int length, byte[][] locals, StatementsNode statements) { + super(startOffset, length); + this.locals = locals; + this.statements = statements; + } + + public void visitChildNodes(AbstractNodeVisitor visitor) { + this.statements.accept(visitor); + } + + public Node[] childNodes() { + return new Node[] { this.statements }; + } + + public T accept(AbstractNodeVisitor visitor) { + return visitor.visitProgramNode(this); + } + + @Override + protected String toString(String indent) { + StringBuilder builder = new StringBuilder(); + builder.append(this.getClass().getSimpleName()); + if (hasNewLineFlag()) { + builder.append("[Li]"); + } + builder.append('\n'); + String nextIndent = indent + " "; + String nextNextIndent = nextIndent + " "; + builder.append(nextIndent); + builder.append("locals: "); + builder.append('\n'); + for (byte[] constant : this.locals) { + builder.append(nextNextIndent).append('"').append(asString(constant)).append('"').append('\n'); + } + builder.append(nextIndent); + builder.append("statements: "); + builder.append(this.statements.toString(nextIndent)); + return builder.toString(); + } + } + + /** + *
+     * Represents the use of the `..` or `...` operators.
+     *
+     *     1..2
+     *     ^^^^
+     *
+     *     c if a =~ /left/ ... b =~ /right/
+     *          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+     * 
+ */ + public static final class RangeNode extends Node { + public final short flags; + /** + *
+         * 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
+         *     ^^^^^
+         * 
+ */ + @Nullable + public final Node 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.
+         * 
+ */ + @Nullable + public final Node right; + + public RangeNode(int startOffset, int length, short flags, Node left, Node right) { + super(startOffset, length); + this.flags = flags; + this.left = left; + this.right = right; + } + + public boolean isExcludeEnd() { + return RangeFlags.isExcludeEnd(flags); + } + + public void visitChildNodes(AbstractNodeVisitor visitor) { + if (this.left != null) { + this.left.accept(visitor); + } + if (this.right != null) { + this.right.accept(visitor); + } + } + + public Node[] childNodes() { + return new Node[] { this.left, this.right }; + } + + public T accept(AbstractNodeVisitor visitor) { + return visitor.visitRangeNode(this); + } + + @Override + protected String toString(String indent) { + StringBuilder builder = new StringBuilder(); + builder.append(this.getClass().getSimpleName()); + if (hasNewLineFlag()) { + builder.append("[Li]"); + } + builder.append('\n'); + String nextIndent = indent + " "; + builder.append(nextIndent); + builder.append("RangeFlags: "); + builder.append(flags); + builder.append('\n'); + builder.append(nextIndent); + builder.append("left: "); + builder.append(this.left == null ? "null\n" : this.left.toString(nextIndent)); + builder.append(nextIndent); + builder.append("right: "); + builder.append(this.right == null ? "null\n" : this.right.toString(nextIndent)); + return builder.toString(); + } + } + + /** + *
+     * Represents a rational number literal.
+     *
+     *     1.0r
+     *     ^^^^
+     * 
+ */ + public static final class RationalNode extends Node { + public final short flags; + /** + *
+         * The numerator of the rational number.
+         *
+         *     1.5r # numerator 3
+         * 
+ */ + public final Object numerator; + /** + *
+         * The denominator of the rational number.
+         *
+         *     1.5r # denominator 2
+         * 
+ */ + public final Object denominator; + + public RationalNode(int startOffset, int length, short flags, Object numerator, Object denominator) { + super(startOffset, length); + this.flags = flags; + this.numerator = numerator; + this.denominator = denominator; + } + + public boolean isBinary() { + return IntegerBaseFlags.isBinary(flags); + } + + public boolean isDecimal() { + return IntegerBaseFlags.isDecimal(flags); + } + + public boolean isOctal() { + return IntegerBaseFlags.isOctal(flags); + } + + public boolean isHexadecimal() { + return IntegerBaseFlags.isHexadecimal(flags); + } + + public void visitChildNodes(AbstractNodeVisitor visitor) { + } + + public Node[] childNodes() { + return EMPTY_ARRAY; + } + + public T accept(AbstractNodeVisitor visitor) { + return visitor.visitRationalNode(this); + } + + @Override + protected String toString(String indent) { + StringBuilder builder = new StringBuilder(); + builder.append(this.getClass().getSimpleName()); + if (hasNewLineFlag()) { + builder.append("[Li]"); + } + builder.append('\n'); + String nextIndent = indent + " "; + builder.append(nextIndent); + builder.append("IntegerBaseFlags: "); + builder.append(flags); + builder.append('\n'); + builder.append(nextIndent); + builder.append("numerator: "); + builder.append(this.numerator); + builder.append('\n'); + builder.append(nextIndent); + builder.append("denominator: "); + builder.append(this.denominator); + builder.append('\n'); + return builder.toString(); + } + } + + /** + *
+     * Represents the use of the `redo` keyword.
+     *
+     *     redo
+     *     ^^^^
+     * 
+ */ + public static final class RedoNode extends Node { + + public RedoNode(int startOffset, int length) { + super(startOffset, length); + } + + public void visitChildNodes(AbstractNodeVisitor visitor) { + } + + public Node[] childNodes() { + return EMPTY_ARRAY; + } + + public T accept(AbstractNodeVisitor visitor) { + return visitor.visitRedoNode(this); + } + + @Override + protected String toString(String indent) { + StringBuilder builder = new StringBuilder(); + builder.append(this.getClass().getSimpleName()); + if (hasNewLineFlag()) { + builder.append("[Li]"); + } + builder.append('\n'); + String nextIndent = indent + " "; + return builder.toString(); + } + } + + /** + *
+     * Represents a regular expression literal with no interpolation.
+     *
+     *     /foo/i
+     *     ^^^^^^
+     * 
+ */ + public static final class RegularExpressionNode extends Node { + public final short flags; + public final byte[] unescaped; + + public RegularExpressionNode(int startOffset, int length, short flags, byte[] unescaped) { + super(startOffset, length); + this.flags = flags; + this.unescaped = unescaped; + } + + public boolean isIgnoreCase() { + return RegularExpressionFlags.isIgnoreCase(flags); + } + + public boolean isExtended() { + return RegularExpressionFlags.isExtended(flags); + } + + public boolean isMultiLine() { + return RegularExpressionFlags.isMultiLine(flags); + } + + public boolean isOnce() { + return RegularExpressionFlags.isOnce(flags); + } + + public boolean isEucJp() { + return RegularExpressionFlags.isEucJp(flags); + } + + public boolean isAscii8bit() { + return RegularExpressionFlags.isAscii8bit(flags); + } + + public boolean isWindows31j() { + return RegularExpressionFlags.isWindows31j(flags); + } + + public boolean isUtf8() { + return RegularExpressionFlags.isUtf8(flags); + } + + public boolean isForcedUtf8Encoding() { + return RegularExpressionFlags.isForcedUtf8Encoding(flags); + } + + public boolean isForcedBinaryEncoding() { + return RegularExpressionFlags.isForcedBinaryEncoding(flags); + } + + public boolean isForcedUsAsciiEncoding() { + return RegularExpressionFlags.isForcedUsAsciiEncoding(flags); + } + + public void visitChildNodes(AbstractNodeVisitor visitor) { + } + + public Node[] childNodes() { + return EMPTY_ARRAY; + } + + public T accept(AbstractNodeVisitor visitor) { + return visitor.visitRegularExpressionNode(this); + } + + @Override + protected String toString(String indent) { + StringBuilder builder = new StringBuilder(); + builder.append(this.getClass().getSimpleName()); + if (hasNewLineFlag()) { + builder.append("[Li]"); + } + builder.append('\n'); + String nextIndent = indent + " "; + builder.append(nextIndent); + builder.append("RegularExpressionFlags: "); + builder.append(flags); + builder.append('\n'); + builder.append(nextIndent); + builder.append("unescaped: "); + builder.append('"' + asString(this.unescaped) + '"'); + builder.append('\n'); + return builder.toString(); + } + } + + /** + *
+     * Represents a required keyword parameter to a method, block, or lambda definition.
+     *
+     *     def a(b: )
+     *           ^^
+     *     end
+     * 
+ */ + public static final class RequiredKeywordParameterNode extends Node { + public final short flags; + public final byte[] name; + + public RequiredKeywordParameterNode(int startOffset, int length, short flags, byte[] name) { + super(startOffset, length); + this.flags = flags; + this.name = name; + } + + public boolean isRepeatedParameter() { + return ParameterFlags.isRepeatedParameter(flags); + } + + public void visitChildNodes(AbstractNodeVisitor visitor) { + } + + public Node[] childNodes() { + return EMPTY_ARRAY; + } + + public T accept(AbstractNodeVisitor visitor) { + return visitor.visitRequiredKeywordParameterNode(this); + } + + @Override + protected String toString(String indent) { + StringBuilder builder = new StringBuilder(); + builder.append(this.getClass().getSimpleName()); + if (hasNewLineFlag()) { + builder.append("[Li]"); + } + builder.append('\n'); + String nextIndent = indent + " "; + builder.append(nextIndent); + builder.append("ParameterFlags: "); + builder.append(flags); + builder.append('\n'); + builder.append(nextIndent); + builder.append("name: "); + builder.append('"').append(asString(this.name)).append('"'); + builder.append('\n'); + return builder.toString(); + } + } + + /** + *
+     * Represents a required parameter to a method, block, or lambda definition.
+     *
+     *     def a(b)
+     *           ^
+     *     end
+     * 
+ */ + public static final class RequiredParameterNode extends Node { + public final short flags; + public final byte[] name; + + public RequiredParameterNode(int startOffset, int length, short flags, byte[] name) { + super(startOffset, length); + this.flags = flags; + this.name = name; + } + + public boolean isRepeatedParameter() { + return ParameterFlags.isRepeatedParameter(flags); + } + + public void visitChildNodes(AbstractNodeVisitor visitor) { + } + + public Node[] childNodes() { + return EMPTY_ARRAY; + } + + public T accept(AbstractNodeVisitor visitor) { + return visitor.visitRequiredParameterNode(this); + } + + @Override + protected String toString(String indent) { + StringBuilder builder = new StringBuilder(); + builder.append(this.getClass().getSimpleName()); + if (hasNewLineFlag()) { + builder.append("[Li]"); + } + builder.append('\n'); + String nextIndent = indent + " "; + builder.append(nextIndent); + builder.append("ParameterFlags: "); + builder.append(flags); + builder.append('\n'); + builder.append(nextIndent); + builder.append("name: "); + builder.append('"').append(asString(this.name)).append('"'); + builder.append('\n'); + return builder.toString(); + } + } + + /** + *
+     * Represents an expression modified with a rescue.
+     *
+     *     foo rescue nil
+     *     ^^^^^^^^^^^^^^
+     * 
+ */ + public static final class RescueModifierNode extends Node { + public final Node expression; + public final Node rescue_expression; + + public RescueModifierNode(int startOffset, int length, Node expression, Node rescue_expression) { + super(startOffset, length); + this.expression = expression; + this.rescue_expression = rescue_expression; + } + + @Override + public void setNewLineFlag(Source source, boolean[] newlineMarked) { + this.expression.setNewLineFlag(source, newlineMarked); + } + + public void visitChildNodes(AbstractNodeVisitor visitor) { + this.expression.accept(visitor); + this.rescue_expression.accept(visitor); + } + + public Node[] childNodes() { + return new Node[] { this.expression, this.rescue_expression }; + } + + public T accept(AbstractNodeVisitor visitor) { + return visitor.visitRescueModifierNode(this); + } + + @Override + protected String toString(String indent) { + StringBuilder builder = new StringBuilder(); + builder.append(this.getClass().getSimpleName()); + if (hasNewLineFlag()) { + builder.append("[Li]"); + } + builder.append('\n'); + String nextIndent = indent + " "; + builder.append(nextIndent); + builder.append("expression: "); + builder.append(this.expression.toString(nextIndent)); + builder.append(nextIndent); + builder.append("rescue_expression: "); + builder.append(this.rescue_expression.toString(nextIndent)); + return builder.toString(); + } + } + + /** + *
+     * 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.
+     * 
+ */ + public static final class RescueNode extends Node { + public final Node[] exceptions; + @Nullable + @UnionType({ LocalVariableTargetNode.class, InstanceVariableTargetNode.class, ClassVariableTargetNode.class, GlobalVariableTargetNode.class, ConstantTargetNode.class, ConstantPathTargetNode.class, CallTargetNode.class, IndexTargetNode.class }) + public final Node reference; + @Nullable + public final StatementsNode statements; + @Nullable + public final RescueNode subsequent; + + public RescueNode(int startOffset, int length, Node[] exceptions, Node reference, StatementsNode statements, RescueNode subsequent) { + super(startOffset, length); + this.exceptions = exceptions; + this.reference = reference; + this.statements = statements; + this.subsequent = subsequent; + } + + public void visitChildNodes(AbstractNodeVisitor visitor) { + for (Nodes.Node child : this.exceptions) { + child.accept(visitor); + } + if (this.reference != null) { + this.reference.accept(visitor); + } + if (this.statements != null) { + this.statements.accept(visitor); + } + if (this.subsequent != null) { + this.subsequent.accept(visitor); + } + } + + public Node[] childNodes() { + ArrayList childNodes = new ArrayList<>(); + childNodes.addAll(Arrays.asList(this.exceptions)); + childNodes.add(this.reference); + childNodes.add(this.statements); + childNodes.add(this.subsequent); + return childNodes.toArray(EMPTY_ARRAY); + } + + public T accept(AbstractNodeVisitor visitor) { + return visitor.visitRescueNode(this); + } + + @Override + protected String toString(String indent) { + StringBuilder builder = new StringBuilder(); + builder.append(this.getClass().getSimpleName()); + if (hasNewLineFlag()) { + builder.append("[Li]"); + } + builder.append('\n'); + String nextIndent = indent + " "; + String nextNextIndent = nextIndent + " "; + builder.append(nextIndent); + builder.append("exceptions: "); + builder.append('\n'); + for (Node child : this.exceptions) { + builder.append(nextNextIndent).append(child.toString(nextNextIndent)); + } + builder.append(nextIndent); + builder.append("reference: "); + builder.append(this.reference == null ? "null\n" : this.reference.toString(nextIndent)); + builder.append(nextIndent); + builder.append("statements: "); + builder.append(this.statements == null ? "null\n" : this.statements.toString(nextIndent)); + builder.append(nextIndent); + builder.append("subsequent: "); + builder.append(this.subsequent == null ? "null\n" : this.subsequent.toString(nextIndent)); + return builder.toString(); + } + } + + /** + *
+     * Represents a rest parameter to a method, block, or lambda definition.
+     *
+     *     def a(*b)
+     *           ^^
+     *     end
+     * 
+ */ + public static final class RestParameterNode extends Node { + public final short flags; + @Nullable + public final byte[] name; + + public RestParameterNode(int startOffset, int length, short flags, byte[] name) { + super(startOffset, length); + this.flags = flags; + this.name = name; + } + + public boolean isRepeatedParameter() { + return ParameterFlags.isRepeatedParameter(flags); + } + + public void visitChildNodes(AbstractNodeVisitor visitor) { + } + + public Node[] childNodes() { + return EMPTY_ARRAY; + } + + public T accept(AbstractNodeVisitor visitor) { + return visitor.visitRestParameterNode(this); + } + + @Override + protected String toString(String indent) { + StringBuilder builder = new StringBuilder(); + builder.append(this.getClass().getSimpleName()); + if (hasNewLineFlag()) { + builder.append("[Li]"); + } + builder.append('\n'); + String nextIndent = indent + " "; + builder.append(nextIndent); + builder.append("ParameterFlags: "); + builder.append(flags); + builder.append('\n'); + builder.append(nextIndent); + builder.append("name: "); + builder.append(this.name == null ? "null" : "\"" + asString(this.name) + "\""); + builder.append('\n'); + return builder.toString(); + } + } + + /** + *
+     * Represents the use of the `retry` keyword.
+     *
+     *     retry
+     *     ^^^^^
+     * 
+ */ + public static final class RetryNode extends Node { + + public RetryNode(int startOffset, int length) { + super(startOffset, length); + } + + public void visitChildNodes(AbstractNodeVisitor visitor) { + } + + public Node[] childNodes() { + return EMPTY_ARRAY; + } + + public T accept(AbstractNodeVisitor visitor) { + return visitor.visitRetryNode(this); + } + + @Override + protected String toString(String indent) { + StringBuilder builder = new StringBuilder(); + builder.append(this.getClass().getSimpleName()); + if (hasNewLineFlag()) { + builder.append("[Li]"); + } + builder.append('\n'); + String nextIndent = indent + " "; + return builder.toString(); + } + } + + /** + *
+     * Represents the use of the `return` keyword.
+     *
+     *     return 1
+     *     ^^^^^^^^
+     * 
+ */ + public static final class ReturnNode extends Node { + @Nullable + public final ArgumentsNode arguments; + + public ReturnNode(int startOffset, int length, ArgumentsNode arguments) { + super(startOffset, length); + this.arguments = arguments; + } + + public void visitChildNodes(AbstractNodeVisitor visitor) { + if (this.arguments != null) { + this.arguments.accept(visitor); + } + } + + public Node[] childNodes() { + return new Node[] { this.arguments }; + } + + public T accept(AbstractNodeVisitor visitor) { + return visitor.visitReturnNode(this); + } + + @Override + protected String toString(String indent) { + StringBuilder builder = new StringBuilder(); + builder.append(this.getClass().getSimpleName()); + if (hasNewLineFlag()) { + builder.append("[Li]"); + } + builder.append('\n'); + String nextIndent = indent + " "; + builder.append(nextIndent); + builder.append("arguments: "); + builder.append(this.arguments == null ? "null\n" : this.arguments.toString(nextIndent)); + return builder.toString(); + } + } + + /** + *
+     * Represents the `self` keyword.
+     *
+     *     self
+     *     ^^^^
+     * 
+ */ + public static final class SelfNode extends Node { + + public SelfNode(int startOffset, int length) { + super(startOffset, length); + } + + public void visitChildNodes(AbstractNodeVisitor visitor) { + } + + public Node[] childNodes() { + return EMPTY_ARRAY; + } + + public T accept(AbstractNodeVisitor visitor) { + return visitor.visitSelfNode(this); + } + + @Override + protected String toString(String indent) { + StringBuilder builder = new StringBuilder(); + builder.append(this.getClass().getSimpleName()); + if (hasNewLineFlag()) { + builder.append("[Li]"); + } + builder.append('\n'); + String nextIndent = indent + " "; + return builder.toString(); + } + } + + /** + *
+     * 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 }
+     *     ^^^^^^^^^^^^
+     * 
+ */ + public static final class ShareableConstantNode extends Node { + public final short flags; + /** + *
+         * The constant write that should be modified with the shareability state.
+         * 
+ */ + @UnionType({ ConstantWriteNode.class, ConstantAndWriteNode.class, ConstantOrWriteNode.class, ConstantOperatorWriteNode.class, ConstantPathWriteNode.class, ConstantPathAndWriteNode.class, ConstantPathOrWriteNode.class, ConstantPathOperatorWriteNode.class }) + public final Node write; + + public ShareableConstantNode(int startOffset, int length, short flags, Node write) { + super(startOffset, length); + this.flags = flags; + this.write = write; + } + + public boolean isLiteral() { + return ShareableConstantNodeFlags.isLiteral(flags); + } + + public boolean isExperimentalEverything() { + return ShareableConstantNodeFlags.isExperimentalEverything(flags); + } + + public boolean isExperimentalCopy() { + return ShareableConstantNodeFlags.isExperimentalCopy(flags); + } + + public void visitChildNodes(AbstractNodeVisitor visitor) { + this.write.accept(visitor); + } + + public Node[] childNodes() { + return new Node[] { this.write }; + } + + public T accept(AbstractNodeVisitor visitor) { + return visitor.visitShareableConstantNode(this); + } + + @Override + protected String toString(String indent) { + StringBuilder builder = new StringBuilder(); + builder.append(this.getClass().getSimpleName()); + if (hasNewLineFlag()) { + builder.append("[Li]"); + } + builder.append('\n'); + String nextIndent = indent + " "; + builder.append(nextIndent); + builder.append("ShareableConstantNodeFlags: "); + builder.append(flags); + builder.append('\n'); + builder.append(nextIndent); + builder.append("write: "); + builder.append(this.write.toString(nextIndent)); + return builder.toString(); + } + } + + /** + *
+     * Represents a singleton class declaration involving the `class` keyword.
+     *
+     *     class << self end
+     *     ^^^^^^^^^^^^^^^^^
+     * 
+ */ + public static final class SingletonClassNode extends Node { + public final byte[][] locals; + public final Node expression; + @Nullable + @UnionType({ StatementsNode.class, BeginNode.class }) + public final Node body; + + public SingletonClassNode(int startOffset, int length, byte[][] locals, Node expression, Node body) { + super(startOffset, length); + this.locals = locals; + this.expression = expression; + this.body = body; + } + + public void visitChildNodes(AbstractNodeVisitor visitor) { + this.expression.accept(visitor); + if (this.body != null) { + this.body.accept(visitor); + } + } + + public Node[] childNodes() { + return new Node[] { this.expression, this.body }; + } + + public T accept(AbstractNodeVisitor visitor) { + return visitor.visitSingletonClassNode(this); + } + + @Override + protected String toString(String indent) { + StringBuilder builder = new StringBuilder(); + builder.append(this.getClass().getSimpleName()); + if (hasNewLineFlag()) { + builder.append("[Li]"); + } + builder.append('\n'); + String nextIndent = indent + " "; + String nextNextIndent = nextIndent + " "; + builder.append(nextIndent); + builder.append("locals: "); + builder.append('\n'); + for (byte[] constant : this.locals) { + builder.append(nextNextIndent).append('"').append(asString(constant)).append('"').append('\n'); + } + builder.append(nextIndent); + builder.append("expression: "); + builder.append(this.expression.toString(nextIndent)); + builder.append(nextIndent); + builder.append("body: "); + builder.append(this.body == null ? "null\n" : this.body.toString(nextIndent)); + return builder.toString(); + } + } + + /** + *
+     * Represents the use of the `__ENCODING__` keyword.
+     *
+     *     __ENCODING__
+     *     ^^^^^^^^^^^^
+     * 
+ */ + public static final class SourceEncodingNode extends Node { + + public SourceEncodingNode(int startOffset, int length) { + super(startOffset, length); + } + + public void visitChildNodes(AbstractNodeVisitor visitor) { + } + + public Node[] childNodes() { + return EMPTY_ARRAY; + } + + public T accept(AbstractNodeVisitor visitor) { + return visitor.visitSourceEncodingNode(this); + } + + @Override + protected String toString(String indent) { + StringBuilder builder = new StringBuilder(); + builder.append(this.getClass().getSimpleName()); + if (hasNewLineFlag()) { + builder.append("[Li]"); + } + builder.append('\n'); + String nextIndent = indent + " "; + return builder.toString(); + } + } + + /** + *
+     * Represents the use of the `__FILE__` keyword.
+     *
+     *     __FILE__
+     *     ^^^^^^^^
+     * 
+ */ + public static final class SourceFileNode extends Node { + public final short flags; + /** + *
+         * Represents the file path being parsed. This corresponds directly to the `filepath` option given to the various `Prism.parse*` APIs.
+         * 
+ */ + public final byte[] filepath; + + public SourceFileNode(int startOffset, int length, short flags, byte[] filepath) { + super(startOffset, length); + this.flags = flags; + this.filepath = filepath; + } + + public boolean isForcedUtf8Encoding() { + return StringFlags.isForcedUtf8Encoding(flags); + } + + public boolean isForcedBinaryEncoding() { + return StringFlags.isForcedBinaryEncoding(flags); + } + + public boolean isFrozen() { + return StringFlags.isFrozen(flags); + } + + public boolean isMutable() { + return StringFlags.isMutable(flags); + } + + public void visitChildNodes(AbstractNodeVisitor visitor) { + } + + public Node[] childNodes() { + return EMPTY_ARRAY; + } + + public T accept(AbstractNodeVisitor visitor) { + return visitor.visitSourceFileNode(this); + } + + @Override + protected String toString(String indent) { + StringBuilder builder = new StringBuilder(); + builder.append(this.getClass().getSimpleName()); + if (hasNewLineFlag()) { + builder.append("[Li]"); + } + builder.append('\n'); + String nextIndent = indent + " "; + builder.append(nextIndent); + builder.append("StringFlags: "); + builder.append(flags); + builder.append('\n'); + builder.append(nextIndent); + builder.append("filepath: "); + builder.append('"' + asString(this.filepath) + '"'); + builder.append('\n'); + return builder.toString(); + } + } + + /** + *
+     * Represents the use of the `__LINE__` keyword.
+     *
+     *     __LINE__
+     *     ^^^^^^^^
+     * 
+ */ + public static final class SourceLineNode extends Node { + + public SourceLineNode(int startOffset, int length) { + super(startOffset, length); + } + + public void visitChildNodes(AbstractNodeVisitor visitor) { + } + + public Node[] childNodes() { + return EMPTY_ARRAY; + } + + public T accept(AbstractNodeVisitor visitor) { + return visitor.visitSourceLineNode(this); + } + + @Override + protected String toString(String indent) { + StringBuilder builder = new StringBuilder(); + builder.append(this.getClass().getSimpleName()); + if (hasNewLineFlag()) { + builder.append("[Li]"); + } + builder.append('\n'); + String nextIndent = indent + " "; + return builder.toString(); + } + } + + /** + *
+     * Represents the use of the splat operator.
+     *
+     *     [*a]
+     *      ^^
+     * 
+ */ + public static final class SplatNode extends Node { + @Nullable + public final Node expression; + + public SplatNode(int startOffset, int length, Node expression) { + super(startOffset, length); + this.expression = expression; + } + + public void visitChildNodes(AbstractNodeVisitor visitor) { + if (this.expression != null) { + this.expression.accept(visitor); + } + } + + public Node[] childNodes() { + return new Node[] { this.expression }; + } + + public T accept(AbstractNodeVisitor visitor) { + return visitor.visitSplatNode(this); + } + + @Override + protected String toString(String indent) { + StringBuilder builder = new StringBuilder(); + builder.append(this.getClass().getSimpleName()); + if (hasNewLineFlag()) { + builder.append("[Li]"); + } + builder.append('\n'); + String nextIndent = indent + " "; + builder.append(nextIndent); + builder.append("expression: "); + builder.append(this.expression == null ? "null\n" : this.expression.toString(nextIndent)); + return builder.toString(); + } + } + + /** + *
+     * Represents a set of statements contained within some scope.
+     *
+     *     foo; bar; baz
+     *     ^^^^^^^^^^^^^
+     * 
+ */ + public static final class StatementsNode extends Node { + public final Node[] body; + + public StatementsNode(int startOffset, int length, Node[] body) { + super(startOffset, length); + this.body = body; + } + + public void visitChildNodes(AbstractNodeVisitor visitor) { + for (Nodes.Node child : this.body) { + child.accept(visitor); + } + } + + public Node[] childNodes() { + ArrayList childNodes = new ArrayList<>(); + childNodes.addAll(Arrays.asList(this.body)); + return childNodes.toArray(EMPTY_ARRAY); + } + + public T accept(AbstractNodeVisitor visitor) { + return visitor.visitStatementsNode(this); + } + + @Override + protected String toString(String indent) { + StringBuilder builder = new StringBuilder(); + builder.append(this.getClass().getSimpleName()); + if (hasNewLineFlag()) { + builder.append("[Li]"); + } + builder.append('\n'); + String nextIndent = indent + " "; + String nextNextIndent = nextIndent + " "; + builder.append(nextIndent); + builder.append("body: "); + builder.append('\n'); + for (Node child : this.body) { + builder.append(nextNextIndent).append(child.toString(nextNextIndent)); + } + return builder.toString(); + } + } + + /** + *
+     * 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"
+     *      ^^^^      ^^^^
+     * 
+ */ + public static final class StringNode extends Node { + public final short flags; + public final byte[] unescaped; + + public StringNode(int startOffset, int length, short flags, byte[] unescaped) { + super(startOffset, length); + this.flags = flags; + this.unescaped = unescaped; + } + + public boolean isForcedUtf8Encoding() { + return StringFlags.isForcedUtf8Encoding(flags); + } + + public boolean isForcedBinaryEncoding() { + return StringFlags.isForcedBinaryEncoding(flags); + } + + public boolean isFrozen() { + return StringFlags.isFrozen(flags); + } + + public boolean isMutable() { + return StringFlags.isMutable(flags); + } + + public void visitChildNodes(AbstractNodeVisitor visitor) { + } + + public Node[] childNodes() { + return EMPTY_ARRAY; + } + + public T accept(AbstractNodeVisitor visitor) { + return visitor.visitStringNode(this); + } + + @Override + protected String toString(String indent) { + StringBuilder builder = new StringBuilder(); + builder.append(this.getClass().getSimpleName()); + if (hasNewLineFlag()) { + builder.append("[Li]"); + } + builder.append('\n'); + String nextIndent = indent + " "; + builder.append(nextIndent); + builder.append("StringFlags: "); + builder.append(flags); + builder.append('\n'); + builder.append(nextIndent); + builder.append("unescaped: "); + builder.append('"' + asString(this.unescaped) + '"'); + builder.append('\n'); + return builder.toString(); + } + } + + /** + *
+     * 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.
+     * 
+ */ + public static final class SuperNode extends Node { + /** + *
+         * Can be only `nil` when there are empty parentheses, like `super()`.
+         * 
+ */ + @Nullable + public final ArgumentsNode arguments; + @Nullable + @UnionType({ BlockNode.class, BlockArgumentNode.class }) + public final Node block; + + public SuperNode(int startOffset, int length, ArgumentsNode arguments, Node block) { + super(startOffset, length); + this.arguments = arguments; + this.block = block; + } + + public void visitChildNodes(AbstractNodeVisitor visitor) { + if (this.arguments != null) { + this.arguments.accept(visitor); + } + if (this.block != null) { + this.block.accept(visitor); + } + } + + public Node[] childNodes() { + return new Node[] { this.arguments, this.block }; + } + + public T accept(AbstractNodeVisitor visitor) { + return visitor.visitSuperNode(this); + } + + @Override + protected String toString(String indent) { + StringBuilder builder = new StringBuilder(); + builder.append(this.getClass().getSimpleName()); + if (hasNewLineFlag()) { + builder.append("[Li]"); + } + builder.append('\n'); + String nextIndent = indent + " "; + builder.append(nextIndent); + builder.append("arguments: "); + builder.append(this.arguments == null ? "null\n" : this.arguments.toString(nextIndent)); + builder.append(nextIndent); + builder.append("block: "); + builder.append(this.block == null ? "null\n" : this.block.toString(nextIndent)); + return builder.toString(); + } + } + + /** + *
+     * Represents a symbol literal or a symbol contained within a `%i` list.
+     *
+     *     :foo
+     *     ^^^^
+     *
+     *     %i[foo]
+     *        ^^^
+     * 
+ */ + public static final class SymbolNode extends Node { + public final short flags; + public final byte[] unescaped; + + public SymbolNode(int startOffset, int length, short flags, byte[] unescaped) { + super(startOffset, length); + this.flags = flags; + this.unescaped = unescaped; + } + + public boolean isForcedUtf8Encoding() { + return SymbolFlags.isForcedUtf8Encoding(flags); + } + + public boolean isForcedBinaryEncoding() { + return SymbolFlags.isForcedBinaryEncoding(flags); + } + + public boolean isForcedUsAsciiEncoding() { + return SymbolFlags.isForcedUsAsciiEncoding(flags); + } + + public void visitChildNodes(AbstractNodeVisitor visitor) { + } + + public Node[] childNodes() { + return EMPTY_ARRAY; + } + + public T accept(AbstractNodeVisitor visitor) { + return visitor.visitSymbolNode(this); + } + + @Override + protected String toString(String indent) { + StringBuilder builder = new StringBuilder(); + builder.append(this.getClass().getSimpleName()); + if (hasNewLineFlag()) { + builder.append("[Li]"); + } + builder.append('\n'); + String nextIndent = indent + " "; + builder.append(nextIndent); + builder.append("SymbolFlags: "); + builder.append(flags); + builder.append('\n'); + builder.append(nextIndent); + builder.append("unescaped: "); + builder.append('"' + asString(this.unescaped) + '"'); + builder.append('\n'); + return builder.toString(); + } + } + + /** + *
+     * Represents the use of the literal `true` keyword.
+     *
+     *     true
+     *     ^^^^
+     * 
+ */ + public static final class TrueNode extends Node { + + public TrueNode(int startOffset, int length) { + super(startOffset, length); + } + + public void visitChildNodes(AbstractNodeVisitor visitor) { + } + + public Node[] childNodes() { + return EMPTY_ARRAY; + } + + public T accept(AbstractNodeVisitor visitor) { + return visitor.visitTrueNode(this); + } + + @Override + protected String toString(String indent) { + StringBuilder builder = new StringBuilder(); + builder.append(this.getClass().getSimpleName()); + if (hasNewLineFlag()) { + builder.append("[Li]"); + } + builder.append('\n'); + String nextIndent = indent + " "; + return builder.toString(); + } + } + + /** + *
+     * Represents the use of the `undef` keyword.
+     *
+     *     undef :foo, :bar, :baz
+     *     ^^^^^^^^^^^^^^^^^^^^^^
+     * 
+ */ + public static final class UndefNode extends Node { + @UnionType({ SymbolNode.class, InterpolatedSymbolNode.class }) + public final Node[] names; + + public UndefNode(int startOffset, int length, Node[] names) { + super(startOffset, length); + this.names = names; + } + + public void visitChildNodes(AbstractNodeVisitor visitor) { + for (Nodes.Node child : this.names) { + child.accept(visitor); + } + } + + public Node[] childNodes() { + ArrayList childNodes = new ArrayList<>(); + childNodes.addAll(Arrays.asList(this.names)); + return childNodes.toArray(EMPTY_ARRAY); + } + + public T accept(AbstractNodeVisitor visitor) { + return visitor.visitUndefNode(this); + } + + @Override + protected String toString(String indent) { + StringBuilder builder = new StringBuilder(); + builder.append(this.getClass().getSimpleName()); + if (hasNewLineFlag()) { + builder.append("[Li]"); + } + builder.append('\n'); + String nextIndent = indent + " "; + String nextNextIndent = nextIndent + " "; + builder.append(nextIndent); + builder.append("names: "); + builder.append('\n'); + for (Node child : this.names) { + builder.append(nextNextIndent).append(child.toString(nextNextIndent)); + } + return builder.toString(); + } + } + + /** + *
+     * Represents the use of the `unless` keyword, either in the block form or the modifier form.
+     *
+     *     bar unless foo
+     *     ^^^^^^^^^^^^^^
+     *
+     *     unless foo then bar end
+     *     ^^^^^^^^^^^^^^^^^^^^^^^
+     * 
+ */ + public static final class UnlessNode extends Node { + /** + *
+         * 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
+         *                ^^^^
+         * 
+ */ + public final Node predicate; + /** + *
+         * 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
+         *                      ^^^
+         * 
+ */ + @Nullable + public final StatementsNode statements; + /** + *
+         * The else clause of the unless expression, if present.
+         *
+         *     unless cond then bar else baz end
+         *                          ^^^^^^^^^^^^
+         * 
+ */ + @Nullable + public final ElseNode else_clause; + + public UnlessNode(int startOffset, int length, Node predicate, StatementsNode statements, ElseNode else_clause) { + super(startOffset, length); + this.predicate = predicate; + this.statements = statements; + this.else_clause = else_clause; + } + + @Override + public void setNewLineFlag(Source source, boolean[] newlineMarked) { + this.predicate.setNewLineFlag(source, newlineMarked); + } + + public void visitChildNodes(AbstractNodeVisitor visitor) { + this.predicate.accept(visitor); + if (this.statements != null) { + this.statements.accept(visitor); + } + if (this.else_clause != null) { + this.else_clause.accept(visitor); + } + } + + public Node[] childNodes() { + return new Node[] { this.predicate, this.statements, this.else_clause }; + } + + public T accept(AbstractNodeVisitor visitor) { + return visitor.visitUnlessNode(this); + } + + @Override + protected String toString(String indent) { + StringBuilder builder = new StringBuilder(); + builder.append(this.getClass().getSimpleName()); + if (hasNewLineFlag()) { + builder.append("[Li]"); + } + builder.append('\n'); + String nextIndent = indent + " "; + builder.append(nextIndent); + builder.append("predicate: "); + builder.append(this.predicate.toString(nextIndent)); + builder.append(nextIndent); + builder.append("statements: "); + builder.append(this.statements == null ? "null\n" : this.statements.toString(nextIndent)); + builder.append(nextIndent); + builder.append("else_clause: "); + builder.append(this.else_clause == null ? "null\n" : this.else_clause.toString(nextIndent)); + return builder.toString(); + } + } + + /** + *
+     * Represents the use of the `until` keyword, either in the block form or the modifier form.
+     *
+     *     bar until foo
+     *     ^^^^^^^^^^^^^
+     *
+     *     until foo do bar end
+     *     ^^^^^^^^^^^^^^^^^^^^
+     * 
+ */ + public static final class UntilNode extends Node { + public final short flags; + public final Node predicate; + @Nullable + public final StatementsNode statements; + + public UntilNode(int startOffset, int length, short flags, Node predicate, StatementsNode statements) { + super(startOffset, length); + this.flags = flags; + this.predicate = predicate; + this.statements = statements; + } + + public boolean isBeginModifier() { + return LoopFlags.isBeginModifier(flags); + } + + @Override + public void setNewLineFlag(Source source, boolean[] newlineMarked) { + this.predicate.setNewLineFlag(source, newlineMarked); + } + + public void visitChildNodes(AbstractNodeVisitor visitor) { + this.predicate.accept(visitor); + if (this.statements != null) { + this.statements.accept(visitor); + } + } + + public Node[] childNodes() { + return new Node[] { this.predicate, this.statements }; + } + + public T accept(AbstractNodeVisitor visitor) { + return visitor.visitUntilNode(this); + } + + @Override + protected String toString(String indent) { + StringBuilder builder = new StringBuilder(); + builder.append(this.getClass().getSimpleName()); + if (hasNewLineFlag()) { + builder.append("[Li]"); + } + builder.append('\n'); + String nextIndent = indent + " "; + builder.append(nextIndent); + builder.append("LoopFlags: "); + builder.append(flags); + builder.append('\n'); + builder.append(nextIndent); + builder.append("predicate: "); + builder.append(this.predicate.toString(nextIndent)); + builder.append(nextIndent); + builder.append("statements: "); + builder.append(this.statements == null ? "null\n" : this.statements.toString(nextIndent)); + return builder.toString(); + } + } + + /** + *
+     * Represents the use of the `when` keyword within a case statement.
+     *
+     *     case true
+     *     when true
+     *     ^^^^^^^^^
+     *     end
+     * 
+ */ + public static final class WhenNode extends Node { + public final Node[] conditions; + @Nullable + public final StatementsNode statements; + + public WhenNode(int startOffset, int length, Node[] conditions, StatementsNode statements) { + super(startOffset, length); + this.conditions = conditions; + this.statements = statements; + } + + public void visitChildNodes(AbstractNodeVisitor visitor) { + for (Nodes.Node child : this.conditions) { + child.accept(visitor); + } + if (this.statements != null) { + this.statements.accept(visitor); + } + } + + public Node[] childNodes() { + ArrayList childNodes = new ArrayList<>(); + childNodes.addAll(Arrays.asList(this.conditions)); + childNodes.add(this.statements); + return childNodes.toArray(EMPTY_ARRAY); + } + + public T accept(AbstractNodeVisitor visitor) { + return visitor.visitWhenNode(this); + } + + @Override + protected String toString(String indent) { + StringBuilder builder = new StringBuilder(); + builder.append(this.getClass().getSimpleName()); + if (hasNewLineFlag()) { + builder.append("[Li]"); + } + builder.append('\n'); + String nextIndent = indent + " "; + String nextNextIndent = nextIndent + " "; + builder.append(nextIndent); + builder.append("conditions: "); + builder.append('\n'); + for (Node child : this.conditions) { + builder.append(nextNextIndent).append(child.toString(nextNextIndent)); + } + builder.append(nextIndent); + builder.append("statements: "); + builder.append(this.statements == null ? "null\n" : this.statements.toString(nextIndent)); + return builder.toString(); + } + } + + /** + *
+     * Represents the use of the `while` keyword, either in the block form or the modifier form.
+     *
+     *     bar while foo
+     *     ^^^^^^^^^^^^^
+     *
+     *     while foo do bar end
+     *     ^^^^^^^^^^^^^^^^^^^^
+     * 
+ */ + public static final class WhileNode extends Node { + public final short flags; + public final Node predicate; + @Nullable + public final StatementsNode statements; + + public WhileNode(int startOffset, int length, short flags, Node predicate, StatementsNode statements) { + super(startOffset, length); + this.flags = flags; + this.predicate = predicate; + this.statements = statements; + } + + public boolean isBeginModifier() { + return LoopFlags.isBeginModifier(flags); + } + + @Override + public void setNewLineFlag(Source source, boolean[] newlineMarked) { + this.predicate.setNewLineFlag(source, newlineMarked); + } + + public void visitChildNodes(AbstractNodeVisitor visitor) { + this.predicate.accept(visitor); + if (this.statements != null) { + this.statements.accept(visitor); + } + } + + public Node[] childNodes() { + return new Node[] { this.predicate, this.statements }; + } + + public T accept(AbstractNodeVisitor visitor) { + return visitor.visitWhileNode(this); + } + + @Override + protected String toString(String indent) { + StringBuilder builder = new StringBuilder(); + builder.append(this.getClass().getSimpleName()); + if (hasNewLineFlag()) { + builder.append("[Li]"); + } + builder.append('\n'); + String nextIndent = indent + " "; + builder.append(nextIndent); + builder.append("LoopFlags: "); + builder.append(flags); + builder.append('\n'); + builder.append(nextIndent); + builder.append("predicate: "); + builder.append(this.predicate.toString(nextIndent)); + builder.append(nextIndent); + builder.append("statements: "); + builder.append(this.statements == null ? "null\n" : this.statements.toString(nextIndent)); + return builder.toString(); + } + } + + /** + *
+     * Represents an xstring literal with no interpolation.
+     *
+     *     `foo`
+     *     ^^^^^
+     * 
+ */ + public static final class XStringNode extends Node { + public final short flags; + public final byte[] unescaped; + + public XStringNode(int startOffset, int length, short flags, byte[] unescaped) { + super(startOffset, length); + this.flags = flags; + this.unescaped = unescaped; + } + + public boolean isForcedUtf8Encoding() { + return EncodingFlags.isForcedUtf8Encoding(flags); + } + + public boolean isForcedBinaryEncoding() { + return EncodingFlags.isForcedBinaryEncoding(flags); + } + + public void visitChildNodes(AbstractNodeVisitor visitor) { + } + + public Node[] childNodes() { + return EMPTY_ARRAY; + } + + public T accept(AbstractNodeVisitor visitor) { + return visitor.visitXStringNode(this); + } + + @Override + protected String toString(String indent) { + StringBuilder builder = new StringBuilder(); + builder.append(this.getClass().getSimpleName()); + if (hasNewLineFlag()) { + builder.append("[Li]"); + } + builder.append('\n'); + String nextIndent = indent + " "; + builder.append(nextIndent); + builder.append("EncodingFlags: "); + builder.append(flags); + builder.append('\n'); + builder.append(nextIndent); + builder.append("unescaped: "); + builder.append('"' + asString(this.unescaped) + '"'); + builder.append('\n'); + return builder.toString(); + } + } + + /** + *
+     * Represents the use of the `yield` keyword.
+     *
+     *     yield 1
+     *     ^^^^^^^
+     * 
+ */ + public static final class YieldNode extends Node { + @Nullable + public final ArgumentsNode arguments; + + public YieldNode(int startOffset, int length, ArgumentsNode arguments) { + super(startOffset, length); + this.arguments = arguments; + } + + public void visitChildNodes(AbstractNodeVisitor visitor) { + if (this.arguments != null) { + this.arguments.accept(visitor); + } + } + + public Node[] childNodes() { + return new Node[] { this.arguments }; + } + + public T accept(AbstractNodeVisitor visitor) { + return visitor.visitYieldNode(this); + } + + @Override + protected String toString(String indent) { + StringBuilder builder = new StringBuilder(); + builder.append(this.getClass().getSimpleName()); + if (hasNewLineFlag()) { + builder.append("[Li]"); + } + builder.append('\n'); + String nextIndent = indent + " "; + builder.append(nextIndent); + builder.append("arguments: "); + builder.append(this.arguments == null ? "null\n" : this.arguments.toString(nextIndent)); + return builder.toString(); + } + } + + public enum ErrorType { + 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_ELLIPSES_LAMBDA, + ARGUMENT_NO_FORWARDING_ELLIPSES_BLOCK, + 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_ENDLESS_DO_BLOCK, + 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_ESCAPED_NON_ASCII_IN_UTF8, + REGEXP_INCOMPAT_CHAR_ENCODING, + REGEXP_INVALID_CHAR_PROPERTY, + 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, + } + + public static ErrorType[] ERROR_TYPES = ErrorType.values(); + + public enum WarningType { + 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, + } + + public static WarningType[] WARNING_TYPES = WarningType.values(); +} +// @formatter:on diff --git a/java/org/prism/ParseResult.java b/java/api/src/main/java/org/ruby_lang/prism/ParseResult.java similarity index 94% rename from java/org/prism/ParseResult.java rename to java/api/src/main/java/org/ruby_lang/prism/ParseResult.java index 144ea16e36..7931327b9a 100644 --- a/java/org/prism/ParseResult.java +++ b/java/api/src/main/java/org/ruby_lang/prism/ParseResult.java @@ -1,4 +1,4 @@ -package org.prism; +package org.ruby_lang.prism; // @formatter:off public final class ParseResult { @@ -69,14 +69,16 @@ public Warning(Nodes.WarningType type, String message, Nodes.Location location, public final Nodes.Location dataLocation; public final Error[] errors; public final Warning[] warnings; + public final boolean continuable; public final Nodes.Source source; - public ParseResult(Nodes.Node value, MagicComment[] magicComments, Nodes.Location dataLocation, Error[] errors, Warning[] warnings, Nodes.Source source) { + public ParseResult(Nodes.Node value, MagicComment[] magicComments, Nodes.Location dataLocation, Error[] errors, Warning[] warnings, boolean continuable, Nodes.Source source) { this.value = value; this.magicComments = magicComments; this.dataLocation = dataLocation; this.errors = errors; this.warnings = warnings; + this.continuable = continuable; this.source = source; } } diff --git a/java/org/prism/ParsingOptions.java b/java/api/src/main/java/org/ruby_lang/prism/ParsingOptions.java similarity index 99% rename from java/org/prism/ParsingOptions.java rename to java/api/src/main/java/org/ruby_lang/prism/ParsingOptions.java index be0a8a7dba..8990f72104 100644 --- a/java/org/prism/ParsingOptions.java +++ b/java/api/src/main/java/org/ruby_lang/prism/ParsingOptions.java @@ -1,4 +1,4 @@ -package org.prism; +package org.ruby_lang.prism; import java.io.ByteArrayOutputStream; import java.nio.ByteBuffer; diff --git a/java/native/pom.xml b/java/native/pom.xml new file mode 100644 index 0000000000..96d6b6e26a --- /dev/null +++ b/java/native/pom.xml @@ -0,0 +1,72 @@ + + + 4.0.0 + + + org.ruby-lang + prism-parser + 0.0.2-SNAPSHOT + + + prism-parser-native + Java Prism Native + Java native bindings Prism parser shared library + https://github.com/ruby/prism + + + + + org.codehaus.mojo + templating-maven-plugin + 3.1.0 + + + filtering-java-templates + + filter-sources + + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.15.0 + + 21 + + + com.dylibso.chicory + annotations-processor + ${chicory.version} + + + + + + org.apache.maven.plugins + maven-surefire-plugin + 3.5.5 + + + org.codehaus.mojo + build-helper-maven-plugin + 3.6.1 + + + generate-sources + + add-source + + + + ../java + + + + + + + + + diff --git a/java/org/prism/Parser.java b/java/native/src/main/java/org/ruby_lang/prism/Parser.java similarity index 92% rename from java/org/prism/Parser.java rename to java/native/src/main/java/org/ruby_lang/prism/Parser.java index 717c3e5179..5c60977d80 100644 --- a/java/org/prism/Parser.java +++ b/java/native/src/main/java/org/ruby_lang/prism/Parser.java @@ -1,4 +1,4 @@ -package org.prism; +package org.ruby_lang.prism; public abstract class Parser { diff --git a/java/pom.xml b/java/pom.xml new file mode 100644 index 0000000000..618b48d71c --- /dev/null +++ b/java/pom.xml @@ -0,0 +1,157 @@ + + + 4.0.0 + + org.ruby-lang + prism-parser + 0.0.2-SNAPSHOT + pom + Java Prism + Java API for the Prism Ruby language parser + https://github.com/ruby/prism + + + + MIT + + + + + + Kevin Newton + kddnewton@gmail.com + + + Benoit Daloze + eregontp@gmail.com + + + Charles Oliver Nutter + headius@headius.com + + + Thomas E Enebo + tom.enebo@gmail.com + + + + + https://github.com/ruby/prism + https://github.com/ruby/prism.git + + + + UTF-8 + UTF-8 + 21 + 21 + + 1.7.3 + 6.0.3 + + + + api + native + wasm + + + + + + org.apache.maven.plugins + maven-gpg-plugin + 3.2.4 + + + org.apache.maven.plugins + maven-compiler-plugin + 3.15.0 + + + org.apache.maven.plugins + maven-source-plugin + 3.4.0 + + + org.apache.maven.plugins + maven-javadoc-plugin + 3.12.0 + + + org.sonatype.central + central-publishing-maven-plugin + 0.7.0 + + + + + + + release + + + + maven-gpg-plugin + 3.2.4 + + + sign-artifacts + verify + + sign + + + + + + --pinentry-mode + loopback + + + + + + + + + + + + maven-source-plugin + + + attach-sources + + jar-no-fork + + + + + + maven-javadoc-plugin + + + attach-javadocs + + jar + + + + + none + + + + org.sonatype.central + central-publishing-maven-plugin + 0.7.0 + true + + central + + + + + + diff --git a/java-wasm/.gitignore b/java/wasm/.gitignore similarity index 100% rename from java-wasm/.gitignore rename to java/wasm/.gitignore diff --git a/java/wasm/README.md b/java/wasm/README.md new file mode 100644 index 0000000000..64b42732fc --- /dev/null +++ b/java/wasm/README.md @@ -0,0 +1,29 @@ +This dir contains the chicory-prism artifact, a version of prism compiled to WASM and then AOT compiled to JVM bytecode by the Chicory project. + +## Building + +Generate the templated sources: + +``` +PRISM_EXCLUDE_PRETTYPRINT=1 PRISM_SERIALIZE_ONLY_SEMANTICS_FIELDS=1 bundle exec rake templates +``` + +Compile to WASM using WASI SDK version 25: + +``` +make java-wasm WASI_SDK_PATH=.../wasi-sdk-25.0-arm64-macos +``` + +Build the AOT-compiled machine and wrapper library: + +``` +mvn -f java/wasm/pom.xml clean package +``` + +This should build the chicory-wasm jar file and pass some basic tests. + +The jar will be under `java/wasm/target/chicory-prism-#####-SNAPSHOT.jar` or can be installed by using `install` instead of `pacakge` in the `mvn` command line above. + +## Releasing + +Pass `-Prelease` to enable release plugins for Maven Central (optional for snapshots) and run the `deploy` target for `mvn`. diff --git a/java/wasm/perf-test/.gitignore b/java/wasm/perf-test/.gitignore new file mode 100644 index 0000000000..60286da241 --- /dev/null +++ b/java/wasm/perf-test/.gitignore @@ -0,0 +1 @@ +profile.html diff --git a/java/wasm/perf-test/bench.sh b/java/wasm/perf-test/bench.sh new file mode 100755 index 0000000000..1ad69239b1 --- /dev/null +++ b/java/wasm/perf-test/bench.sh @@ -0,0 +1,6 @@ +#! /bin/bash +set -euxo pipefail + +SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd ) + +jbang --fresh --deps org.openjdk.jmh:jmh-generator-annprocess:1.36 --javaagent=ap-loader@jvm-profiling-tools/ap-loader=start,event=cpu,file=${SCRIPT_DIR}/profile.html ${SCRIPT_DIR}/test \ No newline at end of file diff --git a/java/wasm/perf-test/test b/java/wasm/perf-test/test new file mode 100755 index 0000000000..f2d5492bc4 --- /dev/null +++ b/java/wasm/perf-test/test @@ -0,0 +1,97 @@ +///usr/bin/env jbang "$0" "$@" ; exit $? + +//DEPS org.jruby:chicory-prism:0.0.1-SNAPSHOT +//DEPS org.jruby:jruby-complete:10.0.2.0 + +import static java.lang.System.*; + +import java.io.DataInputStream; +import java.io.InputStream; +import java.nio.charset.StandardCharsets; +import java.util.EnumSet; + +import org.jruby.Ruby; + +import org.jruby.parser.prism.wasm.Prism; +import org.ruby_lang.prism.ParsingOptions; + +public class test { + + final static String[] JRUBY_BOOT_FILES = { + "jruby/java.rb", + "jruby/java/core_ext.rb", + "jruby/java/core_ext/object.rb", + "jruby/java/java_ext.rb", + "jruby/kernel.rb", + "jruby/kernel/signal.rb", + "jruby/kernel/kernel.rb", + "jruby/kernel/proc.rb", + "jruby/kernel/process.rb", + "jruby/kernel/enumerator.rb", + "jruby/kernel/enumerable.rb", + "jruby/kernel/io.rb", + "jruby/kernel/gc.rb", + "jruby/kernel/range.rb", + "jruby/kernel/file.rb", + "jruby/kernel/method.rb", + "jruby/kernel/thread.rb", + "jruby/kernel/integer.rb", + "jruby/kernel/time.rb", + "jruby/preludes.rb", + "jruby/kernel/prelude.rb", + "jruby/kernel/enc_prelude.rb", + "META-INF/jruby.home/lib/ruby/stdlib/unicode_normalize.rb", + "jruby/kernel/gem_prelude.rb", + "META-INF/jruby.home/lib/ruby/stdlib/rubygems.rb", + "META-INF/jruby.home/lib/ruby/stdlib/rbconfig.rb", + "jruby/kernel/rbconfig.rb", + "META-INF/jruby.home/lib/ruby/stdlib/rubygems/compatibility.rb", + "META-INF/jruby.home/lib/ruby/stdlib/rubygems/defaults.rb", + "META-INF/jruby.home/lib/ruby/stdlib/rubygems/deprecate.rb", + "META-INF/jruby.home/lib/ruby/stdlib/rubygems/errors.rb", + "META-INF/jruby.home/lib/ruby/stdlib/rubygems/target_rbconfig.rb", + "META-INF/jruby.home/lib/ruby/stdlib/rubygems/exceptions.rb", + "META-INF/jruby.home/lib/ruby/stdlib/rubygems/unknown_command_spell_checker.rb", + "META-INF/jruby.home/lib/ruby/stdlib/rubygems/specification.rb", + "META-INF/jruby.home/lib/ruby/stdlib/rubygems/basic_specification.rb", + "META-INF/jruby.home/lib/ruby/stdlib/rubygems/stub_specification.rb", + "META-INF/jruby.home/lib/ruby/stdlib/rubygems/platform.rb", + "META-INF/jruby.home/lib/ruby/stdlib/rubygems/specification_record.rb", + "META-INF/jruby.home/lib/ruby/stdlib/rubygems/util/list.rb", + "META-INF/jruby.home/lib/ruby/stdlib/rubygems/requirement.rb", + "META-INF/jruby.home/lib/ruby/stdlib/rubygems/version.rb", + }; + + public static void main(String... args) throws Exception { + out.println("Starting"); + var count = 100; + + var prism = new Prism(); + byte[] src = new byte[1024 * 1024]; + + for (int i = 0; i < count; i++) { + for (var file : JRUBY_BOOT_FILES) { + byte[] options = ParsingOptions.serialize( + file.getBytes(StandardCharsets.UTF_8), + 1, + "UTF-8".getBytes(StandardCharsets.UTF_8), + false, + EnumSet.noneOf(ParsingOptions.CommandLine.class), + ParsingOptions.SyntaxVersion.LATEST, + false, + false, + false, + new byte[][][]{} + ); + + try (InputStream fileIn = Ruby.getClassLoader().getResourceAsStream(file)) { + DataInputStream dis = new DataInputStream(fileIn); + int read = dis.read(src); + prism.serialize(options, src, read); + } + } + } + + out.println("End"); + } +} diff --git a/java-wasm/pom.xml b/java/wasm/pom.xml similarity index 68% rename from java-wasm/pom.xml rename to java/wasm/pom.xml index f69028bfd8..d652780606 100644 --- a/java-wasm/pom.xml +++ b/java/wasm/pom.xml @@ -2,21 +2,19 @@ 4.0.0 - com.prism - java-prism - 999-SNAPSHOT - Java Prism - Pure Java Prism interpreting WASM + + org.ruby-lang + prism-parser + 0.0.2-SNAPSHOT + + prism-parser-wasm + Java Prism WASM + Java WASM bindings for the Prism parser shared library + https://github.com/ruby/prism - UTF-8 - UTF-8 - 11 - 11 - - 1.6.1 - 6.0.2 + 1.7.3 @@ -32,6 +30,11 @@ + + org.ruby-lang + prism-parser-api + 0.0.2-SNAPSHOT + com.dylibso.chicory runtime @@ -59,6 +62,12 @@ annotations provided + + org.jruby + jruby-complete + 10.0.4.0 + test + @@ -77,11 +86,9 @@ - org.apache.maven.plugins maven-compiler-plugin - 3.14.1 - 11 + 21 com.dylibso.chicory @@ -92,27 +99,9 @@ - org.apache.maven.plugins - maven-surefire-plugin - 3.5.4 - - - org.codehaus.mojo - build-helper-maven-plugin - 3.6.1 - - - generate-sources - - add-source - - - - ../java - - - - + org.apache.maven.plugins + maven-surefire-plugin + 3.5.5 com.dylibso.chicory @@ -125,7 +114,7 @@ compile - org.prism.PrismModule + org.ruby_lang.prism.wasm.PrismParser src/test/resources/prism.wasm diff --git a/java/wasm/prism-parser.iml b/java/wasm/prism-parser.iml new file mode 100644 index 0000000000..99039ba2ed --- /dev/null +++ b/java/wasm/prism-parser.iml @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/java-wasm/src/main/java-templates/org/prism/WasmResource.java b/java/wasm/src/main/java-templates/org/ruby_lang/prism/wasm/WasmResource.java similarity index 83% rename from java-wasm/src/main/java-templates/org/prism/WasmResource.java rename to java/wasm/src/main/java-templates/org/ruby_lang/prism/wasm/WasmResource.java index 48f2671714..785c2bd916 100644 --- a/java-wasm/src/main/java-templates/org/prism/WasmResource.java +++ b/java/wasm/src/main/java-templates/org/ruby_lang/prism/wasm/WasmResource.java @@ -1,4 +1,4 @@ -package org.prism; +package org.ruby_lang.prism.wasm; public final class WasmResource { public static final String absoluteFile = "file://${project.basedir}/src/test/resources/prism.wasm"; diff --git a/java/wasm/src/main/java/org/ruby_lang/prism/wasm/Prism.java b/java/wasm/src/main/java/org/ruby_lang/prism/wasm/Prism.java new file mode 100644 index 0000000000..20d6c286cb --- /dev/null +++ b/java/wasm/src/main/java/org/ruby_lang/prism/wasm/Prism.java @@ -0,0 +1,159 @@ +package org.ruby_lang.prism.wasm; + +import com.dylibso.chicory.annotations.WasmModuleInterface; +import com.dylibso.chicory.runtime.ByteArrayMemory; +import com.dylibso.chicory.runtime.ImportValues; +import com.dylibso.chicory.runtime.Instance; +import com.dylibso.chicory.wasi.WasiOptions; +import com.dylibso.chicory.wasi.WasiPreview1; +import org.ruby_lang.prism.Loader; +import org.ruby_lang.prism.ParseResult; + +import java.nio.charset.StandardCharsets; + +@WasmModuleInterface(WasmResource.absoluteFile) +public class Prism implements AutoCloseable { + private final WasiPreview1 wasi; + protected final Prism_ModuleExports exports; + private final Instance instance; + + public Prism() { + this(WasiOptions.builder().build()); + } + + public Prism(WasiOptions wasiOpts) { + wasi = WasiPreview1.builder().withOptions(wasiOpts).build(); + instance = Instance.builder(PrismParser.load()) + .withMemoryFactory(ByteArrayMemory::new) + .withMachineFactory(PrismParser::create) + .withImportValues(ImportValues.builder().addFunction(wasi.toHostFunctions()).build()) + .build(); + exports = new Prism_ModuleExports(instance); + } + + public String version() { + int versionPointer = exports.pmVersion(); + int length = exports.strchr(versionPointer, 0); + + return new String(instance.memory().readBytes(versionPointer, length - versionPointer)); + } + + public byte[] parse(byte[] sourceBytes, byte[] packedOptions) { + try ( + Buffer buffer = new Buffer(); + Source source = new Source(sourceBytes, 0, sourceBytes.length); + Options options = new Options(packedOptions)) { + + return parse(buffer, source, options); + } + } + + public byte[] lex(byte[] sourceBytes, byte[] packedOptions) { + try ( + Buffer buffer = new Buffer(); + Source source = new Source(sourceBytes, 0, sourceBytes.length); + Options options = new Options(packedOptions)) { + + return lex(buffer, source, options); + } + } + + public byte[] parse(byte[] sourceBytes, int sourceOffset, int sourceLength, byte[] packedOptions) { + try ( + Buffer buffer = new Buffer(); + Source source = new Source(sourceBytes, sourceOffset, sourceLength); + Options options = new Options(packedOptions)) { + + return parse(buffer, source, options); + } + } + + public byte[] parse(Buffer buffer, Source source, Options options) { + exports.pmSerializeParse( + buffer.pointer, source.pointer, source.length, options.pointer); + + return buffer.read(); + } + + public byte[] lex(Buffer buffer, Source source, Options options) { + exports.pmSerializeLex( + buffer.pointer, source.pointer, source.length, options.pointer); + + return buffer.read(); + } + + public class Buffer implements AutoCloseable { + final int pointer; + + Buffer() { + pointer = exports.pmBufferNew(); + clear(); + } + + public void clear() { + exports.pmBufferClear(pointer); + } + + public void close() { + exports.pmBufferFree(pointer); + } + + public byte[] read() { + return instance.memory().readBytes( + exports.pmBufferValue(pointer), + exports.pmBufferLength(pointer)); + } + } + + class Source implements AutoCloseable{ + final int pointer; + final int length; + + Source(int length) { + pointer = exports.calloc(1, length); + this.length = length; + } + + Source(byte[] bytes, int offset, int length) { + this(length + 1); + write(bytes, offset, length); + } + + public void write(byte[] bytes, int offset, int length) { + assert length + 1 <= this.length; + instance.memory().write(pointer, bytes, offset, length); + instance.memory().writeByte(pointer + length, (byte) 0); + } + + public void close() { + exports.free(pointer); + } + } + + class Options implements AutoCloseable { + final int pointer; + + Options(byte[] packedOptions) { + int pointer = exports.calloc(1, packedOptions.length); + instance.memory().write(pointer, packedOptions); + this.pointer = pointer; + } + + public void close() { + exports.free(pointer); + } + } + + public ParseResult serializeParse(byte[] packedOptions, String source) { + var sourceBytes = source.getBytes(StandardCharsets.ISO_8859_1); + byte[] result = parse(sourceBytes, 0, sourceBytes.length, packedOptions); + return Loader.load(result); + } + + @Override + public void close() { + if (wasi != null) { + wasi.close(); + } + } +} diff --git a/java/wasm/src/test/java/org/jruby/parser/prism/JRubyTest.java b/java/wasm/src/test/java/org/jruby/parser/prism/JRubyTest.java new file mode 100644 index 0000000000..2039935003 --- /dev/null +++ b/java/wasm/src/test/java/org/jruby/parser/prism/JRubyTest.java @@ -0,0 +1,142 @@ +package org.jruby.parser.prism; + +import org.junit.jupiter.api.Test; +import org.ruby_lang.prism.ParsingOptions; +import org.ruby_lang.prism.wasm.Prism; + +import java.io.DataInputStream; +import java.io.InputStream; +import java.nio.charset.StandardCharsets; +import java.util.EnumSet; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; + +public class JRubyTest { + // TODO: This list is hardcoded from JRuby 10.0.4.0 and should be made dynamic + final static String[] JRUBY_BOOT_FILES = { + "jruby/java.rb", + "jruby/java/core_ext.rb", + "jruby/java/core_ext/object.rb", + "jruby/java/java_ext.rb", + "jruby/kernel.rb", + "jruby/kernel/signal.rb", + "jruby/kernel/kernel.rb", + "jruby/kernel/proc.rb", + "jruby/kernel/process.rb", + "jruby/kernel/enumerator.rb", + "jruby/kernel/enumerable.rb", + "jruby/kernel/io.rb", + "jruby/kernel/gc.rb", + "jruby/kernel/range.rb", + "jruby/kernel/file.rb", + "jruby/kernel/method.rb", + "jruby/kernel/thread.rb", + "jruby/kernel/integer.rb", + "jruby/kernel/time.rb", + "jruby/kernel/string.rb", + "jruby/preludes.rb", + "jruby/kernel/prelude.rb", + "jruby/kernel/gem_prelude.rb", + "META-INF/jruby.home/lib/ruby/stdlib/rubygems.rb", + "META-INF/jruby.home/lib/ruby/stdlib/rbconfig.rb", + "jruby/kernel/rbconfig.rb", + "META-INF/jruby.home/lib/ruby/stdlib/rubygems/compatibility.rb", + "META-INF/jruby.home/lib/ruby/stdlib/rubygems/defaults.rb", + "META-INF/jruby.home/lib/ruby/stdlib/rubygems/deprecate.rb", + "META-INF/jruby.home/lib/ruby/stdlib/rubygems/errors.rb", + "META-INF/jruby.home/lib/ruby/stdlib/rubygems/target_rbconfig.rb", + "META-INF/jruby.home/lib/ruby/stdlib/rubygems/exceptions.rb", + "META-INF/jruby.home/lib/ruby/stdlib/rubygems/unknown_command_spell_checker.rb", + "META-INF/jruby.home/lib/ruby/stdlib/rubygems/specification.rb", + "META-INF/jruby.home/lib/ruby/stdlib/rubygems/basic_specification.rb", + "META-INF/jruby.home/lib/ruby/stdlib/rubygems/stub_specification.rb", + "META-INF/jruby.home/lib/ruby/stdlib/rubygems/platform.rb", + "META-INF/jruby.home/lib/ruby/stdlib/rubygems/specification_record.rb", + "META-INF/jruby.home/lib/ruby/stdlib/rubygems/util/list.rb", + "META-INF/jruby.home/lib/ruby/stdlib/rubygems/requirement.rb", + "META-INF/jruby.home/lib/ruby/stdlib/rubygems/version.rb", + "META-INF/jruby.home/lib/ruby/stdlib/rubygems/defaults/operating_system.rb", + "META-INF/jruby.home/lib/ruby/stdlib/rubygems/defaults/jruby.rb", + "META-INF/jruby.home/lib/ruby/stdlib/rubygems/util.rb", + "META-INF/jruby.home/lib/ruby/stdlib/rubygems/dependency.rb", + "META-INF/jruby.home/lib/ruby/stdlib/rubygems/core_ext/kernel_gem.rb", + "META-INF/jruby.home/lib/ruby/stdlib/rubygems/core_ext/kernel_warn.rb", + "META-INF/jruby.home/lib/ruby/stdlib/monitor.rb", + "META-INF/jruby.home/lib/ruby/stdlib/bundled_gems.rb", + "META-INF/jruby.home/lib/ruby/stdlib/rubygems/path_support.rb", + "META-INF/jruby.home/lib/ruby/stdlib/did_you_mean.rb", + "META-INF/jruby.home/lib/ruby/stdlib/did_you_mean/version.rb", + "META-INF/jruby.home/lib/ruby/stdlib/did_you_mean/core_ext/name_error.rb", + "META-INF/jruby.home/lib/ruby/stdlib/did_you_mean/spell_checker.rb", + "META-INF/jruby.home/lib/ruby/stdlib/did_you_mean/levenshtein.rb", + "META-INF/jruby.home/lib/ruby/stdlib/did_you_mean/jaro_winkler.rb", + "META-INF/jruby.home/lib/ruby/stdlib/did_you_mean/spell_checkers/name_error_checkers.rb", + "META-INF/jruby.home/lib/ruby/stdlib/did_you_mean/spell_checkers/name_error_checkers/class_name_checker.rb", + "META-INF/jruby.home/lib/ruby/stdlib/did_you_mean/spell_checkers/name_error_checkers/variable_name_checker.rb", + "META-INF/jruby.home/lib/ruby/stdlib/did_you_mean/spell_checkers/method_name_checker.rb", + "META-INF/jruby.home/lib/ruby/stdlib/did_you_mean/spell_checkers/key_error_checker.rb", + "META-INF/jruby.home/lib/ruby/stdlib/did_you_mean/spell_checkers/null_checker.rb", + "META-INF/jruby.home/lib/ruby/stdlib/did_you_mean/spell_checkers/require_path_checker.rb", + "META-INF/jruby.home/lib/ruby/stdlib/did_you_mean/tree_spell_checker.rb", + "META-INF/jruby.home/lib/ruby/stdlib/did_you_mean/spell_checkers/pattern_key_name_checker.rb", + "META-INF/jruby.home/lib/ruby/stdlib/did_you_mean/formatter.rb", + "META-INF/jruby.home/lib/ruby/stdlib/syntax_suggest/core_ext.rb", + }; + + private class TestingPrism extends Prism { + TestingPrism() { + super(); + } + + public int memPages() { + return exports.memory().pages(); + } + } + + @Test + public void jrubyReproducer() throws Exception { + var prism = new TestingPrism(); + + for (int i = 0; i < 3; i++) { + basicJRubyTest(prism); + } + var memoryPagesBefore = prism.memPages(); + for (int i = 0; i < 100; i++) { + var before = System.currentTimeMillis(); + + basicJRubyTest(prism); + + var after = System.currentTimeMillis(); + System.out.println("Elapsed: " + (after - before)); + } + var memoryPagesAfter = prism.memPages(); + assertEquals(memoryPagesBefore, memoryPagesAfter); + } + + private static void basicJRubyTest(Prism prism) throws Exception { + byte[] src = new byte[1024 * 1024]; + + for (var file : JRUBY_BOOT_FILES) { + byte[] options = ParsingOptions.serialize( + file.getBytes(StandardCharsets.UTF_8), + 1, + "UTF-8".getBytes(StandardCharsets.UTF_8), + false, + EnumSet.noneOf(ParsingOptions.CommandLine.class), + ParsingOptions.SyntaxVersion.LATEST, + false, + false, + false, + new byte[][][]{} + ); + + try (InputStream fileIn = JRubyTest.class.getClassLoader().getResourceAsStream(file)) { + assertNotNull(fileIn, "Could not find file: " + file); + DataInputStream dis = new DataInputStream(fileIn); + int read = dis.read(src); + prism.parse(src, 0, read, options); + } + } + } +} diff --git a/java/wasm/src/test/java/org/jruby/parser/prism/WASMTest.java b/java/wasm/src/test/java/org/jruby/parser/prism/WASMTest.java new file mode 100644 index 0000000000..1056075b89 --- /dev/null +++ b/java/wasm/src/test/java/org/jruby/parser/prism/WASMTest.java @@ -0,0 +1,83 @@ +package org.jruby.parser.prism; + +import org.junit.jupiter.api.Test; +import org.ruby_lang.prism.ParseResult; +import org.ruby_lang.prism.ParsingOptions; +import org.ruby_lang.prism.wasm.Prism; + +import java.nio.charset.StandardCharsets; +import java.util.EnumSet; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +public class WASMTest { + + private static final byte[] packedOptions = ParsingOptions.serialize( + new byte[] {}, + 1, + new byte[] {}, + false, + EnumSet.noneOf(ParsingOptions.CommandLine.class), + ParsingOptions.SyntaxVersion.LATEST, + false, + false, + false, + new byte[][][] {} + ); + + @Test + public void test1() { + // The Ruby source code to be processed + var source = "1 + 1"; + + ParseResult pr = null; + try (Prism prism = new Prism()) { + pr = prism.serializeParse(packedOptions, source); + } + + assertEquals(1, pr.value.childNodes().length); + System.out.println("Nodes:"); + System.out.println(pr.value.childNodes()[0]); + assertTrue(pr.value.childNodes()[0].toString().contains("IntegerNode")); + } + + @Test + public void test2() { + // The Ruby source code to be processed + var source = "puts \"h\ne\nl\nl\no\n\""; + + ParseResult pr = null; + try (Prism prism = new Prism()) { + pr = prism.serializeParse(packedOptions, source); + } + + assertEquals(1, pr.value.childNodes().length); + System.out.println("Nodes:"); + System.out.println(pr.value.childNodes()[0]); + assertTrue(pr.value.childNodes()[0].toString().contains("CallNode")); + } + + @Test + public void testMBCIdentifier() { + // The Ruby source code to be processed + var source = new String("hellø = \"hello\"".getBytes(StandardCharsets.UTF_8), StandardCharsets.ISO_8859_1); + + ParseResult pr = null; + try (Prism prism = new Prism()) { + pr = prism.serializeParse(packedOptions, source); + } + + System.out.println("Nodes:"); + System.out.println(pr); + System.out.println(pr.value.childNodes()[0]); + assertTrue(pr.value.childNodes()[0].toString().contains("hell\\xc3\\xb8")); + } + + @Test + public void testVersion() { + try (Prism prism = new Prism()) { + assertEquals("1.9.0", prism.version()); + } + } +} diff --git a/java-wasm/src/test/resources/.gitignore b/java/wasm/src/test/resources/.gitignore similarity index 100% rename from java-wasm/src/test/resources/.gitignore rename to java/wasm/src/test/resources/.gitignore diff --git a/javascript/src/parsePrism.js b/javascript/src/parsePrism.js index 38084da3f1..615cc83d13 100644 --- a/javascript/src/parsePrism.js +++ b/javascript/src/parsePrism.js @@ -31,9 +31,7 @@ export function parsePrism(prism, source, options = {}) { const packedOptions = dumpOptions(options); const optionsPointer = prism.calloc(1, packedOptions.length); - - const bufferPointer = prism.calloc(prism.pm_buffer_sizeof(), 1); - prism.pm_buffer_init(bufferPointer); + const bufferPointer = prism.pm_buffer_new(); const sourceView = new Uint8Array(prism.memory.buffer, sourcePointer, sourceArray.length); sourceView.set(sourceArray); @@ -43,11 +41,10 @@ export function parsePrism(prism, source, options = {}) { prism.pm_serialize_parse(bufferPointer, sourcePointer, sourceArray.length, optionsPointer); const serializedView = new Uint8Array(prism.memory.buffer, prism.pm_buffer_value(bufferPointer), prism.pm_buffer_length(bufferPointer)); - const result = deserialize(sourceArray, serializedView); + const result = deserialize(serializedView); prism.pm_buffer_free(bufferPointer); prism.free(sourcePointer); - prism.free(bufferPointer); prism.free(optionsPointer); return result; } diff --git a/lib/prism.rb b/lib/prism.rb index 781bd4bb01..8f0342724a 100644 --- a/lib/prism.rb +++ b/lib/prism.rb @@ -1,5 +1,7 @@ # frozen_string_literal: true # :markup: markdown +#-- +# rbs_inline: enabled # The Prism Ruby parser. # @@ -21,7 +23,7 @@ module Prism autoload :InspectVisitor, "prism/inspect_visitor" autoload :LexCompat, "prism/lex_compat" autoload :MutationCompiler, "prism/mutation_compiler" - autoload :Pack, "prism/pack" + autoload :NodeFind, "prism/node_find" autoload :Pattern, "prism/pattern" autoload :Reflection, "prism/reflection" autoload :Relocation, "prism/relocation" @@ -33,19 +35,24 @@ module Prism # Some of these constants are not meant to be exposed, so marking them as # private here. - private_constant :LexCompat + if RUBY_ENGINE != "jruby" + private_constant :LexCompat + private_constant :NodeFind + end # 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. + #-- + #: (String version) -> void def initialize(version) message = +"invalid version: Requested to parse as `version: 'current'`; " - segments = + major, minor, = 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)) + if major && minor && ((major < 3) || (major == 3 && minor < 3)) message << " #{version} is below the minimum supported syntax." else message << " #{version} is unknown. Please update the `prism` gem." @@ -56,23 +63,63 @@ def initialize(version) end # :call-seq: - # Prism::lex_compat(source, **options) -> LexCompat::Result + # 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. + # resembles the return value of Ripper.lex. # - # For supported options, see Prism::parse. + # For supported options, see Prism.parse. + #-- + #: (String source, **untyped options) -> LexCompat::Result def self.lex_compat(source, **options) LexCompat.new(source, **options).result # steep:ignore end # :call-seq: - # Prism::load(source, serialized, freeze) -> ParseResult + # load(source, serialized, freeze) -> ParseResult # # Load the serialized AST using the source as a reference into a tree. + #-- + #: (String source, String serialized, ?bool freeze) -> ParseResult def self.load(source, serialized, freeze = false) Serialize.load_parse(source, serialized, freeze) end + + # Given a Method, UnboundMethod, Proc, or Thread::Backtrace::Location, + # returns the Prism node representing it. On CRuby, this uses node_id for + # an exact match. On other implementations, it falls back to best-effort + # matching by source location line number. + #-- + #: (Method | UnboundMethod | Proc | Thread::Backtrace::Location callable, ?rubyvm: bool) -> Node? + def self.find(callable, rubyvm: !!defined?(RubyVM)) + NodeFind.find(callable, rubyvm) + end + + # @rbs! + # VERSION: String + # BACKEND: :CEXT | :FFI + # + # interface _Stream + # def gets: (?Integer integer) -> (String | nil) + # end + # + # def self.parse: (String source, ?filepath: String, ?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: (String source, ?filepath: String, ?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) -> void + # def self.lex: (String source, ?filepath: String, ?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: (String source, ?filepath: String, ?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: (String source, ?filepath: String, ?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_comments: (String source, ?filepath: String, ?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_success?: (String source, ?filepath: String, ?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_failure?: (String source, ?filepath: String, ?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_stream: (_Stream stream, ?filepath: String, ?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.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) -> void + # 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 end require_relative "prism/polyfill/byteindex" diff --git a/lib/prism/desugar_compiler.rb b/lib/prism/desugar_compiler.rb index 5d7d38d841..c64d03f64a 100644 --- a/lib/prism/desugar_compiler.rb +++ b/lib/prism/desugar_compiler.rb @@ -1,12 +1,18 @@ # frozen_string_literal: true # :markup: markdown +#-- +# rbs_inline: enabled module Prism class DesugarAndWriteNode # :nodoc: include DSL - attr_reader :node, :default_source, :read_class, :write_class, :arguments + attr_reader :node #: ClassVariableAndWriteNode | ConstantAndWriteNode | GlobalVariableAndWriteNode | InstanceVariableAndWriteNode | LocalVariableAndWriteNode + attr_reader :default_source #: Source + attr_reader :read_class, :write_class #: Symbol + attr_reader :arguments #: Hash[Symbol, untyped] + #: ((ClassVariableAndWriteNode | ConstantAndWriteNode | GlobalVariableAndWriteNode | InstanceVariableAndWriteNode | LocalVariableAndWriteNode) node, Source default_source, Symbol read_class, Symbol write_class, **untyped arguments) -> void def initialize(node, default_source, read_class, write_class, **arguments) @node = node @default_source = default_source @@ -16,6 +22,8 @@ def initialize(node, default_source, read_class, write_class, **arguments) end # Desugar `x &&= y` to `x && x = y` + #-- + #: () -> node def compile and_node( location: node.location, @@ -36,8 +44,12 @@ def compile class DesugarOrWriteDefinedNode # :nodoc: include DSL - attr_reader :node, :default_source, :read_class, :write_class, :arguments + attr_reader :node #: ClassVariableOrWriteNode | ConstantOrWriteNode | GlobalVariableOrWriteNode + attr_reader :default_source #: Source + attr_reader :read_class, :write_class #: Symbol + attr_reader :arguments #: Hash[Symbol, untyped] + #: ((ClassVariableOrWriteNode | ConstantOrWriteNode | GlobalVariableOrWriteNode) node, Source default_source, Symbol read_class, Symbol write_class, **untyped arguments) -> void def initialize(node, default_source, read_class, write_class, **arguments) @node = node @default_source = default_source @@ -47,6 +59,8 @@ def initialize(node, default_source, read_class, write_class, **arguments) end # Desugar `x ||= y` to `defined?(x) ? x : x = y` + #-- + #: () -> node def compile if_node( location: node.location, @@ -87,8 +101,12 @@ def compile class DesugarOperatorWriteNode # :nodoc: include DSL - attr_reader :node, :default_source, :read_class, :write_class, :arguments + attr_reader :node #: ClassVariableOperatorWriteNode | ConstantOperatorWriteNode | GlobalVariableOperatorWriteNode | InstanceVariableOperatorWriteNode | LocalVariableOperatorWriteNode + attr_reader :default_source #: Source + attr_reader :read_class, :write_class #: Symbol + attr_reader :arguments #: Hash[Symbol, untyped] + #: ((ClassVariableOperatorWriteNode | ConstantOperatorWriteNode | GlobalVariableOperatorWriteNode | InstanceVariableOperatorWriteNode | LocalVariableOperatorWriteNode) node, Source default_source, Symbol read_class, Symbol write_class, **untyped arguments) -> void def initialize(node, default_source, read_class, write_class, **arguments) @node = node @default_source = default_source @@ -98,6 +116,8 @@ def initialize(node, default_source, read_class, write_class, **arguments) end # Desugar `x += y` to `x = x + y` + #-- + #: () -> node def compile binary_operator_loc = node.binary_operator_loc.chop @@ -131,8 +151,12 @@ def compile class DesugarOrWriteNode # :nodoc: include DSL - attr_reader :node, :default_source, :read_class, :write_class, :arguments + attr_reader :node #: InstanceVariableOrWriteNode | LocalVariableOrWriteNode + attr_reader :default_source #: Source + attr_reader :read_class, :write_class #: Symbol + attr_reader :arguments #: Hash[Symbol, untyped] + #: ((InstanceVariableOrWriteNode | LocalVariableOrWriteNode) node, Source default_source, Symbol read_class, Symbol write_class, **untyped arguments) -> void def initialize(node, default_source, read_class, write_class, **arguments) @node = node @default_source = default_source @@ -142,6 +166,8 @@ def initialize(node, default_source, read_class, write_class, **arguments) end # Desugar `x ||= y` to `x || x = y` + #-- + #: () -> node def compile or_node( location: node.location, @@ -162,90 +188,105 @@ def compile private_constant :DesugarAndWriteNode, :DesugarOrWriteNode, :DesugarOrWriteDefinedNode, :DesugarOperatorWriteNode class ClassVariableAndWriteNode + #: () -> node def desugar # :nodoc: DesugarAndWriteNode.new(self, source, :class_variable_read_node, :class_variable_write_node, name: name).compile end end class ClassVariableOrWriteNode + #: () -> node def desugar # :nodoc: DesugarOrWriteDefinedNode.new(self, source, :class_variable_read_node, :class_variable_write_node, name: name).compile end end class ClassVariableOperatorWriteNode + #: () -> node def desugar # :nodoc: DesugarOperatorWriteNode.new(self, source, :class_variable_read_node, :class_variable_write_node, name: name).compile end end class ConstantAndWriteNode + #: () -> node def desugar # :nodoc: DesugarAndWriteNode.new(self, source, :constant_read_node, :constant_write_node, name: name).compile end end class ConstantOrWriteNode + #: () -> node def desugar # :nodoc: DesugarOrWriteDefinedNode.new(self, source, :constant_read_node, :constant_write_node, name: name).compile end end class ConstantOperatorWriteNode + #: () -> node def desugar # :nodoc: DesugarOperatorWriteNode.new(self, source, :constant_read_node, :constant_write_node, name: name).compile end end class GlobalVariableAndWriteNode + #: () -> node def desugar # :nodoc: DesugarAndWriteNode.new(self, source, :global_variable_read_node, :global_variable_write_node, name: name).compile end end class GlobalVariableOrWriteNode + #: () -> node def desugar # :nodoc: DesugarOrWriteDefinedNode.new(self, source, :global_variable_read_node, :global_variable_write_node, name: name).compile end end class GlobalVariableOperatorWriteNode + #: () -> node def desugar # :nodoc: DesugarOperatorWriteNode.new(self, source, :global_variable_read_node, :global_variable_write_node, name: name).compile end end class InstanceVariableAndWriteNode + #: () -> node def desugar # :nodoc: DesugarAndWriteNode.new(self, source, :instance_variable_read_node, :instance_variable_write_node, name: name).compile end end class InstanceVariableOrWriteNode + #: () -> node def desugar # :nodoc: DesugarOrWriteNode.new(self, source, :instance_variable_read_node, :instance_variable_write_node, name: name).compile end end class InstanceVariableOperatorWriteNode + #: () -> node def desugar # :nodoc: DesugarOperatorWriteNode.new(self, source, :instance_variable_read_node, :instance_variable_write_node, name: name).compile end end class LocalVariableAndWriteNode + #: () -> node def desugar # :nodoc: DesugarAndWriteNode.new(self, source, :local_variable_read_node, :local_variable_write_node, name: name, depth: depth).compile end end class LocalVariableOrWriteNode + #: () -> node def desugar # :nodoc: DesugarOrWriteNode.new(self, source, :local_variable_read_node, :local_variable_write_node, name: name, depth: depth).compile end end class LocalVariableOperatorWriteNode + #: () -> node def desugar # :nodoc: DesugarOperatorWriteNode.new(self, source, :local_variable_read_node, :local_variable_write_node, name: name, depth: depth).compile end @@ -254,137 +295,167 @@ def desugar # :nodoc: # 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 + # `@@foo &&= bar` # # becomes # - # @@foo && @@foo = bar + # `@@foo && @@foo = bar` + #-- + #: (ClassVariableAndWriteNode node) -> node def visit_class_variable_and_write_node(node) node.desugar end - # @@foo ||= bar + # `@@foo ||= bar` # # becomes # - # defined?(@@foo) ? @@foo : @@foo = bar + # `defined?(@@foo) ? @@foo : @@foo = bar` + #-- + #: (ClassVariableOrWriteNode node) -> node def visit_class_variable_or_write_node(node) node.desugar end - # @@foo += bar + # `@@foo += bar` # # becomes # - # @@foo = @@foo + bar + # `@@foo = @@foo + bar` + #-- + #: (ClassVariableOperatorWriteNode node) -> node def visit_class_variable_operator_write_node(node) node.desugar end - # Foo &&= bar + # `Foo &&= bar` # # becomes # - # Foo && Foo = bar + # `Foo && Foo = bar` + #-- + #: (ConstantAndWriteNode node) -> node def visit_constant_and_write_node(node) node.desugar end - # Foo ||= bar + # `Foo ||= bar` # # becomes # - # defined?(Foo) ? Foo : Foo = bar + # `defined?(Foo) ? Foo : Foo = bar` + #-- + #: (ConstantOrWriteNode node) -> node def visit_constant_or_write_node(node) node.desugar end - # Foo += bar + # `Foo += bar` # # becomes # - # Foo = Foo + bar + # `Foo = Foo + bar` + #-- + #: (ConstantOperatorWriteNode node) -> node def visit_constant_operator_write_node(node) node.desugar end - # $foo &&= bar + # `$foo &&= bar` # # becomes # - # $foo && $foo = bar + # `$foo && $foo = bar` + #-- + #: (GlobalVariableAndWriteNode node) -> node def visit_global_variable_and_write_node(node) node.desugar end - # $foo ||= bar + # `$foo ||= bar` # # becomes # - # defined?($foo) ? $foo : $foo = bar + # `defined?($foo) ? $foo : $foo = bar` + #-- + #: (GlobalVariableOrWriteNode node) -> node def visit_global_variable_or_write_node(node) node.desugar end - # $foo += bar + # `$foo += bar` # # becomes # - # $foo = $foo + bar + # `$foo = $foo + bar` + #-- + #: (GlobalVariableOperatorWriteNode node) -> node def visit_global_variable_operator_write_node(node) node.desugar end - # @foo &&= bar + # `@foo &&= bar` # # becomes # - # @foo && @foo = bar + # `@foo && @foo = bar` + #-- + #: (InstanceVariableAndWriteNode node) -> node def visit_instance_variable_and_write_node(node) node.desugar end - # @foo ||= bar + # `@foo ||= bar` # # becomes # - # @foo || @foo = bar + # `@foo || @foo = bar` + #-- + #: (InstanceVariableOrWriteNode node) -> node def visit_instance_variable_or_write_node(node) node.desugar end - # @foo += bar + # `@foo += bar` # # becomes # - # @foo = @foo + bar + # `@foo = @foo + bar` + #-- + #: (InstanceVariableOperatorWriteNode node) -> node def visit_instance_variable_operator_write_node(node) node.desugar end - # foo &&= bar + # `foo &&= bar` # # becomes # - # foo && foo = bar + # `foo && foo = bar` + #-- + #: (LocalVariableAndWriteNode node) -> node def visit_local_variable_and_write_node(node) node.desugar end - # foo ||= bar + # `foo ||= bar` # # becomes # - # foo || foo = bar + # `foo || foo = bar` + #-- + #: (LocalVariableOrWriteNode node) -> node def visit_local_variable_or_write_node(node) node.desugar end - # foo += bar + # `foo += bar` # # becomes # - # foo = foo + bar + # `foo = foo + bar` + #-- + #: (LocalVariableOperatorWriteNode node) -> node def visit_local_variable_operator_write_node(node) node.desugar end diff --git a/lib/prism/ffi.rb b/lib/prism/ffi.rb index 57d878a33f..6b9bde51ea 100644 --- a/lib/prism/ffi.rb +++ b/lib/prism/ffi.rb @@ -12,7 +12,7 @@ # autoloaded from within a non-main Ractor. require "prism/serialize" if defined?(Ractor) -module Prism +module Prism # :nodoc: module LibRubyParser # :nodoc: extend FFI::Library @@ -59,6 +59,9 @@ def self.load_exported_functions_from(header, *functions, callbacks) # We only want to load the functions that we are interested in. next unless functions.any? { |function| line.include?(function) } + # Strip trailing attributes (PRISM_NODISCARD, PRISM_NONNULL(...), etc.) + line = line.sub(/\)(\s+PRISM_\w+(?:\([^)]*\))?)+\s*;/, ");") + # Parse the function declaration. unless /^PRISM_EXPORTED_FUNCTION (?.+) (?\w+)\((?.+)\);$/ =~ line raise "Could not parse #{line}" @@ -85,30 +88,44 @@ def self.load_exported_functions_from(header, *functions, callbacks) 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] + callback :pm_source_stream_fgets_t, [:pointer, :int, :pointer], :pointer + callback :pm_source_stream_feof_t, [:pointer], :int + pm_source_init_result_values = %i[PM_SOURCE_INIT_SUCCESS PM_SOURCE_INIT_ERROR_GENERIC PM_SOURCE_INIT_ERROR_DIRECTORY PM_SOURCE_INIT_ERROR_NON_REGULAR] + enum :pm_source_init_result_t, pm_source_init_result_values enum :pm_string_query_t, [:PM_STRING_QUERY_ERROR, -1, :PM_STRING_QUERY_FALSE, :PM_STRING_QUERY_TRUE] + # Ractor-safe lookup table for pm_source_init_result_t, since FFI's + # enum_type accesses module instance variables that are not shareable. + SOURCE_INIT_RESULT = pm_source_init_result_values.freeze + load_exported_functions_from( - "prism.h", + "prism/version.h", "pm_version", + [] + ) + + load_exported_functions_from( + "prism/serialize.h", "pm_serialize_parse", "pm_serialize_parse_stream", "pm_serialize_parse_comments", "pm_serialize_lex", "pm_serialize_parse_lex", - "pm_parse_success_p", + "pm_serialize_parse_success_p", + [] + ) + + load_exported_functions_from( + "prism/string_query.h", "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", + "prism/buffer.h", + "pm_buffer_new", "pm_buffer_value", "pm_buffer_length", "pm_buffer_free", @@ -116,20 +133,19 @@ def self.load_exported_functions_from(header, *functions, callbacks) ) 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", - [] + "prism/source.h", + "pm_source_file_new", + "pm_source_mapped_new", + "pm_source_stream_new", + "pm_source_free", + "pm_source_source", + "pm_source_length", + [:pm_source_stream_fgets_t, :pm_source_stream_feof_t] ) # 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) @@ -151,20 +167,20 @@ def read # 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) + buffer = LibRubyParser.pm_buffer_new + raise unless buffer + + begin + yield new(buffer) ensure - LibRubyParser.pm_buffer_free(pointer) + LibRubyParser.pm_buffer_free(buffer) 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 - + # This object represents source code to be parsed. For strings it wraps a + # pointer directly; for files it uses a pm_source_t under the hood. + class PrismSource # :nodoc: PLATFORM_EXPECTS_UTF8 = RbConfig::CONFIG["host_os"].match?(/bccwin|cygwin|djgpp|mingw|mswin|wince|darwin/i) @@ -181,7 +197,7 @@ def read @pointer.read_string(@length) end - # Yields a pm_string_t pointer to the given block. + # Yields a PrismSource backed by the given string to the block. def self.with_string(string) raise TypeError unless string.is_a?(String) @@ -195,32 +211,38 @@ def self.with_string(string) end end - # Yields a pm_string_t pointer to the given block. + # Yields a PrismSource to the given block, backed by a pm_source_t. 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. + # passing them into pm_source_mapped_new. 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) + FFI::MemoryPointer.new(:int) do |result_ptr| + pm_source = LibRubyParser.pm_source_mapped_new(filepath, 0, result_ptr) + + case SOURCE_INIT_RESULT[result_ptr.read_int] + when :PM_SOURCE_INIT_SUCCESS + pointer = LibRubyParser.pm_source_source(pm_source) + length = LibRubyParser.pm_source_length(pm_source) return yield new(pointer, length, false) - when :PM_STRING_INIT_ERROR_GENERIC + when :PM_SOURCE_INIT_ERROR_GENERIC raise SystemCallError.new(filepath, FFI.errno) - when :PM_STRING_INIT_ERROR_DIRECTORY + when :PM_SOURCE_INIT_ERROR_DIRECTORY raise Errno::EISDIR.new(filepath) + when :PM_SOURCE_INIT_ERROR_NON_REGULAR + # Fall back to reading the file through Ruby IO for non-regular + # files (pipes, character devices, etc.) + return with_string(File.read(filepath)) { |string| yield string } else - raise "Unknown error initializing pm_string_t: #{result.inspect}" + raise "Unknown error initializing pm_source_t: #{result_ptr.read_int}" end ensure - LibRubyParser.pm_string_free(pm_string) + LibRubyParser.pm_source_free(pm_source) if pm_source && !pm_source.null? end end end @@ -236,29 +258,29 @@ def self.with_file(filepath) 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) } + LibRubyParser::PrismSource.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) } + LibRubyParser::PrismSource.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) } + LibRubyParser::PrismSource.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) } + LibRubyParser::PrismSource.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) } + LibRubyParser::PrismSource.with_string(code) { |string| parse_common(string, code, options) } end # Mirror the Prism.parse_file API by using the serialization API. This uses @@ -266,7 +288,7 @@ def parse(code, **options) # when it is available. def parse_file(filepath, **options) options[:filepath] = filepath - LibRubyParser::PrismString.with_file(filepath) { |string| parse_common(string, string.read, options) } + LibRubyParser::PrismSource.with_file(filepath) { |string| parse_common(string, string.read, options) } end # Mirror the Prism.parse_stream API by using the serialization API. @@ -284,19 +306,19 @@ def parse_stream(stream, **options) 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)) + pm_source = LibRubyParser.pm_source_stream_new(nil, callback, eof_callback) + begin + LibRubyParser.pm_serialize_parse_stream(buffer.pointer, pm_source, dump_options(options)) + Prism.load(source, buffer.read, options.fetch(:freeze, false)) + ensure + LibRubyParser.pm_source_free(pm_source) if pm_source && !pm_source.null? + end 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) } + LibRubyParser::PrismSource.with_string(code) { |string| parse_comments_common(string, code, options) } end # Mirror the Prism.parse_file_comments API by using the serialization @@ -304,23 +326,23 @@ def parse_comments(code, **options) # 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) } + LibRubyParser::PrismSource.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) } + LibRubyParser::PrismSource.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) } + LibRubyParser::PrismSource.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) } + LibRubyParser::PrismSource.with_string(code) { |string| parse_file_success_common(string, options) } end # Mirror the Prism.parse_failure? API by using the serialization API. @@ -331,7 +353,7 @@ def parse_failure?(code, **options) # 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) } + LibRubyParser::PrismSource.with_file(filepath) { |string| parse_file_success_common(string, options) } end # Mirror the Prism.parse_file_failure? API by using the serialization API. @@ -341,7 +363,7 @@ def parse_file_failure?(filepath, **options) # Mirror the Prism.profile API by using the serialization API. def profile(source, **options) - LibRubyParser::PrismString.with_string(source) do |string| + LibRubyParser::PrismSource.with_string(source) do |string| LibRubyParser::PrismBuffer.with do |buffer| LibRubyParser.pm_serialize_parse(buffer.pointer, string.pointer, string.length, dump_options(options)) nil @@ -351,7 +373,7 @@ def profile(source, **options) # Mirror the Prism.profile_file API by using the serialization API. def profile_file(filepath, **options) - LibRubyParser::PrismString.with_file(filepath) do |string| + LibRubyParser::PrismSource.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)) @@ -400,7 +422,7 @@ def parse_lex_common(string, code, options) # :nodoc: end def parse_file_success_common(string, options) # :nodoc: - LibRubyParser.pm_parse_success_p(string.pointer, string.length, dump_options(options)) + LibRubyParser.pm_serialize_parse_success_p(string.pointer, string.length, dump_options(options)) end # Return the value that should be dumped for the command_line option. @@ -423,27 +445,28 @@ def dump_options_command_line(options) # Return the value that should be dumped for the version option. def dump_options_version(version) - checking = - case version - when "current" - RUBY_VERSION - when "latest" - nil - when "nearest" - if RUBY_VERSION <= "3.3" - "3.3" - elsif RUBY_VERSION >= "4.1" - "4.1" - else - RUBY_VERSION - end + case version + when "current" + version_string_to_number(RUBY_VERSION) || raise(CurrentVersionError, RUBY_VERSION) + when "latest", nil + 0 # Handled in pm_parser_init + when "nearest" + dump = version_string_to_number(RUBY_VERSION) + return dump if dump + if RUBY_VERSION < "3.3" + version_string_to_number("3.3") else - version + 0 # Handled in pm_parser_init end + else + version_string_to_number(version) || raise(ArgumentError, "invalid version: #{version}") + end + end - case checking - when nil - 0 # Handled in pm_parser_init + # Converts a version string like "4.0.0" or "4.0" into a number. + # Returns nil if the version is unknown. + def version_string_to_number(version) + case version when /\A3\.3(\.\d+)?\z/ 1 when /\A3\.4(\.\d+)?\z/ @@ -452,12 +475,6 @@ def dump_options_version(version) 3 when /\A4\.1(\.\d+)?\z/ 4 - else - if version == "current" - raise CurrentVersionError, RUBY_VERSION - else - raise ArgumentError, "invalid version: #{version}" - end end end @@ -559,7 +576,7 @@ def dump_options(options) # 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 StringQuery # :nodoc: class << self # Mirrors the C extension's StringQuery::local? method. def local?(string) diff --git a/lib/prism/lex_compat.rb b/lib/prism/lex_compat.rb index 5b685716cc..e1b04fc6ce 100644 --- a/lib/prism/lex_compat.rb +++ b/lib/prism/lex_compat.rb @@ -1,26 +1,58 @@ # frozen_string_literal: true # :markup: markdown +#-- +# rbs_inline: enabled module Prism + # @rbs! + # module Translation + # class Ripper + # EXPR_NONE: Integer + # EXPR_BEG: Integer + # EXPR_MID: Integer + # EXPR_END: Integer + # EXPR_CLASS: Integer + # EXPR_VALUE: Integer + # EXPR_ARG: Integer + # EXPR_CMDARG: Integer + # EXPR_ENDARG: Integer + # EXPR_ENDFN: Integer + # + # class Lexer < Ripper + # class State + # def self.[]: (Integer value) -> State + # end + # end + # end + # end + # 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: + # @rbs! + # # A token produced by the Ripper lexer that Prism is replicating. + # type lex_compat_token = [[Integer, Integer], Symbol, String, untyped] + # 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 + attr_reader :value #: Array[lex_compat_token] # Create a new lex compat result object with the given values. - def initialize(value, comments, magic_comments, data_loc, errors, warnings, source) + #-- + #: (Array[lex_compat_token] value, Array[Comment] comments, Array[MagicComment] magic_comments, Location? data_loc, Array[ParseError] errors, Array[ParseWarning] warnings, bool continuable, Source source) -> void + def initialize(value, comments, magic_comments, data_loc, errors, warnings, continuable, source) @value = value - super(comments, magic_comments, data_loc, errors, warnings, source) + super(comments, magic_comments, data_loc, errors, warnings, continuable, source) end # Implement the hash pattern matching interface for Result. - def deconstruct_keys(keys) + #-- + #: (Array[Symbol]? keys) -> Hash[Symbol, untyped] + def deconstruct_keys(keys) # :nodoc: super.merge!(value: value) end end @@ -102,6 +134,7 @@ def deconstruct_keys(keys) KEYWORD_DEF: :on_kw, KEYWORD_DEFINED: :on_kw, KEYWORD_DO: :on_kw, + KEYWORD_DO_BLOCK: :on_kw, KEYWORD_DO_LOOP: :on_kw, KEYWORD_ELSE: :on_kw, KEYWORD_ELSIF: :on_kw, @@ -205,16 +238,19 @@ module Heredoc # :nodoc: # 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 + attr_reader :tokens #: Array[lex_compat_token] + #: () -> void def initialize @tokens = [] end + #: (lex_compat_token token) -> void def <<(token) tokens << token end + #: () -> Array[lex_compat_token] def to_a tokens end @@ -224,21 +260,25 @@ def to_a # 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 + attr_reader :split #: bool + attr_reader :tokens #: Array[lex_compat_token] + #: (bool split) -> void def initialize(split) @split = split @tokens = [] end + #: (lex_compat_token token) -> void def <<(token) tokens << token end + #: () -> Array[lex_compat_token] def to_a embexpr_balance = 0 - tokens.each_with_object([]) do |token, results| #$ Array[Token] + tokens.each_with_object([]) do |token, results| #$ Array[lex_compat_token] case token[1] when :on_embexpr_beg embexpr_balance += 1 @@ -285,8 +325,13 @@ def to_a class DedentingHeredoc # :nodoc: TAB_WIDTH = 8 - attr_reader :tokens, :dedent_next, :dedent, :embexpr_balance + attr_reader :tokens #: Array[lex_compat_token] + attr_reader :dedent_next #: bool + attr_reader :dedent #: Integer? + attr_reader :embexpr_balance #: Integer + # @rbs @ended_on_newline: bool + #: () -> void def initialize @tokens = [] @dedent_next = true @@ -298,6 +343,8 @@ def initialize # 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. + # + #: (lex_compat_token token) -> void def <<(token) case token[1] when :on_embexpr_beg, :on_heredoc_beg @@ -310,7 +357,7 @@ def <<(token) line = token[2] if dedent_next && !(line.strip.empty? && line.end_with?("\n")) - leading = line[/\A(\s*)\n?/, 1] + leading = line[/\A(\s*)\n?/, 1] #: String next_dedent = 0 leading.each_char do |char| @@ -335,11 +382,12 @@ def <<(token) tokens << token end + #: () -> Array[lex_compat_token] 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] + results = [] #: Array[lex_compat_token] embexpr_balance = 0 tokens.each do |token| @@ -374,7 +422,7 @@ def to_a # 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] + results = [] #: Array[lex_compat_token] embexpr_balance = 0 index = 0 @@ -407,7 +455,7 @@ def to_a # 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] + results = [] #: Array[lex_compat_token] dedent_next = true embexpr_balance = 0 @@ -446,7 +494,8 @@ def to_a # 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 + unjoined = splits[index..] #: Array[String] + line = unjoined.join index = splits.length end @@ -511,6 +560,8 @@ def to_a # Here we will split between the two types of heredocs and return the # object that will store their tokens. + #-- + #: (lex_compat_token opening) -> (PlainHeredoc | DashHeredoc | DedentingHeredoc) def self.build(opening) case opening[2][2] when "~" @@ -530,31 +581,38 @@ def self.build(opening) BOM_FLUSHED = RUBY_VERSION >= "3.3.0" private_constant :BOM_FLUSHED - attr_reader :options + attr_reader :options #: Hash[Symbol, untyped] + # @rbs @source: String - def initialize(code, **options) - @code = code + #: (String source, **untyped options) -> void + def initialize(source, **options) + @source = source @options = options end + #: () -> Result def result - tokens = [] #: Array[LexCompat::Token] + tokens = [] #: Array[lex_compat_token] state = :default heredoc_stack = [[]] #: Array[Array[Heredoc::PlainHeredoc | Heredoc::DashHeredoc | Heredoc::DedentingHeredoc]] - result = Prism.lex(@code, **options) + result = Prism.lex(@source, **options) source = result.source result_value = result.value - previous_state = nil #: State? + previous_state = nil #: Translation::Ripper::Lexer::State? last_heredoc_end = nil #: Integer? - eof_token = nil + eof_token = nil #: Token? 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 + result_value.each_with_index do |(prism_token, prism_state), index| + lineno = prism_token.location.start_line + column = prism_token.location.start_column + + event = RIPPER.fetch(prism_token.type) + value = prism_token.value + lex_state = Translation::Ripper::Lexer::State[prism_state] # 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 @@ -566,43 +624,38 @@ def result if index == 0 && column == 0 && !BOM_FLUSHED flushed = - case token.type + case prism_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?("%") + 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[lex_state] - - token = + lex_compat_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")] + value = value[0..value.index("\n")] #: String [[lineno, column], event, value, lex_state] when :on_comment [[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 + last_heredoc_end = prism_token.location.end_offset [[lineno, column], event, value, lex_state] when :on_embexpr_end [[lineno, column], event, value, lex_state] @@ -615,7 +668,7 @@ def result end tokens << [[lineno, column], event, line, lex_state] end - tokens.pop + tokens.pop #: lex_compat_token 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 @@ -647,7 +700,7 @@ def result [[lineno, column], event, value, lex_state] when :on_eof - eof_token = token + eof_token = prism_token previous_token = result_value[index - 1][0] # If we're at the end of the file and the previous token was a @@ -662,7 +715,7 @@ def result # Use the greater offset of the two to determine the start of # the trailing whitespace. start_offset = [previous_token.location.end_offset, last_heredoc_end].compact.max - end_offset = token.location.start_offset + end_offset = prism_token.location.start_offset if start_offset < end_offset if bom @@ -677,7 +730,7 @@ def result [[lineno, column], event, value, lex_state] else [[lineno, column], event, value, lex_state] - end + end #: lex_compat_token previous_state = lex_state @@ -694,19 +747,19 @@ def result when :default # The default state is when there are no heredocs at all. In this # state we can append the token to the list of tokens and move on. - tokens << token + tokens << lex_compat_token # If we get the declaration of a heredoc, then we open a new heredoc # and move into the heredoc_opened state. if event == :on_heredoc_beg state = :heredoc_opened - heredoc_stack.last << Heredoc.build(token) + heredoc_stack.last << Heredoc.build(lex_compat_token) end when :heredoc_opened # The heredoc_opened state is when we've seen the declaration of a # heredoc and are now lexing the body of the heredoc. In this state we # push tokens onto the most recently created heredoc. - heredoc_stack.last.last << token + heredoc_stack.last.last << lex_compat_token case event when :on_heredoc_beg @@ -714,7 +767,7 @@ def result # heredoc, this means we have nested heredocs. In this case we'll # push a new heredoc onto the stack and stay in the heredoc_opened # state since we're now lexing the body of the new heredoc. - heredoc_stack << [Heredoc.build(token)] + heredoc_stack << [Heredoc.build(lex_compat_token)] when :on_heredoc_end # If we receive the end of a heredoc, then we're done lexing the # body of the heredoc. In this case we now have a completed heredoc @@ -723,10 +776,10 @@ def result state = :heredoc_closed end when :heredoc_closed - if %i[on_nl on_ignored_nl on_comment].include?(event) || (event == :on_tstring_content && value.end_with?("\n")) + if %i[on_nl on_ignored_nl on_comment].include?(event) || ((event == :on_tstring_content) && value.end_with?("\n")) if heredoc_stack.size > 1 - flushing = heredoc_stack.pop - heredoc_stack.last.last << token + flushing = heredoc_stack.pop #: Array[Heredoc::PlainHeredoc | Heredoc::DashHeredoc | Heredoc::DedentingHeredoc] + heredoc_stack.last.last << lex_compat_token flushing.each do |heredoc| heredoc.to_a.each do |flushed_token| @@ -738,12 +791,12 @@ def result next end elsif event == :on_heredoc_beg - tokens << token + tokens << lex_compat_token state = :heredoc_opened - heredoc_stack.last << Heredoc.build(token) + heredoc_stack.last << Heredoc.build(lex_compat_token) next elsif heredoc_stack.size > 1 - heredoc_stack[-2].last << token + heredoc_stack[-2].last << lex_compat_token next end @@ -754,13 +807,15 @@ def result heredoc_stack.last.clear state = :default - tokens << token + tokens << lex_compat_token end end # Drop the EOF token from the list. The EOF token may not be # present if the source was syntax invalid - tokens = tokens[0...-1] if tokens.dig(-1, 1) == :on_eof + if tokens.dig(-1, 1) == :on_eof + tokens = tokens[0...-1] #: Array[lex_compat_token] + end # We sort by location because Ripper.lex sorts. tokens.sort_by! do |token| @@ -768,21 +823,25 @@ def result source.byte_offset(line, column) end - # Add :on_sp tokens - tokens = insert_on_sp(tokens, source, result.data_loc, bom, eof_token) + tokens = post_process_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) + Result.new(tokens, result.comments, result.magic_comments, result.data_loc, result.errors, result.warnings, result.continuable?, source) end private - def insert_on_sp(tokens, source, data_loc, bom, eof_token) - new_tokens = [] + #: (Array[lex_compat_token] tokens, Source source, Location? data_loc, bool bom, Token? eof_token) -> Array[lex_compat_token] + def post_process_tokens(tokens, source, data_loc, bom, eof_token) + new_tokens = [] #: Array[lex_compat_token] prev_token_state = Translation::Ripper::Lexer::State[Translation::Ripper::EXPR_BEG] prev_token_end = bom ? 3 : 0 tokens.each do |token| + # Skip missing heredoc ends. + next if token[1] == :on_heredoc_end && token[2] == "" + + # Add :on_sp tokens. line, column = token[0] start_offset = source.byte_offset(line, column) @@ -803,8 +862,8 @@ def insert_on_sp(tokens, source, data_loc, bom, eof_token) 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] + first_whitespace = sp_value[0...continuation_index] #: String + continuation = sp_value[continuation_index...next_whitespace_index] #: String second_whitespace = sp_value[next_whitespace_index..] || "" new_tokens << [[sp_line, sp_column], :on_sp, first_whitespace, prev_token_state] unless first_whitespace.empty? diff --git a/lib/prism/node_ext.rb b/lib/prism/node_ext.rb index 469e54ca0c..8a5691848f 100644 --- a/lib/prism/node_ext.rb +++ b/lib/prism/node_ext.rb @@ -1,5 +1,7 @@ # frozen_string_literal: true # :markup: markdown +#-- +# rbs_inline: enabled #-- # Here we are reopening the prism module to provide methods on nodes that aren't @@ -7,9 +9,9 @@ #++ module Prism class Node + #: (*String replacements) -> void def deprecated(*replacements) # :nodoc: - location = caller_locations(1, 1) - location = location[0].label if location + location = caller_locations(1, 1)&.[](0)&.label suggest = replacements.map { |replacement| "#{self.class}##{replacement}" } warn(<<~MSG, uplevel: 1, category: :deprecated) @@ -23,7 +25,9 @@ def deprecated(*replacements) # :nodoc: module RegularExpressionOptions # :nodoc: # Returns a numeric value that represents the flags that were used to create # the regular expression. - def options + #-- + #: (Integer flags) -> Integer + def self.options(flags) o = 0 o |= Regexp::IGNORECASE if flags.anybits?(RegularExpressionFlags::IGNORE_CASE) o |= Regexp::EXTENDED if flags.anybits?(RegularExpressionFlags::EXTENDED) @@ -35,43 +39,87 @@ def options end class InterpolatedMatchLastLineNode < Node - include RegularExpressionOptions + # Returns a numeric value that represents the flags that were used to create + # the regular expression. + #-- + #: () -> Integer + def options + RegularExpressionOptions.options(flags) + end end class InterpolatedRegularExpressionNode < Node - include RegularExpressionOptions + # Returns a numeric value that represents the flags that were used to create + # the regular expression. + #-- + #: () -> Integer + def options + RegularExpressionOptions.options(flags) + end end class MatchLastLineNode < Node - include RegularExpressionOptions + # Returns a numeric value that represents the flags that were used to create + # the regular expression. + #-- + #: () -> Integer + def options + RegularExpressionOptions.options(flags) + end end class RegularExpressionNode < Node - include RegularExpressionOptions + # Returns a numeric value that represents the flags that were used to create + # the regular expression. + #-- + #: () -> Integer + def options + RegularExpressionOptions.options(flags) + end end private_constant :RegularExpressionOptions module HeredocQuery # :nodoc: # Returns true if this node was represented as a heredoc in the source code. - def heredoc? + #-- + #: (String? opening) -> bool? + def self.heredoc?(opening) + # @type self: InterpolatedStringNode | InterpolatedXStringNode | StringNode | XStringNode opening&.start_with?("<<") end end class InterpolatedStringNode < Node - include HeredocQuery + # Returns true if this node was represented as a heredoc in the source code. + #-- + #: () -> bool? + def heredoc? + HeredocQuery.heredoc?(opening) + end end class InterpolatedXStringNode < Node - include HeredocQuery + # Returns true if this node was represented as a heredoc in the source code. + #-- + #: () -> bool? + def heredoc? + HeredocQuery.heredoc?(opening) + end end class StringNode < Node - include HeredocQuery + # Returns true if this node was represented as a heredoc in the source code. + #-- + #: () -> bool? + def heredoc? + HeredocQuery.heredoc?(opening) + end # Occasionally it's helpful to treat a string as if it were interpolated so # that there's a consistent interface for working with strings. + #-- + #: () -> InterpolatedStringNode def to_interpolated InterpolatedStringNode.new( source, @@ -86,10 +134,17 @@ def to_interpolated end class XStringNode < Node - include HeredocQuery + # Returns true if this node was represented as a heredoc in the source code. + #-- + #: () -> bool? + def heredoc? + HeredocQuery.heredoc?(opening) + end # Occasionally it's helpful to treat a string as if it were interpolated so # that there's a consistent interface for working with strings. + #-- + #: () -> InterpolatedXStringNode def to_interpolated InterpolatedXStringNode.new( source, @@ -107,6 +162,8 @@ def to_interpolated class ImaginaryNode < Node # Returns the value of the node as a Ruby Complex. + #-- + #: () -> Complex def value Complex(0, numeric.value) end @@ -114,31 +171,25 @@ def value class RationalNode < Node # Returns the value of the node as a Ruby Rational. + #-- + #: () -> 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] + #-- + #: () -> Array[Symbol] def full_name_parts [name] end # Returns the full name of this constant. For example: "Foo" + #-- + #: () -> String def full_name name.to_s end @@ -147,11 +198,15 @@ def full_name class ConstantWriteNode < Node # Returns the list of parts for the full name of this constant. # For example: [:Foo] + #-- + #: () -> Array[Symbol] def full_name_parts [name] end # Returns the full name of this constant. For example: "Foo" + #-- + #: () -> String def full_name name.to_s end @@ -173,6 +228,8 @@ class MissingNodesInConstantPathError < StandardError; end # Returns the list of parts for the full name of this constant path. # For example: [:Foo, :Bar] + #-- + #: () -> Array[Symbol] def full_name_parts parts = [] #: Array[Symbol] current = self #: node? @@ -195,30 +252,21 @@ def full_name_parts end # Returns the full name of this constant path. For example: "Foo::Bar" + #-- + #: () -> String 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] + #-- + #: () -> Array[Symbol] def full_name_parts parts = - case parent + case (parent = self.parent) when ConstantPathNode, ConstantReadNode parent.full_name_parts when nil @@ -228,7 +276,7 @@ def full_name_parts raise ConstantPathNode::DynamicPartsInConstantPathError, "Constant target path contains dynamic parts. Cannot compute full name" end - if name.nil? + if (name = self.name).nil? raise ConstantPathNode::MissingNodesInConstantPathError, "Constant target path contains missing nodes. Cannot compute full name" end @@ -236,32 +284,25 @@ def full_name_parts end # Returns the full name of this constant path. For example: "Foo::Bar" + #-- + #: () -> String 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] + #-- + #: () -> Array[Symbol] def full_name_parts [name] end # Returns the full name of this constant. For example: "Foo" + #-- + #: () -> String def full_name name.to_s end @@ -269,6 +310,8 @@ def full_name class ParametersNode < Node # Mirrors the Method#parameters method. + #-- + #: () -> Array[[Symbol, Symbol] | [Symbol]] def signature names = [] #: Array[[Symbol, Symbol] | [Symbol]] @@ -278,7 +321,7 @@ def signature optionals.each { |param| names << [:opt, param.name] } - if rest && rest.is_a?(RestParameterNode) + if (rest = self.rest).is_a?(RestParameterNode) names << [:rest, rest.name || :*] end @@ -286,7 +329,9 @@ def signature case param when MultiTargetNode names << [:req] - when NoKeywordsParameterNode, KeywordRestParameterNode, ForwardingParameterNode + when NoKeywordsParameterNode, KeywordRestParameterNode, + NoBlockParameterNode, BlockParameterNode, + ForwardingParameterNode # Invalid syntax, e.g. "def f(**nil, ...)" moves the NoKeywordsParameterNode to posts raise "Invalid syntax" else @@ -307,7 +352,7 @@ def signature keyopt.each { |param| names << [:key, param.name] } - case keyword_rest + case (keyword_rest = self.keyword_rest) when ForwardingParameterNode names.concat([[:rest, :*], [:keyrest, :**], [:block, :&]]) when KeywordRestParameterNode @@ -316,7 +361,13 @@ def signature names << [:nokey] end - names << [:block, block.name || :&] if block + case (block = self.block) + when BlockParameterNode + names << [:block, block.name || :&] + when NoBlockParameterNode + names << [:noblock] + end + names end end @@ -331,181 +382,10 @@ class CallNode < Node # 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. + #-- + #: () -> Location? 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/lib/prism/node_find.rb b/lib/prism/node_find.rb new file mode 100644 index 0000000000..697ee430e8 --- /dev/null +++ b/lib/prism/node_find.rb @@ -0,0 +1,185 @@ +# frozen_string_literal: true +# :markup: markdown +#-- +# rbs_inline: enabled + +module Prism + # Finds the Prism AST node corresponding to a given Method, UnboundMethod, + # Proc, or Thread::Backtrace::Location. On CRuby, uses node_id from the + # instruction sequence for an exact match. On other implementations, falls + # back to best-effort matching by source location line number. + # + # This module is autoloaded so that programs that don't use Prism.find don't + # pay for its definition. + module NodeFind # :nodoc: + # Find the node for the given callable or backtrace location. + #-- + #: (Method | UnboundMethod | Proc | Thread::Backtrace::Location callable, bool rubyvm) -> Node? + def self.find(callable, rubyvm) + case callable + when Proc + if rubyvm + RubyVMCallableFind.new.find(callable) + elsif callable.lambda? + LineLambdaFind.new.find(callable) + else + LineProcFind.new.find(callable) + end + when Method, UnboundMethod + if rubyvm + RubyVMCallableFind.new.find(callable) + else + LineMethodFind.new.find(callable) + end + when Thread::Backtrace::Location + if rubyvm + RubyVMBacktraceLocationFind.new.find(callable) + else + LineBacktraceLocationFind.new.find(callable) + end + else + raise ArgumentError, "Expected a Method, UnboundMethod, Proc, or Thread::Backtrace::Location, got #{callable.class}" + end + end + + # Base class that handles parsing a file. + class Find + private + + # Parse the given file path, returning a ParseResult or nil. + #-- + #: (String? file) -> ParseResult? + def parse_file(file) + return unless file && File.readable?(file) + result = Prism.parse_file(file) + result if result.success? + end + end + + # Finds the AST node for a Method, UnboundMethod, or Proc using the node_id + # from the instruction sequence. + class RubyVMCallableFind < Find + # Find the node for the given callable using the ISeq node_id. + #-- + #: (Method | UnboundMethod | Proc callable) -> Node? + def find(callable) + return unless (source_location = callable.source_location) + return unless (result = parse_file(source_location[0])) + return unless (iseq = RubyVM::InstructionSequence.of(callable)) + + header = iseq.to_a[4] + return unless header[:parser] == :prism + + result.value.find { |node| node.node_id == header[:node_id] } + end + end + + # Finds the AST node for a Thread::Backtrace::Location using the node_id + # from the backtrace location. + class RubyVMBacktraceLocationFind < Find + # Find the node for the given backtrace location using node_id. + #-- + #: (Thread::Backtrace::Location location) -> Node? + def find(location) + file = location.absolute_path || location.path + return unless (result = parse_file(file)) + return unless RubyVM::AbstractSyntaxTree.respond_to?(:node_id_for_backtrace_location) + + node_id = RubyVM::AbstractSyntaxTree.node_id_for_backtrace_location(location) + + result.value.find { |node| node.node_id == node_id } + end + end + + # Finds the AST node for a Method or UnboundMethod using best-effort line + # matching. Used on non-CRuby implementations. + class LineMethodFind < Find + # Find the node for the given method by matching on name and line. + #-- + #: (Method | UnboundMethod callable) -> Node? + def find(callable) + return unless (source_location = callable.source_location) + return unless (result = parse_file(source_location[0])) + + name = callable.name + start_line = source_location[1] + + result.value.find do |node| + case node + when DefNode + node.name == name && node.location.start_line == start_line + when CallNode + node.block.is_a?(BlockNode) && node.location.start_line == start_line + else + false + end + end + end + end + + # Finds the AST node for a lambda using best-effort line matching. Used + # on non-CRuby implementations. + class LineLambdaFind < Find + # Find the node for the given lambda by matching on line. + #-- + #: (Proc callable) -> Node? + def find(callable) + return unless (source_location = callable.source_location) + return unless (result = parse_file(source_location[0])) + + start_line = source_location[1] + + result.value.find do |node| + case node + when LambdaNode + node.location.start_line == start_line + when CallNode + node.block.is_a?(BlockNode) && node.location.start_line == start_line + else + false + end + end + end + end + + # Finds the AST node for a non-lambda Proc using best-effort line + # matching. Used on non-CRuby implementations. + class LineProcFind < Find + # Find the node for the given proc by matching on line. + #-- + #: (Proc callable) -> Node? + def find(callable) + return unless (source_location = callable.source_location) + return unless (result = parse_file(source_location[0])) + + start_line = source_location[1] + + result.value.find do |node| + case node + when ForNode + node.location.start_line == start_line + when CallNode + node.block.is_a?(BlockNode) && node.location.start_line == start_line + else + false + end + end + end + end + + # Finds the AST node for a Thread::Backtrace::Location using best-effort + # line matching. Used on non-CRuby implementations. + class LineBacktraceLocationFind < Find + # Find the node for the given backtrace location by matching on line. + #-- + #: (Thread::Backtrace::Location location) -> Node? + def find(location) + file = location.absolute_path || location.path + return unless (result = parse_file(file)) + + start_line = location.lineno + result.value.find { |node| node.location.start_line == start_line } + end + end + end +end diff --git a/lib/prism/pack.rb b/lib/prism/pack.rb deleted file mode 100644 index 166c04c3c0..0000000000 --- a/lib/prism/pack.rb +++ /dev/null @@ -1,230 +0,0 @@ -# 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/lib/prism/parse_result.rb b/lib/prism/parse_result.rb index 2498ae7e14..4f7bcf07d6 100644 --- a/lib/prism/parse_result.rb +++ b/lib/prism/parse_result.rb @@ -1,7 +1,16 @@ # frozen_string_literal: true # :markup: markdown +#-- +# rbs_inline: enabled module Prism + # @rbs! + # # An internal interface for a cache that can be used to compute code + # # units from byte offsets. + # interface _CodeUnitsCache + # def []: (Integer byte_offset) -> Integer + # end + # 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. @@ -10,7 +19,18 @@ class Source # 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 = []) + # + # Note that if you are calling this method manually, you will need to supply + # the start_line and offsets parameters. start_line is the line number that + # the source starts on, which is typically 1 but can be different if this + # source is a subset of a larger source or if this is an eval. offsets is an + # array of byte offsets for the start of each line in the source code, which + # can be calculated by iterating through the source code and recording the + # byte offset whenever a newline character is encountered. The first + # element is always 0 to mark the first line. + #-- + #: (String source, Integer start_line, Array[Integer] offsets) -> Source + def self.for(source, start_line, offsets) if source.ascii_only? ASCIISource.new(source, start_line, offsets) elsif source.encoding == Encoding::BINARY @@ -34,49 +54,73 @@ def self.for(source, start_line = 1, offsets = []) end # The source code that this source object represents. - attr_reader :source + attr_reader :source #: String # 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 = []) + attr_reader :start_line #: Integer + + # The list of newline byte offsets in the source code. When initialized from + # the C extension, this may be a packed binary string of uint32_t values + # that is lazily unpacked on first access. + #-- + #: () -> Array[Integer] + def offsets + offsets = @offsets + return offsets if offsets.is_a?(Array) + @offsets = offsets.unpack("L*") + end + + # Create a new source object with the given source code. The offsets + # parameter can be either an Array of Integer byte offsets or a packed + # binary string of uint32_t values (from the C extension). + #-- + #: (String source, Integer start_line, Array[Integer] | String offsets) -> void + def initialize(source, start_line, offsets) @source = source - @start_line = start_line # set after parsing is done - @offsets = offsets # set after parsing is done + @start_line = start_line + @offsets = offsets end # Replace the value of start_line with the given value. + #-- + #: (Integer start_line) -> void def replace_start_line(start_line) @start_line = start_line end # Replace the value of offsets with the given value. + #-- + #: (Array[Integer] offsets) -> void def replace_offsets(offsets) - @offsets.replace(offsets) + @offsets = offsets end # Returns the encoding of the source code, which is set by parameters to the # parser or by the encoding magic comment. + #-- + #: () -> Encoding def encoding source.encoding end # Returns the lines of the source code as an array of strings. + #-- + #: () -> Array[String] def lines source.lines end # Perform a byteslice on the source code using the given byte offset and # byte length. + #-- + #: (Integer byte_offset, Integer length) -> String 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. + #-- + #: (Integer line, Integer column) -> Integer def byte_offset(line, column) normal = line - @start_line raise IndexError if normal < 0 @@ -87,33 +131,45 @@ def byte_offset(line, column) # Binary search through the offsets to find the line number for the given # byte offset. + #-- + #: (Integer byte_offset) -> Integer 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. + #-- + #: (Integer byte_offset) -> Integer 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. + #-- + #: (Integer byte_offset) -> Integer def line_end(byte_offset) offsets[find_line(byte_offset) + 1] || source.bytesize end # Return the column in bytes for the given byte offset. + #-- + #: (Integer byte_offset) -> Integer def column(byte_offset) byte_offset - line_start(byte_offset) end # Return the character offset for the given byte offset. + #-- + #: (Integer byte_offset) -> Integer def character_offset(byte_offset) (source.byteslice(0, byte_offset) or raise).length end # Return the column in characters for the given byte offset. + #-- + #: (Integer byte_offset) -> Integer def character_column(byte_offset) character_offset(byte_offset) - character_offset(line_start(byte_offset)) end @@ -130,6 +186,8 @@ def character_column(byte_offset) # 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. + #-- + #: (Integer byte_offset, Encoding encoding) -> Integer def code_units_offset(byte_offset, encoding) byteslice = (source.byteslice(0, byte_offset) or raise).encode(encoding, invalid: :replace, undef: :replace) @@ -142,17 +200,23 @@ def code_units_offset(byte_offset, encoding) # Generate a cache that targets a specific encoding for calculating code # unit offsets. + #-- + #: (Encoding encoding) -> CodeUnitsCache def code_units_cache(encoding) CodeUnitsCache.new(source, encoding) end # Returns the column in code units for the given encoding for the # given byte offset. + #-- + #: (Integer byte_offset, Encoding encoding) -> Integer 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. + #-- + #: () -> void def deep_freeze source.freeze offsets.freeze @@ -163,7 +227,9 @@ def deep_freeze # Binary search through the offsets to find the line number for the given # byte offset. - def find_line(byte_offset) + #-- + #: (Integer byte_offset) -> Integer + def find_line(byte_offset) # :nodoc: index = offsets.bsearch_index { |offset| offset > byte_offset } || offsets.length index - 1 end @@ -185,30 +251,47 @@ def find_line(byte_offset) # class CodeUnitsCache class UTF16Counter # :nodoc: + # @rbs @source: String + # @rbs @encoding: Encoding + + #: (String source, Encoding encoding) -> void def initialize(source, encoding) @source = source @encoding = encoding end + #: (Integer byte_offset, Integer byte_length) -> Integer def count(byte_offset, byte_length) - @source.byteslice(byte_offset, byte_length).encode(@encoding, invalid: :replace, undef: :replace).bytesize / 2 + (@source.byteslice(byte_offset, byte_length) or raise).encode(@encoding, invalid: :replace, undef: :replace).bytesize / 2 end end class LengthCounter # :nodoc: + # @rbs @source: String + # @rbs @encoding: Encoding + + #: (String source, Encoding encoding) -> void def initialize(source, encoding) @source = source @encoding = encoding end + #: (Integer byte_offset, Integer byte_length) -> Integer def count(byte_offset, byte_length) - @source.byteslice(byte_offset, byte_length).encode(@encoding, invalid: :replace, undef: :replace).length + (@source.byteslice(byte_offset, byte_length) or raise).encode(@encoding, invalid: :replace, undef: :replace).length end end private_constant :UTF16Counter, :LengthCounter + # @rbs @source: String + # @rbs @counter: UTF16Counter | LengthCounter + # @rbs @cache: Hash[Integer, Integer] + # @rbs @offsets: Array[Integer] + # Initialize a new cache with the given source and encoding. + #-- + #: (String source, Encoding encoding) -> void def initialize(source, encoding) @source = source @counter = @@ -223,6 +306,8 @@ def initialize(source, encoding) end # Retrieve the code units offset from the given byte offset. + #-- + #: (Integer byte_offset) -> Integer def [](byte_offset) @cache[byte_offset] ||= if (index = @offsets.bsearch_index { |offset| offset > byte_offset }).nil? @@ -249,11 +334,15 @@ def [](byte_offset) # at that point we will treat everything as single-byte characters. class ASCIISource < Source # Return the character offset for the given byte offset. + #-- + #: (Integer byte_offset) -> Integer def character_offset(byte_offset) byte_offset end # Return the column in characters for the given byte offset. + #-- + #: (Integer byte_offset) -> Integer def character_column(byte_offset) byte_offset - line_start(byte_offset) end @@ -264,6 +353,8 @@ def character_column(byte_offset) # 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. + #-- + #: (Integer byte_offset, Encoding encoding) -> Integer def code_units_offset(byte_offset, encoding) byte_offset end @@ -271,6 +362,8 @@ def code_units_offset(byte_offset, encoding) # 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. + #-- + #: (Encoding encoding) -> _CodeUnitsCache def code_units_cache(encoding) ->(byte_offset) { byte_offset } end @@ -278,6 +371,8 @@ def code_units_cache(encoding) # 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`. + #-- + #: (Integer byte_offset, Encoding encoding) -> Integer def code_units_column(byte_offset, encoding) byte_offset - line_start(byte_offset) end @@ -287,18 +382,23 @@ def code_units_column(byte_offset, encoding) class Location # A Source object that is used to determine more information from the given # offset and length. - attr_reader :source + attr_reader :source #: Source protected :source # The byte offset from the beginning of the source where this location # starts. - attr_reader :start_offset + attr_reader :start_offset #: Integer # The length of this location in bytes. - attr_reader :length + attr_reader :length #: Integer + + # @rbs @leading_comments: Array[Comment]? + # @rbs @trailing_comments: Array[Comment]? # Create a new location object with the given source, start byte offset, and # byte length. + #-- + #: (Source source, Integer start_offset, Integer length) -> void def initialize(source, start_offset, length) @source = source @start_offset = start_offset @@ -313,53 +413,73 @@ def initialize(source, start_offset, length) # These are the comments that are associated with this location that exist # before the start of this location. + #-- + #: () -> Array[Comment] def leading_comments @leading_comments ||= [] end # Attach a comment to the leading comments of this location. + #-- + #: (Comment comment) -> void 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. + #-- + #: () -> Array[Comment] def trailing_comments @trailing_comments ||= [] end # Attach a comment to the trailing comments of this location. + #-- + #: (Comment comment) -> void def trailing_comment(comment) trailing_comments << comment end # Returns all comments that are associated with this location (both leading # and trailing comments). + #-- + #: () -> Array[Comment] def comments - [*@leading_comments, *@trailing_comments] + [*@leading_comments, *@trailing_comments] #: Array[Comment] end # Create a new location object with the given options. + #-- + #: (?source: Source, ?start_offset: Integer, ?length: Integer) -> Location 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. + #-- + #: () -> Location def chop copy(length: length == 0 ? length : length - 1) end # Returns a string representation of this location. - def inspect + #-- + #: () -> String + def inspect # :nodoc: "#" end # Returns all of the lines of the source code associated with this location. + #-- + #: () -> Array[String] def source_lines source.lines end # The source code that this location represents. + #-- + #: () -> String def slice source.slice(start_offset, length) end @@ -367,6 +487,8 @@ def slice # 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. + #-- + #: () -> String def slice_lines line_start = source.line_start(start_offset) line_end = source.line_end(end_offset) @@ -375,118 +497,160 @@ def slice_lines # The character offset from the beginning of the source where this location # starts. + #-- + #: () -> Integer 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. + #-- + #: (Encoding encoding) -> Integer 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. + #-- + #: (_CodeUnitsCache cache) -> Integer def cached_start_code_units_offset(cache) cache[start_offset] end # The byte offset from the beginning of the source where this location ends. + #-- + #: () -> Integer def end_offset start_offset + length end # The character offset from the beginning of the source where this location # ends. + #-- + #: () -> Integer 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. + #-- + #: (Encoding encoding) -> Integer 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. + #-- + #: (_CodeUnitsCache cache) -> Integer def cached_end_code_units_offset(cache) cache[end_offset] end # The line number where this location starts. + #-- + #: () -> Integer def start_line source.line(start_offset) end # The content of the line where this location starts before this location. + #-- + #: () -> String def start_line_slice offset = source.line_start(start_offset) source.slice(offset, start_offset - offset) end # The line number where this location ends. + #-- + #: () -> Integer def end_line source.line(end_offset) end # The column in bytes where this location starts from the start of # the line. + #-- + #: () -> Integer def start_column source.column(start_offset) end # The column in characters where this location ends from the start of # the line. + #-- + #: () -> Integer def start_character_column source.character_column(start_offset) end # The column in code units of the given encoding where this location # starts from the start of the line. + #-- + #: (?Encoding encoding) -> Integer 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. + #-- + #: (_CodeUnitsCache cache) -> Integer def cached_start_code_units_column(cache) cache[start_offset] - cache[source.line_start(start_offset)] end # The column in bytes where this location ends from the start of the # line. + #-- + #: () -> Integer def end_column source.column(end_offset) end # The column in characters where this location ends from the start of # the line. + #-- + #: () -> Integer def end_character_column source.character_column(end_offset) end # The column in code units of the given encoding where this location # ends from the start of the line. + #-- + #: (?Encoding encoding) -> Integer 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. + #-- + #: (_CodeUnitsCache cache) -> Integer 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) + #-- + #: (Array[Symbol]? keys) -> Hash[Symbol, untyped] + def deconstruct_keys(keys) # :nodoc: { start_offset: start_offset, end_offset: end_offset } end # Implement the pretty print interface for Location. - def pretty_print(q) + #-- + #: (PP q) -> void + def pretty_print(q) # :nodoc: q.text("(#{start_line},#{start_column})-(#{end_line},#{end_column})") end # Returns true if the given other location is equal to this location. + #-- + #: (untyped other) -> bool def ==(other) Location === other && other.start_offset == start_offset && @@ -496,6 +660,8 @@ def ==(other) # 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. + #-- + #: (Location other) -> Location def join(other) raise "Incompatible sources" if source != other.source raise "Incompatible locations" if start_offset > other.start_offset @@ -506,6 +672,8 @@ def join(other) # 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. + #-- + #: (String string) -> Location def adjoin(string) line_suffix = source.slice(end_offset, source.line_end(end_offset) - end_offset) @@ -519,23 +687,38 @@ def adjoin(string) # 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 + # The Location of this comment in the source. + attr_reader :location #: Location # Create a new comment object with the given location. + #-- + #: (Location location) -> void def initialize(location) @location = location end # Implement the hash pattern matching interface for Comment. - def deconstruct_keys(keys) + #-- + #: (Array[Symbol]? keys) -> Hash[Symbol, untyped] + def deconstruct_keys(keys) # :nodoc: { location: location } end # Returns the content of the comment by slicing it from the source code. + #-- + #: () -> String def slice location.slice end + + # Returns true if this comment happens on the same line as other code and + # false if the comment is by itself. This can only be true for inline + # comments and should be false for block comments. + #-- + #: () -> bool + def trailing? + raise NotImplementedError, "trailing? is not implemented for #{self.class}" + end end # InlineComment objects are the most common. They correspond to comments in @@ -543,12 +726,16 @@ def slice class InlineComment < Comment # Returns true if this comment happens on the same line as other code and # false if the comment is by itself. + #-- + #: () -> bool def trailing? !location.start_line_slice.strip.empty? end # Returns a string representation of this comment. - def inspect + #-- + #: () -> String + def inspect # :nodoc: "#" end end @@ -556,13 +743,17 @@ def inspect # EmbDocComment objects correspond to comments that are surrounded by =begin # and =end. class EmbDocComment < Comment - # This can only be true for inline comments. + # Returns false. This can only be true for inline comments. + #-- + #: () -> bool def trailing? false end # Returns a string representation of this comment. - def inspect + #-- + #: () -> String + def inspect # :nodoc: "#" end end @@ -570,34 +761,44 @@ def inspect # 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 + attr_reader :key_loc #: Location # A Location object representing the location of the value in the source. - attr_reader :value_loc + attr_reader :value_loc #: Location # Create a new magic comment object with the given key and value locations. + #-- + #: (Location key_loc, Location value_loc) -> void 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. + #-- + #: () -> String def key key_loc.slice end # Returns the value of the magic comment by slicing it from the source code. + #-- + #: () -> String def value value_loc.slice end # Implement the hash pattern matching interface for MagicComment. - def deconstruct_keys(keys) + #-- + #: (Array[Symbol]? keys) -> Hash[Symbol, untyped] + def deconstruct_keys(keys) # :nodoc: { key_loc: key_loc, value_loc: value_loc } end # Returns a string representation of this magic comment. - def inspect + #-- + #: () -> String + def inspect # :nodoc: "#" end end @@ -606,18 +807,20 @@ def inspect 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 + attr_reader :type #: Symbol # The message associated with this error. - attr_reader :message + attr_reader :message #: String # A Location object representing the location of this error in the source. - attr_reader :location + attr_reader :location #: Location # The level of this error. - attr_reader :level + attr_reader :level #: Symbol # Create a new error object with the given message and location. + #-- + #: (Symbol type, String message, Location location, Symbol level) -> void def initialize(type, message, location, level) @type = type @message = message @@ -626,12 +829,16 @@ def initialize(type, message, location, level) end # Implement the hash pattern matching interface for ParseError. - def deconstruct_keys(keys) + #-- + #: (Array[Symbol]? keys) -> Hash[Symbol, untyped] + def deconstruct_keys(keys) # :nodoc: { type: type, message: message, location: location, level: level } end # Returns a string representation of this error. - def inspect + #-- + #: () -> String + def inspect # :nodoc: "#" end end @@ -640,18 +847,20 @@ def inspect 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 + attr_reader :type #: Symbol # The message associated with this warning. - attr_reader :message + attr_reader :message #: String # A Location object representing the location of this warning in the source. - attr_reader :location + attr_reader :location #: Location # The level of this warning. - attr_reader :level + attr_reader :level #: Symbol # Create a new warning object with the given message and location. + #-- + #: (Symbol type, String message, Location location, Symbol level) -> void def initialize(type, message, location, level) @type = type @message = message @@ -660,73 +869,116 @@ def initialize(type, message, location, level) end # Implement the hash pattern matching interface for ParseWarning. - def deconstruct_keys(keys) + #-- + #: (Array[Symbol]? keys) -> Hash[Symbol, untyped] + def deconstruct_keys(keys) # :nodoc: { type: type, message: message, location: location, level: level } end # Returns a string representation of this warning. - def inspect + #-- + #: () -> String + def inspect # :nodoc: "#" 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. + # This represents the result of a call to Prism.parse or Prism.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 + attr_reader :comments #: Array[Comment] # The list of magic comments that were encountered during parsing. - attr_reader :magic_comments + attr_reader :magic_comments #: Array[MagicComment] # 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 + attr_reader :data_loc #: Location? # The list of errors that were generated during parsing. - attr_reader :errors + attr_reader :errors #: Array[ParseError] # The list of warnings that were generated during parsing. - attr_reader :warnings + attr_reader :warnings #: Array[ParseWarning] # A Source instance that represents the source code that was parsed. - attr_reader :source + attr_reader :source #: Source # Create a new result object with the given values. - def initialize(comments, magic_comments, data_loc, errors, warnings, source) + #-- + #: (Array[Comment] comments, Array[MagicComment] magic_comments, Location? data_loc, Array[ParseError] errors, Array[ParseWarning] warnings, bool continuable, Source source) -> void + def initialize(comments, magic_comments, data_loc, errors, warnings, continuable, source) @comments = comments @magic_comments = magic_comments @data_loc = data_loc @errors = errors @warnings = warnings + @continuable = continuable @source = source end # Implement the hash pattern matching interface for Result. - def deconstruct_keys(keys) + #-- + #: (Array[Symbol]? keys) -> Hash[Symbol, untyped] + def deconstruct_keys(keys) # :nodoc: { 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. + #-- + #: () -> Encoding def encoding source.encoding end # Returns true if there were no errors during parsing and false if there # were. + #-- + #: () -> bool def success? errors.empty? end # Returns true if there were errors during parsing and false if there were # not. + #-- + #: () -> bool def failure? !success? end + # Returns true if the parsed source is an incomplete expression that could + # become valid with additional input. This is useful for REPL contexts (such + # as IRB) where the user may be entering a multi-line expression one line at + # a time and the implementation needs to determine whether to wait for more + # input or to evaluate what has been entered so far. + # + # Concretely, this returns true when every error present is caused by the + # parser reaching the end of the input before a construct was closed (e.g. + # an unclosed string, array, block, or keyword), and returns false when any + # error is caused by a token that makes the input structurally invalid + # regardless of what might follow (e.g. a stray `end`, `]`, or `)` with no + # matching opener). + # + # Examples: + # + # Prism.parse("1 + [").continuable? #=> true (unclosed array) + # Prism.parse("1 + ]").continuable? #=> false (stray ]) + # Prism.parse("tap do").continuable? #=> true (unclosed block) + # Prism.parse("end.tap do").continuable? #=> false (stray end) + # + #-- + #: () -> bool + def continuable? + @continuable + end + # Create a code units cache for the given encoding. + #-- + #: (Encoding encoding) -> _CodeUnitsCache def code_units_cache(encoding) source.code_units_cache(encoding) end @@ -743,32 +995,42 @@ class ParseResult < Result private_constant :Newlines # The syntax tree that was parsed from the source code. - attr_reader :value + attr_reader :value #: ProgramNode # Create a new parse result object with the given values. - def initialize(value, comments, magic_comments, data_loc, errors, warnings, source) + #-- + #: (ProgramNode value, Array[Comment] comments, Array[MagicComment] magic_comments, Location? data_loc, Array[ParseError] errors, Array[ParseWarning] warnings, bool continuable, Source source) -> void + def initialize(value, comments, magic_comments, data_loc, errors, warnings, continuable, source) @value = value - super(comments, magic_comments, data_loc, errors, warnings, source) + super(comments, magic_comments, data_loc, errors, warnings, continuable, source) end # Implement the hash pattern matching interface for ParseResult. - def deconstruct_keys(keys) + #-- + #: (Array[Symbol]? keys) -> Hash[Symbol, untyped] + def deconstruct_keys(keys) # :nodoc: super.merge!(value: value) end # Attach the list of comments to their respective locations in the tree. + #-- + #: () -> void 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. + #-- + #: () -> void 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. + #-- + #: () -> String def errors_format Errors.new(self).format end @@ -777,16 +1039,20 @@ def errors_format # 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 + attr_reader :value #: Array[[Token, Integer]] # Create a new lex result object with the given values. - def initialize(value, comments, magic_comments, data_loc, errors, warnings, source) + #-- + #: (Array[[Token, Integer]] value, Array[Comment] comments, Array[MagicComment] magic_comments, Location? data_loc, Array[ParseError] errors, Array[ParseWarning] warnings, bool continuable, Source source) -> void + def initialize(value, comments, magic_comments, data_loc, errors, warnings, continuable, source) @value = value - super(comments, magic_comments, data_loc, errors, warnings, source) + super(comments, magic_comments, data_loc, errors, warnings, continuable, source) end # Implement the hash pattern matching interface for LexResult. - def deconstruct_keys(keys) + #-- + #: (Array[Symbol]? keys) -> Hash[Symbol, untyped] + def deconstruct_keys(keys) # :nodoc: super.merge!(value: value) end end @@ -795,16 +1061,20 @@ def deconstruct_keys(keys) class ParseLexResult < Result # A tuple of the syntax tree and the list of tokens that were parsed from # the source code. - attr_reader :value + attr_reader :value #: [ProgramNode, Array[[Token, Integer]]] # Create a new parse lex result object with the given values. - def initialize(value, comments, magic_comments, data_loc, errors, warnings, source) + #-- + #: ([ProgramNode, Array[[Token, Integer]]] value, Array[Comment] comments, Array[MagicComment] magic_comments, Location? data_loc, Array[ParseError] errors, Array[ParseWarning] warnings, bool continuable, Source source) -> void + def initialize(value, comments, magic_comments, data_loc, errors, warnings, continuable, source) @value = value - super(comments, magic_comments, data_loc, errors, warnings, source) + super(comments, magic_comments, data_loc, errors, warnings, continuable, source) end # Implement the hash pattern matching interface for ParseLexResult. - def deconstruct_keys(keys) + #-- + #: (Array[Symbol]? keys) -> Hash[Symbol, untyped] + def deconstruct_keys(keys) # :nodoc: super.merge!(value: value) end end @@ -812,16 +1082,20 @@ def deconstruct_keys(keys) # This represents a token from the Ruby source. class Token # The Source object that represents the source this token came from. - attr_reader :source + attr_reader :source #: Source private :source # The type of token that this token is. - attr_reader :type + attr_reader :type #: Symbol # A byteslice of the source that this token represents. - attr_reader :value + attr_reader :value #: String + + # @rbs @location: Location | Integer # Create a new token object with the given type, value, and location. + #-- + #: (Source source, Symbol type, String value, Location | Integer location) -> void def initialize(source, type, value, location) @source = source @type = type @@ -830,11 +1104,15 @@ def initialize(source, type, value, location) end # Implement the hash pattern matching interface for Token. - def deconstruct_keys(keys) + #-- + #: (Array[Symbol]? keys) -> Hash[Symbol, untyped] + def deconstruct_keys(keys) # :nodoc: { type: type, value: value, location: location } end # A Location object representing the location of this token in the source. + #-- + #: () -> Location def location location = @location return location if location.is_a?(Location) @@ -842,7 +1120,9 @@ def location end # Implement the pretty print interface for Token. - def pretty_print(q) + #-- + #: (PP q) -> void + def pretty_print(q) # :nodoc: q.group do q.text(type.to_s) self.location.pretty_print(q) @@ -857,6 +1137,8 @@ def pretty_print(q) end # Returns true if the given other token is equal to this token. + #-- + #: (untyped other) -> bool def ==(other) Token === other && other.type == type && @@ -864,12 +1146,16 @@ def ==(other) end # Returns a string representation of this token. - def inspect + #-- + #: () -> String + def inspect # :nodoc: location super end # Freeze this object and the objects it contains. + #-- + #: () -> void def deep_freeze value.freeze location.freeze @@ -884,14 +1170,16 @@ def deep_freeze 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 + attr_reader :locals #: Array[Symbol] # 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 + attr_reader :forwarding #: Array[Symbol] # Create a new scope object with the given locals and forwarding. + #-- + #: (Array[Symbol] locals, Array[Symbol] forwarding) -> void def initialize(locals, forwarding) @locals = locals @forwarding = forwarding @@ -901,6 +1189,8 @@ def initialize(locals, forwarding) # 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. + #-- + #: (?locals: Array[Symbol], ?forwarding: Array[Symbol]) -> Scope def self.scope(locals: [], forwarding: []) Scope.new(locals, forwarding) end diff --git a/lib/prism/parse_result/comments.rb b/lib/prism/parse_result/comments.rb index 3e93316aff..df80792d39 100644 --- a/lib/prism/parse_result/comments.rb +++ b/lib/prism/parse_result/comments.rb @@ -1,5 +1,7 @@ # frozen_string_literal: true # :markup: markdown +#-- +# rbs_inline: enabled module Prism class ParseResult < Result @@ -18,32 +20,49 @@ class ParseResult < Result # the comment. Otherwise it will favor attaching to the nearest location # that is after the comment. class Comments + # @rbs! + # # An internal interface for a target that comments can be attached + # # to. This is either going to be a NodeTarget or a CommentTarget. + # interface _CommentTarget + # def start_offset: () -> Integer + # def end_offset: () -> Integer + # def encloses?: (Comment) -> bool + # def leading_comment: (Comment) -> void + # def trailing_comment: (Comment) -> void + # end + # A target for attaching comments that is based on a specific node's # location. class NodeTarget # :nodoc: - attr_reader :node + attr_reader :node #: node + #: (node node) -> void def initialize(node) @node = node end + #: () -> Integer def start_offset node.start_offset end + #: () -> Integer def end_offset node.end_offset end + #: (Comment comment) -> bool def encloses?(comment) start_offset <= comment.location.start_offset && comment.location.end_offset <= end_offset end + #: (Comment comment) -> void def leading_comment(comment) node.location.leading_comment(comment) end + #: (Comment comment) -> void def trailing_comment(comment) node.location.trailing_comment(comment) end @@ -52,44 +71,54 @@ def trailing_comment(comment) # 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 + attr_reader :location #: Location + #: (Location location) -> void def initialize(location) @location = location end + #: () -> Integer def start_offset location.start_offset end + #: () -> Integer def end_offset location.end_offset end + #: (Comment comment) -> bool def encloses?(comment) false end + #: (Comment comment) -> void def leading_comment(comment) location.leading_comment(comment) end + #: (Comment comment) -> void def trailing_comment(comment) location.trailing_comment(comment) end end # The parse result that we are attaching comments to. - attr_reader :parse_result + attr_reader :parse_result #: ParseResult # Create a new Comments object that will attach comments to the given # parse result. + #-- + #: (ParseResult parse_result) -> void def initialize(parse_result) @parse_result = parse_result end # Attach the comments to their respective locations in the tree by # mutating the parse result. + #-- + #: () -> void def attach! parse_result.comments.each do |comment| preceding, enclosing, following = nearest_targets(parse_result.value, comment) @@ -117,11 +146,13 @@ def attach! # Responsible for finding the nearest targets to the given comment within # the context of the given encapsulating node. + #-- + #: (node node, Comment comment) -> [_CommentTarget?, _CommentTarget?, _CommentTarget?] def nearest_targets(node, comment) comment_start = comment.location.start_offset comment_end = comment.location.end_offset - targets = [] #: Array[_Target] + targets = [] #: Array[_CommentTarget] node.comment_targets.map do |value| case value when StatementsNode @@ -134,8 +165,8 @@ def nearest_targets(node, comment) end targets.sort_by!(&:start_offset) - preceding = nil #: _Target? - following = nil #: _Target? + preceding = nil #: _CommentTarget? + following = nil #: _CommentTarget? left = 0 right = targets.length diff --git a/lib/prism/parse_result/errors.rb b/lib/prism/parse_result/errors.rb index 26c376b3ce..388309d23d 100644 --- a/lib/prism/parse_result/errors.rb +++ b/lib/prism/parse_result/errors.rb @@ -1,5 +1,7 @@ # frozen_string_literal: true # :markup: markdown +#-- +# rbs_inline: enabled require "stringio" @@ -9,14 +11,18 @@ class ParseResult < Result # 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 + attr_reader :parse_result #: ParseResult # Initialize a new set of errors from the given parse result. + #-- + #: (ParseResult parse_result) -> void def initialize(parse_result) @parse_result = parse_result end # Formats the errors in a human-readable way and return them as a string. + #-- + #: () -> String def format error_lines = {} #: Hash[Integer, Array[ParseError]] parse_result.errors.each do |error| diff --git a/lib/prism/parse_result/newlines.rb b/lib/prism/parse_result/newlines.rb index e7fd62cafe..450c790226 100644 --- a/lib/prism/parse_result/newlines.rb +++ b/lib/prism/parse_result/newlines.rb @@ -1,5 +1,7 @@ # frozen_string_literal: true # :markup: markdown +#-- +# rbs_inline: enabled module Prism class ParseResult < Result @@ -24,13 +26,20 @@ class ParseResult < Result # 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 + # The map of lines indices to whether or not they have been marked as + # emitting a newline event. + # @rbs @lines: Array[bool] + # Create a new Newlines visitor with the given newline offsets. + #-- + #: (Integer lines) -> void def initialize(lines) - # @type var lines: Integer @lines = Array.new(1 + lines, false) end - # Permit block/lambda nodes to mark newlines within themselves. + # Permit block nodes to mark newlines within themselves. + #-- + #: (BlockNode node) -> void def visit_block_node(node) old_lines = @lines @lines = Array.new(old_lines.size, false) @@ -42,17 +51,39 @@ def visit_block_node(node) end end - alias_method :visit_lambda_node, :visit_block_node + # Permit lambda nodes to mark newlines within themselves. + #-- + #: (LambdaNode node) -> void + def visit_lambda_node(node) + old_lines = @lines + @lines = Array.new(old_lines.size, false) + + begin + super(node) + ensure + @lines = old_lines + end + end - # Mark if/unless nodes as newlines. + # Mark if nodes as newlines. + #-- + #: (IfNode node) -> void def visit_if_node(node) node.newline_flag!(@lines) super(node) end - alias_method :visit_unless_node, :visit_if_node + # Mark unless nodes as newlines. + #-- + #: (UnlessNode node) -> void + def visit_unless_node(node) + node.newline_flag!(@lines) + super(node) + end # Permit statements lists to mark newlines within themselves. + #-- + #: (StatementsNode node) -> void def visit_statements_node(node) node.body.each do |child| child.newline_flag!(@lines) @@ -63,10 +94,16 @@ def visit_statements_node(node) end class Node + # Tracks whether or not this node should emit a newline event when the + # instructions that it represents are executed. + # @rbs @newline_flag: bool + + #: () -> bool def newline_flag? # :nodoc: !!defined?(@newline_flag) end + #: (Array[bool] lines) -> void def newline_flag!(lines) # :nodoc: line = location.start_line unless lines[line] @@ -77,48 +114,56 @@ def newline_flag!(lines) # :nodoc: end class BeginNode < Node + #: (Array[bool] lines) -> void def newline_flag!(lines) # :nodoc: # Never mark BeginNode with a newline flag, mark children instead. end end class ParenthesesNode < Node + #: (Array[bool] lines) -> void def newline_flag!(lines) # :nodoc: # Never mark ParenthesesNode with a newline flag, mark children instead. end end class IfNode < Node + #: (Array[bool] lines) -> void def newline_flag!(lines) # :nodoc: predicate.newline_flag!(lines) end end class UnlessNode < Node + #: (Array[bool] lines) -> void def newline_flag!(lines) # :nodoc: predicate.newline_flag!(lines) end end class UntilNode < Node + #: (Array[bool] lines) -> void def newline_flag!(lines) # :nodoc: predicate.newline_flag!(lines) end end class WhileNode < Node + #: (Array[bool] lines) -> void def newline_flag!(lines) # :nodoc: predicate.newline_flag!(lines) end end class RescueModifierNode < Node + #: (Array[bool] lines) -> void def newline_flag!(lines) # :nodoc: expression.newline_flag!(lines) end end class InterpolatedMatchLastLineNode < Node + #: (Array[bool] lines) -> void def newline_flag!(lines) # :nodoc: first = parts.first first.newline_flag!(lines) if first @@ -126,6 +171,7 @@ def newline_flag!(lines) # :nodoc: end class InterpolatedRegularExpressionNode < Node + #: (Array[bool] lines) -> void def newline_flag!(lines) # :nodoc: first = parts.first first.newline_flag!(lines) if first @@ -133,6 +179,7 @@ def newline_flag!(lines) # :nodoc: end class InterpolatedStringNode < Node + #: (Array[bool] lines) -> void def newline_flag!(lines) # :nodoc: first = parts.first first.newline_flag!(lines) if first @@ -140,6 +187,7 @@ def newline_flag!(lines) # :nodoc: end class InterpolatedSymbolNode < Node + #: (Array[bool] lines) -> void def newline_flag!(lines) # :nodoc: first = parts.first first.newline_flag!(lines) if first @@ -147,6 +195,7 @@ def newline_flag!(lines) # :nodoc: end class InterpolatedXStringNode < Node + #: (Array[bool] lines) -> void def newline_flag!(lines) # :nodoc: first = parts.first first.newline_flag!(lines) if first diff --git a/lib/prism/pattern.rb b/lib/prism/pattern.rb index 6ad2d9e5b9..be0493df05 100644 --- a/lib/prism/pattern.rb +++ b/lib/prism/pattern.rb @@ -1,5 +1,7 @@ # frozen_string_literal: true # :markup: markdown +#-- +# rbs_inline: enabled module Prism # A pattern is an object that wraps a Ruby pattern matching expression. The @@ -41,7 +43,9 @@ class Pattern class CompilationError < StandardError # Create a new CompilationError with the given representation of the node # that caused the error. - def initialize(repr) + #-- + #: (String repr) -> void + def initialize(repr) # :nodoc: super(<<~ERROR) prism was unable to compile the pattern you provided into a usable expression. It failed on to understand the node represented by: @@ -57,10 +61,13 @@ def initialize(repr) end # The query that this pattern was initialized with. - attr_reader :query + attr_reader :query #: String + # @rbs @compiled: Proc? # Create a new pattern with the given query. The query should be a string # containing a Ruby pattern matching expression. + #-- + #: (String query) -> void def initialize(query) @query = query @compiled = nil @@ -68,6 +75,8 @@ def initialize(query) # Compile the query into a callable object that can be used to match against # nodes. + #-- + #: () -> Proc def compile result = Prism.parse("case nil\nin #{query}\nend") @@ -84,7 +93,10 @@ def compile # 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) + #-- + #: (node root) -> Enumerator[node, void] + #: (node root) { (node) -> void } -> void + def scan(root, &blk) return to_enum(:scan, root) unless block_given? @compiled ||= compile @@ -100,23 +112,33 @@ def scan(root) # Shortcut for combining two procs into one that returns true if both return # true. - def combine_and(left, right) + #-- + #: (Proc left, Proc right) -> Proc + def combine_and(left, right) # :nodoc: ->(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) + #-- + #: (Proc left, Proc right) -> Proc + def combine_or(left, right) # :nodoc: ->(other) { left.call(other) || right.call(other) } end - # Raise an error because the given node is not supported. - def compile_error(node) + # Raise an error because the given node is not supported. Note purposefully + # not typing this method since it is a no return method that Steep does not + # understand. + #-- + #: (node node) -> bot + def compile_error(node) # :nodoc: raise CompilationError, node.inspect end # in [foo, bar, baz] - def compile_array_pattern_node(node) + #-- + #: (ArrayPatternNode node) -> Proc + def compile_array_pattern_node(node) # :nodoc: compile_error(node) if !node.rest.nil? || node.posts.any? constant = node.constant @@ -141,12 +163,16 @@ def compile_array_pattern_node(node) end # in foo | bar - def compile_alternation_pattern_node(node) + #-- + #: (AlternationPatternNode node) -> Proc + def compile_alternation_pattern_node(node) # :nodoc: combine_or(compile_node(node.left), compile_node(node.right)) end # in Prism::ConstantReadNode - def compile_constant_path_node(node) + #-- + #: (ConstantPathNode node) -> Proc + def compile_constant_path_node(node) # :nodoc: parent = node.parent if parent.is_a?(ConstantReadNode) && parent.slice == "Prism" @@ -161,12 +187,16 @@ def compile_constant_path_node(node) # in ConstantReadNode # in String - def compile_constant_read_node(node) + #-- + #: (ConstantReadNode node) -> Proc + def compile_constant_read_node(node) # :nodoc: compile_constant_name(node, node.name) end # Compile a name associated with a constant. - def compile_constant_name(node, name) + #-- + #: ((ConstantPathNode | ConstantReadNode) node, Symbol name) -> Proc + def compile_constant_name(node, name) # :nodoc: if Prism.const_defined?(name, false) clazz = Prism.const_get(name) @@ -182,9 +212,14 @@ def compile_constant_name(node, name) # in InstanceVariableReadNode[name: Symbol] # in { name: Symbol } - def compile_hash_pattern_node(node) + #-- + #: (HashPatternNode node) -> Proc + def compile_hash_pattern_node(node) # :nodoc: compile_error(node) if node.rest - compiled_constant = compile_node(node.constant) if node.constant + + if (constant = node.constant) + compiled_constant = compile_node(constant) + end preprocessed = node.elements.to_h do |element| @@ -212,12 +247,16 @@ def compile_hash_pattern_node(node) end # in nil - def compile_nil_node(node) + #-- + #: (NilNode node) -> Proc + def compile_nil_node(node) # :nodoc: ->(attribute) { attribute.nil? } end # in /foo/ - def compile_regular_expression_node(node) + #-- + #: (RegularExpressionNode node) -> Proc + def compile_regular_expression_node(node) # :nodoc: regexp = Regexp.new(node.unescaped, node.closing[1..]) ->(attribute) { regexp === attribute } @@ -225,7 +264,9 @@ def compile_regular_expression_node(node) # in "" # in "foo" - def compile_string_node(node) + #-- + #: (StringNode node) -> Proc + def compile_string_node(node) # :nodoc: string = node.unescaped ->(attribute) { string === attribute } @@ -233,7 +274,9 @@ def compile_string_node(node) # in :+ # in :foo - def compile_symbol_node(node) + #-- + #: (SymbolNode node) -> Proc + def compile_symbol_node(node) # :nodoc: symbol = node.unescaped.to_sym ->(attribute) { symbol === attribute } @@ -241,7 +284,9 @@ def compile_symbol_node(node) # Compile any kind of node. Dispatch out to the individual compilation # methods based on the type of node. - def compile_node(node) + #-- + #: (node node) -> Proc + def compile_node(node) # :nodoc: case node when AlternationPatternNode compile_alternation_pattern_node(node) diff --git a/lib/prism/relocation.rb b/lib/prism/relocation.rb index 3e9210a785..af0f792827 100644 --- a/lib/prism/relocation.rb +++ b/lib/prism/relocation.rb @@ -1,5 +1,7 @@ # frozen_string_literal: true # :markup: markdown +#-- +# rbs_inline: enabled module Prism # Prism parses deterministically for the same input. This provides a nice @@ -12,6 +14,33 @@ module Prism # "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 + # @rbs! + # type entry_value = untyped + # type entry_values = Hash[Symbol, entry_value] + # + # 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 + # An entry in a repository that will lazily reify its values when they are # first accessed. class Entry @@ -21,109 +50,152 @@ class Entry class MissingValueError < StandardError end + # @rbs @repository: Repository? + # @rbs @values: Hash[Symbol, untyped]? + # Initialize a new entry with the given repository. + #-- + #: (Repository repository) -> void def initialize(repository) @repository = repository @values = nil end # Fetch the filepath of the value. + #-- + #: () -> String def filepath fetch_value(:filepath) end # Fetch the start line of the value. + #-- + #: () -> Integer def start_line fetch_value(:start_line) end # Fetch the end line of the value. + #-- + #: () -> Integer def end_line fetch_value(:end_line) end # Fetch the start byte offset of the value. + #-- + #: () -> Integer def start_offset fetch_value(:start_offset) end # Fetch the end byte offset of the value. + #-- + #: () -> Integer def end_offset fetch_value(:end_offset) end # Fetch the start character offset of the value. + #-- + #: () -> Integer def start_character_offset fetch_value(:start_character_offset) end # Fetch the end character offset of the value. + #-- + #: () -> Integer 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. + #-- + #: () -> Integer 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. + #-- + #: () -> Integer def end_code_units_offset fetch_value(:end_code_units_offset) end # Fetch the start byte column of the value. + #-- + #: () -> Integer def start_column fetch_value(:start_column) end # Fetch the end byte column of the value. + #-- + #: () -> Integer def end_column fetch_value(:end_column) end # Fetch the start character column of the value. + #-- + #: () -> Integer def start_character_column fetch_value(:start_character_column) end # Fetch the end character column of the value. + #-- + #: () -> Integer 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. + #-- + #: () -> Integer 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. + #-- + #: () -> Integer def end_code_units_column fetch_value(:end_code_units_column) end # Fetch the leading comments of the value. + #-- + #: () -> Array[CommentsField::Comment] def leading_comments fetch_value(:leading_comments) end # Fetch the trailing comments of the value. + #-- + #: () -> Array[CommentsField::Comment] def trailing_comments fetch_value(:trailing_comments) end # Fetch the leading and trailing comments of the value. + #-- + #: () -> Array[CommentsField::Comment] def comments - leading_comments.concat(trailing_comments) + [*leading_comments, *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. + #-- + #: (entry_values values) -> void def reify!(values) # :nodoc: @repository = nil @values = values @@ -132,6 +204,8 @@ def reify!(values) # :nodoc: private # Fetch a value from the entry, raising an error if it is missing. + #-- + #: (Symbol name) -> entry_value def fetch_value(name) values.fetch(name) do raise MissingValueError, "No value for #{name}, make sure the " \ @@ -140,27 +214,35 @@ def fetch_value(name) end # Return the values from the repository, reifying them if necessary. + #-- + #: () -> entry_values def values - @values || (@repository.reify!; @values) + @values || (@repository&.reify!; @values) #: entry_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 + attr_reader :value #: untyped # Initialize the source with the given value. + #-- + #: (untyped value) -> void def initialize(value) @value = value end # Reparse the value and return the parse result. + #-- + #: () -> ParseResult def result raise NotImplementedError, "Subclasses must implement #result" end # Create a code units cache for the given encoding. + #-- + #: (Encoding encoding) -> _CodeUnitsCache def code_units_cache(encoding) result.code_units_cache(encoding) end @@ -169,6 +251,8 @@ def code_units_cache(encoding) # A source that is represented by a file path. class SourceFilepath < Source # Reparse the file and return the parse result. + #-- + #: () -> ParseResult def result Prism.parse_file(value) end @@ -177,6 +261,8 @@ def result # A source that is represented by a string. class SourceString < Source # Reparse the string and return the parse result. + #-- + #: () -> ParseResult def result Prism.parse(value) end @@ -185,14 +271,18 @@ def result # A field that represents the file path. class FilepathField # The file path that this field represents. - attr_reader :value + attr_reader :value #: String # Initialize a new field with the given file path. + #-- + #: (String value) -> void def initialize(value) @value = value end # Fetch the file path. + #-- + #: (_Value _value) -> entry_values def fields(_value) { filepath: value } end @@ -201,6 +291,8 @@ def fields(_value) # A field representing the start and end lines. class LinesField # Fetches the start and end line of a value. + #-- + #: (_Value value) -> entry_values def fields(value) { start_line: value.start_line, end_line: value.end_line } end @@ -209,6 +301,8 @@ def fields(value) # A field representing the start and end byte offsets. class OffsetsField # Fetches the start and end byte offset of a value. + #-- + #: (_Value value) -> entry_values def fields(value) { start_offset: value.start_offset, end_offset: value.end_offset } end @@ -217,6 +311,8 @@ def fields(value) # A field representing the start and end character offsets. class CharacterOffsetsField # Fetches the start and end character offset of a value. + #-- + #: (_Value value) -> entry_values def fields(value) { start_character_offset: value.start_character_offset, @@ -229,12 +325,16 @@ def fields(value) class CodeUnitOffsetsField # A pointer to the repository object that is used for lazily creating a # code units cache. - attr_reader :repository + attr_reader :repository #: Repository # The associated encoding for the code units. - attr_reader :encoding + attr_reader :encoding #: Encoding + + # @rbs @cache: _CodeUnitsCache? # Initialize a new field with the associated repository and encoding. + #-- + #: (Repository repository, Encoding encoding) -> void def initialize(repository, encoding) @repository = repository @encoding = encoding @@ -243,6 +343,8 @@ def initialize(repository, encoding) # Fetches the start and end code units offset of a value for a particular # encoding. + #-- + #: (_Value value) -> entry_values def fields(value) { start_code_units_offset: value.cached_start_code_units_offset(cache), @@ -253,6 +355,8 @@ def fields(value) private # Lazily create a code units cache for the associated encoding. + #-- + #: () -> _CodeUnitsCache def cache @cache ||= repository.code_units_cache(encoding) end @@ -261,6 +365,8 @@ def cache # A field representing the start and end byte columns. class ColumnsField # Fetches the start and end byte column of a value. + #-- + #: (_Value value) -> entry_values def fields(value) { start_column: value.start_column, end_column: value.end_column } end @@ -269,6 +375,8 @@ def fields(value) # A field representing the start and end character columns. class CharacterColumnsField # Fetches the start and end character column of a value. + #-- + #: (_Value value) -> entry_values def fields(value) { start_character_column: value.start_character_column, @@ -282,12 +390,16 @@ def fields(value) class CodeUnitColumnsField # The repository object that is used for lazily creating a code units # cache. - attr_reader :repository + attr_reader :repository #: Repository # The associated encoding for the code units. - attr_reader :encoding + attr_reader :encoding #: Encoding + + # @rbs @cache: _CodeUnitsCache? # Initialize a new field with the associated repository and encoding. + #-- + #: (Repository repository, Encoding encoding) -> void def initialize(repository, encoding) @repository = repository @encoding = encoding @@ -296,6 +408,8 @@ def initialize(repository, encoding) # Fetches the start and end code units column of a value for a particular # encoding. + #-- + #: (_Value value) -> entry_values def fields(value) { start_code_units_column: value.cached_start_code_units_column(cache), @@ -306,6 +420,8 @@ def fields(value) private # Lazily create a code units cache for the associated encoding. + #-- + #: () -> _CodeUnitsCache def cache @cache ||= repository.code_units_cache(encoding) end @@ -316,9 +432,11 @@ class CommentsField # An object that represents a slice of a comment. class Comment # The slice of the comment. - attr_reader :slice + attr_reader :slice #: String # Initialize a new comment with the given slice. + # + #: (String slice) -> void def initialize(slice) @slice = slice end @@ -327,6 +445,8 @@ def initialize(slice) private # Create comment objects from the given values. + #-- + #: (entry_value values) -> Array[Comment] def comments(values) values.map { |value| Comment.new(value.slice) } end @@ -335,6 +455,8 @@ def comments(values) # A field representing the leading comments. class LeadingCommentsField < CommentsField # Fetches the leading comments of a value. + #-- + #: (_Value value) -> entry_values def fields(value) { leading_comments: comments(value.leading_comments) } end @@ -343,6 +465,8 @@ def fields(value) # A field representing the trailing comments. class TrailingCommentsField < CommentsField # Fetches the trailing comments of a value. + #-- + #: (_Value value) -> entry_values def fields(value) { trailing_comments: comments(value.trailing_comments) } end @@ -358,15 +482,17 @@ class ConfigurationError < StandardError # The source associated with this repository. This will be either a # SourceFilepath (the most common use case) or a SourceString. - attr_reader :source + attr_reader :source #: Source # The fields that have been configured on this repository. - attr_reader :fields + attr_reader :fields #: Hash[Symbol, _Field] # The entries that have been saved on this repository. - attr_reader :entries + attr_reader :entries #: Hash[Integer, Hash[Symbol, Entry]] # Initialize a new repository with the given source. + #-- + #: (Source source) -> void def initialize(source) @source = source @fields = {} @@ -374,69 +500,93 @@ def initialize(source) end # Create a code units cache for the given encoding from the source. + #-- + #: (Encoding encoding) -> _CodeUnitsCache def code_units_cache(encoding) source.code_units_cache(encoding) end # Configure the filepath field for this repository and return self. + #-- + #: () -> 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. + #-- + #: () -> self def lines field(:lines, LinesField.new) end # Configure the offsets field for this repository and return self. + #-- + #: () -> self def offsets field(:offsets, OffsetsField.new) end # Configure the character offsets field for this repository and return # self. + #-- + #: () -> 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. + #-- + #: (Encoding encoding) -> 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. + #-- + #: () -> self def columns field(:columns, ColumnsField.new) end # Configure the character columns field for this repository and return # self. + #-- + #: () -> 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. + #-- + #: (Encoding encoding) -> 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. + #-- + #: () -> self def leading_comments field(:leading_comments, LeadingCommentsField.new) end # Configure the trailing comments field for this repository and return # self. + #-- + #: () -> self def trailing_comments field(:trailing_comments, TrailingCommentsField.new) end # Configure both the leading and trailing comment fields for this # repository and return self. + #-- + #: () -> self def comments leading_comments.trailing_comments end @@ -444,6 +594,8 @@ def comments # 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. + #-- + #: (Integer node_id, Symbol field_name) -> Entry def enter(node_id, field_name) # :nodoc: entry = Entry.new(self) @entries[node_id][field_name] = entry @@ -453,6 +605,8 @@ def enter(node_id, field_name) # :nodoc: # 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. + #-- + #: () -> void def reify! # :nodoc: result = source.result @@ -466,7 +620,7 @@ def reify! # :nodoc: while (node = queue.shift) @entries[node.node_id].each do |field_name, entry| value = node.public_send(field_name) - values = {} #: Hash[Symbol, untyped] + values = {} #: entry_values fields.each_value do |field| values.merge!(field.fields(value)) @@ -485,6 +639,8 @@ def reify! # :nodoc: # Append the given field to the repository and return the repository so # that these calls can be chained. + #-- + #: (Symbol name, _Field) -> self def field(name, value) raise ConfigurationError, "Cannot specify multiple #{name} fields" if @fields.key?(name) @fields[name] = value @@ -493,11 +649,15 @@ def field(name, value) end # Create a new repository for the given filepath. + #-- + #: (String value) -> Repository def self.filepath(value) Repository.new(SourceFilepath.new(value)) end # Create a new repository for the given string. + #-- + #: (String value) -> Repository def self.string(value) Repository.new(SourceString.new(value)) end diff --git a/lib/prism/string_query.rb b/lib/prism/string_query.rb index 547f58d2fa..99ce57e5fe 100644 --- a/lib/prism/string_query.rb +++ b/lib/prism/string_query.rb @@ -1,29 +1,44 @@ # frozen_string_literal: true # :markup: markdown +#-- +# rbs_inline: enabled 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 + # @rbs! + # def self.local?: (String string) -> bool + # def self.constant?: (String string) -> bool + # def self.method_name?: (String string) -> bool + # The string that this query is wrapping. - attr_reader :string + attr_reader :string #: String # Initialize a new query with the given string. + #-- + #: (String string) -> void def initialize(string) @string = string end # Whether or not this string is a valid local variable name. + #-- + #: () -> bool def local? StringQuery.local?(string) end # Whether or not this string is a valid constant name. + #-- + #: () -> bool def constant? StringQuery.constant?(string) end # Whether or not this string is a valid method name. + #-- + #: () -> bool def method_name? StringQuery.method_name?(string) end diff --git a/lib/prism/translation.rb b/lib/prism/translation.rb index 57b57135bc..5a086a7542 100644 --- a/lib/prism/translation.rb +++ b/lib/prism/translation.rb @@ -1,5 +1,7 @@ # frozen_string_literal: true # :markup: markdown +#-- +# rbs_inline: enabled module Prism # This module is responsible for converting the prism syntax tree into other diff --git a/lib/prism/translation/parser.rb b/lib/prism/translation/parser.rb index fed4ac4cd1..70031f133a 100644 --- a/lib/prism/translation/parser.rb +++ b/lib/prism/translation/parser.rb @@ -33,7 +33,7 @@ class Parser < ::Parser::Base # 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 + class PrismDiagnostic < Diagnostic # :nodoc: # This is the cached message coming from prism. attr_reader :message diff --git a/lib/prism/translation/parser/builder.rb b/lib/prism/translation/parser/builder.rb index 6b620c25bc..7fc3bba6b7 100644 --- a/lib/prism/translation/parser/builder.rb +++ b/lib/prism/translation/parser/builder.rb @@ -7,12 +7,14 @@ 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. + # 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. + # 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 @@ -56,6 +58,12 @@ def block(method_call, begin_t, args, body, end_t) method_call.loc.with_expression(join_exprs(method_call, block))) end end + + # def foo(&nil); end + # ^^^^ + def blocknilarg(amper_t, nil_t) + n0(:blocknilarg, arg_prefix_map(amper_t, nil_t)) + end end end end diff --git a/lib/prism/translation/parser/compiler.rb b/lib/prism/translation/parser/compiler.rb index bd3618b162..21aa7796c3 100644 --- a/lib/prism/translation/parser/compiler.rb +++ b/lib/prism/translation/parser/compiler.rb @@ -6,9 +6,9 @@ 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 + class Compiler < ::Prism::Compiler # :nodoc: # Raised when the tree is malformed or there is a bug in the compiler. - class CompilationError < StandardError + class CompilationError < StandardError # :nodoc: end # The Parser::Base instance that is being used to build the AST. @@ -1390,6 +1390,12 @@ def visit_nil_node(node) builder.nil(token(node.location)) end + # def foo(&nil); end + # ^^^^ + def visit_no_block_parameter_node(node) + builder.blocknilarg(token(node.operator_loc), token(node.keyword_loc)) + end + # def foo(**nil); end # ^^^^^ def visit_no_keywords_parameter_node(node) diff --git a/lib/prism/translation/parser/lexer.rb b/lib/prism/translation/parser/lexer.rb index 0491e79cd2..e82042867f 100644 --- a/lib/prism/translation/parser/lexer.rb +++ b/lib/prism/translation/parser/lexer.rb @@ -10,7 +10,7 @@ module Translation class Parser # Accepts a list of prism tokens and converts them into the expected # format for the parser gem. - class Lexer + class Lexer # :nodoc: # These tokens are always skipped TYPES_ALWAYS_SKIP = Set.new(%i[IGNORED_NEWLINE __END__ EOF]) private_constant :TYPES_ALWAYS_SKIP @@ -87,6 +87,7 @@ class Lexer KEYWORD_DEF: :kDEF, KEYWORD_DEFINED: :kDEFINED, KEYWORD_DO: :kDO, + KEYWORD_DO_BLOCK: :kDO_BLOCK, KEYWORD_DO_LOOP: :kDO_COND, KEYWORD_END: :kEND, KEYWORD_END_UPCASE: :klEND, @@ -188,8 +189,8 @@ class Lexer # 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: + EXPR_BEG = 0x1 + EXPR_LABEL = 0x400 # It is used to determine whether `do` is of the token type `kDO` or `kDO_LAMBDA`. # @@ -232,7 +233,7 @@ def initialize(source_buffer, lexed, offset_cache) @offset_cache = offset_cache end - Range = ::Parser::Source::Range # :nodoc: + Range = ::Parser::Source::Range private_constant :Range # Convert the prism tokens into the expected format for the parser gem. diff --git a/lib/prism/translation/parser_current.rb b/lib/prism/translation/parser_current.rb index f13eff6bbe..f7c1070e30 100644 --- a/lib/prism/translation/parser_current.rb +++ b/lib/prism/translation/parser_current.rb @@ -1,8 +1,8 @@ # frozen_string_literal: true # :markup: markdown +#-- # typed: ignore -# module Prism module Translation case RUBY_VERSION diff --git a/lib/prism/translation/ripper.rb b/lib/prism/translation/ripper.rb index 5b2aa37833..d1c28a2401 100644 --- a/lib/prism/translation/ripper.rb +++ b/lib/prism/translation/ripper.rb @@ -594,6 +594,8 @@ def parse # Visitor methods ########################################################################## + # :stopdoc: + # alias foo bar # ^^^^^^^^^^^^^ def visit_alias_method_node(node) @@ -2621,6 +2623,13 @@ def visit_nil_node(node) on_var_ref(on_kw("nil")) end + # def foo(&nil); end + # ^^^^ + def visit_no_block_parameter_node(node) + bounds(node.location) + on_blockarg(:nil) + end + # def foo(**nil); end # ^^^^^ def visit_no_keywords_parameter_node(node) @@ -3458,6 +3467,8 @@ def bounds(location) @column = location.start_column end + # :startdoc: + ########################################################################## # Ripper interface ########################################################################## diff --git a/lib/prism/translation/ripper/lexer.rb b/lib/prism/translation/ripper/lexer.rb index cbcdcd47cc..c6aeae4bd7 100644 --- a/lib/prism/translation/ripper/lexer.rb +++ b/lib/prism/translation/ripper/lexer.rb @@ -7,8 +7,7 @@ module Prism module Translation class Ripper class Lexer < Ripper # :nodoc: - # :stopdoc: - class State + class State # :nodoc: attr_reader :to_int, :to_s def initialize(i) @@ -48,7 +47,7 @@ def self.[](i) end end - class Elem + class Elem # :nodoc: attr_accessor :pos, :event, :tok, :state, :message def initialize(pos, event, tok, state, message = nil) @@ -128,8 +127,6 @@ def parse(...) def scan(...) parse(...) end - - # :startdoc: end end end diff --git a/lib/prism/translation/ripper/sexp.rb b/lib/prism/translation/ripper/sexp.rb index 8cfefc8472..46c0333544 100644 --- a/lib/prism/translation/ripper/sexp.rb +++ b/lib/prism/translation/ripper/sexp.rb @@ -8,9 +8,7 @@ module Translation class Ripper # This class mirrors the ::Ripper::SexpBuilder subclass of ::Ripper that # returns the arrays of [type, *children]. - class SexpBuilder < Ripper - # :stopdoc: - + class SexpBuilder < Ripper # :nodoc: attr_reader :error private @@ -65,16 +63,12 @@ def on_error(mesg) 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: - + class SexpBuilderPP < SexpBuilder # :nodoc: private def on_heredoc_dedent(val, width) @@ -118,8 +112,6 @@ def on_mlhs_add_post(list, post) alias_method "on_#{event}", :_dispatch_event_push end end - - # :startdoc: end end end diff --git a/lib/prism/translation/ruby_parser.rb b/lib/prism/translation/ruby_parser.rb index c026c4ad9c..c1fb8adfc1 100644 --- a/lib/prism/translation/ruby_parser.rb +++ b/lib/prism/translation/ruby_parser.rb @@ -19,7 +19,7 @@ module Translation # seattlerb/ruby_parser gem's syntax tree. class RubyParser # A prism visitor that builds Sexp objects. - class Compiler < ::Prism::Compiler + class Compiler < ::Prism::Compiler # :nodoc: # 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. @@ -40,34 +40,26 @@ def initialize(file, in_def: false, in_pattern: false) @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) @@ -84,10 +76,8 @@ def visit_and_node(node) end end - # ``` # [] # ^^ - # ``` def visit_array_node(node) if in_pattern s(node, :array_pat, nil).concat(visit_all(node.elements)) @@ -96,10 +86,8 @@ def visit_array_node(node) 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) @@ -121,29 +109,23 @@ def visit_array_pattern_node(node) 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)] @@ -152,18 +134,14 @@ def visit_assoc_splat_node(node) 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) @@ -195,20 +173,16 @@ def visit_begin_node(node) 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 @@ -218,10 +192,8 @@ 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 @@ -262,13 +234,11 @@ def visit_block_parameters_node(node) result end - # ``` # break # ^^^^^ # # break foo # ^^^^^^^^^ - # ``` def visit_break_node(node) if node.arguments.nil? s(node, :break) @@ -279,7 +249,6 @@ def visit_break_node(node) end end - # ``` # foo # ^^^ # @@ -288,7 +257,6 @@ def visit_break_node(node) # # foo.bar() {} # ^^^^^^^^^^^^ - # ``` def visit_call_node(node) case node.name when :!~ @@ -327,10 +295,8 @@ def visit_call_node(node) 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) @@ -339,10 +305,8 @@ def visit_call_operator_write_node(node) 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, :"&&") @@ -351,10 +315,8 @@ def visit_call_and_write_node(node) 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, :"||") @@ -376,42 +338,32 @@ def visit_call_or_write_node(node) 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) @@ -434,53 +386,41 @@ def visit_class_node(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 @@ -491,61 +431,47 @@ def visit_class_variable_target_node(node) 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) @@ -554,45 +480,35 @@ def visit_constant_path_node(node) 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? @@ -604,13 +520,11 @@ def visit_constant_path_target_node(node) 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 = @@ -639,71 +553,55 @@ def visit_def_node(node) 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?)) @@ -712,112 +610,86 @@ def visit_flip_flop_node(node) 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) }) @@ -831,7 +703,6 @@ def visit_hash_pattern_node(node) result end - # ``` # if foo then bar end # ^^^^^^^^^^^^^^^^^^^ # @@ -840,7 +711,6 @@ def visit_hash_pattern_node(node) # # foo ? bar : baz # ^^^^^^^^^^^^^^^ - # ``` def visit_if_node(node) s(node, :if, visit(node.predicate), visit(node.statements), visit(node.subsequent)) end @@ -850,24 +720,18 @@ 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) @@ -879,10 +743,8 @@ def visit_in_node(node) 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 @@ -894,10 +756,8 @@ def visit_index_operator_write_node(node) 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 @@ -909,10 +769,8 @@ def visit_index_and_write_node(node) 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 @@ -924,10 +782,8 @@ def visit_index_or_write_node(node) 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? @@ -935,69 +791,53 @@ def visit_index_target_node(node) 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 = @@ -1013,10 +853,8 @@ def visit_interpolated_match_last_line_node(node) s(node, :match, regexp) end - # ``` # /foo #{bar}/ # ^^^^^^^^^^^^ - # ``` def visit_interpolated_regular_expression_node(node) parts = visit_interpolated_parts(node.parts) @@ -1030,28 +868,22 @@ def visit_interpolated_regular_expression_node(node) 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) @@ -1131,29 +963,23 @@ def visit_interpolated_x_string_node(node) 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 @@ -1175,10 +1001,8 @@ def visit_lambda_node(node) end end - # ``` # foo # ^^^ - # ``` def visit_local_variable_read_node(node) if node.name.match?(/^_\d$/) s(node, :call, nil, node.name) @@ -1187,77 +1011,59 @@ def visit_local_variable_read_node(node) 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 @@ -1269,10 +1075,8 @@ 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) @@ -1295,10 +1099,8 @@ def visit_module_node(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) @@ -1307,10 +1109,8 @@ def visit_multi_target_node(node) 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) @@ -1330,13 +1130,11 @@ def visit_multi_write_node(node) 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) @@ -1348,58 +1146,50 @@ def visit_next_node(node) end end - # ``` # nil # ^^^ - # ``` def visit_nil_node(node) s(node, :nil) end - # ``` + # def foo(&nil); end + # ^^^^ + def visit_no_block_parameter_node(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) @@ -1416,10 +1206,8 @@ def visit_or_node(node) end end - # ``` # def foo(bar, *baz); end # ^^^^^^^^^ - # ``` def visit_parameters_node(node) children = node.each_child_node.map do |element| @@ -1433,10 +1221,8 @@ def visit_parameters_node(node) 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| @@ -1455,13 +1241,11 @@ def visit_parameters_node(node) s(node, :masgn).concat(children) end - # ``` # () # ^^ # # (1) # ^^^ - # ``` def visit_parentheses_node(node) if node.body.nil? s(node, :nil) @@ -1470,18 +1254,14 @@ def visit_parentheses_node(node) 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) @@ -1505,10 +1285,8 @@ 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) @@ -1529,58 +1307,44 @@ def visit_range_node(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) @@ -1596,32 +1360,26 @@ def visit_rescue_node(node) 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) @@ -1633,10 +1391,8 @@ def visit_return_node(node) end end - # ``` # self # ^^^^ - # ``` def visit_self_node(node) s(node, :self) end @@ -1646,42 +1402,33 @@ 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) # ^^^^ # @@ -1690,7 +1437,6 @@ def visit_source_line_node(node) # # def foo(*); bar(*); end # ^ - # ``` def visit_splat_node(node) if node.expression.nil? s(node, :splat) @@ -1710,10 +1456,8 @@ def visit_statements_node(node) end end - # ``` # "foo" # ^^^^^ - # ``` def visit_string_node(node) unescaped = node.unescaped @@ -1725,10 +1469,8 @@ def visit_string_node(node) s(node, :str, unescaped) end - # ``` # super(foo) # ^^^^^^^^^^ - # ``` def visit_super_node(node) arguments = node.arguments&.arguments || [] block = node.block @@ -1741,76 +1483,60 @@ def visit_super_node(node) 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) @@ -1822,13 +1548,11 @@ def visit_x_string_node(node) result end - # ``` # yield # ^^^^^ # # yield 1 # ^^^^^^^ - # ``` def visit_yield_node(node) s(node, :yield).concat(visit_all(node.arguments&.arguments || [])) end diff --git a/prism.gemspec b/prism.gemspec index 20c66a562e..aac056b3f8 100644 --- a/prism.gemspec +++ b/prism.gemspec @@ -42,31 +42,69 @@ Gem::Specification.new do |spec| "docs/serialization.md", "docs/testing.md", "ext/prism/api_node.c", - "ext/prism/api_pack.c", + "ext/prism/extconf.rb", "ext/prism/extension.c", "ext/prism/extension.h", "include/prism.h", + "include/prism/compiler/accel.h", + "include/prism/compiler/align.h", + "include/prism/compiler/exported.h", + "include/prism/compiler/fallthrough.h", + "include/prism/compiler/filesystem.h", + "include/prism/compiler/flex_array.h", + "include/prism/compiler/force_inline.h", + "include/prism/compiler/format.h", + "include/prism/compiler/inline.h", + "include/prism/compiler/nodiscard.h", + "include/prism/compiler/nonnull.h", + "include/prism/compiler/unused.h", + "include/prism/internal/allocator.h", + "include/prism/internal/allocator_debug.h", + "include/prism/internal/arena.h", + "include/prism/internal/bit.h", + "include/prism/internal/buffer.h", + "include/prism/internal/char.h", + "include/prism/internal/comments.h", + "include/prism/internal/constant_pool.h", + "include/prism/internal/diagnostic.h", + "include/prism/internal/encoding.h", + "include/prism/internal/integer.h", + "include/prism/internal/isinf.h", + "include/prism/internal/line_offset_list.h", + "include/prism/internal/list.h", + "include/prism/internal/magic_comments.h", + "include/prism/internal/memchr.h", + "include/prism/internal/node.h", + "include/prism/internal/options.h", + "include/prism/internal/parser.h", + "include/prism/internal/regexp.h", + "include/prism/internal/serialize.h", + "include/prism/internal/source.h", + "include/prism/internal/static_literals.h", + "include/prism/internal/strncasecmp.h", + "include/prism/internal/stringy.h", + "include/prism/internal/strpbrk.h", + "include/prism/internal/tokens.h", + "include/prism/arena.h", "include/prism/ast.h", - "include/prism/defines.h", + "include/prism/buffer.h", + "include/prism/comments.h", + "include/prism/constant_pool.h", "include/prism/diagnostic.h", - "include/prism/encoding.h", + "include/prism/excludes.h", + "include/prism/integer.h", + "include/prism/json.h", + "include/prism/line_offset_list.h", + "include/prism/magic_comments.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/serialize.h", + "include/prism/source.h", + "include/prism/stream.h", + "include/prism/string_query.h", + "include/prism/stringy.h", "include/prism/version.h", "lib/prism.rb", "lib/prism/compiler.rb", @@ -79,8 +117,8 @@ Gem::Specification.new do |spec| "lib/prism/lex_compat.rb", "lib/prism/mutation_compiler.rb", "lib/prism/node_ext.rb", + "lib/prism/node_find.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", @@ -110,59 +148,81 @@ Gem::Specification.new do |spec| "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/generated/prism.rbi", + "rbi/generated/prism/compiler.rbi", + "rbi/generated/prism/desugar_compiler.rbi", + "rbi/generated/prism/dispatcher.rbi", + "rbi/generated/prism/dot_visitor.rbi", + "rbi/generated/prism/dsl.rbi", + "rbi/generated/prism/inspect_visitor.rbi", + "rbi/generated/prism/lex_compat.rbi", + "rbi/generated/prism/mutation_compiler.rbi", + "rbi/generated/prism/node.rbi", + "rbi/generated/prism/node_ext.rbi", + "rbi/generated/prism/node_find.rbi", + "rbi/generated/prism/parse_result.rbi", + "rbi/generated/prism/pattern.rbi", + "rbi/generated/prism/reflection.rbi", + "rbi/generated/prism/relocation.rbi", + "rbi/generated/prism/serialize.rbi", + "rbi/generated/prism/string_query.rbi", + "rbi/generated/prism/translation.rbi", + "rbi/generated/prism/visitor.rbi", + "rbi/generated/prism/parse_result/comments.rbi", + "rbi/generated/prism/parse_result/errors.rbi", + "rbi/generated/prism/parse_result/newlines.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", + "rbi/rubyvm/node_find.rbi", + "sig/generated/prism.rbs", + "sig/generated/prism/compiler.rbs", + "sig/generated/prism/desugar_compiler.rbs", + "sig/generated/prism/dispatcher.rbs", + "sig/generated/prism/dot_visitor.rbs", + "sig/generated/prism/dsl.rbs", + "sig/generated/prism/inspect_visitor.rbs", + "sig/generated/prism/lex_compat.rbs", + "sig/generated/prism/mutation_compiler.rbs", + "sig/generated/prism/node.rbs", + "sig/generated/prism/node_ext.rbs", + "sig/generated/prism/node_find.rbs", + "sig/generated/prism/parse_result.rbs", + "sig/generated/prism/pattern.rbs", + "sig/generated/prism/reflection.rbs", + "sig/generated/prism/relocation.rbs", + "sig/generated/prism/serialize.rbs", + "sig/generated/prism/string_query.rbs", + "sig/generated/prism/translation.rbs", + "sig/generated/prism/visitor.rbs", + "sig/generated/prism/parse_result/comments.rbs", + "sig/generated/prism/parse_result/errors.rbs", + "sig/generated/prism/parse_result/newlines.rbs", + "src/arena.c", + "src/buffer.c", + "src/char.c", + "src/constant_pool.c", "src/diagnostic.c", "src/encoding.c", + "src/integer.c", + "src/json.c", + "src/line_offset_list.c", + "src/list.c", + "src/memchr.c", "src/node.c", "src/options.c", - "src/pack.c", + "src/parser.c", "src/prettyprint.c", "src/prism.c", "src/regexp.c", "src/serialize.c", + "src/source.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" + "src/string_query.c", + "src/stringy.c", + "src/strncasecmp.c", + "src/strpbrk.c", + "src/tokens.c" ] spec.extensions = ["ext/prism/extconf.rb"] diff --git a/rakelib/char.rake b/rakelib/char.rake index 112e20b50d..d1486a440e 100644 --- a/rakelib/char.rake +++ b/rakelib/char.rake @@ -1,6 +1,6 @@ # frozen_string_literal: true -desc "Generate the lookup tables for pm_char.c" +desc "Generate the lookup tables for char.c" namespace :generate do task :char do puts "static const uint8_t pm_char_table[256] = {" diff --git a/rakelib/check_manifest.rake b/rakelib/check_manifest.rake index 2c8fced816..ff88543fa9 100644 --- a/rakelib/check_manifest.rake +++ b/rakelib/check_manifest.rake @@ -19,7 +19,7 @@ task check_manifest: :templates do gemfiles javascript java - java-wasm + java/wasm pkg rakelib rust diff --git a/rakelib/rdoc.rake b/rakelib/rdoc.rake index 340a5cd831..deb98b0730 100644 --- a/rakelib/rdoc.rake +++ b/rakelib/rdoc.rake @@ -7,33 +7,8 @@ rescue LoadError return end -if RDoc::VERSION <= "6.5.0" - # RDoc 6.5.0 and earlier did not create an rdoc:coverage task. This patches it - # in in the same way newer versions do. - RDoc::Task.prepend( - Module.new { - def define - super.tap do - namespace rdoc_task_name do - desc "Print RDoc coverage report" - task :coverage do - @before_running_rdoc.call if @before_running_rdoc - opts = option_list << "-C" - args = opts + @rdoc_files - - $stderr.puts "rdoc #{args.join(" ")}" if Rake.application.options.trace - RDoc::RDoc.new.document(args) - end - end - end - end - } - ) -end - RDoc::Task.new(:rdoc) do |rdoc| rdoc.main = "README.md" - rdoc.markup = "markdown" rdoc.rdoc_dir = "doc/rb" rdoc.options.push("--all", "-x", "lib/prism/translation/ripper/shim.rb") diff --git a/rakelib/serialization.rake b/rakelib/serialization.rake index a57a7ba700..516e8fe5ba 100644 --- a/rakelib/serialization.rake +++ b/rakelib/serialization.rake @@ -25,8 +25,7 @@ task "test:java_loader:internal" => :compile do puts puts path serialized = Prism.dump_file(path) - source_bytes = File.binread(path).unpack('c*') - parse_result = org.prism.Loader.load(serialized.unpack('c*'), source_bytes) + parse_result = org.ruby_lang.prism.Loader.load(serialized.unpack('c*')) puts parse_result.value end end diff --git a/rakelib/typecheck.rake b/rakelib/typecheck.rake index 5da23ac34f..56a216cd9f 100644 --- a/rakelib/typecheck.rake +++ b/rakelib/typecheck.rake @@ -1,35 +1,373 @@ # frozen_string_literal: true namespace :typecheck do + class RBICompiler + INODE = Set.new(%i[accept child_nodes comment_targets compact_child_nodes deconstruct each_child_node inspect type]) + + attr_reader :parent + + def initialize(parent, abstract: Set.new, override: Set.new) + @parent = parent + @abstract = abstract + @override = override + @visibility = RBI::Public.new + end + + def compile_comments(node) + comments = [] + + if (comment = node.comment) + index = 0 + lines = comment.string.lines(chomp: true) + + while index < lines.length + case (line = lines[index]) + when ":call-seq:" + # skip RDoc call-seq annotations + index += 1 until index >= lines.length || lines[index + 1].empty? + index += 1 + when "--" + # break when RDoc private starts + break + when /\A:/ + # skip RBS type annotations + else + comments << RBI::Comment.new(line) + end + + index += 1 + end + end + + comments + end + + def compile(node) + case node + when RBS::AST::Members::AttrReader + parent << + RBI::AttrReader.new( + node.name.name, + visibility: @visibility, + sigs: [RBI::Sig.new(return_type: compile_type(node.type).to_rbi)], + comments: compile_comments(node) + ) + when RBS::AST::Members::Include + parent << RBI::Include.new(compile_name(node.name)) + when RBS::AST::Members::Private + @visibility = RBI::Private.new + when RBS::AST::Declarations::Constant + parent << + RBI::Const.new( + compile_name(node.name), + "T.let(nil, #{compile_type(node.type).to_rbi})", + comments: compile_comments(node) + ) + when RBS::AST::Declarations::Class + parent << RBI::Class.new(compile_name(node.name), comments: compile_comments(node)) do |cls| + abstract = Set.new + override = Set.new + + if parent && parent.name.name == "Prism" && cls.name.name == "Node" + cls << RBI::Helper.new("abstract") + abstract = INODE + end + + if (super_class = node.super_class).is_a?(RBS::AST::Declarations::Class::Super) + cls.superclass_name = compile_name(super_class.name) + override = INODE if parent && parent.name.name == "Prism" && super_class.name.name.name == "Node" + end + + compiler = RBICompiler.new(cls, abstract: abstract, override: override) + node.members.each { |member| compiler.compile(member) } + end + when RBS::AST::Declarations::Module + parent << RBI::Module.new(compile_name(node.name), comments: compile_comments(node)) do |mod| + compiler = RBICompiler.new(mod) + node.members.each { |member| compiler.compile(member) } + end + when RBS::AST::Members::MethodDefinition + parent << + RBI::Method.new( + node.name.name, + visibility: @visibility, + is_singleton: node.kind == :singleton, + comments: compile_comments(node) + ) do |method| + params = Set.new + node.overloads.each_with_index do |overload, index| + method_type = overload.method_type + func = method_type.type + + sig = + RBI::Sig.new( + return_type: compile_type(func.return_type).to_rbi, + is_abstract: @abstract.include?(node.name), + is_override: @override.include?(node.name) + ) + + compile_function(func) do |kind, name, type| + if !params.include?(name) + case kind + when :param then method.add_param(name) + when :opt_param then method.add_opt_param(name, "T.unsafe(nil)") + when :rest_param then method.add_rest_param(name) + when :kw_opt_param then method.add_kw_opt_param(name, "T.unsafe(nil)") + when :kw_rest_param then method.add_kw_rest_param(name) + else raise + end + params.add(name) + end + + sig.params << RBI::SigParam.new(name, type) + end + + if (block = method_type.block) + if !params.include?("blk") + method.add_block_param("blk") + params.add("blk") + end + + proc = RBI::Type.proc + compile_function(block.type) { |kind, name, type| proc.proc_params[name] = type } + proc.returns(compile_type(block.type.return_type)) + + sig.params << RBI::SigParam.new("blk", proc) + end + + method.sigs << sig + end + end + when RBS::AST::Members::Alias + case [node.new_name, node.old_name] + when [:dispatch, :visit] + parent << + RBI::Method.new("dispatch", visibility: @visibility, comments: compile_comments(node)) do |method| + method.add_param("node") + method.sigs << + RBI::Sig.new( + params: [RBI::SigParam.new("node", RBI::Type.nilable(RBI::Type.simple("Node")))], + return_type: RBI::Type.untyped + ) + end + when [:script_lines, :source_lines], + [:find, :breadth_first_search], + [:find_all, :breadth_first_search_all], + [:deconstruct, :child_nodes] + found = parent.nodes.find { |child| child.is_a?(RBI::Method) && child.name == node.old_name.name } + parent << + found.dup.tap do |method| + method.name = node.new_name.name + method.comments = compile_comments(node) + end + else + raise + end + when RBS::AST::Members::InstanceVariable, RBS::AST::Declarations::Interface, RBS::AST::Declarations::TypeAlias + # skip + else + raise + end + end + + private + + def compile_name(name) + RBI::Type.simple(name.to_namespace.path.join("::")) + end + + def compile_function(func) + argidx = -1 + kwargidx = -1 + + func.required_positionals.each do |param| + yield :param, param.name&.name || "arg#{argidx += 1}", compile_type(param.type) + end + + func.optional_positionals.each do |param| + yield :opt_param, param.name&.name || "arg#{argidx += 1}", compile_type(param.type) + end + + if (rest_positionals = func.rest_positionals) + yield :rest_param, rest_positionals.name&.name || "args", compile_type(rest_positionals.type) + end + + func.trailing_positionals.each do |param| + raise + end + + func.required_keywords.each do |param| + raise + end + + func.optional_keywords.each do |name, param| + yield :kw_opt_param, name, compile_type(param.type) + end + + if (rest_keywords = func.rest_keywords) + yield :kw_rest_param, rest_keywords.name&.name || "kwargs", compile_type(rest_keywords.type) + end + end + + def compile_type(type) + case type + when RBS::Types::Literal + case type.literal + when FalseClass + RBI::Type.simple("FalseClass") + when Symbol + RBI::Type.simple("Symbol") + else + raise + end + when RBS::Types::Bases::Any + RBI::Type.untyped + when RBS::Types::Bases::Bool + RBI::Type.boolean + when RBS::Types::Bases::Bottom + RBI::Type.noreturn + when RBS::Types::Bases::Self + RBI::Type.self_type + when RBS::Types::Bases::Void + RBI::Type.void + when RBS::Types::Union + RBI::Type.any(*type.types.map { |child| compile_type(child) }) + when RBS::Types::Tuple + RBI::Type.tuple(*type.types.map { |child| compile_type(child) }) + when RBS::Types::ClassSingleton + RBI::Type.class_of(compile_name(type.name)) + when RBS::Types::Optional + RBI::Type.nilable(compile_type(type.type)) + when RBS::Types::ClassInstance + if (args = type.args).any? + case (name = type.name.name) + when :Array, :Hash + RBI::Type.generic("T::#{name.name}", *args.map { |arg| compile_type(arg) }) + when :Enumerator + RBI::Type.generic("T::Enumerator", compile_type(args.first)) + else + raise + end + else + compile_name(type.name) + end + when RBS::Types::Alias + case type.name.name + when :node + RBI::Type.simple("Node") + when :lex_compat_token + RBI::Type.tuple( + RBI::Type.tuple(RBI::Type.simple("Integer"), RBI::Type.simple("Integer")), + RBI::Type.simple("Symbol"), + RBI::Type.simple("String"), + RBI::Type.untyped + ) + when :entry_values + RBI::Type.generic("T::Hash", RBI::Type.simple("Symbol"), RBI::Type.untyped) + when :entry_value + RBI::Type.untyped + when :boolish + RBI::Type.nilable(RBI::Type.boolean) + else + raise + end + when RBS::Types::Interface + case type.name.name + when :_CodeUnitsCache, :_CommentTarget, :_Field, :_Repository, :_Stream, :_Value + RBI::Type.untyped + when :_Visitor + RBI::Type.simple("Visitor") + else + raise + end + else + raise + end + end + end + + def with_gemfile + Bundler.with_original_env do + ENV['BUNDLE_GEMFILE'] = "gemfiles/typecheck/Gemfile" + yield + end + end + + desc "Generate RBS with rbs-inline" + task rbs_inline: :templates do + with_gemfile do + sh "bundle", "exec", "rbs-inline", "lib", "--output", "lib" + end + end + + desc "Generate RBIs from RBSs" + task rbi: :templates do + with_gemfile do + require "fileutils" + require "rbs" + require "rbi" + require "set" + + rbs_base = File.expand_path("../sig/generated", __dir__) + rbi_base = File.expand_path("../rbi/generated", __dir__) + + Dir["**/*.rbs", base: rbs_base].each do |filepath| + RBI::File.new(strictness: "true") do |file| + compiler = RBICompiler.new(file) + + _, _, decls = RBS::Parser.parse_signature(File.read(File.join(rbs_base, filepath))) + decls.each { |decl| compiler.compile(decl) } + + mkdir_p((dirpath = File.join(rbi_base, File.dirname(filepath)))) + File.write(File.join(dirpath, "#{File.basename(filepath, ".rbs")}.rbi"), file.string) + end + end + end + end + + desc "Typecheck with Steep" + task steep: :templates do + with_gemfile do + sh "bundle", "exec", "steep", "check" + end + end + + desc "Generate RBIs with Tapioca" task tapioca: :templates do Rake::Task["compile:prism"].invoke - require "tapioca/internal" - Tapioca::Cli.start(["configure"]) - Tapioca::Cli.start(["gems", "--exclude", "prism"]) - Tapioca::Cli.start(["todo"]) + with_gemfile do + sh "bundle", "exec", "tapioca", "configure" + sh "bundle", "exec", "tapioca", "gems", "--exclude", "prism" + sh "bundle", "exec", "tapioca", "todo" + end end desc "Typecheck with Sorbet" task sorbet: :templates do + require "fileutils" + locals = { polyfills: Dir["lib/prism/polyfill/**/*.rb"], gem_rbis: Dir["sorbet/rbi/**/*.rbi"] } + FileUtils.mkdir_p("sorbet") File.write("sorbet/typed_overrides.yml", ERB.new(<<~YAML, trim_mode: "-").result_with_hash(locals)) false: - ./lib/prism/lex_compat.rb + - ./lib/prism/node.rb - ./lib/prism/node_ext.rb + - ./lib/prism/node_find.rb - ./lib/prism/parse_result.rb + - ./lib/prism/pattern.rb - ./lib/prism/visitor.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/ruby_parser.rb - ./lib/prism/inspect_visitor.rb + - ./lib/prism/serialize.rb - ./sample/prism/multiplex_constants.rb # We want to treat all polyfill files as "typed: false" <% polyfills.each do |file| -%> @@ -63,11 +401,8 @@ namespace :typecheck do --suppress-error-code=7001 CONFIG - exec "#{::Gem::Specification.find_by_name("sorbet-static").full_gem_path}/libexec/sorbet" - end - - desc "Typecheck with Steep" - task steep: :templates do - exec Gem.bin_path("steep", "steep"), "check" + with_gemfile do + sh "bundle", "exec", "srb" + end end end diff --git a/rbi/generated/prism.rbi b/rbi/generated/prism.rbi new file mode 100644 index 0000000000..8c1b4e99bb --- /dev/null +++ b/rbi/generated/prism.rbi @@ -0,0 +1,86 @@ +# typed: true + +# The Prism Ruby parser. +# +# "Parsing Ruby is suddenly manageable!" +# - You, hopefully +module Prism + # 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. + sig { params(version: String).void } + def initialize(version); end + end + + # 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. + sig { params(source: String, options: ::T.untyped).returns(LexCompat::Result) } + def self.lex_compat(source, **options); end + + # Load the serialized AST using the source as a reference into a tree. + sig { params(source: String, serialized: String, freeze: T::Boolean).returns(ParseResult) } + def self.load(source, serialized, freeze = T.unsafe(nil)); end + + # Given a Method, UnboundMethod, Proc, or Thread::Backtrace::Location, + # returns the Prism node representing it. On CRuby, this uses node_id for + # an exact match. On other implementations, it falls back to best-effort + # matching by source location line number. + sig { params(callable: ::T.any(Method, UnboundMethod, Proc, Thread::Backtrace::Location), rubyvm: T::Boolean).returns(::T.nilable(Node)) } + def self.find(callable, rubyvm: T.unsafe(nil)); end + + VERSION = T.let(nil, String) + BACKEND = T.let(nil, Symbol) + + sig { params(source: String, filepath: String, command_line: String, encoding: ::T.any(Encoding, FalseClass), freeze: T::Boolean, frozen_string_literal: T::Boolean, line: Integer, main_script: T::Boolean, partial_script: T::Boolean, scopes: T::Array[T::Array[Symbol]], version: String).returns(ParseResult) } + def self.parse(source, filepath: T.unsafe(nil), command_line: T.unsafe(nil), encoding: T.unsafe(nil), freeze: T.unsafe(nil), frozen_string_literal: T.unsafe(nil), line: T.unsafe(nil), main_script: T.unsafe(nil), partial_script: T.unsafe(nil), scopes: T.unsafe(nil), version: T.unsafe(nil)); end + + sig { params(source: String, filepath: String, command_line: String, encoding: ::T.any(Encoding, FalseClass), freeze: T::Boolean, frozen_string_literal: T::Boolean, line: Integer, main_script: T::Boolean, partial_script: T::Boolean, scopes: T::Array[T::Array[Symbol]], version: String).void } + def self.profile(source, filepath: T.unsafe(nil), command_line: T.unsafe(nil), encoding: T.unsafe(nil), freeze: T.unsafe(nil), frozen_string_literal: T.unsafe(nil), line: T.unsafe(nil), main_script: T.unsafe(nil), partial_script: T.unsafe(nil), scopes: T.unsafe(nil), version: T.unsafe(nil)); end + + sig { params(source: String, filepath: String, command_line: String, encoding: ::T.any(Encoding, FalseClass), freeze: T::Boolean, frozen_string_literal: T::Boolean, line: Integer, main_script: T::Boolean, partial_script: T::Boolean, scopes: T::Array[T::Array[Symbol]], version: String).returns(LexResult) } + def self.lex(source, filepath: T.unsafe(nil), command_line: T.unsafe(nil), encoding: T.unsafe(nil), freeze: T.unsafe(nil), frozen_string_literal: T.unsafe(nil), line: T.unsafe(nil), main_script: T.unsafe(nil), partial_script: T.unsafe(nil), scopes: T.unsafe(nil), version: T.unsafe(nil)); end + + sig { params(source: String, filepath: String, command_line: String, encoding: ::T.any(Encoding, FalseClass), freeze: T::Boolean, frozen_string_literal: T::Boolean, line: Integer, main_script: T::Boolean, partial_script: T::Boolean, scopes: T::Array[T::Array[Symbol]], version: String).returns(ParseLexResult) } + def self.parse_lex(source, filepath: T.unsafe(nil), command_line: T.unsafe(nil), encoding: T.unsafe(nil), freeze: T.unsafe(nil), frozen_string_literal: T.unsafe(nil), line: T.unsafe(nil), main_script: T.unsafe(nil), partial_script: T.unsafe(nil), scopes: T.unsafe(nil), version: T.unsafe(nil)); end + + sig { params(source: String, filepath: String, command_line: String, encoding: ::T.any(Encoding, FalseClass), freeze: T::Boolean, frozen_string_literal: T::Boolean, line: Integer, main_script: T::Boolean, partial_script: T::Boolean, scopes: T::Array[T::Array[Symbol]], version: String).returns(String) } + def self.dump(source, filepath: T.unsafe(nil), command_line: T.unsafe(nil), encoding: T.unsafe(nil), freeze: T.unsafe(nil), frozen_string_literal: T.unsafe(nil), line: T.unsafe(nil), main_script: T.unsafe(nil), partial_script: T.unsafe(nil), scopes: T.unsafe(nil), version: T.unsafe(nil)); end + + sig { params(source: String, filepath: String, command_line: String, encoding: ::T.any(Encoding, FalseClass), freeze: T::Boolean, frozen_string_literal: T::Boolean, line: Integer, main_script: T::Boolean, partial_script: T::Boolean, scopes: T::Array[T::Array[Symbol]], version: String).returns(T::Array[Comment]) } + def self.parse_comments(source, filepath: T.unsafe(nil), command_line: T.unsafe(nil), encoding: T.unsafe(nil), freeze: T.unsafe(nil), frozen_string_literal: T.unsafe(nil), line: T.unsafe(nil), main_script: T.unsafe(nil), partial_script: T.unsafe(nil), scopes: T.unsafe(nil), version: T.unsafe(nil)); end + + sig { params(source: String, filepath: String, command_line: String, encoding: ::T.any(Encoding, FalseClass), freeze: T::Boolean, frozen_string_literal: T::Boolean, line: Integer, main_script: T::Boolean, partial_script: T::Boolean, scopes: T::Array[T::Array[Symbol]], version: String).returns(T::Boolean) } + def self.parse_success?(source, filepath: T.unsafe(nil), command_line: T.unsafe(nil), encoding: T.unsafe(nil), freeze: T.unsafe(nil), frozen_string_literal: T.unsafe(nil), line: T.unsafe(nil), main_script: T.unsafe(nil), partial_script: T.unsafe(nil), scopes: T.unsafe(nil), version: T.unsafe(nil)); end + + sig { params(source: String, filepath: String, command_line: String, encoding: ::T.any(Encoding, FalseClass), freeze: T::Boolean, frozen_string_literal: T::Boolean, line: Integer, main_script: T::Boolean, partial_script: T::Boolean, scopes: T::Array[T::Array[Symbol]], version: String).returns(T::Boolean) } + def self.parse_failure?(source, filepath: T.unsafe(nil), command_line: T.unsafe(nil), encoding: T.unsafe(nil), freeze: T.unsafe(nil), frozen_string_literal: T.unsafe(nil), line: T.unsafe(nil), main_script: T.unsafe(nil), partial_script: T.unsafe(nil), scopes: T.unsafe(nil), version: T.unsafe(nil)); end + + sig { params(stream: ::T.untyped, filepath: String, command_line: String, encoding: ::T.any(Encoding, FalseClass), freeze: T::Boolean, frozen_string_literal: T::Boolean, line: Integer, main_script: T::Boolean, partial_script: T::Boolean, scopes: T::Array[T::Array[Symbol]], version: String).returns(ParseResult) } + def self.parse_stream(stream, filepath: T.unsafe(nil), command_line: T.unsafe(nil), encoding: T.unsafe(nil), freeze: T.unsafe(nil), frozen_string_literal: T.unsafe(nil), line: T.unsafe(nil), main_script: T.unsafe(nil), partial_script: T.unsafe(nil), scopes: T.unsafe(nil), version: T.unsafe(nil)); end + + sig { params(filepath: String, command_line: String, encoding: ::T.any(Encoding, FalseClass), freeze: T::Boolean, frozen_string_literal: T::Boolean, line: Integer, main_script: T::Boolean, partial_script: T::Boolean, scopes: T::Array[T::Array[Symbol]], version: String).returns(ParseResult) } + def self.parse_file(filepath, command_line: T.unsafe(nil), encoding: T.unsafe(nil), freeze: T.unsafe(nil), frozen_string_literal: T.unsafe(nil), line: T.unsafe(nil), main_script: T.unsafe(nil), partial_script: T.unsafe(nil), scopes: T.unsafe(nil), version: T.unsafe(nil)); end + + sig { params(filepath: String, command_line: String, encoding: ::T.any(Encoding, FalseClass), freeze: T::Boolean, frozen_string_literal: T::Boolean, line: Integer, main_script: T::Boolean, partial_script: T::Boolean, scopes: T::Array[T::Array[Symbol]], version: String).void } + def self.profile_file(filepath, command_line: T.unsafe(nil), encoding: T.unsafe(nil), freeze: T.unsafe(nil), frozen_string_literal: T.unsafe(nil), line: T.unsafe(nil), main_script: T.unsafe(nil), partial_script: T.unsafe(nil), scopes: T.unsafe(nil), version: T.unsafe(nil)); end + + sig { params(filepath: String, command_line: String, encoding: ::T.any(Encoding, FalseClass), freeze: T::Boolean, frozen_string_literal: T::Boolean, line: Integer, main_script: T::Boolean, partial_script: T::Boolean, scopes: T::Array[T::Array[Symbol]], version: String).returns(LexResult) } + def self.lex_file(filepath, command_line: T.unsafe(nil), encoding: T.unsafe(nil), freeze: T.unsafe(nil), frozen_string_literal: T.unsafe(nil), line: T.unsafe(nil), main_script: T.unsafe(nil), partial_script: T.unsafe(nil), scopes: T.unsafe(nil), version: T.unsafe(nil)); end + + sig { params(filepath: String, command_line: String, encoding: ::T.any(Encoding, FalseClass), freeze: T::Boolean, frozen_string_literal: T::Boolean, line: Integer, main_script: T::Boolean, partial_script: T::Boolean, scopes: T::Array[T::Array[Symbol]], version: String).returns(ParseLexResult) } + def self.parse_lex_file(filepath, command_line: T.unsafe(nil), encoding: T.unsafe(nil), freeze: T.unsafe(nil), frozen_string_literal: T.unsafe(nil), line: T.unsafe(nil), main_script: T.unsafe(nil), partial_script: T.unsafe(nil), scopes: T.unsafe(nil), version: T.unsafe(nil)); end + + sig { params(filepath: String, command_line: String, encoding: ::T.any(Encoding, FalseClass), freeze: T::Boolean, frozen_string_literal: T::Boolean, line: Integer, main_script: T::Boolean, partial_script: T::Boolean, scopes: T::Array[T::Array[Symbol]], version: String).returns(String) } + def self.dump_file(filepath, command_line: T.unsafe(nil), encoding: T.unsafe(nil), freeze: T.unsafe(nil), frozen_string_literal: T.unsafe(nil), line: T.unsafe(nil), main_script: T.unsafe(nil), partial_script: T.unsafe(nil), scopes: T.unsafe(nil), version: T.unsafe(nil)); end + + sig { params(filepath: String, command_line: String, encoding: ::T.any(Encoding, FalseClass), freeze: T::Boolean, frozen_string_literal: T::Boolean, line: Integer, main_script: T::Boolean, partial_script: T::Boolean, scopes: T::Array[T::Array[Symbol]], version: String).returns(T::Array[Comment]) } + def self.parse_file_comments(filepath, command_line: T.unsafe(nil), encoding: T.unsafe(nil), freeze: T.unsafe(nil), frozen_string_literal: T.unsafe(nil), line: T.unsafe(nil), main_script: T.unsafe(nil), partial_script: T.unsafe(nil), scopes: T.unsafe(nil), version: T.unsafe(nil)); end + + sig { params(filepath: String, command_line: String, encoding: ::T.any(Encoding, FalseClass), freeze: T::Boolean, frozen_string_literal: T::Boolean, line: Integer, main_script: T::Boolean, partial_script: T::Boolean, scopes: T::Array[T::Array[Symbol]], version: String).returns(T::Boolean) } + def self.parse_file_success?(filepath, command_line: T.unsafe(nil), encoding: T.unsafe(nil), freeze: T.unsafe(nil), frozen_string_literal: T.unsafe(nil), line: T.unsafe(nil), main_script: T.unsafe(nil), partial_script: T.unsafe(nil), scopes: T.unsafe(nil), version: T.unsafe(nil)); end + + sig { params(filepath: String, command_line: String, encoding: ::T.any(Encoding, FalseClass), freeze: T::Boolean, frozen_string_literal: T::Boolean, line: Integer, main_script: T::Boolean, partial_script: T::Boolean, scopes: T::Array[T::Array[Symbol]], version: String).returns(T::Boolean) } + def self.parse_file_failure?(filepath, command_line: T.unsafe(nil), encoding: T.unsafe(nil), freeze: T.unsafe(nil), frozen_string_literal: T.unsafe(nil), line: T.unsafe(nil), main_script: T.unsafe(nil), partial_script: T.unsafe(nil), scopes: T.unsafe(nil), version: T.unsafe(nil)); end +end diff --git a/rbi/generated/prism/compiler.rbi b/rbi/generated/prism/compiler.rbi new file mode 100644 index 0000000000..af86919953 --- /dev/null +++ b/rbi/generated/prism/compiler.rbi @@ -0,0 +1,489 @@ +# typed: true + +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. + sig { params(arg0: ::T.nilable(Node)).returns(::T.untyped) } + def visit(arg0); end + + # Visit a list of nodes. + sig { params(arg0: T::Array[::T.nilable(Node)]).returns(::T.untyped) } + def visit_all(arg0); end + + # Visit the child nodes of the given node. + sig { params(arg0: Node).returns(T::Array[::T.untyped]) } + def visit_child_nodes(arg0); end + + sig { params(arg0: AliasGlobalVariableNode).returns(T::Array[::T.untyped]) } + def visit_alias_global_variable_node(arg0); end + + sig { params(arg0: AliasMethodNode).returns(T::Array[::T.untyped]) } + def visit_alias_method_node(arg0); end + + sig { params(arg0: AlternationPatternNode).returns(T::Array[::T.untyped]) } + def visit_alternation_pattern_node(arg0); end + + sig { params(arg0: AndNode).returns(T::Array[::T.untyped]) } + def visit_and_node(arg0); end + + sig { params(arg0: ArgumentsNode).returns(T::Array[::T.untyped]) } + def visit_arguments_node(arg0); end + + sig { params(arg0: ArrayNode).returns(T::Array[::T.untyped]) } + def visit_array_node(arg0); end + + sig { params(arg0: ArrayPatternNode).returns(T::Array[::T.untyped]) } + def visit_array_pattern_node(arg0); end + + sig { params(arg0: AssocNode).returns(T::Array[::T.untyped]) } + def visit_assoc_node(arg0); end + + sig { params(arg0: AssocSplatNode).returns(T::Array[::T.untyped]) } + def visit_assoc_splat_node(arg0); end + + sig { params(arg0: BackReferenceReadNode).returns(T::Array[::T.untyped]) } + def visit_back_reference_read_node(arg0); end + + sig { params(arg0: BeginNode).returns(T::Array[::T.untyped]) } + def visit_begin_node(arg0); end + + sig { params(arg0: BlockArgumentNode).returns(T::Array[::T.untyped]) } + def visit_block_argument_node(arg0); end + + sig { params(arg0: BlockLocalVariableNode).returns(T::Array[::T.untyped]) } + def visit_block_local_variable_node(arg0); end + + sig { params(arg0: BlockNode).returns(T::Array[::T.untyped]) } + def visit_block_node(arg0); end + + sig { params(arg0: BlockParameterNode).returns(T::Array[::T.untyped]) } + def visit_block_parameter_node(arg0); end + + sig { params(arg0: BlockParametersNode).returns(T::Array[::T.untyped]) } + def visit_block_parameters_node(arg0); end + + sig { params(arg0: BreakNode).returns(T::Array[::T.untyped]) } + def visit_break_node(arg0); end + + sig { params(arg0: CallAndWriteNode).returns(T::Array[::T.untyped]) } + def visit_call_and_write_node(arg0); end + + sig { params(arg0: CallNode).returns(T::Array[::T.untyped]) } + def visit_call_node(arg0); end + + sig { params(arg0: CallOperatorWriteNode).returns(T::Array[::T.untyped]) } + def visit_call_operator_write_node(arg0); end + + sig { params(arg0: CallOrWriteNode).returns(T::Array[::T.untyped]) } + def visit_call_or_write_node(arg0); end + + sig { params(arg0: CallTargetNode).returns(T::Array[::T.untyped]) } + def visit_call_target_node(arg0); end + + sig { params(arg0: CapturePatternNode).returns(T::Array[::T.untyped]) } + def visit_capture_pattern_node(arg0); end + + sig { params(arg0: CaseMatchNode).returns(T::Array[::T.untyped]) } + def visit_case_match_node(arg0); end + + sig { params(arg0: CaseNode).returns(T::Array[::T.untyped]) } + def visit_case_node(arg0); end + + sig { params(arg0: ClassNode).returns(T::Array[::T.untyped]) } + def visit_class_node(arg0); end + + sig { params(arg0: ClassVariableAndWriteNode).returns(T::Array[::T.untyped]) } + def visit_class_variable_and_write_node(arg0); end + + sig { params(arg0: ClassVariableOperatorWriteNode).returns(T::Array[::T.untyped]) } + def visit_class_variable_operator_write_node(arg0); end + + sig { params(arg0: ClassVariableOrWriteNode).returns(T::Array[::T.untyped]) } + def visit_class_variable_or_write_node(arg0); end + + sig { params(arg0: ClassVariableReadNode).returns(T::Array[::T.untyped]) } + def visit_class_variable_read_node(arg0); end + + sig { params(arg0: ClassVariableTargetNode).returns(T::Array[::T.untyped]) } + def visit_class_variable_target_node(arg0); end + + sig { params(arg0: ClassVariableWriteNode).returns(T::Array[::T.untyped]) } + def visit_class_variable_write_node(arg0); end + + sig { params(arg0: ConstantAndWriteNode).returns(T::Array[::T.untyped]) } + def visit_constant_and_write_node(arg0); end + + sig { params(arg0: ConstantOperatorWriteNode).returns(T::Array[::T.untyped]) } + def visit_constant_operator_write_node(arg0); end + + sig { params(arg0: ConstantOrWriteNode).returns(T::Array[::T.untyped]) } + def visit_constant_or_write_node(arg0); end + + sig { params(arg0: ConstantPathAndWriteNode).returns(T::Array[::T.untyped]) } + def visit_constant_path_and_write_node(arg0); end + + sig { params(arg0: ConstantPathNode).returns(T::Array[::T.untyped]) } + def visit_constant_path_node(arg0); end + + sig { params(arg0: ConstantPathOperatorWriteNode).returns(T::Array[::T.untyped]) } + def visit_constant_path_operator_write_node(arg0); end + + sig { params(arg0: ConstantPathOrWriteNode).returns(T::Array[::T.untyped]) } + def visit_constant_path_or_write_node(arg0); end + + sig { params(arg0: ConstantPathTargetNode).returns(T::Array[::T.untyped]) } + def visit_constant_path_target_node(arg0); end + + sig { params(arg0: ConstantPathWriteNode).returns(T::Array[::T.untyped]) } + def visit_constant_path_write_node(arg0); end + + sig { params(arg0: ConstantReadNode).returns(T::Array[::T.untyped]) } + def visit_constant_read_node(arg0); end + + sig { params(arg0: ConstantTargetNode).returns(T::Array[::T.untyped]) } + def visit_constant_target_node(arg0); end + + sig { params(arg0: ConstantWriteNode).returns(T::Array[::T.untyped]) } + def visit_constant_write_node(arg0); end + + sig { params(arg0: DefNode).returns(T::Array[::T.untyped]) } + def visit_def_node(arg0); end + + sig { params(arg0: DefinedNode).returns(T::Array[::T.untyped]) } + def visit_defined_node(arg0); end + + sig { params(arg0: ElseNode).returns(T::Array[::T.untyped]) } + def visit_else_node(arg0); end + + sig { params(arg0: EmbeddedStatementsNode).returns(T::Array[::T.untyped]) } + def visit_embedded_statements_node(arg0); end + + sig { params(arg0: EmbeddedVariableNode).returns(T::Array[::T.untyped]) } + def visit_embedded_variable_node(arg0); end + + sig { params(arg0: EnsureNode).returns(T::Array[::T.untyped]) } + def visit_ensure_node(arg0); end + + sig { params(arg0: FalseNode).returns(T::Array[::T.untyped]) } + def visit_false_node(arg0); end + + sig { params(arg0: FindPatternNode).returns(T::Array[::T.untyped]) } + def visit_find_pattern_node(arg0); end + + sig { params(arg0: FlipFlopNode).returns(T::Array[::T.untyped]) } + def visit_flip_flop_node(arg0); end + + sig { params(arg0: FloatNode).returns(T::Array[::T.untyped]) } + def visit_float_node(arg0); end + + sig { params(arg0: ForNode).returns(T::Array[::T.untyped]) } + def visit_for_node(arg0); end + + sig { params(arg0: ForwardingArgumentsNode).returns(T::Array[::T.untyped]) } + def visit_forwarding_arguments_node(arg0); end + + sig { params(arg0: ForwardingParameterNode).returns(T::Array[::T.untyped]) } + def visit_forwarding_parameter_node(arg0); end + + sig { params(arg0: ForwardingSuperNode).returns(T::Array[::T.untyped]) } + def visit_forwarding_super_node(arg0); end + + sig { params(arg0: GlobalVariableAndWriteNode).returns(T::Array[::T.untyped]) } + def visit_global_variable_and_write_node(arg0); end + + sig { params(arg0: GlobalVariableOperatorWriteNode).returns(T::Array[::T.untyped]) } + def visit_global_variable_operator_write_node(arg0); end + + sig { params(arg0: GlobalVariableOrWriteNode).returns(T::Array[::T.untyped]) } + def visit_global_variable_or_write_node(arg0); end + + sig { params(arg0: GlobalVariableReadNode).returns(T::Array[::T.untyped]) } + def visit_global_variable_read_node(arg0); end + + sig { params(arg0: GlobalVariableTargetNode).returns(T::Array[::T.untyped]) } + def visit_global_variable_target_node(arg0); end + + sig { params(arg0: GlobalVariableWriteNode).returns(T::Array[::T.untyped]) } + def visit_global_variable_write_node(arg0); end + + sig { params(arg0: HashNode).returns(T::Array[::T.untyped]) } + def visit_hash_node(arg0); end + + sig { params(arg0: HashPatternNode).returns(T::Array[::T.untyped]) } + def visit_hash_pattern_node(arg0); end + + sig { params(arg0: IfNode).returns(T::Array[::T.untyped]) } + def visit_if_node(arg0); end + + sig { params(arg0: ImaginaryNode).returns(T::Array[::T.untyped]) } + def visit_imaginary_node(arg0); end + + sig { params(arg0: ImplicitNode).returns(T::Array[::T.untyped]) } + def visit_implicit_node(arg0); end + + sig { params(arg0: ImplicitRestNode).returns(T::Array[::T.untyped]) } + def visit_implicit_rest_node(arg0); end + + sig { params(arg0: InNode).returns(T::Array[::T.untyped]) } + def visit_in_node(arg0); end + + sig { params(arg0: IndexAndWriteNode).returns(T::Array[::T.untyped]) } + def visit_index_and_write_node(arg0); end + + sig { params(arg0: IndexOperatorWriteNode).returns(T::Array[::T.untyped]) } + def visit_index_operator_write_node(arg0); end + + sig { params(arg0: IndexOrWriteNode).returns(T::Array[::T.untyped]) } + def visit_index_or_write_node(arg0); end + + sig { params(arg0: IndexTargetNode).returns(T::Array[::T.untyped]) } + def visit_index_target_node(arg0); end + + sig { params(arg0: InstanceVariableAndWriteNode).returns(T::Array[::T.untyped]) } + def visit_instance_variable_and_write_node(arg0); end + + sig { params(arg0: InstanceVariableOperatorWriteNode).returns(T::Array[::T.untyped]) } + def visit_instance_variable_operator_write_node(arg0); end + + sig { params(arg0: InstanceVariableOrWriteNode).returns(T::Array[::T.untyped]) } + def visit_instance_variable_or_write_node(arg0); end + + sig { params(arg0: InstanceVariableReadNode).returns(T::Array[::T.untyped]) } + def visit_instance_variable_read_node(arg0); end + + sig { params(arg0: InstanceVariableTargetNode).returns(T::Array[::T.untyped]) } + def visit_instance_variable_target_node(arg0); end + + sig { params(arg0: InstanceVariableWriteNode).returns(T::Array[::T.untyped]) } + def visit_instance_variable_write_node(arg0); end + + sig { params(arg0: IntegerNode).returns(T::Array[::T.untyped]) } + def visit_integer_node(arg0); end + + sig { params(arg0: InterpolatedMatchLastLineNode).returns(T::Array[::T.untyped]) } + def visit_interpolated_match_last_line_node(arg0); end + + sig { params(arg0: InterpolatedRegularExpressionNode).returns(T::Array[::T.untyped]) } + def visit_interpolated_regular_expression_node(arg0); end + + sig { params(arg0: InterpolatedStringNode).returns(T::Array[::T.untyped]) } + def visit_interpolated_string_node(arg0); end + + sig { params(arg0: InterpolatedSymbolNode).returns(T::Array[::T.untyped]) } + def visit_interpolated_symbol_node(arg0); end + + sig { params(arg0: InterpolatedXStringNode).returns(T::Array[::T.untyped]) } + def visit_interpolated_x_string_node(arg0); end + + sig { params(arg0: ItLocalVariableReadNode).returns(T::Array[::T.untyped]) } + def visit_it_local_variable_read_node(arg0); end + + sig { params(arg0: ItParametersNode).returns(T::Array[::T.untyped]) } + def visit_it_parameters_node(arg0); end + + sig { params(arg0: KeywordHashNode).returns(T::Array[::T.untyped]) } + def visit_keyword_hash_node(arg0); end + + sig { params(arg0: KeywordRestParameterNode).returns(T::Array[::T.untyped]) } + def visit_keyword_rest_parameter_node(arg0); end + + sig { params(arg0: LambdaNode).returns(T::Array[::T.untyped]) } + def visit_lambda_node(arg0); end + + sig { params(arg0: LocalVariableAndWriteNode).returns(T::Array[::T.untyped]) } + def visit_local_variable_and_write_node(arg0); end + + sig { params(arg0: LocalVariableOperatorWriteNode).returns(T::Array[::T.untyped]) } + def visit_local_variable_operator_write_node(arg0); end + + sig { params(arg0: LocalVariableOrWriteNode).returns(T::Array[::T.untyped]) } + def visit_local_variable_or_write_node(arg0); end + + sig { params(arg0: LocalVariableReadNode).returns(T::Array[::T.untyped]) } + def visit_local_variable_read_node(arg0); end + + sig { params(arg0: LocalVariableTargetNode).returns(T::Array[::T.untyped]) } + def visit_local_variable_target_node(arg0); end + + sig { params(arg0: LocalVariableWriteNode).returns(T::Array[::T.untyped]) } + def visit_local_variable_write_node(arg0); end + + sig { params(arg0: MatchLastLineNode).returns(T::Array[::T.untyped]) } + def visit_match_last_line_node(arg0); end + + sig { params(arg0: MatchPredicateNode).returns(T::Array[::T.untyped]) } + def visit_match_predicate_node(arg0); end + + sig { params(arg0: MatchRequiredNode).returns(T::Array[::T.untyped]) } + def visit_match_required_node(arg0); end + + sig { params(arg0: MatchWriteNode).returns(T::Array[::T.untyped]) } + def visit_match_write_node(arg0); end + + sig { params(arg0: MissingNode).returns(T::Array[::T.untyped]) } + def visit_missing_node(arg0); end + + sig { params(arg0: ModuleNode).returns(T::Array[::T.untyped]) } + def visit_module_node(arg0); end + + sig { params(arg0: MultiTargetNode).returns(T::Array[::T.untyped]) } + def visit_multi_target_node(arg0); end + + sig { params(arg0: MultiWriteNode).returns(T::Array[::T.untyped]) } + def visit_multi_write_node(arg0); end + + sig { params(arg0: NextNode).returns(T::Array[::T.untyped]) } + def visit_next_node(arg0); end + + sig { params(arg0: NilNode).returns(T::Array[::T.untyped]) } + def visit_nil_node(arg0); end + + sig { params(arg0: NoBlockParameterNode).returns(T::Array[::T.untyped]) } + def visit_no_block_parameter_node(arg0); end + + sig { params(arg0: NoKeywordsParameterNode).returns(T::Array[::T.untyped]) } + def visit_no_keywords_parameter_node(arg0); end + + sig { params(arg0: NumberedParametersNode).returns(T::Array[::T.untyped]) } + def visit_numbered_parameters_node(arg0); end + + sig { params(arg0: NumberedReferenceReadNode).returns(T::Array[::T.untyped]) } + def visit_numbered_reference_read_node(arg0); end + + sig { params(arg0: OptionalKeywordParameterNode).returns(T::Array[::T.untyped]) } + def visit_optional_keyword_parameter_node(arg0); end + + sig { params(arg0: OptionalParameterNode).returns(T::Array[::T.untyped]) } + def visit_optional_parameter_node(arg0); end + + sig { params(arg0: OrNode).returns(T::Array[::T.untyped]) } + def visit_or_node(arg0); end + + sig { params(arg0: ParametersNode).returns(T::Array[::T.untyped]) } + def visit_parameters_node(arg0); end + + sig { params(arg0: ParenthesesNode).returns(T::Array[::T.untyped]) } + def visit_parentheses_node(arg0); end + + sig { params(arg0: PinnedExpressionNode).returns(T::Array[::T.untyped]) } + def visit_pinned_expression_node(arg0); end + + sig { params(arg0: PinnedVariableNode).returns(T::Array[::T.untyped]) } + def visit_pinned_variable_node(arg0); end + + sig { params(arg0: PostExecutionNode).returns(T::Array[::T.untyped]) } + def visit_post_execution_node(arg0); end + + sig { params(arg0: PreExecutionNode).returns(T::Array[::T.untyped]) } + def visit_pre_execution_node(arg0); end + + sig { params(arg0: ProgramNode).returns(T::Array[::T.untyped]) } + def visit_program_node(arg0); end + + sig { params(arg0: RangeNode).returns(T::Array[::T.untyped]) } + def visit_range_node(arg0); end + + sig { params(arg0: RationalNode).returns(T::Array[::T.untyped]) } + def visit_rational_node(arg0); end + + sig { params(arg0: RedoNode).returns(T::Array[::T.untyped]) } + def visit_redo_node(arg0); end + + sig { params(arg0: RegularExpressionNode).returns(T::Array[::T.untyped]) } + def visit_regular_expression_node(arg0); end + + sig { params(arg0: RequiredKeywordParameterNode).returns(T::Array[::T.untyped]) } + def visit_required_keyword_parameter_node(arg0); end + + sig { params(arg0: RequiredParameterNode).returns(T::Array[::T.untyped]) } + def visit_required_parameter_node(arg0); end + + sig { params(arg0: RescueModifierNode).returns(T::Array[::T.untyped]) } + def visit_rescue_modifier_node(arg0); end + + sig { params(arg0: RescueNode).returns(T::Array[::T.untyped]) } + def visit_rescue_node(arg0); end + + sig { params(arg0: RestParameterNode).returns(T::Array[::T.untyped]) } + def visit_rest_parameter_node(arg0); end + + sig { params(arg0: RetryNode).returns(T::Array[::T.untyped]) } + def visit_retry_node(arg0); end + + sig { params(arg0: ReturnNode).returns(T::Array[::T.untyped]) } + def visit_return_node(arg0); end + + sig { params(arg0: SelfNode).returns(T::Array[::T.untyped]) } + def visit_self_node(arg0); end + + sig { params(arg0: ShareableConstantNode).returns(T::Array[::T.untyped]) } + def visit_shareable_constant_node(arg0); end + + sig { params(arg0: SingletonClassNode).returns(T::Array[::T.untyped]) } + def visit_singleton_class_node(arg0); end + + sig { params(arg0: SourceEncodingNode).returns(T::Array[::T.untyped]) } + def visit_source_encoding_node(arg0); end + + sig { params(arg0: SourceFileNode).returns(T::Array[::T.untyped]) } + def visit_source_file_node(arg0); end + + sig { params(arg0: SourceLineNode).returns(T::Array[::T.untyped]) } + def visit_source_line_node(arg0); end + + sig { params(arg0: SplatNode).returns(T::Array[::T.untyped]) } + def visit_splat_node(arg0); end + + sig { params(arg0: StatementsNode).returns(T::Array[::T.untyped]) } + def visit_statements_node(arg0); end + + sig { params(arg0: StringNode).returns(T::Array[::T.untyped]) } + def visit_string_node(arg0); end + + sig { params(arg0: SuperNode).returns(T::Array[::T.untyped]) } + def visit_super_node(arg0); end + + sig { params(arg0: SymbolNode).returns(T::Array[::T.untyped]) } + def visit_symbol_node(arg0); end + + sig { params(arg0: TrueNode).returns(T::Array[::T.untyped]) } + def visit_true_node(arg0); end + + sig { params(arg0: UndefNode).returns(T::Array[::T.untyped]) } + def visit_undef_node(arg0); end + + sig { params(arg0: UnlessNode).returns(T::Array[::T.untyped]) } + def visit_unless_node(arg0); end + + sig { params(arg0: UntilNode).returns(T::Array[::T.untyped]) } + def visit_until_node(arg0); end + + sig { params(arg0: WhenNode).returns(T::Array[::T.untyped]) } + def visit_when_node(arg0); end + + sig { params(arg0: WhileNode).returns(T::Array[::T.untyped]) } + def visit_while_node(arg0); end + + sig { params(arg0: XStringNode).returns(T::Array[::T.untyped]) } + def visit_x_string_node(arg0); end + + sig { params(arg0: YieldNode).returns(T::Array[::T.untyped]) } + def visit_yield_node(arg0); end + end +end diff --git a/rbi/generated/prism/desugar_compiler.rbi b/rbi/generated/prism/desugar_compiler.rbi new file mode 100644 index 0000000000..167624b9b6 --- /dev/null +++ b/rbi/generated/prism/desugar_compiler.rbi @@ -0,0 +1,306 @@ +# typed: true + +module Prism + class DesugarAndWriteNode + include DSL + + sig { returns(::T.any(ClassVariableAndWriteNode, ConstantAndWriteNode, GlobalVariableAndWriteNode, InstanceVariableAndWriteNode, LocalVariableAndWriteNode)) } + attr_reader :node + + sig { returns(Source) } + attr_reader :default_source + + sig { returns(Symbol) } + attr_reader :read_class + + sig { returns(Symbol) } + attr_reader :write_class + + sig { returns(T::Hash[Symbol, ::T.untyped]) } + attr_reader :arguments + + sig { params(node: ::T.any(ClassVariableAndWriteNode, ConstantAndWriteNode, GlobalVariableAndWriteNode, InstanceVariableAndWriteNode, LocalVariableAndWriteNode), default_source: Source, read_class: Symbol, write_class: Symbol, arguments: ::T.untyped).void } + def initialize(node, default_source, read_class, write_class, **arguments); end + + # Desugar `x &&= y` to `x && x = y` + sig { returns(Node) } + def compile; end + end + + class DesugarOrWriteDefinedNode + include DSL + + sig { returns(::T.any(ClassVariableOrWriteNode, ConstantOrWriteNode, GlobalVariableOrWriteNode)) } + attr_reader :node + + sig { returns(Source) } + attr_reader :default_source + + sig { returns(Symbol) } + attr_reader :read_class + + sig { returns(Symbol) } + attr_reader :write_class + + sig { returns(T::Hash[Symbol, ::T.untyped]) } + attr_reader :arguments + + sig { params(node: ::T.any(ClassVariableOrWriteNode, ConstantOrWriteNode, GlobalVariableOrWriteNode), default_source: Source, read_class: Symbol, write_class: Symbol, arguments: ::T.untyped).void } + def initialize(node, default_source, read_class, write_class, **arguments); end + + # Desugar `x ||= y` to `defined?(x) ? x : x = y` + sig { returns(Node) } + def compile; end + end + + class DesugarOperatorWriteNode + include DSL + + sig { returns(::T.any(ClassVariableOperatorWriteNode, ConstantOperatorWriteNode, GlobalVariableOperatorWriteNode, InstanceVariableOperatorWriteNode, LocalVariableOperatorWriteNode)) } + attr_reader :node + + sig { returns(Source) } + attr_reader :default_source + + sig { returns(Symbol) } + attr_reader :read_class + + sig { returns(Symbol) } + attr_reader :write_class + + sig { returns(T::Hash[Symbol, ::T.untyped]) } + attr_reader :arguments + + sig { params(node: ::T.any(ClassVariableOperatorWriteNode, ConstantOperatorWriteNode, GlobalVariableOperatorWriteNode, InstanceVariableOperatorWriteNode, LocalVariableOperatorWriteNode), default_source: Source, read_class: Symbol, write_class: Symbol, arguments: ::T.untyped).void } + def initialize(node, default_source, read_class, write_class, **arguments); end + + # Desugar `x += y` to `x = x + y` + sig { returns(Node) } + def compile; end + end + + class DesugarOrWriteNode + include DSL + + sig { returns(::T.any(InstanceVariableOrWriteNode, LocalVariableOrWriteNode)) } + attr_reader :node + + sig { returns(Source) } + attr_reader :default_source + + sig { returns(Symbol) } + attr_reader :read_class + + sig { returns(Symbol) } + attr_reader :write_class + + sig { returns(T::Hash[Symbol, ::T.untyped]) } + attr_reader :arguments + + sig { params(node: ::T.any(InstanceVariableOrWriteNode, LocalVariableOrWriteNode), default_source: Source, read_class: Symbol, write_class: Symbol, arguments: ::T.untyped).void } + def initialize(node, default_source, read_class, write_class, **arguments); end + + # Desugar `x ||= y` to `x || x = y` + sig { returns(Node) } + def compile; end + end + + class ClassVariableAndWriteNode + sig { returns(Node) } + def desugar; end + end + + class ClassVariableOrWriteNode + sig { returns(Node) } + def desugar; end + end + + class ClassVariableOperatorWriteNode + sig { returns(Node) } + def desugar; end + end + + class ConstantAndWriteNode + sig { returns(Node) } + def desugar; end + end + + class ConstantOrWriteNode + sig { returns(Node) } + def desugar; end + end + + class ConstantOperatorWriteNode + sig { returns(Node) } + def desugar; end + end + + class GlobalVariableAndWriteNode + sig { returns(Node) } + def desugar; end + end + + class GlobalVariableOrWriteNode + sig { returns(Node) } + def desugar; end + end + + class GlobalVariableOperatorWriteNode + sig { returns(Node) } + def desugar; end + end + + class InstanceVariableAndWriteNode + sig { returns(Node) } + def desugar; end + end + + class InstanceVariableOrWriteNode + sig { returns(Node) } + def desugar; end + end + + class InstanceVariableOperatorWriteNode + sig { returns(Node) } + def desugar; end + end + + class LocalVariableAndWriteNode + sig { returns(Node) } + def desugar; end + end + + class LocalVariableOrWriteNode + sig { returns(Node) } + def desugar; end + end + + class LocalVariableOperatorWriteNode + sig { returns(Node) } + def desugar; 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` + sig { params(node: ClassVariableAndWriteNode).returns(Node) } + def visit_class_variable_and_write_node(node); end + + # `@@foo ||= bar` + # + # becomes + # + # `defined?(@@foo) ? @@foo : @@foo = bar` + sig { params(node: ClassVariableOrWriteNode).returns(Node) } + def visit_class_variable_or_write_node(node); end + + # `@@foo += bar` + # + # becomes + # + # `@@foo = @@foo + bar` + sig { params(node: ClassVariableOperatorWriteNode).returns(Node) } + def visit_class_variable_operator_write_node(node); end + + # `Foo &&= bar` + # + # becomes + # + # `Foo && Foo = bar` + sig { params(node: ConstantAndWriteNode).returns(Node) } + def visit_constant_and_write_node(node); end + + # `Foo ||= bar` + # + # becomes + # + # `defined?(Foo) ? Foo : Foo = bar` + sig { params(node: ConstantOrWriteNode).returns(Node) } + def visit_constant_or_write_node(node); end + + # `Foo += bar` + # + # becomes + # + # `Foo = Foo + bar` + sig { params(node: ConstantOperatorWriteNode).returns(Node) } + def visit_constant_operator_write_node(node); end + + # `$foo &&= bar` + # + # becomes + # + # `$foo && $foo = bar` + sig { params(node: GlobalVariableAndWriteNode).returns(Node) } + def visit_global_variable_and_write_node(node); end + + # `$foo ||= bar` + # + # becomes + # + # `defined?($foo) ? $foo : $foo = bar` + sig { params(node: GlobalVariableOrWriteNode).returns(Node) } + def visit_global_variable_or_write_node(node); end + + # `$foo += bar` + # + # becomes + # + # `$foo = $foo + bar` + sig { params(node: GlobalVariableOperatorWriteNode).returns(Node) } + def visit_global_variable_operator_write_node(node); end + + # `@foo &&= bar` + # + # becomes + # + # `@foo && @foo = bar` + sig { params(node: InstanceVariableAndWriteNode).returns(Node) } + def visit_instance_variable_and_write_node(node); end + + # `@foo ||= bar` + # + # becomes + # + # `@foo || @foo = bar` + sig { params(node: InstanceVariableOrWriteNode).returns(Node) } + def visit_instance_variable_or_write_node(node); end + + # `@foo += bar` + # + # becomes + # + # `@foo = @foo + bar` + sig { params(node: InstanceVariableOperatorWriteNode).returns(Node) } + def visit_instance_variable_operator_write_node(node); end + + # `foo &&= bar` + # + # becomes + # + # `foo && foo = bar` + sig { params(node: LocalVariableAndWriteNode).returns(Node) } + def visit_local_variable_and_write_node(node); end + + # `foo ||= bar` + # + # becomes + # + # `foo || foo = bar` + sig { params(node: LocalVariableOrWriteNode).returns(Node) } + def visit_local_variable_or_write_node(node); end + + # `foo += bar` + # + # becomes + # + # `foo = foo + bar` + sig { params(node: LocalVariableOperatorWriteNode).returns(Node) } + def visit_local_variable_operator_write_node(node); end + end +end diff --git a/rbi/generated/prism/dispatcher.rbi b/rbi/generated/prism/dispatcher.rbi new file mode 100644 index 0000000000..2dfeedf26f --- /dev/null +++ b/rbi/generated/prism/dispatcher.rbi @@ -0,0 +1,1138 @@ +# typed: true + +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 + # A hash mapping event names to arrays of listeners that should be notified + # when that event is fired. + sig { returns(T::Hash[Symbol, T::Array[::T.untyped]]) } + attr_reader :listeners + + # Initialize a new dispatcher. + sig { void } + def initialize; end + + # Register a listener for one or more events. + sig { params(arg0: ::T.untyped, args: Symbol).void } + def register(arg0, *args); end + + # Register all public methods of a listener that match the pattern + # `on__(enter|leave)`. + sig { params(arg0: ::T.untyped).void } + def register_public_methods(arg0); end + + # Register a listener for the given events. + sig { params(arg0: ::T.untyped, arg1: T::Array[Symbol]).void } + def register_events(arg0, arg1); end + + # Walks `root` dispatching events to all registered listeners. + sig { params(node: ::T.nilable(Node)).returns(::T.untyped) } + def dispatch(node); end + + # Dispatches a single event for `node` to all registered listeners. + sig { params(node: Node).void } + def dispatch_once(node); end + + sig { params(node: AliasGlobalVariableNode).void } + def visit_alias_global_variable_node(node); end + + sig { params(node: AliasMethodNode).void } + def visit_alias_method_node(node); end + + sig { params(node: AlternationPatternNode).void } + def visit_alternation_pattern_node(node); end + + sig { params(node: AndNode).void } + def visit_and_node(node); end + + sig { params(node: ArgumentsNode).void } + def visit_arguments_node(node); end + + sig { params(node: ArrayNode).void } + def visit_array_node(node); end + + sig { params(node: ArrayPatternNode).void } + def visit_array_pattern_node(node); end + + sig { params(node: AssocNode).void } + def visit_assoc_node(node); end + + sig { params(node: AssocSplatNode).void } + def visit_assoc_splat_node(node); end + + sig { params(node: BackReferenceReadNode).void } + def visit_back_reference_read_node(node); end + + sig { params(node: BeginNode).void } + def visit_begin_node(node); end + + sig { params(node: BlockArgumentNode).void } + def visit_block_argument_node(node); end + + sig { params(node: BlockLocalVariableNode).void } + def visit_block_local_variable_node(node); end + + sig { params(node: BlockNode).void } + def visit_block_node(node); end + + sig { params(node: BlockParameterNode).void } + def visit_block_parameter_node(node); end + + sig { params(node: BlockParametersNode).void } + def visit_block_parameters_node(node); end + + sig { params(node: BreakNode).void } + def visit_break_node(node); end + + sig { params(node: CallAndWriteNode).void } + def visit_call_and_write_node(node); end + + sig { params(node: CallNode).void } + def visit_call_node(node); end + + sig { params(node: CallOperatorWriteNode).void } + def visit_call_operator_write_node(node); end + + sig { params(node: CallOrWriteNode).void } + def visit_call_or_write_node(node); end + + sig { params(node: CallTargetNode).void } + def visit_call_target_node(node); end + + sig { params(node: CapturePatternNode).void } + def visit_capture_pattern_node(node); end + + sig { params(node: CaseMatchNode).void } + def visit_case_match_node(node); end + + sig { params(node: CaseNode).void } + def visit_case_node(node); end + + sig { params(node: ClassNode).void } + def visit_class_node(node); end + + sig { params(node: ClassVariableAndWriteNode).void } + def visit_class_variable_and_write_node(node); end + + sig { params(node: ClassVariableOperatorWriteNode).void } + def visit_class_variable_operator_write_node(node); end + + sig { params(node: ClassVariableOrWriteNode).void } + def visit_class_variable_or_write_node(node); end + + sig { params(node: ClassVariableReadNode).void } + def visit_class_variable_read_node(node); end + + sig { params(node: ClassVariableTargetNode).void } + def visit_class_variable_target_node(node); end + + sig { params(node: ClassVariableWriteNode).void } + def visit_class_variable_write_node(node); end + + sig { params(node: ConstantAndWriteNode).void } + def visit_constant_and_write_node(node); end + + sig { params(node: ConstantOperatorWriteNode).void } + def visit_constant_operator_write_node(node); end + + sig { params(node: ConstantOrWriteNode).void } + def visit_constant_or_write_node(node); end + + sig { params(node: ConstantPathAndWriteNode).void } + def visit_constant_path_and_write_node(node); end + + sig { params(node: ConstantPathNode).void } + def visit_constant_path_node(node); end + + sig { params(node: ConstantPathOperatorWriteNode).void } + def visit_constant_path_operator_write_node(node); end + + sig { params(node: ConstantPathOrWriteNode).void } + def visit_constant_path_or_write_node(node); end + + sig { params(node: ConstantPathTargetNode).void } + def visit_constant_path_target_node(node); end + + sig { params(node: ConstantPathWriteNode).void } + def visit_constant_path_write_node(node); end + + sig { params(node: ConstantReadNode).void } + def visit_constant_read_node(node); end + + sig { params(node: ConstantTargetNode).void } + def visit_constant_target_node(node); end + + sig { params(node: ConstantWriteNode).void } + def visit_constant_write_node(node); end + + sig { params(node: DefNode).void } + def visit_def_node(node); end + + sig { params(node: DefinedNode).void } + def visit_defined_node(node); end + + sig { params(node: ElseNode).void } + def visit_else_node(node); end + + sig { params(node: EmbeddedStatementsNode).void } + def visit_embedded_statements_node(node); end + + sig { params(node: EmbeddedVariableNode).void } + def visit_embedded_variable_node(node); end + + sig { params(node: EnsureNode).void } + def visit_ensure_node(node); end + + sig { params(node: FalseNode).void } + def visit_false_node(node); end + + sig { params(node: FindPatternNode).void } + def visit_find_pattern_node(node); end + + sig { params(node: FlipFlopNode).void } + def visit_flip_flop_node(node); end + + sig { params(node: FloatNode).void } + def visit_float_node(node); end + + sig { params(node: ForNode).void } + def visit_for_node(node); end + + sig { params(node: ForwardingArgumentsNode).void } + def visit_forwarding_arguments_node(node); end + + sig { params(node: ForwardingParameterNode).void } + def visit_forwarding_parameter_node(node); end + + sig { params(node: ForwardingSuperNode).void } + def visit_forwarding_super_node(node); end + + sig { params(node: GlobalVariableAndWriteNode).void } + def visit_global_variable_and_write_node(node); end + + sig { params(node: GlobalVariableOperatorWriteNode).void } + def visit_global_variable_operator_write_node(node); end + + sig { params(node: GlobalVariableOrWriteNode).void } + def visit_global_variable_or_write_node(node); end + + sig { params(node: GlobalVariableReadNode).void } + def visit_global_variable_read_node(node); end + + sig { params(node: GlobalVariableTargetNode).void } + def visit_global_variable_target_node(node); end + + sig { params(node: GlobalVariableWriteNode).void } + def visit_global_variable_write_node(node); end + + sig { params(node: HashNode).void } + def visit_hash_node(node); end + + sig { params(node: HashPatternNode).void } + def visit_hash_pattern_node(node); end + + sig { params(node: IfNode).void } + def visit_if_node(node); end + + sig { params(node: ImaginaryNode).void } + def visit_imaginary_node(node); end + + sig { params(node: ImplicitNode).void } + def visit_implicit_node(node); end + + sig { params(node: ImplicitRestNode).void } + def visit_implicit_rest_node(node); end + + sig { params(node: InNode).void } + def visit_in_node(node); end + + sig { params(node: IndexAndWriteNode).void } + def visit_index_and_write_node(node); end + + sig { params(node: IndexOperatorWriteNode).void } + def visit_index_operator_write_node(node); end + + sig { params(node: IndexOrWriteNode).void } + def visit_index_or_write_node(node); end + + sig { params(node: IndexTargetNode).void } + def visit_index_target_node(node); end + + sig { params(node: InstanceVariableAndWriteNode).void } + def visit_instance_variable_and_write_node(node); end + + sig { params(node: InstanceVariableOperatorWriteNode).void } + def visit_instance_variable_operator_write_node(node); end + + sig { params(node: InstanceVariableOrWriteNode).void } + def visit_instance_variable_or_write_node(node); end + + sig { params(node: InstanceVariableReadNode).void } + def visit_instance_variable_read_node(node); end + + sig { params(node: InstanceVariableTargetNode).void } + def visit_instance_variable_target_node(node); end + + sig { params(node: InstanceVariableWriteNode).void } + def visit_instance_variable_write_node(node); end + + sig { params(node: IntegerNode).void } + def visit_integer_node(node); end + + sig { params(node: InterpolatedMatchLastLineNode).void } + def visit_interpolated_match_last_line_node(node); end + + sig { params(node: InterpolatedRegularExpressionNode).void } + def visit_interpolated_regular_expression_node(node); end + + sig { params(node: InterpolatedStringNode).void } + def visit_interpolated_string_node(node); end + + sig { params(node: InterpolatedSymbolNode).void } + def visit_interpolated_symbol_node(node); end + + sig { params(node: InterpolatedXStringNode).void } + def visit_interpolated_x_string_node(node); end + + sig { params(node: ItLocalVariableReadNode).void } + def visit_it_local_variable_read_node(node); end + + sig { params(node: ItParametersNode).void } + def visit_it_parameters_node(node); end + + sig { params(node: KeywordHashNode).void } + def visit_keyword_hash_node(node); end + + sig { params(node: KeywordRestParameterNode).void } + def visit_keyword_rest_parameter_node(node); end + + sig { params(node: LambdaNode).void } + def visit_lambda_node(node); end + + sig { params(node: LocalVariableAndWriteNode).void } + def visit_local_variable_and_write_node(node); end + + sig { params(node: LocalVariableOperatorWriteNode).void } + def visit_local_variable_operator_write_node(node); end + + sig { params(node: LocalVariableOrWriteNode).void } + def visit_local_variable_or_write_node(node); end + + sig { params(node: LocalVariableReadNode).void } + def visit_local_variable_read_node(node); end + + sig { params(node: LocalVariableTargetNode).void } + def visit_local_variable_target_node(node); end + + sig { params(node: LocalVariableWriteNode).void } + def visit_local_variable_write_node(node); end + + sig { params(node: MatchLastLineNode).void } + def visit_match_last_line_node(node); end + + sig { params(node: MatchPredicateNode).void } + def visit_match_predicate_node(node); end + + sig { params(node: MatchRequiredNode).void } + def visit_match_required_node(node); end + + sig { params(node: MatchWriteNode).void } + def visit_match_write_node(node); end + + sig { params(node: MissingNode).void } + def visit_missing_node(node); end + + sig { params(node: ModuleNode).void } + def visit_module_node(node); end + + sig { params(node: MultiTargetNode).void } + def visit_multi_target_node(node); end + + sig { params(node: MultiWriteNode).void } + def visit_multi_write_node(node); end + + sig { params(node: NextNode).void } + def visit_next_node(node); end + + sig { params(node: NilNode).void } + def visit_nil_node(node); end + + sig { params(node: NoBlockParameterNode).void } + def visit_no_block_parameter_node(node); end + + sig { params(node: NoKeywordsParameterNode).void } + def visit_no_keywords_parameter_node(node); end + + sig { params(node: NumberedParametersNode).void } + def visit_numbered_parameters_node(node); end + + sig { params(node: NumberedReferenceReadNode).void } + def visit_numbered_reference_read_node(node); end + + sig { params(node: OptionalKeywordParameterNode).void } + def visit_optional_keyword_parameter_node(node); end + + sig { params(node: OptionalParameterNode).void } + def visit_optional_parameter_node(node); end + + sig { params(node: OrNode).void } + def visit_or_node(node); end + + sig { params(node: ParametersNode).void } + def visit_parameters_node(node); end + + sig { params(node: ParenthesesNode).void } + def visit_parentheses_node(node); end + + sig { params(node: PinnedExpressionNode).void } + def visit_pinned_expression_node(node); end + + sig { params(node: PinnedVariableNode).void } + def visit_pinned_variable_node(node); end + + sig { params(node: PostExecutionNode).void } + def visit_post_execution_node(node); end + + sig { params(node: PreExecutionNode).void } + def visit_pre_execution_node(node); end + + sig { params(node: ProgramNode).void } + def visit_program_node(node); end + + sig { params(node: RangeNode).void } + def visit_range_node(node); end + + sig { params(node: RationalNode).void } + def visit_rational_node(node); end + + sig { params(node: RedoNode).void } + def visit_redo_node(node); end + + sig { params(node: RegularExpressionNode).void } + def visit_regular_expression_node(node); end + + sig { params(node: RequiredKeywordParameterNode).void } + def visit_required_keyword_parameter_node(node); end + + sig { params(node: RequiredParameterNode).void } + def visit_required_parameter_node(node); end + + sig { params(node: RescueModifierNode).void } + def visit_rescue_modifier_node(node); end + + sig { params(node: RescueNode).void } + def visit_rescue_node(node); end + + sig { params(node: RestParameterNode).void } + def visit_rest_parameter_node(node); end + + sig { params(node: RetryNode).void } + def visit_retry_node(node); end + + sig { params(node: ReturnNode).void } + def visit_return_node(node); end + + sig { params(node: SelfNode).void } + def visit_self_node(node); end + + sig { params(node: ShareableConstantNode).void } + def visit_shareable_constant_node(node); end + + sig { params(node: SingletonClassNode).void } + def visit_singleton_class_node(node); end + + sig { params(node: SourceEncodingNode).void } + def visit_source_encoding_node(node); end + + sig { params(node: SourceFileNode).void } + def visit_source_file_node(node); end + + sig { params(node: SourceLineNode).void } + def visit_source_line_node(node); end + + sig { params(node: SplatNode).void } + def visit_splat_node(node); end + + sig { params(node: StatementsNode).void } + def visit_statements_node(node); end + + sig { params(node: StringNode).void } + def visit_string_node(node); end + + sig { params(node: SuperNode).void } + def visit_super_node(node); end + + sig { params(node: SymbolNode).void } + def visit_symbol_node(node); end + + sig { params(node: TrueNode).void } + def visit_true_node(node); end + + sig { params(node: UndefNode).void } + def visit_undef_node(node); end + + sig { params(node: UnlessNode).void } + def visit_unless_node(node); end + + sig { params(node: UntilNode).void } + def visit_until_node(node); end + + sig { params(node: WhenNode).void } + def visit_when_node(node); end + + sig { params(node: WhileNode).void } + def visit_while_node(node); end + + sig { params(node: XStringNode).void } + def visit_x_string_node(node); end + + sig { params(node: YieldNode).void } + def visit_yield_node(node); end + + class DispatchOnce < Visitor + sig { returns(T::Hash[Symbol, T::Array[::T.untyped]]) } + attr_reader :listeners + + sig { params(listeners: T::Hash[Symbol, T::Array[::T.untyped]]).void } + def initialize(listeners); end + + # Dispatch enter and leave events for AliasGlobalVariableNode nodes. + sig { params(node: AliasGlobalVariableNode).void } + def visit_alias_global_variable_node(node); end + + # Dispatch enter and leave events for AliasMethodNode nodes. + sig { params(node: AliasMethodNode).void } + def visit_alias_method_node(node); end + + # Dispatch enter and leave events for AlternationPatternNode nodes. + sig { params(node: AlternationPatternNode).void } + def visit_alternation_pattern_node(node); end + + # Dispatch enter and leave events for AndNode nodes. + sig { params(node: AndNode).void } + def visit_and_node(node); end + + # Dispatch enter and leave events for ArgumentsNode nodes. + sig { params(node: ArgumentsNode).void } + def visit_arguments_node(node); end + + # Dispatch enter and leave events for ArrayNode nodes. + sig { params(node: ArrayNode).void } + def visit_array_node(node); end + + # Dispatch enter and leave events for ArrayPatternNode nodes. + sig { params(node: ArrayPatternNode).void } + def visit_array_pattern_node(node); end + + # Dispatch enter and leave events for AssocNode nodes. + sig { params(node: AssocNode).void } + def visit_assoc_node(node); end + + # Dispatch enter and leave events for AssocSplatNode nodes. + sig { params(node: AssocSplatNode).void } + def visit_assoc_splat_node(node); end + + # Dispatch enter and leave events for BackReferenceReadNode nodes. + sig { params(node: BackReferenceReadNode).void } + def visit_back_reference_read_node(node); end + + # Dispatch enter and leave events for BeginNode nodes. + sig { params(node: BeginNode).void } + def visit_begin_node(node); end + + # Dispatch enter and leave events for BlockArgumentNode nodes. + sig { params(node: BlockArgumentNode).void } + def visit_block_argument_node(node); end + + # Dispatch enter and leave events for BlockLocalVariableNode nodes. + sig { params(node: BlockLocalVariableNode).void } + def visit_block_local_variable_node(node); end + + # Dispatch enter and leave events for BlockNode nodes. + sig { params(node: BlockNode).void } + def visit_block_node(node); end + + # Dispatch enter and leave events for BlockParameterNode nodes. + sig { params(node: BlockParameterNode).void } + def visit_block_parameter_node(node); end + + # Dispatch enter and leave events for BlockParametersNode nodes. + sig { params(node: BlockParametersNode).void } + def visit_block_parameters_node(node); end + + # Dispatch enter and leave events for BreakNode nodes. + sig { params(node: BreakNode).void } + def visit_break_node(node); end + + # Dispatch enter and leave events for CallAndWriteNode nodes. + sig { params(node: CallAndWriteNode).void } + def visit_call_and_write_node(node); end + + # Dispatch enter and leave events for CallNode nodes. + sig { params(node: CallNode).void } + def visit_call_node(node); end + + # Dispatch enter and leave events for CallOperatorWriteNode nodes. + sig { params(node: CallOperatorWriteNode).void } + def visit_call_operator_write_node(node); end + + # Dispatch enter and leave events for CallOrWriteNode nodes. + sig { params(node: CallOrWriteNode).void } + def visit_call_or_write_node(node); end + + # Dispatch enter and leave events for CallTargetNode nodes. + sig { params(node: CallTargetNode).void } + def visit_call_target_node(node); end + + # Dispatch enter and leave events for CapturePatternNode nodes. + sig { params(node: CapturePatternNode).void } + def visit_capture_pattern_node(node); end + + # Dispatch enter and leave events for CaseMatchNode nodes. + sig { params(node: CaseMatchNode).void } + def visit_case_match_node(node); end + + # Dispatch enter and leave events for CaseNode nodes. + sig { params(node: CaseNode).void } + def visit_case_node(node); end + + # Dispatch enter and leave events for ClassNode nodes. + sig { params(node: ClassNode).void } + def visit_class_node(node); end + + # Dispatch enter and leave events for ClassVariableAndWriteNode nodes. + sig { params(node: ClassVariableAndWriteNode).void } + def visit_class_variable_and_write_node(node); end + + # Dispatch enter and leave events for ClassVariableOperatorWriteNode nodes. + sig { params(node: ClassVariableOperatorWriteNode).void } + def visit_class_variable_operator_write_node(node); end + + # Dispatch enter and leave events for ClassVariableOrWriteNode nodes. + sig { params(node: ClassVariableOrWriteNode).void } + def visit_class_variable_or_write_node(node); end + + # Dispatch enter and leave events for ClassVariableReadNode nodes. + sig { params(node: ClassVariableReadNode).void } + def visit_class_variable_read_node(node); end + + # Dispatch enter and leave events for ClassVariableTargetNode nodes. + sig { params(node: ClassVariableTargetNode).void } + def visit_class_variable_target_node(node); end + + # Dispatch enter and leave events for ClassVariableWriteNode nodes. + sig { params(node: ClassVariableWriteNode).void } + def visit_class_variable_write_node(node); end + + # Dispatch enter and leave events for ConstantAndWriteNode nodes. + sig { params(node: ConstantAndWriteNode).void } + def visit_constant_and_write_node(node); end + + # Dispatch enter and leave events for ConstantOperatorWriteNode nodes. + sig { params(node: ConstantOperatorWriteNode).void } + def visit_constant_operator_write_node(node); end + + # Dispatch enter and leave events for ConstantOrWriteNode nodes. + sig { params(node: ConstantOrWriteNode).void } + def visit_constant_or_write_node(node); end + + # Dispatch enter and leave events for ConstantPathAndWriteNode nodes. + sig { params(node: ConstantPathAndWriteNode).void } + def visit_constant_path_and_write_node(node); end + + # Dispatch enter and leave events for ConstantPathNode nodes. + sig { params(node: ConstantPathNode).void } + def visit_constant_path_node(node); end + + # Dispatch enter and leave events for ConstantPathOperatorWriteNode nodes. + sig { params(node: ConstantPathOperatorWriteNode).void } + def visit_constant_path_operator_write_node(node); end + + # Dispatch enter and leave events for ConstantPathOrWriteNode nodes. + sig { params(node: ConstantPathOrWriteNode).void } + def visit_constant_path_or_write_node(node); end + + # Dispatch enter and leave events for ConstantPathTargetNode nodes. + sig { params(node: ConstantPathTargetNode).void } + def visit_constant_path_target_node(node); end + + # Dispatch enter and leave events for ConstantPathWriteNode nodes. + sig { params(node: ConstantPathWriteNode).void } + def visit_constant_path_write_node(node); end + + # Dispatch enter and leave events for ConstantReadNode nodes. + sig { params(node: ConstantReadNode).void } + def visit_constant_read_node(node); end + + # Dispatch enter and leave events for ConstantTargetNode nodes. + sig { params(node: ConstantTargetNode).void } + def visit_constant_target_node(node); end + + # Dispatch enter and leave events for ConstantWriteNode nodes. + sig { params(node: ConstantWriteNode).void } + def visit_constant_write_node(node); end + + # Dispatch enter and leave events for DefNode nodes. + sig { params(node: DefNode).void } + def visit_def_node(node); end + + # Dispatch enter and leave events for DefinedNode nodes. + sig { params(node: DefinedNode).void } + def visit_defined_node(node); end + + # Dispatch enter and leave events for ElseNode nodes. + sig { params(node: ElseNode).void } + def visit_else_node(node); end + + # Dispatch enter and leave events for EmbeddedStatementsNode nodes. + sig { params(node: EmbeddedStatementsNode).void } + def visit_embedded_statements_node(node); end + + # Dispatch enter and leave events for EmbeddedVariableNode nodes. + sig { params(node: EmbeddedVariableNode).void } + def visit_embedded_variable_node(node); end + + # Dispatch enter and leave events for EnsureNode nodes. + sig { params(node: EnsureNode).void } + def visit_ensure_node(node); end + + # Dispatch enter and leave events for FalseNode nodes. + sig { params(node: FalseNode).void } + def visit_false_node(node); end + + # Dispatch enter and leave events for FindPatternNode nodes. + sig { params(node: FindPatternNode).void } + def visit_find_pattern_node(node); end + + # Dispatch enter and leave events for FlipFlopNode nodes. + sig { params(node: FlipFlopNode).void } + def visit_flip_flop_node(node); end + + # Dispatch enter and leave events for FloatNode nodes. + sig { params(node: FloatNode).void } + def visit_float_node(node); end + + # Dispatch enter and leave events for ForNode nodes. + sig { params(node: ForNode).void } + def visit_for_node(node); end + + # Dispatch enter and leave events for ForwardingArgumentsNode nodes. + sig { params(node: ForwardingArgumentsNode).void } + def visit_forwarding_arguments_node(node); end + + # Dispatch enter and leave events for ForwardingParameterNode nodes. + sig { params(node: ForwardingParameterNode).void } + def visit_forwarding_parameter_node(node); end + + # Dispatch enter and leave events for ForwardingSuperNode nodes. + sig { params(node: ForwardingSuperNode).void } + def visit_forwarding_super_node(node); end + + # Dispatch enter and leave events for GlobalVariableAndWriteNode nodes. + sig { params(node: GlobalVariableAndWriteNode).void } + def visit_global_variable_and_write_node(node); end + + # Dispatch enter and leave events for GlobalVariableOperatorWriteNode nodes. + sig { params(node: GlobalVariableOperatorWriteNode).void } + def visit_global_variable_operator_write_node(node); end + + # Dispatch enter and leave events for GlobalVariableOrWriteNode nodes. + sig { params(node: GlobalVariableOrWriteNode).void } + def visit_global_variable_or_write_node(node); end + + # Dispatch enter and leave events for GlobalVariableReadNode nodes. + sig { params(node: GlobalVariableReadNode).void } + def visit_global_variable_read_node(node); end + + # Dispatch enter and leave events for GlobalVariableTargetNode nodes. + sig { params(node: GlobalVariableTargetNode).void } + def visit_global_variable_target_node(node); end + + # Dispatch enter and leave events for GlobalVariableWriteNode nodes. + sig { params(node: GlobalVariableWriteNode).void } + def visit_global_variable_write_node(node); end + + # Dispatch enter and leave events for HashNode nodes. + sig { params(node: HashNode).void } + def visit_hash_node(node); end + + # Dispatch enter and leave events for HashPatternNode nodes. + sig { params(node: HashPatternNode).void } + def visit_hash_pattern_node(node); end + + # Dispatch enter and leave events for IfNode nodes. + sig { params(node: IfNode).void } + def visit_if_node(node); end + + # Dispatch enter and leave events for ImaginaryNode nodes. + sig { params(node: ImaginaryNode).void } + def visit_imaginary_node(node); end + + # Dispatch enter and leave events for ImplicitNode nodes. + sig { params(node: ImplicitNode).void } + def visit_implicit_node(node); end + + # Dispatch enter and leave events for ImplicitRestNode nodes. + sig { params(node: ImplicitRestNode).void } + def visit_implicit_rest_node(node); end + + # Dispatch enter and leave events for InNode nodes. + sig { params(node: InNode).void } + def visit_in_node(node); end + + # Dispatch enter and leave events for IndexAndWriteNode nodes. + sig { params(node: IndexAndWriteNode).void } + def visit_index_and_write_node(node); end + + # Dispatch enter and leave events for IndexOperatorWriteNode nodes. + sig { params(node: IndexOperatorWriteNode).void } + def visit_index_operator_write_node(node); end + + # Dispatch enter and leave events for IndexOrWriteNode nodes. + sig { params(node: IndexOrWriteNode).void } + def visit_index_or_write_node(node); end + + # Dispatch enter and leave events for IndexTargetNode nodes. + sig { params(node: IndexTargetNode).void } + def visit_index_target_node(node); end + + # Dispatch enter and leave events for InstanceVariableAndWriteNode nodes. + sig { params(node: InstanceVariableAndWriteNode).void } + def visit_instance_variable_and_write_node(node); end + + # Dispatch enter and leave events for InstanceVariableOperatorWriteNode nodes. + sig { params(node: InstanceVariableOperatorWriteNode).void } + def visit_instance_variable_operator_write_node(node); end + + # Dispatch enter and leave events for InstanceVariableOrWriteNode nodes. + sig { params(node: InstanceVariableOrWriteNode).void } + def visit_instance_variable_or_write_node(node); end + + # Dispatch enter and leave events for InstanceVariableReadNode nodes. + sig { params(node: InstanceVariableReadNode).void } + def visit_instance_variable_read_node(node); end + + # Dispatch enter and leave events for InstanceVariableTargetNode nodes. + sig { params(node: InstanceVariableTargetNode).void } + def visit_instance_variable_target_node(node); end + + # Dispatch enter and leave events for InstanceVariableWriteNode nodes. + sig { params(node: InstanceVariableWriteNode).void } + def visit_instance_variable_write_node(node); end + + # Dispatch enter and leave events for IntegerNode nodes. + sig { params(node: IntegerNode).void } + def visit_integer_node(node); end + + # Dispatch enter and leave events for InterpolatedMatchLastLineNode nodes. + sig { params(node: InterpolatedMatchLastLineNode).void } + def visit_interpolated_match_last_line_node(node); end + + # Dispatch enter and leave events for InterpolatedRegularExpressionNode nodes. + sig { params(node: InterpolatedRegularExpressionNode).void } + def visit_interpolated_regular_expression_node(node); end + + # Dispatch enter and leave events for InterpolatedStringNode nodes. + sig { params(node: InterpolatedStringNode).void } + def visit_interpolated_string_node(node); end + + # Dispatch enter and leave events for InterpolatedSymbolNode nodes. + sig { params(node: InterpolatedSymbolNode).void } + def visit_interpolated_symbol_node(node); end + + # Dispatch enter and leave events for InterpolatedXStringNode nodes. + sig { params(node: InterpolatedXStringNode).void } + def visit_interpolated_x_string_node(node); end + + # Dispatch enter and leave events for ItLocalVariableReadNode nodes. + sig { params(node: ItLocalVariableReadNode).void } + def visit_it_local_variable_read_node(node); end + + # Dispatch enter and leave events for ItParametersNode nodes. + sig { params(node: ItParametersNode).void } + def visit_it_parameters_node(node); end + + # Dispatch enter and leave events for KeywordHashNode nodes. + sig { params(node: KeywordHashNode).void } + def visit_keyword_hash_node(node); end + + # Dispatch enter and leave events for KeywordRestParameterNode nodes. + sig { params(node: KeywordRestParameterNode).void } + def visit_keyword_rest_parameter_node(node); end + + # Dispatch enter and leave events for LambdaNode nodes. + sig { params(node: LambdaNode).void } + def visit_lambda_node(node); end + + # Dispatch enter and leave events for LocalVariableAndWriteNode nodes. + sig { params(node: LocalVariableAndWriteNode).void } + def visit_local_variable_and_write_node(node); end + + # Dispatch enter and leave events for LocalVariableOperatorWriteNode nodes. + sig { params(node: LocalVariableOperatorWriteNode).void } + def visit_local_variable_operator_write_node(node); end + + # Dispatch enter and leave events for LocalVariableOrWriteNode nodes. + sig { params(node: LocalVariableOrWriteNode).void } + def visit_local_variable_or_write_node(node); end + + # Dispatch enter and leave events for LocalVariableReadNode nodes. + sig { params(node: LocalVariableReadNode).void } + def visit_local_variable_read_node(node); end + + # Dispatch enter and leave events for LocalVariableTargetNode nodes. + sig { params(node: LocalVariableTargetNode).void } + def visit_local_variable_target_node(node); end + + # Dispatch enter and leave events for LocalVariableWriteNode nodes. + sig { params(node: LocalVariableWriteNode).void } + def visit_local_variable_write_node(node); end + + # Dispatch enter and leave events for MatchLastLineNode nodes. + sig { params(node: MatchLastLineNode).void } + def visit_match_last_line_node(node); end + + # Dispatch enter and leave events for MatchPredicateNode nodes. + sig { params(node: MatchPredicateNode).void } + def visit_match_predicate_node(node); end + + # Dispatch enter and leave events for MatchRequiredNode nodes. + sig { params(node: MatchRequiredNode).void } + def visit_match_required_node(node); end + + # Dispatch enter and leave events for MatchWriteNode nodes. + sig { params(node: MatchWriteNode).void } + def visit_match_write_node(node); end + + # Dispatch enter and leave events for MissingNode nodes. + sig { params(node: MissingNode).void } + def visit_missing_node(node); end + + # Dispatch enter and leave events for ModuleNode nodes. + sig { params(node: ModuleNode).void } + def visit_module_node(node); end + + # Dispatch enter and leave events for MultiTargetNode nodes. + sig { params(node: MultiTargetNode).void } + def visit_multi_target_node(node); end + + # Dispatch enter and leave events for MultiWriteNode nodes. + sig { params(node: MultiWriteNode).void } + def visit_multi_write_node(node); end + + # Dispatch enter and leave events for NextNode nodes. + sig { params(node: NextNode).void } + def visit_next_node(node); end + + # Dispatch enter and leave events for NilNode nodes. + sig { params(node: NilNode).void } + def visit_nil_node(node); end + + # Dispatch enter and leave events for NoBlockParameterNode nodes. + sig { params(node: NoBlockParameterNode).void } + def visit_no_block_parameter_node(node); end + + # Dispatch enter and leave events for NoKeywordsParameterNode nodes. + sig { params(node: NoKeywordsParameterNode).void } + def visit_no_keywords_parameter_node(node); end + + # Dispatch enter and leave events for NumberedParametersNode nodes. + sig { params(node: NumberedParametersNode).void } + def visit_numbered_parameters_node(node); end + + # Dispatch enter and leave events for NumberedReferenceReadNode nodes. + sig { params(node: NumberedReferenceReadNode).void } + def visit_numbered_reference_read_node(node); end + + # Dispatch enter and leave events for OptionalKeywordParameterNode nodes. + sig { params(node: OptionalKeywordParameterNode).void } + def visit_optional_keyword_parameter_node(node); end + + # Dispatch enter and leave events for OptionalParameterNode nodes. + sig { params(node: OptionalParameterNode).void } + def visit_optional_parameter_node(node); end + + # Dispatch enter and leave events for OrNode nodes. + sig { params(node: OrNode).void } + def visit_or_node(node); end + + # Dispatch enter and leave events for ParametersNode nodes. + sig { params(node: ParametersNode).void } + def visit_parameters_node(node); end + + # Dispatch enter and leave events for ParenthesesNode nodes. + sig { params(node: ParenthesesNode).void } + def visit_parentheses_node(node); end + + # Dispatch enter and leave events for PinnedExpressionNode nodes. + sig { params(node: PinnedExpressionNode).void } + def visit_pinned_expression_node(node); end + + # Dispatch enter and leave events for PinnedVariableNode nodes. + sig { params(node: PinnedVariableNode).void } + def visit_pinned_variable_node(node); end + + # Dispatch enter and leave events for PostExecutionNode nodes. + sig { params(node: PostExecutionNode).void } + def visit_post_execution_node(node); end + + # Dispatch enter and leave events for PreExecutionNode nodes. + sig { params(node: PreExecutionNode).void } + def visit_pre_execution_node(node); end + + # Dispatch enter and leave events for ProgramNode nodes. + sig { params(node: ProgramNode).void } + def visit_program_node(node); end + + # Dispatch enter and leave events for RangeNode nodes. + sig { params(node: RangeNode).void } + def visit_range_node(node); end + + # Dispatch enter and leave events for RationalNode nodes. + sig { params(node: RationalNode).void } + def visit_rational_node(node); end + + # Dispatch enter and leave events for RedoNode nodes. + sig { params(node: RedoNode).void } + def visit_redo_node(node); end + + # Dispatch enter and leave events for RegularExpressionNode nodes. + sig { params(node: RegularExpressionNode).void } + def visit_regular_expression_node(node); end + + # Dispatch enter and leave events for RequiredKeywordParameterNode nodes. + sig { params(node: RequiredKeywordParameterNode).void } + def visit_required_keyword_parameter_node(node); end + + # Dispatch enter and leave events for RequiredParameterNode nodes. + sig { params(node: RequiredParameterNode).void } + def visit_required_parameter_node(node); end + + # Dispatch enter and leave events for RescueModifierNode nodes. + sig { params(node: RescueModifierNode).void } + def visit_rescue_modifier_node(node); end + + # Dispatch enter and leave events for RescueNode nodes. + sig { params(node: RescueNode).void } + def visit_rescue_node(node); end + + # Dispatch enter and leave events for RestParameterNode nodes. + sig { params(node: RestParameterNode).void } + def visit_rest_parameter_node(node); end + + # Dispatch enter and leave events for RetryNode nodes. + sig { params(node: RetryNode).void } + def visit_retry_node(node); end + + # Dispatch enter and leave events for ReturnNode nodes. + sig { params(node: ReturnNode).void } + def visit_return_node(node); end + + # Dispatch enter and leave events for SelfNode nodes. + sig { params(node: SelfNode).void } + def visit_self_node(node); end + + # Dispatch enter and leave events for ShareableConstantNode nodes. + sig { params(node: ShareableConstantNode).void } + def visit_shareable_constant_node(node); end + + # Dispatch enter and leave events for SingletonClassNode nodes. + sig { params(node: SingletonClassNode).void } + def visit_singleton_class_node(node); end + + # Dispatch enter and leave events for SourceEncodingNode nodes. + sig { params(node: SourceEncodingNode).void } + def visit_source_encoding_node(node); end + + # Dispatch enter and leave events for SourceFileNode nodes. + sig { params(node: SourceFileNode).void } + def visit_source_file_node(node); end + + # Dispatch enter and leave events for SourceLineNode nodes. + sig { params(node: SourceLineNode).void } + def visit_source_line_node(node); end + + # Dispatch enter and leave events for SplatNode nodes. + sig { params(node: SplatNode).void } + def visit_splat_node(node); end + + # Dispatch enter and leave events for StatementsNode nodes. + sig { params(node: StatementsNode).void } + def visit_statements_node(node); end + + # Dispatch enter and leave events for StringNode nodes. + sig { params(node: StringNode).void } + def visit_string_node(node); end + + # Dispatch enter and leave events for SuperNode nodes. + sig { params(node: SuperNode).void } + def visit_super_node(node); end + + # Dispatch enter and leave events for SymbolNode nodes. + sig { params(node: SymbolNode).void } + def visit_symbol_node(node); end + + # Dispatch enter and leave events for TrueNode nodes. + sig { params(node: TrueNode).void } + def visit_true_node(node); end + + # Dispatch enter and leave events for UndefNode nodes. + sig { params(node: UndefNode).void } + def visit_undef_node(node); end + + # Dispatch enter and leave events for UnlessNode nodes. + sig { params(node: UnlessNode).void } + def visit_unless_node(node); end + + # Dispatch enter and leave events for UntilNode nodes. + sig { params(node: UntilNode).void } + def visit_until_node(node); end + + # Dispatch enter and leave events for WhenNode nodes. + sig { params(node: WhenNode).void } + def visit_when_node(node); end + + # Dispatch enter and leave events for WhileNode nodes. + sig { params(node: WhileNode).void } + def visit_while_node(node); end + + # Dispatch enter and leave events for XStringNode nodes. + sig { params(node: XStringNode).void } + def visit_x_string_node(node); end + + # Dispatch enter and leave events for YieldNode nodes. + sig { params(node: YieldNode).void } + def visit_yield_node(node); end + end + end +end diff --git a/rbi/generated/prism/dot_visitor.rbi b/rbi/generated/prism/dot_visitor.rbi new file mode 100644 index 0000000000..d707be5a1a --- /dev/null +++ b/rbi/generated/prism/dot_visitor.rbi @@ -0,0 +1,618 @@ +# typed: true + +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 + sig { returns(String) } + attr_reader :name + + sig { returns(::T.nilable(String)) } + attr_reader :value + + sig { returns(T::Boolean) } + attr_reader :port + + sig { params(name: String, value: ::T.nilable(String), port: T::Boolean).void } + def initialize(name, value, port); end + + sig { returns(String) } + def to_dot; end + end + + class Table + sig { returns(String) } + attr_reader :name + + sig { returns(T::Array[Field]) } + attr_reader :fields + + sig { params(name: String).void } + def initialize(name); end + + sig { params(name: String, value: ::T.nilable(String), port: T::Boolean).void } + def field(name, value = T.unsafe(nil), port: T.unsafe(nil)); end + + sig { returns(String) } + def to_dot; end + end + + class Digraph + sig { returns(T::Array[String]) } + attr_reader :nodes + + sig { returns(T::Array[String]) } + attr_reader :waypoints + + sig { returns(T::Array[String]) } + attr_reader :edges + + sig { void } + def initialize; end + + sig { params(value: String).void } + def node(value); end + + sig { params(value: String).void } + def waypoint(value); end + + sig { params(value: String).void } + def edge(value); end + + sig { returns(String) } + def to_dot; end + end + + # The digraph that is being built. + sig { returns(Digraph) } + attr_reader :digraph + + # Initialize a new dot visitor. + sig { void } + def initialize; end + + # Convert this visitor into a graphviz dot graph string. + sig { returns(String) } + def to_dot; end + + sig { params(arg0: AliasGlobalVariableNode).void } + def visit_alias_global_variable_node(arg0); end + + sig { params(arg0: AliasMethodNode).void } + def visit_alias_method_node(arg0); end + + sig { params(arg0: AlternationPatternNode).void } + def visit_alternation_pattern_node(arg0); end + + sig { params(arg0: AndNode).void } + def visit_and_node(arg0); end + + sig { params(arg0: ArgumentsNode).void } + def visit_arguments_node(arg0); end + + sig { params(arg0: ArrayNode).void } + def visit_array_node(arg0); end + + sig { params(arg0: ArrayPatternNode).void } + def visit_array_pattern_node(arg0); end + + sig { params(arg0: AssocNode).void } + def visit_assoc_node(arg0); end + + sig { params(arg0: AssocSplatNode).void } + def visit_assoc_splat_node(arg0); end + + sig { params(arg0: BackReferenceReadNode).void } + def visit_back_reference_read_node(arg0); end + + sig { params(arg0: BeginNode).void } + def visit_begin_node(arg0); end + + sig { params(arg0: BlockArgumentNode).void } + def visit_block_argument_node(arg0); end + + sig { params(arg0: BlockLocalVariableNode).void } + def visit_block_local_variable_node(arg0); end + + sig { params(arg0: BlockNode).void } + def visit_block_node(arg0); end + + sig { params(arg0: BlockParameterNode).void } + def visit_block_parameter_node(arg0); end + + sig { params(arg0: BlockParametersNode).void } + def visit_block_parameters_node(arg0); end + + sig { params(arg0: BreakNode).void } + def visit_break_node(arg0); end + + sig { params(arg0: CallAndWriteNode).void } + def visit_call_and_write_node(arg0); end + + sig { params(arg0: CallNode).void } + def visit_call_node(arg0); end + + sig { params(arg0: CallOperatorWriteNode).void } + def visit_call_operator_write_node(arg0); end + + sig { params(arg0: CallOrWriteNode).void } + def visit_call_or_write_node(arg0); end + + sig { params(arg0: CallTargetNode).void } + def visit_call_target_node(arg0); end + + sig { params(arg0: CapturePatternNode).void } + def visit_capture_pattern_node(arg0); end + + sig { params(arg0: CaseMatchNode).void } + def visit_case_match_node(arg0); end + + sig { params(arg0: CaseNode).void } + def visit_case_node(arg0); end + + sig { params(arg0: ClassNode).void } + def visit_class_node(arg0); end + + sig { params(arg0: ClassVariableAndWriteNode).void } + def visit_class_variable_and_write_node(arg0); end + + sig { params(arg0: ClassVariableOperatorWriteNode).void } + def visit_class_variable_operator_write_node(arg0); end + + sig { params(arg0: ClassVariableOrWriteNode).void } + def visit_class_variable_or_write_node(arg0); end + + sig { params(arg0: ClassVariableReadNode).void } + def visit_class_variable_read_node(arg0); end + + sig { params(arg0: ClassVariableTargetNode).void } + def visit_class_variable_target_node(arg0); end + + sig { params(arg0: ClassVariableWriteNode).void } + def visit_class_variable_write_node(arg0); end + + sig { params(arg0: ConstantAndWriteNode).void } + def visit_constant_and_write_node(arg0); end + + sig { params(arg0: ConstantOperatorWriteNode).void } + def visit_constant_operator_write_node(arg0); end + + sig { params(arg0: ConstantOrWriteNode).void } + def visit_constant_or_write_node(arg0); end + + sig { params(arg0: ConstantPathAndWriteNode).void } + def visit_constant_path_and_write_node(arg0); end + + sig { params(arg0: ConstantPathNode).void } + def visit_constant_path_node(arg0); end + + sig { params(arg0: ConstantPathOperatorWriteNode).void } + def visit_constant_path_operator_write_node(arg0); end + + sig { params(arg0: ConstantPathOrWriteNode).void } + def visit_constant_path_or_write_node(arg0); end + + sig { params(arg0: ConstantPathTargetNode).void } + def visit_constant_path_target_node(arg0); end + + sig { params(arg0: ConstantPathWriteNode).void } + def visit_constant_path_write_node(arg0); end + + sig { params(arg0: ConstantReadNode).void } + def visit_constant_read_node(arg0); end + + sig { params(arg0: ConstantTargetNode).void } + def visit_constant_target_node(arg0); end + + sig { params(arg0: ConstantWriteNode).void } + def visit_constant_write_node(arg0); end + + sig { params(arg0: DefNode).void } + def visit_def_node(arg0); end + + sig { params(arg0: DefinedNode).void } + def visit_defined_node(arg0); end + + sig { params(arg0: ElseNode).void } + def visit_else_node(arg0); end + + sig { params(arg0: EmbeddedStatementsNode).void } + def visit_embedded_statements_node(arg0); end + + sig { params(arg0: EmbeddedVariableNode).void } + def visit_embedded_variable_node(arg0); end + + sig { params(arg0: EnsureNode).void } + def visit_ensure_node(arg0); end + + sig { params(arg0: FalseNode).void } + def visit_false_node(arg0); end + + sig { params(arg0: FindPatternNode).void } + def visit_find_pattern_node(arg0); end + + sig { params(arg0: FlipFlopNode).void } + def visit_flip_flop_node(arg0); end + + sig { params(arg0: FloatNode).void } + def visit_float_node(arg0); end + + sig { params(arg0: ForNode).void } + def visit_for_node(arg0); end + + sig { params(arg0: ForwardingArgumentsNode).void } + def visit_forwarding_arguments_node(arg0); end + + sig { params(arg0: ForwardingParameterNode).void } + def visit_forwarding_parameter_node(arg0); end + + sig { params(arg0: ForwardingSuperNode).void } + def visit_forwarding_super_node(arg0); end + + sig { params(arg0: GlobalVariableAndWriteNode).void } + def visit_global_variable_and_write_node(arg0); end + + sig { params(arg0: GlobalVariableOperatorWriteNode).void } + def visit_global_variable_operator_write_node(arg0); end + + sig { params(arg0: GlobalVariableOrWriteNode).void } + def visit_global_variable_or_write_node(arg0); end + + sig { params(arg0: GlobalVariableReadNode).void } + def visit_global_variable_read_node(arg0); end + + sig { params(arg0: GlobalVariableTargetNode).void } + def visit_global_variable_target_node(arg0); end + + sig { params(arg0: GlobalVariableWriteNode).void } + def visit_global_variable_write_node(arg0); end + + sig { params(arg0: HashNode).void } + def visit_hash_node(arg0); end + + sig { params(arg0: HashPatternNode).void } + def visit_hash_pattern_node(arg0); end + + sig { params(arg0: IfNode).void } + def visit_if_node(arg0); end + + sig { params(arg0: ImaginaryNode).void } + def visit_imaginary_node(arg0); end + + sig { params(arg0: ImplicitNode).void } + def visit_implicit_node(arg0); end + + sig { params(arg0: ImplicitRestNode).void } + def visit_implicit_rest_node(arg0); end + + sig { params(arg0: InNode).void } + def visit_in_node(arg0); end + + sig { params(arg0: IndexAndWriteNode).void } + def visit_index_and_write_node(arg0); end + + sig { params(arg0: IndexOperatorWriteNode).void } + def visit_index_operator_write_node(arg0); end + + sig { params(arg0: IndexOrWriteNode).void } + def visit_index_or_write_node(arg0); end + + sig { params(arg0: IndexTargetNode).void } + def visit_index_target_node(arg0); end + + sig { params(arg0: InstanceVariableAndWriteNode).void } + def visit_instance_variable_and_write_node(arg0); end + + sig { params(arg0: InstanceVariableOperatorWriteNode).void } + def visit_instance_variable_operator_write_node(arg0); end + + sig { params(arg0: InstanceVariableOrWriteNode).void } + def visit_instance_variable_or_write_node(arg0); end + + sig { params(arg0: InstanceVariableReadNode).void } + def visit_instance_variable_read_node(arg0); end + + sig { params(arg0: InstanceVariableTargetNode).void } + def visit_instance_variable_target_node(arg0); end + + sig { params(arg0: InstanceVariableWriteNode).void } + def visit_instance_variable_write_node(arg0); end + + sig { params(arg0: IntegerNode).void } + def visit_integer_node(arg0); end + + sig { params(arg0: InterpolatedMatchLastLineNode).void } + def visit_interpolated_match_last_line_node(arg0); end + + sig { params(arg0: InterpolatedRegularExpressionNode).void } + def visit_interpolated_regular_expression_node(arg0); end + + sig { params(arg0: InterpolatedStringNode).void } + def visit_interpolated_string_node(arg0); end + + sig { params(arg0: InterpolatedSymbolNode).void } + def visit_interpolated_symbol_node(arg0); end + + sig { params(arg0: InterpolatedXStringNode).void } + def visit_interpolated_x_string_node(arg0); end + + sig { params(arg0: ItLocalVariableReadNode).void } + def visit_it_local_variable_read_node(arg0); end + + sig { params(arg0: ItParametersNode).void } + def visit_it_parameters_node(arg0); end + + sig { params(arg0: KeywordHashNode).void } + def visit_keyword_hash_node(arg0); end + + sig { params(arg0: KeywordRestParameterNode).void } + def visit_keyword_rest_parameter_node(arg0); end + + sig { params(arg0: LambdaNode).void } + def visit_lambda_node(arg0); end + + sig { params(arg0: LocalVariableAndWriteNode).void } + def visit_local_variable_and_write_node(arg0); end + + sig { params(arg0: LocalVariableOperatorWriteNode).void } + def visit_local_variable_operator_write_node(arg0); end + + sig { params(arg0: LocalVariableOrWriteNode).void } + def visit_local_variable_or_write_node(arg0); end + + sig { params(arg0: LocalVariableReadNode).void } + def visit_local_variable_read_node(arg0); end + + sig { params(arg0: LocalVariableTargetNode).void } + def visit_local_variable_target_node(arg0); end + + sig { params(arg0: LocalVariableWriteNode).void } + def visit_local_variable_write_node(arg0); end + + sig { params(arg0: MatchLastLineNode).void } + def visit_match_last_line_node(arg0); end + + sig { params(arg0: MatchPredicateNode).void } + def visit_match_predicate_node(arg0); end + + sig { params(arg0: MatchRequiredNode).void } + def visit_match_required_node(arg0); end + + sig { params(arg0: MatchWriteNode).void } + def visit_match_write_node(arg0); end + + sig { params(arg0: MissingNode).void } + def visit_missing_node(arg0); end + + sig { params(arg0: ModuleNode).void } + def visit_module_node(arg0); end + + sig { params(arg0: MultiTargetNode).void } + def visit_multi_target_node(arg0); end + + sig { params(arg0: MultiWriteNode).void } + def visit_multi_write_node(arg0); end + + sig { params(arg0: NextNode).void } + def visit_next_node(arg0); end + + sig { params(arg0: NilNode).void } + def visit_nil_node(arg0); end + + sig { params(arg0: NoBlockParameterNode).void } + def visit_no_block_parameter_node(arg0); end + + sig { params(arg0: NoKeywordsParameterNode).void } + def visit_no_keywords_parameter_node(arg0); end + + sig { params(arg0: NumberedParametersNode).void } + def visit_numbered_parameters_node(arg0); end + + sig { params(arg0: NumberedReferenceReadNode).void } + def visit_numbered_reference_read_node(arg0); end + + sig { params(arg0: OptionalKeywordParameterNode).void } + def visit_optional_keyword_parameter_node(arg0); end + + sig { params(arg0: OptionalParameterNode).void } + def visit_optional_parameter_node(arg0); end + + sig { params(arg0: OrNode).void } + def visit_or_node(arg0); end + + sig { params(arg0: ParametersNode).void } + def visit_parameters_node(arg0); end + + sig { params(arg0: ParenthesesNode).void } + def visit_parentheses_node(arg0); end + + sig { params(arg0: PinnedExpressionNode).void } + def visit_pinned_expression_node(arg0); end + + sig { params(arg0: PinnedVariableNode).void } + def visit_pinned_variable_node(arg0); end + + sig { params(arg0: PostExecutionNode).void } + def visit_post_execution_node(arg0); end + + sig { params(arg0: PreExecutionNode).void } + def visit_pre_execution_node(arg0); end + + sig { params(arg0: ProgramNode).void } + def visit_program_node(arg0); end + + sig { params(arg0: RangeNode).void } + def visit_range_node(arg0); end + + sig { params(arg0: RationalNode).void } + def visit_rational_node(arg0); end + + sig { params(arg0: RedoNode).void } + def visit_redo_node(arg0); end + + sig { params(arg0: RegularExpressionNode).void } + def visit_regular_expression_node(arg0); end + + sig { params(arg0: RequiredKeywordParameterNode).void } + def visit_required_keyword_parameter_node(arg0); end + + sig { params(arg0: RequiredParameterNode).void } + def visit_required_parameter_node(arg0); end + + sig { params(arg0: RescueModifierNode).void } + def visit_rescue_modifier_node(arg0); end + + sig { params(arg0: RescueNode).void } + def visit_rescue_node(arg0); end + + sig { params(arg0: RestParameterNode).void } + def visit_rest_parameter_node(arg0); end + + sig { params(arg0: RetryNode).void } + def visit_retry_node(arg0); end + + sig { params(arg0: ReturnNode).void } + def visit_return_node(arg0); end + + sig { params(arg0: SelfNode).void } + def visit_self_node(arg0); end + + sig { params(arg0: ShareableConstantNode).void } + def visit_shareable_constant_node(arg0); end + + sig { params(arg0: SingletonClassNode).void } + def visit_singleton_class_node(arg0); end + + sig { params(arg0: SourceEncodingNode).void } + def visit_source_encoding_node(arg0); end + + sig { params(arg0: SourceFileNode).void } + def visit_source_file_node(arg0); end + + sig { params(arg0: SourceLineNode).void } + def visit_source_line_node(arg0); end + + sig { params(arg0: SplatNode).void } + def visit_splat_node(arg0); end + + sig { params(arg0: StatementsNode).void } + def visit_statements_node(arg0); end + + sig { params(arg0: StringNode).void } + def visit_string_node(arg0); end + + sig { params(arg0: SuperNode).void } + def visit_super_node(arg0); end + + sig { params(arg0: SymbolNode).void } + def visit_symbol_node(arg0); end + + sig { params(arg0: TrueNode).void } + def visit_true_node(arg0); end + + sig { params(arg0: UndefNode).void } + def visit_undef_node(arg0); end + + sig { params(arg0: UnlessNode).void } + def visit_unless_node(arg0); end + + sig { params(arg0: UntilNode).void } + def visit_until_node(arg0); end + + sig { params(arg0: WhenNode).void } + def visit_when_node(arg0); end + + sig { params(arg0: WhileNode).void } + def visit_while_node(arg0); end + + sig { params(arg0: XStringNode).void } + def visit_x_string_node(arg0); end + + sig { params(arg0: YieldNode).void } + def visit_yield_node(arg0); end + + # Generate a unique node ID for a node throughout the digraph. + sig { params(arg0: Node).returns(String) } + private def node_id(arg0); end + + # Inspect a location to display the start and end line and columns in bytes. + sig { params(arg0: Location).returns(String) } + private def location_inspect(arg0); end + + # Inspect a node that has arguments_node_flags flags to display the flags as a + # comma-separated list. + sig { params(node: ArgumentsNode).returns(String) } + private def arguments_node_flags_inspect(node); end + + # Inspect a node that has array_node_flags flags to display the flags as a + # comma-separated list. + sig { params(node: ArrayNode).returns(String) } + private def array_node_flags_inspect(node); end + + # Inspect a node that has call_node_flags flags to display the flags as a + # comma-separated list. + sig { params(node: ::T.any(CallAndWriteNode, CallNode, CallOperatorWriteNode, CallOrWriteNode, CallTargetNode, IndexAndWriteNode, IndexOperatorWriteNode, IndexOrWriteNode, IndexTargetNode)).returns(String) } + private def call_node_flags_inspect(node); end + + # Inspect a node that has encoding_flags flags to display the flags as a + # comma-separated list. + sig { params(node: XStringNode).returns(String) } + private def encoding_flags_inspect(node); end + + # Inspect a node that has integer_base_flags flags to display the flags as a + # comma-separated list. + sig { params(node: ::T.any(IntegerNode, RationalNode)).returns(String) } + private def integer_base_flags_inspect(node); end + + # Inspect a node that has interpolated_string_node_flags flags to display the flags as a + # comma-separated list. + sig { params(node: InterpolatedStringNode).returns(String) } + private def interpolated_string_node_flags_inspect(node); end + + # Inspect a node that has keyword_hash_node_flags flags to display the flags as a + # comma-separated list. + sig { params(node: KeywordHashNode).returns(String) } + private def keyword_hash_node_flags_inspect(node); end + + # Inspect a node that has loop_flags flags to display the flags as a + # comma-separated list. + sig { params(node: ::T.any(UntilNode, WhileNode)).returns(String) } + private def loop_flags_inspect(node); end + + # Inspect a node that has parameter_flags flags to display the flags as a + # comma-separated list. + sig { params(node: ::T.any(BlockLocalVariableNode, BlockParameterNode, KeywordRestParameterNode, OptionalKeywordParameterNode, OptionalParameterNode, RequiredKeywordParameterNode, RequiredParameterNode, RestParameterNode)).returns(String) } + private def parameter_flags_inspect(node); end + + # Inspect a node that has parentheses_node_flags flags to display the flags as a + # comma-separated list. + sig { params(node: ParenthesesNode).returns(String) } + private def parentheses_node_flags_inspect(node); end + + # Inspect a node that has range_flags flags to display the flags as a + # comma-separated list. + sig { params(node: ::T.any(FlipFlopNode, RangeNode)).returns(String) } + private def range_flags_inspect(node); end + + # Inspect a node that has regular_expression_flags flags to display the flags as a + # comma-separated list. + sig { params(node: ::T.any(InterpolatedMatchLastLineNode, InterpolatedRegularExpressionNode, MatchLastLineNode, RegularExpressionNode)).returns(String) } + private def regular_expression_flags_inspect(node); end + + # Inspect a node that has shareable_constant_node_flags flags to display the flags as a + # comma-separated list. + sig { params(node: ShareableConstantNode).returns(String) } + private def shareable_constant_node_flags_inspect(node); end + + # Inspect a node that has string_flags flags to display the flags as a + # comma-separated list. + sig { params(node: ::T.any(SourceFileNode, StringNode)).returns(String) } + private def string_flags_inspect(node); end + + # Inspect a node that has symbol_flags flags to display the flags as a + # comma-separated list. + sig { params(node: SymbolNode).returns(String) } + private def symbol_flags_inspect(node); end + end +end diff --git a/rbi/generated/prism/dsl.rbi b/rbi/generated/prism/dsl.rbi new file mode 100644 index 0000000000..148d4724de --- /dev/null +++ b/rbi/generated/prism/dsl.rbi @@ -0,0 +1,751 @@ +# typed: true + +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]", 1, [0]) + # + # 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 + # Create a new Source object. + sig { params(string: String).returns(Source) } + def source(string); end + + # Create a new Location object. + sig { params(source: Source, start_offset: Integer, length: Integer).returns(Location) } + def location(source: T.unsafe(nil), start_offset: T.unsafe(nil), length: T.unsafe(nil)); end + + # Create a new AliasGlobalVariableNode node. + sig { params(source: Source, node_id: Integer, location: Location, flags: Integer, new_name: ::T.any(GlobalVariableReadNode, BackReferenceReadNode, NumberedReferenceReadNode), old_name: ::T.any(GlobalVariableReadNode, BackReferenceReadNode, NumberedReferenceReadNode, SymbolNode, MissingNode), keyword_loc: Location).returns(AliasGlobalVariableNode) } + def alias_global_variable_node(source: T.unsafe(nil), node_id: T.unsafe(nil), location: T.unsafe(nil), flags: T.unsafe(nil), new_name: T.unsafe(nil), old_name: T.unsafe(nil), keyword_loc: T.unsafe(nil)); end + + # Create a new AliasMethodNode node. + sig { params(source: Source, node_id: Integer, location: Location, flags: Integer, new_name: ::T.any(SymbolNode, InterpolatedSymbolNode), old_name: ::T.any(SymbolNode, InterpolatedSymbolNode, GlobalVariableReadNode, MissingNode), keyword_loc: Location).returns(AliasMethodNode) } + def alias_method_node(source: T.unsafe(nil), node_id: T.unsafe(nil), location: T.unsafe(nil), flags: T.unsafe(nil), new_name: T.unsafe(nil), old_name: T.unsafe(nil), keyword_loc: T.unsafe(nil)); end + + # Create a new AlternationPatternNode node. + sig { params(source: Source, node_id: Integer, location: Location, flags: Integer, left: Node, right: Node, operator_loc: Location).returns(AlternationPatternNode) } + def alternation_pattern_node(source: T.unsafe(nil), node_id: T.unsafe(nil), location: T.unsafe(nil), flags: T.unsafe(nil), left: T.unsafe(nil), right: T.unsafe(nil), operator_loc: T.unsafe(nil)); end + + # Create a new AndNode node. + sig { params(source: Source, node_id: Integer, location: Location, flags: Integer, left: Node, right: Node, operator_loc: Location).returns(AndNode) } + def and_node(source: T.unsafe(nil), node_id: T.unsafe(nil), location: T.unsafe(nil), flags: T.unsafe(nil), left: T.unsafe(nil), right: T.unsafe(nil), operator_loc: T.unsafe(nil)); end + + # Create a new ArgumentsNode node. + sig { params(source: Source, node_id: Integer, location: Location, flags: Integer, arguments: T::Array[Node]).returns(ArgumentsNode) } + def arguments_node(source: T.unsafe(nil), node_id: T.unsafe(nil), location: T.unsafe(nil), flags: T.unsafe(nil), arguments: T.unsafe(nil)); end + + # Create a new ArrayNode node. + sig { params(source: Source, node_id: Integer, location: Location, flags: Integer, elements: T::Array[Node], opening_loc: ::T.nilable(Location), closing_loc: ::T.nilable(Location)).returns(ArrayNode) } + def array_node(source: T.unsafe(nil), node_id: T.unsafe(nil), location: T.unsafe(nil), flags: T.unsafe(nil), elements: T.unsafe(nil), opening_loc: T.unsafe(nil), closing_loc: T.unsafe(nil)); end + + # Create a new ArrayPatternNode node. + sig { params(source: Source, node_id: Integer, location: Location, flags: Integer, constant: ::T.nilable(::T.any(ConstantPathNode, ConstantReadNode)), requireds: T::Array[Node], rest: ::T.nilable(Node), posts: T::Array[Node], opening_loc: ::T.nilable(Location), closing_loc: ::T.nilable(Location)).returns(ArrayPatternNode) } + def array_pattern_node(source: T.unsafe(nil), node_id: T.unsafe(nil), location: T.unsafe(nil), flags: T.unsafe(nil), constant: T.unsafe(nil), requireds: T.unsafe(nil), rest: T.unsafe(nil), posts: T.unsafe(nil), opening_loc: T.unsafe(nil), closing_loc: T.unsafe(nil)); end + + # Create a new AssocNode node. + sig { params(source: Source, node_id: Integer, location: Location, flags: Integer, key: Node, value: Node, operator_loc: ::T.nilable(Location)).returns(AssocNode) } + def assoc_node(source: T.unsafe(nil), node_id: T.unsafe(nil), location: T.unsafe(nil), flags: T.unsafe(nil), key: T.unsafe(nil), value: T.unsafe(nil), operator_loc: T.unsafe(nil)); end + + # Create a new AssocSplatNode node. + sig { params(source: Source, node_id: Integer, location: Location, flags: Integer, value: ::T.nilable(Node), operator_loc: Location).returns(AssocSplatNode) } + def assoc_splat_node(source: T.unsafe(nil), node_id: T.unsafe(nil), location: T.unsafe(nil), flags: T.unsafe(nil), value: T.unsafe(nil), operator_loc: T.unsafe(nil)); end + + # Create a new BackReferenceReadNode node. + sig { params(source: Source, node_id: Integer, location: Location, flags: Integer, name: Symbol).returns(BackReferenceReadNode) } + def back_reference_read_node(source: T.unsafe(nil), node_id: T.unsafe(nil), location: T.unsafe(nil), flags: T.unsafe(nil), name: T.unsafe(nil)); end + + # Create a new BeginNode node. + sig { params(source: Source, node_id: Integer, location: Location, flags: Integer, begin_keyword_loc: ::T.nilable(Location), statements: ::T.nilable(StatementsNode), rescue_clause: ::T.nilable(RescueNode), else_clause: ::T.nilable(ElseNode), ensure_clause: ::T.nilable(EnsureNode), end_keyword_loc: ::T.nilable(Location)).returns(BeginNode) } + def begin_node(source: T.unsafe(nil), node_id: T.unsafe(nil), location: T.unsafe(nil), flags: T.unsafe(nil), begin_keyword_loc: T.unsafe(nil), statements: T.unsafe(nil), rescue_clause: T.unsafe(nil), else_clause: T.unsafe(nil), ensure_clause: T.unsafe(nil), end_keyword_loc: T.unsafe(nil)); end + + # Create a new BlockArgumentNode node. + sig { params(source: Source, node_id: Integer, location: Location, flags: Integer, expression: ::T.nilable(Node), operator_loc: Location).returns(BlockArgumentNode) } + def block_argument_node(source: T.unsafe(nil), node_id: T.unsafe(nil), location: T.unsafe(nil), flags: T.unsafe(nil), expression: T.unsafe(nil), operator_loc: T.unsafe(nil)); end + + # Create a new BlockLocalVariableNode node. + sig { params(source: Source, node_id: Integer, location: Location, flags: Integer, name: Symbol).returns(BlockLocalVariableNode) } + def block_local_variable_node(source: T.unsafe(nil), node_id: T.unsafe(nil), location: T.unsafe(nil), flags: T.unsafe(nil), name: T.unsafe(nil)); end + + # Create a new BlockNode node. + sig { params(source: Source, node_id: Integer, location: Location, flags: Integer, locals: T::Array[Symbol], parameters: ::T.nilable(::T.any(BlockParametersNode, NumberedParametersNode, ItParametersNode)), body: ::T.nilable(::T.any(StatementsNode, BeginNode)), opening_loc: Location, closing_loc: Location).returns(BlockNode) } + def block_node(source: T.unsafe(nil), node_id: T.unsafe(nil), location: T.unsafe(nil), flags: T.unsafe(nil), locals: T.unsafe(nil), parameters: T.unsafe(nil), body: T.unsafe(nil), opening_loc: T.unsafe(nil), closing_loc: T.unsafe(nil)); end + + # Create a new BlockParameterNode node. + sig { params(source: Source, node_id: Integer, location: Location, flags: Integer, name: ::T.nilable(Symbol), name_loc: ::T.nilable(Location), operator_loc: Location).returns(BlockParameterNode) } + def block_parameter_node(source: T.unsafe(nil), node_id: T.unsafe(nil), location: T.unsafe(nil), flags: T.unsafe(nil), name: T.unsafe(nil), name_loc: T.unsafe(nil), operator_loc: T.unsafe(nil)); end + + # Create a new BlockParametersNode node. + sig { params(source: Source, node_id: Integer, location: Location, flags: Integer, parameters: ::T.nilable(ParametersNode), locals: T::Array[BlockLocalVariableNode], opening_loc: ::T.nilable(Location), closing_loc: ::T.nilable(Location)).returns(BlockParametersNode) } + def block_parameters_node(source: T.unsafe(nil), node_id: T.unsafe(nil), location: T.unsafe(nil), flags: T.unsafe(nil), parameters: T.unsafe(nil), locals: T.unsafe(nil), opening_loc: T.unsafe(nil), closing_loc: T.unsafe(nil)); end + + # Create a new BreakNode node. + sig { params(source: Source, node_id: Integer, location: Location, flags: Integer, arguments: ::T.nilable(ArgumentsNode), keyword_loc: Location).returns(BreakNode) } + def break_node(source: T.unsafe(nil), node_id: T.unsafe(nil), location: T.unsafe(nil), flags: T.unsafe(nil), arguments: T.unsafe(nil), keyword_loc: T.unsafe(nil)); end + + # Create a new CallAndWriteNode node. + sig { params(source: Source, node_id: Integer, location: Location, flags: Integer, receiver: ::T.nilable(Node), call_operator_loc: ::T.nilable(Location), message_loc: ::T.nilable(Location), read_name: Symbol, write_name: Symbol, operator_loc: Location, value: Node).returns(CallAndWriteNode) } + def call_and_write_node(source: T.unsafe(nil), node_id: T.unsafe(nil), location: T.unsafe(nil), flags: T.unsafe(nil), receiver: T.unsafe(nil), call_operator_loc: T.unsafe(nil), message_loc: T.unsafe(nil), read_name: T.unsafe(nil), write_name: T.unsafe(nil), operator_loc: T.unsafe(nil), value: T.unsafe(nil)); end + + # Create a new CallNode node. + sig { params(source: Source, node_id: Integer, location: Location, flags: Integer, receiver: ::T.nilable(Node), call_operator_loc: ::T.nilable(Location), name: Symbol, message_loc: ::T.nilable(Location), opening_loc: ::T.nilable(Location), arguments: ::T.nilable(ArgumentsNode), closing_loc: ::T.nilable(Location), equal_loc: ::T.nilable(Location), block: ::T.nilable(::T.any(BlockNode, BlockArgumentNode))).returns(CallNode) } + def call_node(source: T.unsafe(nil), node_id: T.unsafe(nil), location: T.unsafe(nil), flags: T.unsafe(nil), receiver: T.unsafe(nil), call_operator_loc: T.unsafe(nil), name: T.unsafe(nil), message_loc: T.unsafe(nil), opening_loc: T.unsafe(nil), arguments: T.unsafe(nil), closing_loc: T.unsafe(nil), equal_loc: T.unsafe(nil), block: T.unsafe(nil)); end + + # Create a new CallOperatorWriteNode node. + sig { params(source: Source, node_id: Integer, location: Location, flags: Integer, receiver: ::T.nilable(Node), call_operator_loc: ::T.nilable(Location), message_loc: ::T.nilable(Location), read_name: Symbol, write_name: Symbol, binary_operator: Symbol, binary_operator_loc: Location, value: Node).returns(CallOperatorWriteNode) } + def call_operator_write_node(source: T.unsafe(nil), node_id: T.unsafe(nil), location: T.unsafe(nil), flags: T.unsafe(nil), receiver: T.unsafe(nil), call_operator_loc: T.unsafe(nil), message_loc: T.unsafe(nil), read_name: T.unsafe(nil), write_name: T.unsafe(nil), binary_operator: T.unsafe(nil), binary_operator_loc: T.unsafe(nil), value: T.unsafe(nil)); end + + # Create a new CallOrWriteNode node. + sig { params(source: Source, node_id: Integer, location: Location, flags: Integer, receiver: ::T.nilable(Node), call_operator_loc: ::T.nilable(Location), message_loc: ::T.nilable(Location), read_name: Symbol, write_name: Symbol, operator_loc: Location, value: Node).returns(CallOrWriteNode) } + def call_or_write_node(source: T.unsafe(nil), node_id: T.unsafe(nil), location: T.unsafe(nil), flags: T.unsafe(nil), receiver: T.unsafe(nil), call_operator_loc: T.unsafe(nil), message_loc: T.unsafe(nil), read_name: T.unsafe(nil), write_name: T.unsafe(nil), operator_loc: T.unsafe(nil), value: T.unsafe(nil)); end + + # Create a new CallTargetNode node. + sig { params(source: Source, node_id: Integer, location: Location, flags: Integer, receiver: Node, call_operator_loc: Location, name: Symbol, message_loc: Location).returns(CallTargetNode) } + def call_target_node(source: T.unsafe(nil), node_id: T.unsafe(nil), location: T.unsafe(nil), flags: T.unsafe(nil), receiver: T.unsafe(nil), call_operator_loc: T.unsafe(nil), name: T.unsafe(nil), message_loc: T.unsafe(nil)); end + + # Create a new CapturePatternNode node. + sig { params(source: Source, node_id: Integer, location: Location, flags: Integer, value: Node, target: LocalVariableTargetNode, operator_loc: Location).returns(CapturePatternNode) } + def capture_pattern_node(source: T.unsafe(nil), node_id: T.unsafe(nil), location: T.unsafe(nil), flags: T.unsafe(nil), value: T.unsafe(nil), target: T.unsafe(nil), operator_loc: T.unsafe(nil)); end + + # Create a new CaseMatchNode node. + sig { params(source: Source, node_id: Integer, location: Location, flags: Integer, predicate: ::T.nilable(Node), conditions: T::Array[InNode], else_clause: ::T.nilable(ElseNode), case_keyword_loc: Location, end_keyword_loc: Location).returns(CaseMatchNode) } + def case_match_node(source: T.unsafe(nil), node_id: T.unsafe(nil), location: T.unsafe(nil), flags: T.unsafe(nil), predicate: T.unsafe(nil), conditions: T.unsafe(nil), else_clause: T.unsafe(nil), case_keyword_loc: T.unsafe(nil), end_keyword_loc: T.unsafe(nil)); end + + # Create a new CaseNode node. + sig { params(source: Source, node_id: Integer, location: Location, flags: Integer, predicate: ::T.nilable(Node), conditions: T::Array[WhenNode], else_clause: ::T.nilable(ElseNode), case_keyword_loc: Location, end_keyword_loc: Location).returns(CaseNode) } + def case_node(source: T.unsafe(nil), node_id: T.unsafe(nil), location: T.unsafe(nil), flags: T.unsafe(nil), predicate: T.unsafe(nil), conditions: T.unsafe(nil), else_clause: T.unsafe(nil), case_keyword_loc: T.unsafe(nil), end_keyword_loc: T.unsafe(nil)); end + + # Create a new ClassNode node. + sig { params(source: Source, node_id: Integer, location: Location, flags: Integer, locals: T::Array[Symbol], class_keyword_loc: Location, constant_path: ::T.any(ConstantReadNode, ConstantPathNode, CallNode), inheritance_operator_loc: ::T.nilable(Location), superclass: ::T.nilable(Node), body: ::T.nilable(::T.any(StatementsNode, BeginNode)), end_keyword_loc: Location, name: Symbol).returns(ClassNode) } + def class_node(source: T.unsafe(nil), node_id: T.unsafe(nil), location: T.unsafe(nil), flags: T.unsafe(nil), locals: T.unsafe(nil), class_keyword_loc: T.unsafe(nil), constant_path: T.unsafe(nil), inheritance_operator_loc: T.unsafe(nil), superclass: T.unsafe(nil), body: T.unsafe(nil), end_keyword_loc: T.unsafe(nil), name: T.unsafe(nil)); end + + # Create a new ClassVariableAndWriteNode node. + sig { params(source: Source, node_id: Integer, location: Location, flags: Integer, name: Symbol, name_loc: Location, operator_loc: Location, value: Node).returns(ClassVariableAndWriteNode) } + def class_variable_and_write_node(source: T.unsafe(nil), node_id: T.unsafe(nil), location: T.unsafe(nil), flags: T.unsafe(nil), name: T.unsafe(nil), name_loc: T.unsafe(nil), operator_loc: T.unsafe(nil), value: T.unsafe(nil)); end + + # Create a new ClassVariableOperatorWriteNode node. + sig { params(source: Source, node_id: Integer, location: Location, flags: Integer, name: Symbol, name_loc: Location, binary_operator_loc: Location, value: Node, binary_operator: Symbol).returns(ClassVariableOperatorWriteNode) } + def class_variable_operator_write_node(source: T.unsafe(nil), node_id: T.unsafe(nil), location: T.unsafe(nil), flags: T.unsafe(nil), name: T.unsafe(nil), name_loc: T.unsafe(nil), binary_operator_loc: T.unsafe(nil), value: T.unsafe(nil), binary_operator: T.unsafe(nil)); end + + # Create a new ClassVariableOrWriteNode node. + sig { params(source: Source, node_id: Integer, location: Location, flags: Integer, name: Symbol, name_loc: Location, operator_loc: Location, value: Node).returns(ClassVariableOrWriteNode) } + def class_variable_or_write_node(source: T.unsafe(nil), node_id: T.unsafe(nil), location: T.unsafe(nil), flags: T.unsafe(nil), name: T.unsafe(nil), name_loc: T.unsafe(nil), operator_loc: T.unsafe(nil), value: T.unsafe(nil)); end + + # Create a new ClassVariableReadNode node. + sig { params(source: Source, node_id: Integer, location: Location, flags: Integer, name: Symbol).returns(ClassVariableReadNode) } + def class_variable_read_node(source: T.unsafe(nil), node_id: T.unsafe(nil), location: T.unsafe(nil), flags: T.unsafe(nil), name: T.unsafe(nil)); end + + # Create a new ClassVariableTargetNode node. + sig { params(source: Source, node_id: Integer, location: Location, flags: Integer, name: Symbol).returns(ClassVariableTargetNode) } + def class_variable_target_node(source: T.unsafe(nil), node_id: T.unsafe(nil), location: T.unsafe(nil), flags: T.unsafe(nil), name: T.unsafe(nil)); end + + # Create a new ClassVariableWriteNode node. + sig { params(source: Source, node_id: Integer, location: Location, flags: Integer, name: Symbol, name_loc: Location, value: Node, operator_loc: Location).returns(ClassVariableWriteNode) } + def class_variable_write_node(source: T.unsafe(nil), node_id: T.unsafe(nil), location: T.unsafe(nil), flags: T.unsafe(nil), name: T.unsafe(nil), name_loc: T.unsafe(nil), value: T.unsafe(nil), operator_loc: T.unsafe(nil)); end + + # Create a new ConstantAndWriteNode node. + sig { params(source: Source, node_id: Integer, location: Location, flags: Integer, name: Symbol, name_loc: Location, operator_loc: Location, value: Node).returns(ConstantAndWriteNode) } + def constant_and_write_node(source: T.unsafe(nil), node_id: T.unsafe(nil), location: T.unsafe(nil), flags: T.unsafe(nil), name: T.unsafe(nil), name_loc: T.unsafe(nil), operator_loc: T.unsafe(nil), value: T.unsafe(nil)); end + + # Create a new ConstantOperatorWriteNode node. + sig { params(source: Source, node_id: Integer, location: Location, flags: Integer, name: Symbol, name_loc: Location, binary_operator_loc: Location, value: Node, binary_operator: Symbol).returns(ConstantOperatorWriteNode) } + def constant_operator_write_node(source: T.unsafe(nil), node_id: T.unsafe(nil), location: T.unsafe(nil), flags: T.unsafe(nil), name: T.unsafe(nil), name_loc: T.unsafe(nil), binary_operator_loc: T.unsafe(nil), value: T.unsafe(nil), binary_operator: T.unsafe(nil)); end + + # Create a new ConstantOrWriteNode node. + sig { params(source: Source, node_id: Integer, location: Location, flags: Integer, name: Symbol, name_loc: Location, operator_loc: Location, value: Node).returns(ConstantOrWriteNode) } + def constant_or_write_node(source: T.unsafe(nil), node_id: T.unsafe(nil), location: T.unsafe(nil), flags: T.unsafe(nil), name: T.unsafe(nil), name_loc: T.unsafe(nil), operator_loc: T.unsafe(nil), value: T.unsafe(nil)); end + + # Create a new ConstantPathAndWriteNode node. + sig { params(source: Source, node_id: Integer, location: Location, flags: Integer, target: ConstantPathNode, operator_loc: Location, value: Node).returns(ConstantPathAndWriteNode) } + def constant_path_and_write_node(source: T.unsafe(nil), node_id: T.unsafe(nil), location: T.unsafe(nil), flags: T.unsafe(nil), target: T.unsafe(nil), operator_loc: T.unsafe(nil), value: T.unsafe(nil)); end + + # Create a new ConstantPathNode node. + sig { params(source: Source, node_id: Integer, location: Location, flags: Integer, parent: ::T.nilable(Node), name: ::T.nilable(Symbol), delimiter_loc: Location, name_loc: Location).returns(ConstantPathNode) } + def constant_path_node(source: T.unsafe(nil), node_id: T.unsafe(nil), location: T.unsafe(nil), flags: T.unsafe(nil), parent: T.unsafe(nil), name: T.unsafe(nil), delimiter_loc: T.unsafe(nil), name_loc: T.unsafe(nil)); end + + # Create a new ConstantPathOperatorWriteNode node. + sig { params(source: Source, node_id: Integer, location: Location, flags: Integer, target: ConstantPathNode, binary_operator_loc: Location, value: Node, binary_operator: Symbol).returns(ConstantPathOperatorWriteNode) } + def constant_path_operator_write_node(source: T.unsafe(nil), node_id: T.unsafe(nil), location: T.unsafe(nil), flags: T.unsafe(nil), target: T.unsafe(nil), binary_operator_loc: T.unsafe(nil), value: T.unsafe(nil), binary_operator: T.unsafe(nil)); end + + # Create a new ConstantPathOrWriteNode node. + sig { params(source: Source, node_id: Integer, location: Location, flags: Integer, target: ConstantPathNode, operator_loc: Location, value: Node).returns(ConstantPathOrWriteNode) } + def constant_path_or_write_node(source: T.unsafe(nil), node_id: T.unsafe(nil), location: T.unsafe(nil), flags: T.unsafe(nil), target: T.unsafe(nil), operator_loc: T.unsafe(nil), value: T.unsafe(nil)); end + + # Create a new ConstantPathTargetNode node. + sig { params(source: Source, node_id: Integer, location: Location, flags: Integer, parent: ::T.nilable(Node), name: ::T.nilable(Symbol), delimiter_loc: Location, name_loc: Location).returns(ConstantPathTargetNode) } + def constant_path_target_node(source: T.unsafe(nil), node_id: T.unsafe(nil), location: T.unsafe(nil), flags: T.unsafe(nil), parent: T.unsafe(nil), name: T.unsafe(nil), delimiter_loc: T.unsafe(nil), name_loc: T.unsafe(nil)); end + + # Create a new ConstantPathWriteNode node. + sig { params(source: Source, node_id: Integer, location: Location, flags: Integer, target: ConstantPathNode, operator_loc: Location, value: Node).returns(ConstantPathWriteNode) } + def constant_path_write_node(source: T.unsafe(nil), node_id: T.unsafe(nil), location: T.unsafe(nil), flags: T.unsafe(nil), target: T.unsafe(nil), operator_loc: T.unsafe(nil), value: T.unsafe(nil)); end + + # Create a new ConstantReadNode node. + sig { params(source: Source, node_id: Integer, location: Location, flags: Integer, name: Symbol).returns(ConstantReadNode) } + def constant_read_node(source: T.unsafe(nil), node_id: T.unsafe(nil), location: T.unsafe(nil), flags: T.unsafe(nil), name: T.unsafe(nil)); end + + # Create a new ConstantTargetNode node. + sig { params(source: Source, node_id: Integer, location: Location, flags: Integer, name: Symbol).returns(ConstantTargetNode) } + def constant_target_node(source: T.unsafe(nil), node_id: T.unsafe(nil), location: T.unsafe(nil), flags: T.unsafe(nil), name: T.unsafe(nil)); end + + # Create a new ConstantWriteNode node. + sig { params(source: Source, node_id: Integer, location: Location, flags: Integer, name: Symbol, name_loc: Location, value: Node, operator_loc: Location).returns(ConstantWriteNode) } + def constant_write_node(source: T.unsafe(nil), node_id: T.unsafe(nil), location: T.unsafe(nil), flags: T.unsafe(nil), name: T.unsafe(nil), name_loc: T.unsafe(nil), value: T.unsafe(nil), operator_loc: T.unsafe(nil)); end + + # Create a new DefNode node. + sig { params(source: Source, node_id: Integer, location: Location, flags: Integer, name: Symbol, name_loc: Location, receiver: ::T.nilable(Node), parameters: ::T.nilable(ParametersNode), body: ::T.nilable(::T.any(StatementsNode, BeginNode)), locals: T::Array[Symbol], def_keyword_loc: Location, operator_loc: ::T.nilable(Location), lparen_loc: ::T.nilable(Location), rparen_loc: ::T.nilable(Location), equal_loc: ::T.nilable(Location), end_keyword_loc: ::T.nilable(Location)).returns(DefNode) } + def def_node(source: T.unsafe(nil), node_id: T.unsafe(nil), location: T.unsafe(nil), flags: T.unsafe(nil), name: T.unsafe(nil), name_loc: T.unsafe(nil), receiver: T.unsafe(nil), parameters: T.unsafe(nil), body: T.unsafe(nil), locals: T.unsafe(nil), def_keyword_loc: T.unsafe(nil), operator_loc: T.unsafe(nil), lparen_loc: T.unsafe(nil), rparen_loc: T.unsafe(nil), equal_loc: T.unsafe(nil), end_keyword_loc: T.unsafe(nil)); end + + # Create a new DefinedNode node. + sig { params(source: Source, node_id: Integer, location: Location, flags: Integer, lparen_loc: ::T.nilable(Location), value: Node, rparen_loc: ::T.nilable(Location), keyword_loc: Location).returns(DefinedNode) } + def defined_node(source: T.unsafe(nil), node_id: T.unsafe(nil), location: T.unsafe(nil), flags: T.unsafe(nil), lparen_loc: T.unsafe(nil), value: T.unsafe(nil), rparen_loc: T.unsafe(nil), keyword_loc: T.unsafe(nil)); end + + # Create a new ElseNode node. + sig { params(source: Source, node_id: Integer, location: Location, flags: Integer, else_keyword_loc: Location, statements: ::T.nilable(StatementsNode), end_keyword_loc: ::T.nilable(Location)).returns(ElseNode) } + def else_node(source: T.unsafe(nil), node_id: T.unsafe(nil), location: T.unsafe(nil), flags: T.unsafe(nil), else_keyword_loc: T.unsafe(nil), statements: T.unsafe(nil), end_keyword_loc: T.unsafe(nil)); end + + # Create a new EmbeddedStatementsNode node. + sig { params(source: Source, node_id: Integer, location: Location, flags: Integer, opening_loc: Location, statements: ::T.nilable(StatementsNode), closing_loc: Location).returns(EmbeddedStatementsNode) } + def embedded_statements_node(source: T.unsafe(nil), node_id: T.unsafe(nil), location: T.unsafe(nil), flags: T.unsafe(nil), opening_loc: T.unsafe(nil), statements: T.unsafe(nil), closing_loc: T.unsafe(nil)); end + + # Create a new EmbeddedVariableNode node. + sig { params(source: Source, node_id: Integer, location: Location, flags: Integer, operator_loc: Location, variable: ::T.any(InstanceVariableReadNode, ClassVariableReadNode, GlobalVariableReadNode, BackReferenceReadNode, NumberedReferenceReadNode)).returns(EmbeddedVariableNode) } + def embedded_variable_node(source: T.unsafe(nil), node_id: T.unsafe(nil), location: T.unsafe(nil), flags: T.unsafe(nil), operator_loc: T.unsafe(nil), variable: T.unsafe(nil)); end + + # Create a new EnsureNode node. + sig { params(source: Source, node_id: Integer, location: Location, flags: Integer, ensure_keyword_loc: Location, statements: ::T.nilable(StatementsNode), end_keyword_loc: Location).returns(EnsureNode) } + def ensure_node(source: T.unsafe(nil), node_id: T.unsafe(nil), location: T.unsafe(nil), flags: T.unsafe(nil), ensure_keyword_loc: T.unsafe(nil), statements: T.unsafe(nil), end_keyword_loc: T.unsafe(nil)); end + + # Create a new FalseNode node. + sig { params(source: Source, node_id: Integer, location: Location, flags: Integer).returns(FalseNode) } + def false_node(source: T.unsafe(nil), node_id: T.unsafe(nil), location: T.unsafe(nil), flags: T.unsafe(nil)); end + + # Create a new FindPatternNode node. + sig { params(source: Source, node_id: Integer, location: Location, flags: Integer, constant: ::T.nilable(::T.any(ConstantPathNode, ConstantReadNode)), left: SplatNode, requireds: T::Array[Node], right: ::T.any(SplatNode, MissingNode), opening_loc: ::T.nilable(Location), closing_loc: ::T.nilable(Location)).returns(FindPatternNode) } + def find_pattern_node(source: T.unsafe(nil), node_id: T.unsafe(nil), location: T.unsafe(nil), flags: T.unsafe(nil), constant: T.unsafe(nil), left: T.unsafe(nil), requireds: T.unsafe(nil), right: T.unsafe(nil), opening_loc: T.unsafe(nil), closing_loc: T.unsafe(nil)); end + + # Create a new FlipFlopNode node. + sig { params(source: Source, node_id: Integer, location: Location, flags: Integer, left: ::T.nilable(Node), right: ::T.nilable(Node), operator_loc: Location).returns(FlipFlopNode) } + def flip_flop_node(source: T.unsafe(nil), node_id: T.unsafe(nil), location: T.unsafe(nil), flags: T.unsafe(nil), left: T.unsafe(nil), right: T.unsafe(nil), operator_loc: T.unsafe(nil)); end + + # Create a new FloatNode node. + sig { params(source: Source, node_id: Integer, location: Location, flags: Integer, value: Float).returns(FloatNode) } + def float_node(source: T.unsafe(nil), node_id: T.unsafe(nil), location: T.unsafe(nil), flags: T.unsafe(nil), value: T.unsafe(nil)); end + + # Create a new ForNode node. + sig { params(source: Source, node_id: Integer, location: Location, flags: Integer, index: ::T.any(LocalVariableTargetNode, InstanceVariableTargetNode, ClassVariableTargetNode, GlobalVariableTargetNode, ConstantTargetNode, ConstantPathTargetNode, CallTargetNode, IndexTargetNode, MultiTargetNode, BackReferenceReadNode, NumberedReferenceReadNode, MissingNode), collection: Node, statements: ::T.nilable(StatementsNode), for_keyword_loc: Location, in_keyword_loc: Location, do_keyword_loc: ::T.nilable(Location), end_keyword_loc: Location).returns(ForNode) } + def for_node(source: T.unsafe(nil), node_id: T.unsafe(nil), location: T.unsafe(nil), flags: T.unsafe(nil), index: T.unsafe(nil), collection: T.unsafe(nil), statements: T.unsafe(nil), for_keyword_loc: T.unsafe(nil), in_keyword_loc: T.unsafe(nil), do_keyword_loc: T.unsafe(nil), end_keyword_loc: T.unsafe(nil)); end + + # Create a new ForwardingArgumentsNode node. + sig { params(source: Source, node_id: Integer, location: Location, flags: Integer).returns(ForwardingArgumentsNode) } + def forwarding_arguments_node(source: T.unsafe(nil), node_id: T.unsafe(nil), location: T.unsafe(nil), flags: T.unsafe(nil)); end + + # Create a new ForwardingParameterNode node. + sig { params(source: Source, node_id: Integer, location: Location, flags: Integer).returns(ForwardingParameterNode) } + def forwarding_parameter_node(source: T.unsafe(nil), node_id: T.unsafe(nil), location: T.unsafe(nil), flags: T.unsafe(nil)); end + + # Create a new ForwardingSuperNode node. + sig { params(source: Source, node_id: Integer, location: Location, flags: Integer, block: ::T.nilable(BlockNode)).returns(ForwardingSuperNode) } + def forwarding_super_node(source: T.unsafe(nil), node_id: T.unsafe(nil), location: T.unsafe(nil), flags: T.unsafe(nil), block: T.unsafe(nil)); end + + # Create a new GlobalVariableAndWriteNode node. + sig { params(source: Source, node_id: Integer, location: Location, flags: Integer, name: Symbol, name_loc: Location, operator_loc: Location, value: Node).returns(GlobalVariableAndWriteNode) } + def global_variable_and_write_node(source: T.unsafe(nil), node_id: T.unsafe(nil), location: T.unsafe(nil), flags: T.unsafe(nil), name: T.unsafe(nil), name_loc: T.unsafe(nil), operator_loc: T.unsafe(nil), value: T.unsafe(nil)); end + + # Create a new GlobalVariableOperatorWriteNode node. + sig { params(source: Source, node_id: Integer, location: Location, flags: Integer, name: Symbol, name_loc: Location, binary_operator_loc: Location, value: Node, binary_operator: Symbol).returns(GlobalVariableOperatorWriteNode) } + def global_variable_operator_write_node(source: T.unsafe(nil), node_id: T.unsafe(nil), location: T.unsafe(nil), flags: T.unsafe(nil), name: T.unsafe(nil), name_loc: T.unsafe(nil), binary_operator_loc: T.unsafe(nil), value: T.unsafe(nil), binary_operator: T.unsafe(nil)); end + + # Create a new GlobalVariableOrWriteNode node. + sig { params(source: Source, node_id: Integer, location: Location, flags: Integer, name: Symbol, name_loc: Location, operator_loc: Location, value: Node).returns(GlobalVariableOrWriteNode) } + def global_variable_or_write_node(source: T.unsafe(nil), node_id: T.unsafe(nil), location: T.unsafe(nil), flags: T.unsafe(nil), name: T.unsafe(nil), name_loc: T.unsafe(nil), operator_loc: T.unsafe(nil), value: T.unsafe(nil)); end + + # Create a new GlobalVariableReadNode node. + sig { params(source: Source, node_id: Integer, location: Location, flags: Integer, name: Symbol).returns(GlobalVariableReadNode) } + def global_variable_read_node(source: T.unsafe(nil), node_id: T.unsafe(nil), location: T.unsafe(nil), flags: T.unsafe(nil), name: T.unsafe(nil)); end + + # Create a new GlobalVariableTargetNode node. + sig { params(source: Source, node_id: Integer, location: Location, flags: Integer, name: Symbol).returns(GlobalVariableTargetNode) } + def global_variable_target_node(source: T.unsafe(nil), node_id: T.unsafe(nil), location: T.unsafe(nil), flags: T.unsafe(nil), name: T.unsafe(nil)); end + + # Create a new GlobalVariableWriteNode node. + sig { params(source: Source, node_id: Integer, location: Location, flags: Integer, name: Symbol, name_loc: Location, value: Node, operator_loc: Location).returns(GlobalVariableWriteNode) } + def global_variable_write_node(source: T.unsafe(nil), node_id: T.unsafe(nil), location: T.unsafe(nil), flags: T.unsafe(nil), name: T.unsafe(nil), name_loc: T.unsafe(nil), value: T.unsafe(nil), operator_loc: T.unsafe(nil)); end + + # Create a new HashNode node. + sig { params(source: Source, node_id: Integer, location: Location, flags: Integer, opening_loc: Location, elements: T::Array[::T.any(AssocNode, AssocSplatNode)], closing_loc: Location).returns(HashNode) } + def hash_node(source: T.unsafe(nil), node_id: T.unsafe(nil), location: T.unsafe(nil), flags: T.unsafe(nil), opening_loc: T.unsafe(nil), elements: T.unsafe(nil), closing_loc: T.unsafe(nil)); end + + # Create a new HashPatternNode node. + sig { params(source: Source, node_id: Integer, location: Location, flags: Integer, constant: ::T.nilable(::T.any(ConstantPathNode, ConstantReadNode)), elements: T::Array[AssocNode], rest: ::T.nilable(::T.any(AssocSplatNode, NoKeywordsParameterNode)), opening_loc: ::T.nilable(Location), closing_loc: ::T.nilable(Location)).returns(HashPatternNode) } + def hash_pattern_node(source: T.unsafe(nil), node_id: T.unsafe(nil), location: T.unsafe(nil), flags: T.unsafe(nil), constant: T.unsafe(nil), elements: T.unsafe(nil), rest: T.unsafe(nil), opening_loc: T.unsafe(nil), closing_loc: T.unsafe(nil)); end + + # Create a new IfNode node. + sig { params(source: Source, node_id: Integer, location: Location, flags: Integer, if_keyword_loc: ::T.nilable(Location), predicate: Node, then_keyword_loc: ::T.nilable(Location), statements: ::T.nilable(StatementsNode), subsequent: ::T.nilable(::T.any(ElseNode, IfNode)), end_keyword_loc: ::T.nilable(Location)).returns(IfNode) } + def if_node(source: T.unsafe(nil), node_id: T.unsafe(nil), location: T.unsafe(nil), flags: T.unsafe(nil), if_keyword_loc: T.unsafe(nil), predicate: T.unsafe(nil), then_keyword_loc: T.unsafe(nil), statements: T.unsafe(nil), subsequent: T.unsafe(nil), end_keyword_loc: T.unsafe(nil)); end + + # Create a new ImaginaryNode node. + sig { params(source: Source, node_id: Integer, location: Location, flags: Integer, numeric: ::T.any(FloatNode, IntegerNode, RationalNode)).returns(ImaginaryNode) } + def imaginary_node(source: T.unsafe(nil), node_id: T.unsafe(nil), location: T.unsafe(nil), flags: T.unsafe(nil), numeric: T.unsafe(nil)); end + + # Create a new ImplicitNode node. + sig { params(source: Source, node_id: Integer, location: Location, flags: Integer, value: ::T.any(LocalVariableReadNode, CallNode, ConstantReadNode, LocalVariableTargetNode)).returns(ImplicitNode) } + def implicit_node(source: T.unsafe(nil), node_id: T.unsafe(nil), location: T.unsafe(nil), flags: T.unsafe(nil), value: T.unsafe(nil)); end + + # Create a new ImplicitRestNode node. + sig { params(source: Source, node_id: Integer, location: Location, flags: Integer).returns(ImplicitRestNode) } + def implicit_rest_node(source: T.unsafe(nil), node_id: T.unsafe(nil), location: T.unsafe(nil), flags: T.unsafe(nil)); end + + # Create a new InNode node. + sig { params(source: Source, node_id: Integer, location: Location, flags: Integer, pattern: Node, statements: ::T.nilable(StatementsNode), in_loc: Location, then_loc: ::T.nilable(Location)).returns(InNode) } + def in_node(source: T.unsafe(nil), node_id: T.unsafe(nil), location: T.unsafe(nil), flags: T.unsafe(nil), pattern: T.unsafe(nil), statements: T.unsafe(nil), in_loc: T.unsafe(nil), then_loc: T.unsafe(nil)); end + + # Create a new IndexAndWriteNode node. + sig { params(source: Source, node_id: Integer, location: Location, flags: Integer, receiver: ::T.nilable(Node), call_operator_loc: ::T.nilable(Location), opening_loc: Location, arguments: ::T.nilable(ArgumentsNode), closing_loc: Location, block: ::T.nilable(BlockArgumentNode), operator_loc: Location, value: Node).returns(IndexAndWriteNode) } + def index_and_write_node(source: T.unsafe(nil), node_id: T.unsafe(nil), location: T.unsafe(nil), flags: T.unsafe(nil), receiver: T.unsafe(nil), call_operator_loc: T.unsafe(nil), opening_loc: T.unsafe(nil), arguments: T.unsafe(nil), closing_loc: T.unsafe(nil), block: T.unsafe(nil), operator_loc: T.unsafe(nil), value: T.unsafe(nil)); end + + # Create a new IndexOperatorWriteNode node. + sig { params(source: Source, node_id: Integer, location: Location, flags: Integer, receiver: ::T.nilable(Node), call_operator_loc: ::T.nilable(Location), opening_loc: Location, arguments: ::T.nilable(ArgumentsNode), closing_loc: Location, block: ::T.nilable(BlockArgumentNode), binary_operator: Symbol, binary_operator_loc: Location, value: Node).returns(IndexOperatorWriteNode) } + def index_operator_write_node(source: T.unsafe(nil), node_id: T.unsafe(nil), location: T.unsafe(nil), flags: T.unsafe(nil), receiver: T.unsafe(nil), call_operator_loc: T.unsafe(nil), opening_loc: T.unsafe(nil), arguments: T.unsafe(nil), closing_loc: T.unsafe(nil), block: T.unsafe(nil), binary_operator: T.unsafe(nil), binary_operator_loc: T.unsafe(nil), value: T.unsafe(nil)); end + + # Create a new IndexOrWriteNode node. + sig { params(source: Source, node_id: Integer, location: Location, flags: Integer, receiver: ::T.nilable(Node), call_operator_loc: ::T.nilable(Location), opening_loc: Location, arguments: ::T.nilable(ArgumentsNode), closing_loc: Location, block: ::T.nilable(BlockArgumentNode), operator_loc: Location, value: Node).returns(IndexOrWriteNode) } + def index_or_write_node(source: T.unsafe(nil), node_id: T.unsafe(nil), location: T.unsafe(nil), flags: T.unsafe(nil), receiver: T.unsafe(nil), call_operator_loc: T.unsafe(nil), opening_loc: T.unsafe(nil), arguments: T.unsafe(nil), closing_loc: T.unsafe(nil), block: T.unsafe(nil), operator_loc: T.unsafe(nil), value: T.unsafe(nil)); end + + # Create a new IndexTargetNode node. + sig { params(source: Source, node_id: Integer, location: Location, flags: Integer, receiver: Node, opening_loc: Location, arguments: ::T.nilable(ArgumentsNode), closing_loc: Location, block: ::T.nilable(BlockArgumentNode)).returns(IndexTargetNode) } + def index_target_node(source: T.unsafe(nil), node_id: T.unsafe(nil), location: T.unsafe(nil), flags: T.unsafe(nil), receiver: T.unsafe(nil), opening_loc: T.unsafe(nil), arguments: T.unsafe(nil), closing_loc: T.unsafe(nil), block: T.unsafe(nil)); end + + # Create a new InstanceVariableAndWriteNode node. + sig { params(source: Source, node_id: Integer, location: Location, flags: Integer, name: Symbol, name_loc: Location, operator_loc: Location, value: Node).returns(InstanceVariableAndWriteNode) } + def instance_variable_and_write_node(source: T.unsafe(nil), node_id: T.unsafe(nil), location: T.unsafe(nil), flags: T.unsafe(nil), name: T.unsafe(nil), name_loc: T.unsafe(nil), operator_loc: T.unsafe(nil), value: T.unsafe(nil)); end + + # Create a new InstanceVariableOperatorWriteNode node. + sig { params(source: Source, node_id: Integer, location: Location, flags: Integer, name: Symbol, name_loc: Location, binary_operator_loc: Location, value: Node, binary_operator: Symbol).returns(InstanceVariableOperatorWriteNode) } + def instance_variable_operator_write_node(source: T.unsafe(nil), node_id: T.unsafe(nil), location: T.unsafe(nil), flags: T.unsafe(nil), name: T.unsafe(nil), name_loc: T.unsafe(nil), binary_operator_loc: T.unsafe(nil), value: T.unsafe(nil), binary_operator: T.unsafe(nil)); end + + # Create a new InstanceVariableOrWriteNode node. + sig { params(source: Source, node_id: Integer, location: Location, flags: Integer, name: Symbol, name_loc: Location, operator_loc: Location, value: Node).returns(InstanceVariableOrWriteNode) } + def instance_variable_or_write_node(source: T.unsafe(nil), node_id: T.unsafe(nil), location: T.unsafe(nil), flags: T.unsafe(nil), name: T.unsafe(nil), name_loc: T.unsafe(nil), operator_loc: T.unsafe(nil), value: T.unsafe(nil)); end + + # Create a new InstanceVariableReadNode node. + sig { params(source: Source, node_id: Integer, location: Location, flags: Integer, name: Symbol).returns(InstanceVariableReadNode) } + def instance_variable_read_node(source: T.unsafe(nil), node_id: T.unsafe(nil), location: T.unsafe(nil), flags: T.unsafe(nil), name: T.unsafe(nil)); end + + # Create a new InstanceVariableTargetNode node. + sig { params(source: Source, node_id: Integer, location: Location, flags: Integer, name: Symbol).returns(InstanceVariableTargetNode) } + def instance_variable_target_node(source: T.unsafe(nil), node_id: T.unsafe(nil), location: T.unsafe(nil), flags: T.unsafe(nil), name: T.unsafe(nil)); end + + # Create a new InstanceVariableWriteNode node. + sig { params(source: Source, node_id: Integer, location: Location, flags: Integer, name: Symbol, name_loc: Location, value: Node, operator_loc: Location).returns(InstanceVariableWriteNode) } + def instance_variable_write_node(source: T.unsafe(nil), node_id: T.unsafe(nil), location: T.unsafe(nil), flags: T.unsafe(nil), name: T.unsafe(nil), name_loc: T.unsafe(nil), value: T.unsafe(nil), operator_loc: T.unsafe(nil)); end + + # Create a new IntegerNode node. + sig { params(source: Source, node_id: Integer, location: Location, flags: Integer, value: Integer).returns(IntegerNode) } + def integer_node(source: T.unsafe(nil), node_id: T.unsafe(nil), location: T.unsafe(nil), flags: T.unsafe(nil), value: T.unsafe(nil)); end + + # Create a new InterpolatedMatchLastLineNode node. + sig { params(source: Source, node_id: Integer, location: Location, flags: Integer, opening_loc: Location, parts: T::Array[::T.any(StringNode, EmbeddedStatementsNode, EmbeddedVariableNode)], closing_loc: Location).returns(InterpolatedMatchLastLineNode) } + def interpolated_match_last_line_node(source: T.unsafe(nil), node_id: T.unsafe(nil), location: T.unsafe(nil), flags: T.unsafe(nil), opening_loc: T.unsafe(nil), parts: T.unsafe(nil), closing_loc: T.unsafe(nil)); end + + # Create a new InterpolatedRegularExpressionNode node. + sig { params(source: Source, node_id: Integer, location: Location, flags: Integer, opening_loc: Location, parts: T::Array[::T.any(StringNode, EmbeddedStatementsNode, EmbeddedVariableNode)], closing_loc: Location).returns(InterpolatedRegularExpressionNode) } + def interpolated_regular_expression_node(source: T.unsafe(nil), node_id: T.unsafe(nil), location: T.unsafe(nil), flags: T.unsafe(nil), opening_loc: T.unsafe(nil), parts: T.unsafe(nil), closing_loc: T.unsafe(nil)); end + + # Create a new InterpolatedStringNode node. + sig { params(source: Source, node_id: Integer, location: Location, flags: Integer, opening_loc: ::T.nilable(Location), parts: T::Array[::T.any(StringNode, EmbeddedStatementsNode, EmbeddedVariableNode, InterpolatedStringNode, XStringNode, InterpolatedXStringNode, SymbolNode, InterpolatedSymbolNode)], closing_loc: ::T.nilable(Location)).returns(InterpolatedStringNode) } + def interpolated_string_node(source: T.unsafe(nil), node_id: T.unsafe(nil), location: T.unsafe(nil), flags: T.unsafe(nil), opening_loc: T.unsafe(nil), parts: T.unsafe(nil), closing_loc: T.unsafe(nil)); end + + # Create a new InterpolatedSymbolNode node. + sig { params(source: Source, node_id: Integer, location: Location, flags: Integer, opening_loc: ::T.nilable(Location), parts: T::Array[::T.any(StringNode, EmbeddedStatementsNode, EmbeddedVariableNode)], closing_loc: ::T.nilable(Location)).returns(InterpolatedSymbolNode) } + def interpolated_symbol_node(source: T.unsafe(nil), node_id: T.unsafe(nil), location: T.unsafe(nil), flags: T.unsafe(nil), opening_loc: T.unsafe(nil), parts: T.unsafe(nil), closing_loc: T.unsafe(nil)); end + + # Create a new InterpolatedXStringNode node. + sig { params(source: Source, node_id: Integer, location: Location, flags: Integer, opening_loc: Location, parts: T::Array[::T.any(StringNode, EmbeddedStatementsNode, EmbeddedVariableNode)], closing_loc: Location).returns(InterpolatedXStringNode) } + def interpolated_x_string_node(source: T.unsafe(nil), node_id: T.unsafe(nil), location: T.unsafe(nil), flags: T.unsafe(nil), opening_loc: T.unsafe(nil), parts: T.unsafe(nil), closing_loc: T.unsafe(nil)); end + + # Create a new ItLocalVariableReadNode node. + sig { params(source: Source, node_id: Integer, location: Location, flags: Integer).returns(ItLocalVariableReadNode) } + def it_local_variable_read_node(source: T.unsafe(nil), node_id: T.unsafe(nil), location: T.unsafe(nil), flags: T.unsafe(nil)); end + + # Create a new ItParametersNode node. + sig { params(source: Source, node_id: Integer, location: Location, flags: Integer).returns(ItParametersNode) } + def it_parameters_node(source: T.unsafe(nil), node_id: T.unsafe(nil), location: T.unsafe(nil), flags: T.unsafe(nil)); end + + # Create a new KeywordHashNode node. + sig { params(source: Source, node_id: Integer, location: Location, flags: Integer, elements: T::Array[::T.any(AssocNode, AssocSplatNode)]).returns(KeywordHashNode) } + def keyword_hash_node(source: T.unsafe(nil), node_id: T.unsafe(nil), location: T.unsafe(nil), flags: T.unsafe(nil), elements: T.unsafe(nil)); end + + # Create a new KeywordRestParameterNode node. + sig { params(source: Source, node_id: Integer, location: Location, flags: Integer, name: ::T.nilable(Symbol), name_loc: ::T.nilable(Location), operator_loc: Location).returns(KeywordRestParameterNode) } + def keyword_rest_parameter_node(source: T.unsafe(nil), node_id: T.unsafe(nil), location: T.unsafe(nil), flags: T.unsafe(nil), name: T.unsafe(nil), name_loc: T.unsafe(nil), operator_loc: T.unsafe(nil)); end + + # Create a new LambdaNode node. + sig { params(source: Source, node_id: Integer, location: Location, flags: Integer, locals: T::Array[Symbol], operator_loc: Location, opening_loc: Location, closing_loc: Location, parameters: ::T.nilable(::T.any(BlockParametersNode, NumberedParametersNode, ItParametersNode)), body: ::T.nilable(::T.any(StatementsNode, BeginNode))).returns(LambdaNode) } + def lambda_node(source: T.unsafe(nil), node_id: T.unsafe(nil), location: T.unsafe(nil), flags: T.unsafe(nil), locals: T.unsafe(nil), operator_loc: T.unsafe(nil), opening_loc: T.unsafe(nil), closing_loc: T.unsafe(nil), parameters: T.unsafe(nil), body: T.unsafe(nil)); end + + # Create a new LocalVariableAndWriteNode node. + sig { params(source: Source, node_id: Integer, location: Location, flags: Integer, name_loc: Location, operator_loc: Location, value: Node, name: Symbol, depth: Integer).returns(LocalVariableAndWriteNode) } + def local_variable_and_write_node(source: T.unsafe(nil), node_id: T.unsafe(nil), location: T.unsafe(nil), flags: T.unsafe(nil), name_loc: T.unsafe(nil), operator_loc: T.unsafe(nil), value: T.unsafe(nil), name: T.unsafe(nil), depth: T.unsafe(nil)); end + + # Create a new LocalVariableOperatorWriteNode node. + sig { params(source: Source, node_id: Integer, location: Location, flags: Integer, name_loc: Location, binary_operator_loc: Location, value: Node, name: Symbol, binary_operator: Symbol, depth: Integer).returns(LocalVariableOperatorWriteNode) } + def local_variable_operator_write_node(source: T.unsafe(nil), node_id: T.unsafe(nil), location: T.unsafe(nil), flags: T.unsafe(nil), name_loc: T.unsafe(nil), binary_operator_loc: T.unsafe(nil), value: T.unsafe(nil), name: T.unsafe(nil), binary_operator: T.unsafe(nil), depth: T.unsafe(nil)); end + + # Create a new LocalVariableOrWriteNode node. + sig { params(source: Source, node_id: Integer, location: Location, flags: Integer, name_loc: Location, operator_loc: Location, value: Node, name: Symbol, depth: Integer).returns(LocalVariableOrWriteNode) } + def local_variable_or_write_node(source: T.unsafe(nil), node_id: T.unsafe(nil), location: T.unsafe(nil), flags: T.unsafe(nil), name_loc: T.unsafe(nil), operator_loc: T.unsafe(nil), value: T.unsafe(nil), name: T.unsafe(nil), depth: T.unsafe(nil)); end + + # Create a new LocalVariableReadNode node. + sig { params(source: Source, node_id: Integer, location: Location, flags: Integer, name: Symbol, depth: Integer).returns(LocalVariableReadNode) } + def local_variable_read_node(source: T.unsafe(nil), node_id: T.unsafe(nil), location: T.unsafe(nil), flags: T.unsafe(nil), name: T.unsafe(nil), depth: T.unsafe(nil)); end + + # Create a new LocalVariableTargetNode node. + sig { params(source: Source, node_id: Integer, location: Location, flags: Integer, name: Symbol, depth: Integer).returns(LocalVariableTargetNode) } + def local_variable_target_node(source: T.unsafe(nil), node_id: T.unsafe(nil), location: T.unsafe(nil), flags: T.unsafe(nil), name: T.unsafe(nil), depth: T.unsafe(nil)); end + + # Create a new LocalVariableWriteNode node. + sig { params(source: Source, node_id: Integer, location: Location, flags: Integer, name: Symbol, depth: Integer, name_loc: Location, value: Node, operator_loc: Location).returns(LocalVariableWriteNode) } + def local_variable_write_node(source: T.unsafe(nil), node_id: T.unsafe(nil), location: T.unsafe(nil), flags: T.unsafe(nil), name: T.unsafe(nil), depth: T.unsafe(nil), name_loc: T.unsafe(nil), value: T.unsafe(nil), operator_loc: T.unsafe(nil)); end + + # Create a new MatchLastLineNode node. + sig { params(source: Source, node_id: Integer, location: Location, flags: Integer, opening_loc: Location, content_loc: Location, closing_loc: Location, unescaped: String).returns(MatchLastLineNode) } + def match_last_line_node(source: T.unsafe(nil), node_id: T.unsafe(nil), location: T.unsafe(nil), flags: T.unsafe(nil), opening_loc: T.unsafe(nil), content_loc: T.unsafe(nil), closing_loc: T.unsafe(nil), unescaped: T.unsafe(nil)); end + + # Create a new MatchPredicateNode node. + sig { params(source: Source, node_id: Integer, location: Location, flags: Integer, value: Node, pattern: Node, operator_loc: Location).returns(MatchPredicateNode) } + def match_predicate_node(source: T.unsafe(nil), node_id: T.unsafe(nil), location: T.unsafe(nil), flags: T.unsafe(nil), value: T.unsafe(nil), pattern: T.unsafe(nil), operator_loc: T.unsafe(nil)); end + + # Create a new MatchRequiredNode node. + sig { params(source: Source, node_id: Integer, location: Location, flags: Integer, value: Node, pattern: Node, operator_loc: Location).returns(MatchRequiredNode) } + def match_required_node(source: T.unsafe(nil), node_id: T.unsafe(nil), location: T.unsafe(nil), flags: T.unsafe(nil), value: T.unsafe(nil), pattern: T.unsafe(nil), operator_loc: T.unsafe(nil)); end + + # Create a new MatchWriteNode node. + sig { params(source: Source, node_id: Integer, location: Location, flags: Integer, call: CallNode, targets: T::Array[LocalVariableTargetNode]).returns(MatchWriteNode) } + def match_write_node(source: T.unsafe(nil), node_id: T.unsafe(nil), location: T.unsafe(nil), flags: T.unsafe(nil), call: T.unsafe(nil), targets: T.unsafe(nil)); end + + # Create a new MissingNode node. + sig { params(source: Source, node_id: Integer, location: Location, flags: Integer).returns(MissingNode) } + def missing_node(source: T.unsafe(nil), node_id: T.unsafe(nil), location: T.unsafe(nil), flags: T.unsafe(nil)); end + + # Create a new ModuleNode node. + sig { params(source: Source, node_id: Integer, location: Location, flags: Integer, locals: T::Array[Symbol], module_keyword_loc: Location, constant_path: ::T.any(ConstantReadNode, ConstantPathNode, MissingNode), body: ::T.nilable(::T.any(StatementsNode, BeginNode)), end_keyword_loc: Location, name: Symbol).returns(ModuleNode) } + def module_node(source: T.unsafe(nil), node_id: T.unsafe(nil), location: T.unsafe(nil), flags: T.unsafe(nil), locals: T.unsafe(nil), module_keyword_loc: T.unsafe(nil), constant_path: T.unsafe(nil), body: T.unsafe(nil), end_keyword_loc: T.unsafe(nil), name: T.unsafe(nil)); end + + # Create a new MultiTargetNode node. + sig { params(source: Source, node_id: Integer, location: Location, flags: Integer, lefts: T::Array[::T.any(LocalVariableTargetNode, InstanceVariableTargetNode, ClassVariableTargetNode, GlobalVariableTargetNode, ConstantTargetNode, ConstantPathTargetNode, CallTargetNode, IndexTargetNode, MultiTargetNode, RequiredParameterNode, BackReferenceReadNode, NumberedReferenceReadNode)], rest: ::T.nilable(::T.any(ImplicitRestNode, SplatNode)), rights: T::Array[::T.any(LocalVariableTargetNode, InstanceVariableTargetNode, ClassVariableTargetNode, GlobalVariableTargetNode, ConstantTargetNode, ConstantPathTargetNode, CallTargetNode, IndexTargetNode, MultiTargetNode, RequiredParameterNode, BackReferenceReadNode, NumberedReferenceReadNode)], lparen_loc: ::T.nilable(Location), rparen_loc: ::T.nilable(Location)).returns(MultiTargetNode) } + def multi_target_node(source: T.unsafe(nil), node_id: T.unsafe(nil), location: T.unsafe(nil), flags: T.unsafe(nil), lefts: T.unsafe(nil), rest: T.unsafe(nil), rights: T.unsafe(nil), lparen_loc: T.unsafe(nil), rparen_loc: T.unsafe(nil)); end + + # Create a new MultiWriteNode node. + sig { params(source: Source, node_id: Integer, location: Location, flags: Integer, lefts: T::Array[::T.any(LocalVariableTargetNode, InstanceVariableTargetNode, ClassVariableTargetNode, GlobalVariableTargetNode, ConstantTargetNode, ConstantPathTargetNode, CallTargetNode, IndexTargetNode, MultiTargetNode, BackReferenceReadNode, NumberedReferenceReadNode)], rest: ::T.nilable(::T.any(ImplicitRestNode, SplatNode)), rights: T::Array[::T.any(LocalVariableTargetNode, InstanceVariableTargetNode, ClassVariableTargetNode, GlobalVariableTargetNode, ConstantTargetNode, ConstantPathTargetNode, CallTargetNode, IndexTargetNode, MultiTargetNode, BackReferenceReadNode, NumberedReferenceReadNode)], lparen_loc: ::T.nilable(Location), rparen_loc: ::T.nilable(Location), operator_loc: Location, value: Node).returns(MultiWriteNode) } + def multi_write_node(source: T.unsafe(nil), node_id: T.unsafe(nil), location: T.unsafe(nil), flags: T.unsafe(nil), lefts: T.unsafe(nil), rest: T.unsafe(nil), rights: T.unsafe(nil), lparen_loc: T.unsafe(nil), rparen_loc: T.unsafe(nil), operator_loc: T.unsafe(nil), value: T.unsafe(nil)); end + + # Create a new NextNode node. + sig { params(source: Source, node_id: Integer, location: Location, flags: Integer, arguments: ::T.nilable(ArgumentsNode), keyword_loc: Location).returns(NextNode) } + def next_node(source: T.unsafe(nil), node_id: T.unsafe(nil), location: T.unsafe(nil), flags: T.unsafe(nil), arguments: T.unsafe(nil), keyword_loc: T.unsafe(nil)); end + + # Create a new NilNode node. + sig { params(source: Source, node_id: Integer, location: Location, flags: Integer).returns(NilNode) } + def nil_node(source: T.unsafe(nil), node_id: T.unsafe(nil), location: T.unsafe(nil), flags: T.unsafe(nil)); end + + # Create a new NoBlockParameterNode node. + sig { params(source: Source, node_id: Integer, location: Location, flags: Integer, operator_loc: Location, keyword_loc: Location).returns(NoBlockParameterNode) } + def no_block_parameter_node(source: T.unsafe(nil), node_id: T.unsafe(nil), location: T.unsafe(nil), flags: T.unsafe(nil), operator_loc: T.unsafe(nil), keyword_loc: T.unsafe(nil)); end + + # Create a new NoKeywordsParameterNode node. + sig { params(source: Source, node_id: Integer, location: Location, flags: Integer, operator_loc: Location, keyword_loc: Location).returns(NoKeywordsParameterNode) } + def no_keywords_parameter_node(source: T.unsafe(nil), node_id: T.unsafe(nil), location: T.unsafe(nil), flags: T.unsafe(nil), operator_loc: T.unsafe(nil), keyword_loc: T.unsafe(nil)); end + + # Create a new NumberedParametersNode node. + sig { params(source: Source, node_id: Integer, location: Location, flags: Integer, maximum: Integer).returns(NumberedParametersNode) } + def numbered_parameters_node(source: T.unsafe(nil), node_id: T.unsafe(nil), location: T.unsafe(nil), flags: T.unsafe(nil), maximum: T.unsafe(nil)); end + + # Create a new NumberedReferenceReadNode node. + sig { params(source: Source, node_id: Integer, location: Location, flags: Integer, number: Integer).returns(NumberedReferenceReadNode) } + def numbered_reference_read_node(source: T.unsafe(nil), node_id: T.unsafe(nil), location: T.unsafe(nil), flags: T.unsafe(nil), number: T.unsafe(nil)); end + + # Create a new OptionalKeywordParameterNode node. + sig { params(source: Source, node_id: Integer, location: Location, flags: Integer, name: Symbol, name_loc: Location, value: Node).returns(OptionalKeywordParameterNode) } + def optional_keyword_parameter_node(source: T.unsafe(nil), node_id: T.unsafe(nil), location: T.unsafe(nil), flags: T.unsafe(nil), name: T.unsafe(nil), name_loc: T.unsafe(nil), value: T.unsafe(nil)); end + + # Create a new OptionalParameterNode node. + sig { params(source: Source, node_id: Integer, location: Location, flags: Integer, name: Symbol, name_loc: Location, operator_loc: Location, value: Node).returns(OptionalParameterNode) } + def optional_parameter_node(source: T.unsafe(nil), node_id: T.unsafe(nil), location: T.unsafe(nil), flags: T.unsafe(nil), name: T.unsafe(nil), name_loc: T.unsafe(nil), operator_loc: T.unsafe(nil), value: T.unsafe(nil)); end + + # Create a new OrNode node. + sig { params(source: Source, node_id: Integer, location: Location, flags: Integer, left: Node, right: Node, operator_loc: Location).returns(OrNode) } + def or_node(source: T.unsafe(nil), node_id: T.unsafe(nil), location: T.unsafe(nil), flags: T.unsafe(nil), left: T.unsafe(nil), right: T.unsafe(nil), operator_loc: T.unsafe(nil)); end + + # Create a new ParametersNode node. + sig { params(source: Source, node_id: Integer, location: Location, flags: Integer, requireds: T::Array[::T.any(RequiredParameterNode, MultiTargetNode)], optionals: T::Array[OptionalParameterNode], rest: ::T.nilable(::T.any(RestParameterNode, ImplicitRestNode)), posts: T::Array[::T.any(RequiredParameterNode, MultiTargetNode, KeywordRestParameterNode, NoKeywordsParameterNode, ForwardingParameterNode, BlockParameterNode, NoBlockParameterNode)], keywords: T::Array[::T.any(RequiredKeywordParameterNode, OptionalKeywordParameterNode)], keyword_rest: ::T.nilable(::T.any(KeywordRestParameterNode, ForwardingParameterNode, NoKeywordsParameterNode)), block: ::T.nilable(::T.any(BlockParameterNode, NoBlockParameterNode))).returns(ParametersNode) } + def parameters_node(source: T.unsafe(nil), node_id: T.unsafe(nil), location: T.unsafe(nil), flags: T.unsafe(nil), requireds: T.unsafe(nil), optionals: T.unsafe(nil), rest: T.unsafe(nil), posts: T.unsafe(nil), keywords: T.unsafe(nil), keyword_rest: T.unsafe(nil), block: T.unsafe(nil)); end + + # Create a new ParenthesesNode node. + sig { params(source: Source, node_id: Integer, location: Location, flags: Integer, body: ::T.nilable(Node), opening_loc: Location, closing_loc: Location).returns(ParenthesesNode) } + def parentheses_node(source: T.unsafe(nil), node_id: T.unsafe(nil), location: T.unsafe(nil), flags: T.unsafe(nil), body: T.unsafe(nil), opening_loc: T.unsafe(nil), closing_loc: T.unsafe(nil)); end + + # Create a new PinnedExpressionNode node. + sig { params(source: Source, node_id: Integer, location: Location, flags: Integer, expression: Node, operator_loc: Location, lparen_loc: Location, rparen_loc: Location).returns(PinnedExpressionNode) } + def pinned_expression_node(source: T.unsafe(nil), node_id: T.unsafe(nil), location: T.unsafe(nil), flags: T.unsafe(nil), expression: T.unsafe(nil), operator_loc: T.unsafe(nil), lparen_loc: T.unsafe(nil), rparen_loc: T.unsafe(nil)); end + + # Create a new PinnedVariableNode node. + sig { params(source: Source, node_id: Integer, location: Location, flags: Integer, variable: ::T.any(LocalVariableReadNode, InstanceVariableReadNode, ClassVariableReadNode, GlobalVariableReadNode, BackReferenceReadNode, NumberedReferenceReadNode, ItLocalVariableReadNode, MissingNode), operator_loc: Location).returns(PinnedVariableNode) } + def pinned_variable_node(source: T.unsafe(nil), node_id: T.unsafe(nil), location: T.unsafe(nil), flags: T.unsafe(nil), variable: T.unsafe(nil), operator_loc: T.unsafe(nil)); end + + # Create a new PostExecutionNode node. + sig { params(source: Source, node_id: Integer, location: Location, flags: Integer, statements: ::T.nilable(StatementsNode), keyword_loc: Location, opening_loc: Location, closing_loc: Location).returns(PostExecutionNode) } + def post_execution_node(source: T.unsafe(nil), node_id: T.unsafe(nil), location: T.unsafe(nil), flags: T.unsafe(nil), statements: T.unsafe(nil), keyword_loc: T.unsafe(nil), opening_loc: T.unsafe(nil), closing_loc: T.unsafe(nil)); end + + # Create a new PreExecutionNode node. + sig { params(source: Source, node_id: Integer, location: Location, flags: Integer, statements: ::T.nilable(StatementsNode), keyword_loc: Location, opening_loc: Location, closing_loc: Location).returns(PreExecutionNode) } + def pre_execution_node(source: T.unsafe(nil), node_id: T.unsafe(nil), location: T.unsafe(nil), flags: T.unsafe(nil), statements: T.unsafe(nil), keyword_loc: T.unsafe(nil), opening_loc: T.unsafe(nil), closing_loc: T.unsafe(nil)); end + + # Create a new ProgramNode node. + sig { params(source: Source, node_id: Integer, location: Location, flags: Integer, locals: T::Array[Symbol], statements: StatementsNode).returns(ProgramNode) } + def program_node(source: T.unsafe(nil), node_id: T.unsafe(nil), location: T.unsafe(nil), flags: T.unsafe(nil), locals: T.unsafe(nil), statements: T.unsafe(nil)); end + + # Create a new RangeNode node. + sig { params(source: Source, node_id: Integer, location: Location, flags: Integer, left: ::T.nilable(Node), right: ::T.nilable(Node), operator_loc: Location).returns(RangeNode) } + def range_node(source: T.unsafe(nil), node_id: T.unsafe(nil), location: T.unsafe(nil), flags: T.unsafe(nil), left: T.unsafe(nil), right: T.unsafe(nil), operator_loc: T.unsafe(nil)); end + + # Create a new RationalNode node. + sig { params(source: Source, node_id: Integer, location: Location, flags: Integer, numerator: Integer, denominator: Integer).returns(RationalNode) } + def rational_node(source: T.unsafe(nil), node_id: T.unsafe(nil), location: T.unsafe(nil), flags: T.unsafe(nil), numerator: T.unsafe(nil), denominator: T.unsafe(nil)); end + + # Create a new RedoNode node. + sig { params(source: Source, node_id: Integer, location: Location, flags: Integer).returns(RedoNode) } + def redo_node(source: T.unsafe(nil), node_id: T.unsafe(nil), location: T.unsafe(nil), flags: T.unsafe(nil)); end + + # Create a new RegularExpressionNode node. + sig { params(source: Source, node_id: Integer, location: Location, flags: Integer, opening_loc: Location, content_loc: Location, closing_loc: Location, unescaped: String).returns(RegularExpressionNode) } + def regular_expression_node(source: T.unsafe(nil), node_id: T.unsafe(nil), location: T.unsafe(nil), flags: T.unsafe(nil), opening_loc: T.unsafe(nil), content_loc: T.unsafe(nil), closing_loc: T.unsafe(nil), unescaped: T.unsafe(nil)); end + + # Create a new RequiredKeywordParameterNode node. + sig { params(source: Source, node_id: Integer, location: Location, flags: Integer, name: Symbol, name_loc: Location).returns(RequiredKeywordParameterNode) } + def required_keyword_parameter_node(source: T.unsafe(nil), node_id: T.unsafe(nil), location: T.unsafe(nil), flags: T.unsafe(nil), name: T.unsafe(nil), name_loc: T.unsafe(nil)); end + + # Create a new RequiredParameterNode node. + sig { params(source: Source, node_id: Integer, location: Location, flags: Integer, name: Symbol).returns(RequiredParameterNode) } + def required_parameter_node(source: T.unsafe(nil), node_id: T.unsafe(nil), location: T.unsafe(nil), flags: T.unsafe(nil), name: T.unsafe(nil)); end + + # Create a new RescueModifierNode node. + sig { params(source: Source, node_id: Integer, location: Location, flags: Integer, expression: Node, keyword_loc: Location, rescue_expression: Node).returns(RescueModifierNode) } + def rescue_modifier_node(source: T.unsafe(nil), node_id: T.unsafe(nil), location: T.unsafe(nil), flags: T.unsafe(nil), expression: T.unsafe(nil), keyword_loc: T.unsafe(nil), rescue_expression: T.unsafe(nil)); end + + # Create a new RescueNode node. + sig { params(source: Source, node_id: Integer, location: Location, flags: Integer, keyword_loc: Location, exceptions: T::Array[Node], operator_loc: ::T.nilable(Location), reference: ::T.nilable(::T.any(LocalVariableTargetNode, InstanceVariableTargetNode, ClassVariableTargetNode, GlobalVariableTargetNode, ConstantTargetNode, ConstantPathTargetNode, CallTargetNode, IndexTargetNode, BackReferenceReadNode, NumberedReferenceReadNode, MissingNode)), then_keyword_loc: ::T.nilable(Location), statements: ::T.nilable(StatementsNode), subsequent: ::T.nilable(RescueNode)).returns(RescueNode) } + def rescue_node(source: T.unsafe(nil), node_id: T.unsafe(nil), location: T.unsafe(nil), flags: T.unsafe(nil), keyword_loc: T.unsafe(nil), exceptions: T.unsafe(nil), operator_loc: T.unsafe(nil), reference: T.unsafe(nil), then_keyword_loc: T.unsafe(nil), statements: T.unsafe(nil), subsequent: T.unsafe(nil)); end + + # Create a new RestParameterNode node. + sig { params(source: Source, node_id: Integer, location: Location, flags: Integer, name: ::T.nilable(Symbol), name_loc: ::T.nilable(Location), operator_loc: Location).returns(RestParameterNode) } + def rest_parameter_node(source: T.unsafe(nil), node_id: T.unsafe(nil), location: T.unsafe(nil), flags: T.unsafe(nil), name: T.unsafe(nil), name_loc: T.unsafe(nil), operator_loc: T.unsafe(nil)); end + + # Create a new RetryNode node. + sig { params(source: Source, node_id: Integer, location: Location, flags: Integer).returns(RetryNode) } + def retry_node(source: T.unsafe(nil), node_id: T.unsafe(nil), location: T.unsafe(nil), flags: T.unsafe(nil)); end + + # Create a new ReturnNode node. + sig { params(source: Source, node_id: Integer, location: Location, flags: Integer, keyword_loc: Location, arguments: ::T.nilable(ArgumentsNode)).returns(ReturnNode) } + def return_node(source: T.unsafe(nil), node_id: T.unsafe(nil), location: T.unsafe(nil), flags: T.unsafe(nil), keyword_loc: T.unsafe(nil), arguments: T.unsafe(nil)); end + + # Create a new SelfNode node. + sig { params(source: Source, node_id: Integer, location: Location, flags: Integer).returns(SelfNode) } + def self_node(source: T.unsafe(nil), node_id: T.unsafe(nil), location: T.unsafe(nil), flags: T.unsafe(nil)); end + + # Create a new ShareableConstantNode node. + sig { params(source: Source, node_id: Integer, location: Location, flags: Integer, write: ::T.any(ConstantWriteNode, ConstantAndWriteNode, ConstantOrWriteNode, ConstantOperatorWriteNode, ConstantPathWriteNode, ConstantPathAndWriteNode, ConstantPathOrWriteNode, ConstantPathOperatorWriteNode)).returns(ShareableConstantNode) } + def shareable_constant_node(source: T.unsafe(nil), node_id: T.unsafe(nil), location: T.unsafe(nil), flags: T.unsafe(nil), write: T.unsafe(nil)); end + + # Create a new SingletonClassNode node. + sig { params(source: Source, node_id: Integer, location: Location, flags: Integer, locals: T::Array[Symbol], class_keyword_loc: Location, operator_loc: Location, expression: Node, body: ::T.nilable(::T.any(StatementsNode, BeginNode)), end_keyword_loc: Location).returns(SingletonClassNode) } + def singleton_class_node(source: T.unsafe(nil), node_id: T.unsafe(nil), location: T.unsafe(nil), flags: T.unsafe(nil), locals: T.unsafe(nil), class_keyword_loc: T.unsafe(nil), operator_loc: T.unsafe(nil), expression: T.unsafe(nil), body: T.unsafe(nil), end_keyword_loc: T.unsafe(nil)); end + + # Create a new SourceEncodingNode node. + sig { params(source: Source, node_id: Integer, location: Location, flags: Integer).returns(SourceEncodingNode) } + def source_encoding_node(source: T.unsafe(nil), node_id: T.unsafe(nil), location: T.unsafe(nil), flags: T.unsafe(nil)); end + + # Create a new SourceFileNode node. + sig { params(source: Source, node_id: Integer, location: Location, flags: Integer, filepath: String).returns(SourceFileNode) } + def source_file_node(source: T.unsafe(nil), node_id: T.unsafe(nil), location: T.unsafe(nil), flags: T.unsafe(nil), filepath: T.unsafe(nil)); end + + # Create a new SourceLineNode node. + sig { params(source: Source, node_id: Integer, location: Location, flags: Integer).returns(SourceLineNode) } + def source_line_node(source: T.unsafe(nil), node_id: T.unsafe(nil), location: T.unsafe(nil), flags: T.unsafe(nil)); end + + # Create a new SplatNode node. + sig { params(source: Source, node_id: Integer, location: Location, flags: Integer, operator_loc: Location, expression: ::T.nilable(Node)).returns(SplatNode) } + def splat_node(source: T.unsafe(nil), node_id: T.unsafe(nil), location: T.unsafe(nil), flags: T.unsafe(nil), operator_loc: T.unsafe(nil), expression: T.unsafe(nil)); end + + # Create a new StatementsNode node. + sig { params(source: Source, node_id: Integer, location: Location, flags: Integer, body: T::Array[Node]).returns(StatementsNode) } + def statements_node(source: T.unsafe(nil), node_id: T.unsafe(nil), location: T.unsafe(nil), flags: T.unsafe(nil), body: T.unsafe(nil)); end + + # Create a new StringNode node. + sig { params(source: Source, node_id: Integer, location: Location, flags: Integer, opening_loc: ::T.nilable(Location), content_loc: Location, closing_loc: ::T.nilable(Location), unescaped: String).returns(StringNode) } + def string_node(source: T.unsafe(nil), node_id: T.unsafe(nil), location: T.unsafe(nil), flags: T.unsafe(nil), opening_loc: T.unsafe(nil), content_loc: T.unsafe(nil), closing_loc: T.unsafe(nil), unescaped: T.unsafe(nil)); end + + # Create a new SuperNode node. + sig { params(source: Source, node_id: Integer, location: Location, flags: Integer, keyword_loc: Location, lparen_loc: ::T.nilable(Location), arguments: ::T.nilable(ArgumentsNode), rparen_loc: ::T.nilable(Location), block: ::T.nilable(::T.any(BlockNode, BlockArgumentNode))).returns(SuperNode) } + def super_node(source: T.unsafe(nil), node_id: T.unsafe(nil), location: T.unsafe(nil), flags: T.unsafe(nil), keyword_loc: T.unsafe(nil), lparen_loc: T.unsafe(nil), arguments: T.unsafe(nil), rparen_loc: T.unsafe(nil), block: T.unsafe(nil)); end + + # Create a new SymbolNode node. + sig { params(source: Source, node_id: Integer, location: Location, flags: Integer, opening_loc: ::T.nilable(Location), value_loc: ::T.nilable(Location), closing_loc: ::T.nilable(Location), unescaped: String).returns(SymbolNode) } + def symbol_node(source: T.unsafe(nil), node_id: T.unsafe(nil), location: T.unsafe(nil), flags: T.unsafe(nil), opening_loc: T.unsafe(nil), value_loc: T.unsafe(nil), closing_loc: T.unsafe(nil), unescaped: T.unsafe(nil)); end + + # Create a new TrueNode node. + sig { params(source: Source, node_id: Integer, location: Location, flags: Integer).returns(TrueNode) } + def true_node(source: T.unsafe(nil), node_id: T.unsafe(nil), location: T.unsafe(nil), flags: T.unsafe(nil)); end + + # Create a new UndefNode node. + sig { params(source: Source, node_id: Integer, location: Location, flags: Integer, names: T::Array[::T.any(SymbolNode, InterpolatedSymbolNode)], keyword_loc: Location).returns(UndefNode) } + def undef_node(source: T.unsafe(nil), node_id: T.unsafe(nil), location: T.unsafe(nil), flags: T.unsafe(nil), names: T.unsafe(nil), keyword_loc: T.unsafe(nil)); end + + # Create a new UnlessNode node. + sig { params(source: Source, node_id: Integer, location: Location, flags: Integer, keyword_loc: Location, predicate: Node, then_keyword_loc: ::T.nilable(Location), statements: ::T.nilable(StatementsNode), else_clause: ::T.nilable(ElseNode), end_keyword_loc: ::T.nilable(Location)).returns(UnlessNode) } + def unless_node(source: T.unsafe(nil), node_id: T.unsafe(nil), location: T.unsafe(nil), flags: T.unsafe(nil), keyword_loc: T.unsafe(nil), predicate: T.unsafe(nil), then_keyword_loc: T.unsafe(nil), statements: T.unsafe(nil), else_clause: T.unsafe(nil), end_keyword_loc: T.unsafe(nil)); end + + # Create a new UntilNode node. + sig { params(source: Source, node_id: Integer, location: Location, flags: Integer, keyword_loc: Location, do_keyword_loc: ::T.nilable(Location), closing_loc: ::T.nilable(Location), predicate: Node, statements: ::T.nilable(StatementsNode)).returns(UntilNode) } + def until_node(source: T.unsafe(nil), node_id: T.unsafe(nil), location: T.unsafe(nil), flags: T.unsafe(nil), keyword_loc: T.unsafe(nil), do_keyword_loc: T.unsafe(nil), closing_loc: T.unsafe(nil), predicate: T.unsafe(nil), statements: T.unsafe(nil)); end + + # Create a new WhenNode node. + sig { params(source: Source, node_id: Integer, location: Location, flags: Integer, keyword_loc: Location, conditions: T::Array[Node], then_keyword_loc: ::T.nilable(Location), statements: ::T.nilable(StatementsNode)).returns(WhenNode) } + def when_node(source: T.unsafe(nil), node_id: T.unsafe(nil), location: T.unsafe(nil), flags: T.unsafe(nil), keyword_loc: T.unsafe(nil), conditions: T.unsafe(nil), then_keyword_loc: T.unsafe(nil), statements: T.unsafe(nil)); end + + # Create a new WhileNode node. + sig { params(source: Source, node_id: Integer, location: Location, flags: Integer, keyword_loc: Location, do_keyword_loc: ::T.nilable(Location), closing_loc: ::T.nilable(Location), predicate: Node, statements: ::T.nilable(StatementsNode)).returns(WhileNode) } + def while_node(source: T.unsafe(nil), node_id: T.unsafe(nil), location: T.unsafe(nil), flags: T.unsafe(nil), keyword_loc: T.unsafe(nil), do_keyword_loc: T.unsafe(nil), closing_loc: T.unsafe(nil), predicate: T.unsafe(nil), statements: T.unsafe(nil)); end + + # Create a new XStringNode node. + sig { params(source: Source, node_id: Integer, location: Location, flags: Integer, opening_loc: Location, content_loc: Location, closing_loc: Location, unescaped: String).returns(XStringNode) } + def x_string_node(source: T.unsafe(nil), node_id: T.unsafe(nil), location: T.unsafe(nil), flags: T.unsafe(nil), opening_loc: T.unsafe(nil), content_loc: T.unsafe(nil), closing_loc: T.unsafe(nil), unescaped: T.unsafe(nil)); end + + # Create a new YieldNode node. + sig { params(source: Source, node_id: Integer, location: Location, flags: Integer, keyword_loc: Location, lparen_loc: ::T.nilable(Location), arguments: ::T.nilable(ArgumentsNode), rparen_loc: ::T.nilable(Location)).returns(YieldNode) } + def yield_node(source: T.unsafe(nil), node_id: T.unsafe(nil), location: T.unsafe(nil), flags: T.unsafe(nil), keyword_loc: T.unsafe(nil), lparen_loc: T.unsafe(nil), arguments: T.unsafe(nil), rparen_loc: T.unsafe(nil)); end + + # Retrieve the value of one of the ArgumentsNodeFlags flags. + sig { params(name: Symbol).returns(Integer) } + def arguments_node_flag(name); end + + # Retrieve the value of one of the ArrayNodeFlags flags. + sig { params(name: Symbol).returns(Integer) } + def array_node_flag(name); end + + # Retrieve the value of one of the CallNodeFlags flags. + sig { params(name: Symbol).returns(Integer) } + def call_node_flag(name); end + + # Retrieve the value of one of the EncodingFlags flags. + sig { params(name: Symbol).returns(Integer) } + def encoding_flag(name); end + + # Retrieve the value of one of the IntegerBaseFlags flags. + sig { params(name: Symbol).returns(Integer) } + def integer_base_flag(name); end + + # Retrieve the value of one of the InterpolatedStringNodeFlags flags. + sig { params(name: Symbol).returns(Integer) } + def interpolated_string_node_flag(name); end + + # Retrieve the value of one of the KeywordHashNodeFlags flags. + sig { params(name: Symbol).returns(Integer) } + def keyword_hash_node_flag(name); end + + # Retrieve the value of one of the LoopFlags flags. + sig { params(name: Symbol).returns(Integer) } + def loop_flag(name); end + + # Retrieve the value of one of the ParameterFlags flags. + sig { params(name: Symbol).returns(Integer) } + def parameter_flag(name); end + + # Retrieve the value of one of the ParenthesesNodeFlags flags. + sig { params(name: Symbol).returns(Integer) } + def parentheses_node_flag(name); end + + # Retrieve the value of one of the RangeFlags flags. + sig { params(name: Symbol).returns(Integer) } + def range_flag(name); end + + # Retrieve the value of one of the RegularExpressionFlags flags. + sig { params(name: Symbol).returns(Integer) } + def regular_expression_flag(name); end + + # Retrieve the value of one of the ShareableConstantNodeFlags flags. + sig { params(name: Symbol).returns(Integer) } + def shareable_constant_node_flag(name); end + + # Retrieve the value of one of the StringFlags flags. + sig { params(name: Symbol).returns(Integer) } + def string_flag(name); end + + # Retrieve the value of one of the SymbolFlags flags. + sig { params(name: Symbol).returns(Integer) } + def symbol_flag(name); end + + # The default source object that gets attached to nodes and locations if no + # source is specified. + sig { returns(Source) } + private def default_source; end + + # The default location object that gets attached to nodes if no location is + # specified, which uses the given source. + sig { returns(Location) } + private def default_location; end + + # The default node that gets attached to nodes if no node is specified for a + # required node field. + sig { params(source: Source, location: Location).returns(Node) } + private def default_node(source, location); end + + # Build the newline byte offset array for the given source string. + sig { params(source: String).returns(T::Array[Integer]) } + private def build_offsets(source); end + end +end diff --git a/rbi/generated/prism/inspect_visitor.rbi b/rbi/generated/prism/inspect_visitor.rbi new file mode 100644 index 0000000000..449a531c38 --- /dev/null +++ b/rbi/generated/prism/inspect_visitor.rbi @@ -0,0 +1,503 @@ +# typed: true + +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 + sig { returns(String) } + attr_reader :value + + sig { params(value: String).void } + def initialize(value); end + end + + # The current prefix string. + sig { returns(String) } + attr_reader :indent + + # The list of commands that we need to execute in order to compose the + # final string. + sig { returns(T::Array[[::T.any(String, Node, Replace), String]]) } + attr_reader :commands + + sig { params(indent: String).void } + def initialize(indent = T.unsafe(nil)); end + + # Compose an inspect string for the given node. + sig { params(node: Node).returns(String) } + def self.compose(node); end + + # Compose the final string. + sig { returns(String) } + def compose; end + + sig { params(node: AliasGlobalVariableNode).void } + def visit_alias_global_variable_node(node); end + + sig { params(node: AliasMethodNode).void } + def visit_alias_method_node(node); end + + sig { params(node: AlternationPatternNode).void } + def visit_alternation_pattern_node(node); end + + sig { params(node: AndNode).void } + def visit_and_node(node); end + + sig { params(node: ArgumentsNode).void } + def visit_arguments_node(node); end + + sig { params(node: ArrayNode).void } + def visit_array_node(node); end + + sig { params(node: ArrayPatternNode).void } + def visit_array_pattern_node(node); end + + sig { params(node: AssocNode).void } + def visit_assoc_node(node); end + + sig { params(node: AssocSplatNode).void } + def visit_assoc_splat_node(node); end + + sig { params(node: BackReferenceReadNode).void } + def visit_back_reference_read_node(node); end + + sig { params(node: BeginNode).void } + def visit_begin_node(node); end + + sig { params(node: BlockArgumentNode).void } + def visit_block_argument_node(node); end + + sig { params(node: BlockLocalVariableNode).void } + def visit_block_local_variable_node(node); end + + sig { params(node: BlockNode).void } + def visit_block_node(node); end + + sig { params(node: BlockParameterNode).void } + def visit_block_parameter_node(node); end + + sig { params(node: BlockParametersNode).void } + def visit_block_parameters_node(node); end + + sig { params(node: BreakNode).void } + def visit_break_node(node); end + + sig { params(node: CallAndWriteNode).void } + def visit_call_and_write_node(node); end + + sig { params(node: CallNode).void } + def visit_call_node(node); end + + sig { params(node: CallOperatorWriteNode).void } + def visit_call_operator_write_node(node); end + + sig { params(node: CallOrWriteNode).void } + def visit_call_or_write_node(node); end + + sig { params(node: CallTargetNode).void } + def visit_call_target_node(node); end + + sig { params(node: CapturePatternNode).void } + def visit_capture_pattern_node(node); end + + sig { params(node: CaseMatchNode).void } + def visit_case_match_node(node); end + + sig { params(node: CaseNode).void } + def visit_case_node(node); end + + sig { params(node: ClassNode).void } + def visit_class_node(node); end + + sig { params(node: ClassVariableAndWriteNode).void } + def visit_class_variable_and_write_node(node); end + + sig { params(node: ClassVariableOperatorWriteNode).void } + def visit_class_variable_operator_write_node(node); end + + sig { params(node: ClassVariableOrWriteNode).void } + def visit_class_variable_or_write_node(node); end + + sig { params(node: ClassVariableReadNode).void } + def visit_class_variable_read_node(node); end + + sig { params(node: ClassVariableTargetNode).void } + def visit_class_variable_target_node(node); end + + sig { params(node: ClassVariableWriteNode).void } + def visit_class_variable_write_node(node); end + + sig { params(node: ConstantAndWriteNode).void } + def visit_constant_and_write_node(node); end + + sig { params(node: ConstantOperatorWriteNode).void } + def visit_constant_operator_write_node(node); end + + sig { params(node: ConstantOrWriteNode).void } + def visit_constant_or_write_node(node); end + + sig { params(node: ConstantPathAndWriteNode).void } + def visit_constant_path_and_write_node(node); end + + sig { params(node: ConstantPathNode).void } + def visit_constant_path_node(node); end + + sig { params(node: ConstantPathOperatorWriteNode).void } + def visit_constant_path_operator_write_node(node); end + + sig { params(node: ConstantPathOrWriteNode).void } + def visit_constant_path_or_write_node(node); end + + sig { params(node: ConstantPathTargetNode).void } + def visit_constant_path_target_node(node); end + + sig { params(node: ConstantPathWriteNode).void } + def visit_constant_path_write_node(node); end + + sig { params(node: ConstantReadNode).void } + def visit_constant_read_node(node); end + + sig { params(node: ConstantTargetNode).void } + def visit_constant_target_node(node); end + + sig { params(node: ConstantWriteNode).void } + def visit_constant_write_node(node); end + + sig { params(node: DefNode).void } + def visit_def_node(node); end + + sig { params(node: DefinedNode).void } + def visit_defined_node(node); end + + sig { params(node: ElseNode).void } + def visit_else_node(node); end + + sig { params(node: EmbeddedStatementsNode).void } + def visit_embedded_statements_node(node); end + + sig { params(node: EmbeddedVariableNode).void } + def visit_embedded_variable_node(node); end + + sig { params(node: EnsureNode).void } + def visit_ensure_node(node); end + + sig { params(node: FalseNode).void } + def visit_false_node(node); end + + sig { params(node: FindPatternNode).void } + def visit_find_pattern_node(node); end + + sig { params(node: FlipFlopNode).void } + def visit_flip_flop_node(node); end + + sig { params(node: FloatNode).void } + def visit_float_node(node); end + + sig { params(node: ForNode).void } + def visit_for_node(node); end + + sig { params(node: ForwardingArgumentsNode).void } + def visit_forwarding_arguments_node(node); end + + sig { params(node: ForwardingParameterNode).void } + def visit_forwarding_parameter_node(node); end + + sig { params(node: ForwardingSuperNode).void } + def visit_forwarding_super_node(node); end + + sig { params(node: GlobalVariableAndWriteNode).void } + def visit_global_variable_and_write_node(node); end + + sig { params(node: GlobalVariableOperatorWriteNode).void } + def visit_global_variable_operator_write_node(node); end + + sig { params(node: GlobalVariableOrWriteNode).void } + def visit_global_variable_or_write_node(node); end + + sig { params(node: GlobalVariableReadNode).void } + def visit_global_variable_read_node(node); end + + sig { params(node: GlobalVariableTargetNode).void } + def visit_global_variable_target_node(node); end + + sig { params(node: GlobalVariableWriteNode).void } + def visit_global_variable_write_node(node); end + + sig { params(node: HashNode).void } + def visit_hash_node(node); end + + sig { params(node: HashPatternNode).void } + def visit_hash_pattern_node(node); end + + sig { params(node: IfNode).void } + def visit_if_node(node); end + + sig { params(node: ImaginaryNode).void } + def visit_imaginary_node(node); end + + sig { params(node: ImplicitNode).void } + def visit_implicit_node(node); end + + sig { params(node: ImplicitRestNode).void } + def visit_implicit_rest_node(node); end + + sig { params(node: InNode).void } + def visit_in_node(node); end + + sig { params(node: IndexAndWriteNode).void } + def visit_index_and_write_node(node); end + + sig { params(node: IndexOperatorWriteNode).void } + def visit_index_operator_write_node(node); end + + sig { params(node: IndexOrWriteNode).void } + def visit_index_or_write_node(node); end + + sig { params(node: IndexTargetNode).void } + def visit_index_target_node(node); end + + sig { params(node: InstanceVariableAndWriteNode).void } + def visit_instance_variable_and_write_node(node); end + + sig { params(node: InstanceVariableOperatorWriteNode).void } + def visit_instance_variable_operator_write_node(node); end + + sig { params(node: InstanceVariableOrWriteNode).void } + def visit_instance_variable_or_write_node(node); end + + sig { params(node: InstanceVariableReadNode).void } + def visit_instance_variable_read_node(node); end + + sig { params(node: InstanceVariableTargetNode).void } + def visit_instance_variable_target_node(node); end + + sig { params(node: InstanceVariableWriteNode).void } + def visit_instance_variable_write_node(node); end + + sig { params(node: IntegerNode).void } + def visit_integer_node(node); end + + sig { params(node: InterpolatedMatchLastLineNode).void } + def visit_interpolated_match_last_line_node(node); end + + sig { params(node: InterpolatedRegularExpressionNode).void } + def visit_interpolated_regular_expression_node(node); end + + sig { params(node: InterpolatedStringNode).void } + def visit_interpolated_string_node(node); end + + sig { params(node: InterpolatedSymbolNode).void } + def visit_interpolated_symbol_node(node); end + + sig { params(node: InterpolatedXStringNode).void } + def visit_interpolated_x_string_node(node); end + + sig { params(node: ItLocalVariableReadNode).void } + def visit_it_local_variable_read_node(node); end + + sig { params(node: ItParametersNode).void } + def visit_it_parameters_node(node); end + + sig { params(node: KeywordHashNode).void } + def visit_keyword_hash_node(node); end + + sig { params(node: KeywordRestParameterNode).void } + def visit_keyword_rest_parameter_node(node); end + + sig { params(node: LambdaNode).void } + def visit_lambda_node(node); end + + sig { params(node: LocalVariableAndWriteNode).void } + def visit_local_variable_and_write_node(node); end + + sig { params(node: LocalVariableOperatorWriteNode).void } + def visit_local_variable_operator_write_node(node); end + + sig { params(node: LocalVariableOrWriteNode).void } + def visit_local_variable_or_write_node(node); end + + sig { params(node: LocalVariableReadNode).void } + def visit_local_variable_read_node(node); end + + sig { params(node: LocalVariableTargetNode).void } + def visit_local_variable_target_node(node); end + + sig { params(node: LocalVariableWriteNode).void } + def visit_local_variable_write_node(node); end + + sig { params(node: MatchLastLineNode).void } + def visit_match_last_line_node(node); end + + sig { params(node: MatchPredicateNode).void } + def visit_match_predicate_node(node); end + + sig { params(node: MatchRequiredNode).void } + def visit_match_required_node(node); end + + sig { params(node: MatchWriteNode).void } + def visit_match_write_node(node); end + + sig { params(node: MissingNode).void } + def visit_missing_node(node); end + + sig { params(node: ModuleNode).void } + def visit_module_node(node); end + + sig { params(node: MultiTargetNode).void } + def visit_multi_target_node(node); end + + sig { params(node: MultiWriteNode).void } + def visit_multi_write_node(node); end + + sig { params(node: NextNode).void } + def visit_next_node(node); end + + sig { params(node: NilNode).void } + def visit_nil_node(node); end + + sig { params(node: NoBlockParameterNode).void } + def visit_no_block_parameter_node(node); end + + sig { params(node: NoKeywordsParameterNode).void } + def visit_no_keywords_parameter_node(node); end + + sig { params(node: NumberedParametersNode).void } + def visit_numbered_parameters_node(node); end + + sig { params(node: NumberedReferenceReadNode).void } + def visit_numbered_reference_read_node(node); end + + sig { params(node: OptionalKeywordParameterNode).void } + def visit_optional_keyword_parameter_node(node); end + + sig { params(node: OptionalParameterNode).void } + def visit_optional_parameter_node(node); end + + sig { params(node: OrNode).void } + def visit_or_node(node); end + + sig { params(node: ParametersNode).void } + def visit_parameters_node(node); end + + sig { params(node: ParenthesesNode).void } + def visit_parentheses_node(node); end + + sig { params(node: PinnedExpressionNode).void } + def visit_pinned_expression_node(node); end + + sig { params(node: PinnedVariableNode).void } + def visit_pinned_variable_node(node); end + + sig { params(node: PostExecutionNode).void } + def visit_post_execution_node(node); end + + sig { params(node: PreExecutionNode).void } + def visit_pre_execution_node(node); end + + sig { params(node: ProgramNode).void } + def visit_program_node(node); end + + sig { params(node: RangeNode).void } + def visit_range_node(node); end + + sig { params(node: RationalNode).void } + def visit_rational_node(node); end + + sig { params(node: RedoNode).void } + def visit_redo_node(node); end + + sig { params(node: RegularExpressionNode).void } + def visit_regular_expression_node(node); end + + sig { params(node: RequiredKeywordParameterNode).void } + def visit_required_keyword_parameter_node(node); end + + sig { params(node: RequiredParameterNode).void } + def visit_required_parameter_node(node); end + + sig { params(node: RescueModifierNode).void } + def visit_rescue_modifier_node(node); end + + sig { params(node: RescueNode).void } + def visit_rescue_node(node); end + + sig { params(node: RestParameterNode).void } + def visit_rest_parameter_node(node); end + + sig { params(node: RetryNode).void } + def visit_retry_node(node); end + + sig { params(node: ReturnNode).void } + def visit_return_node(node); end + + sig { params(node: SelfNode).void } + def visit_self_node(node); end + + sig { params(node: ShareableConstantNode).void } + def visit_shareable_constant_node(node); end + + sig { params(node: SingletonClassNode).void } + def visit_singleton_class_node(node); end + + sig { params(node: SourceEncodingNode).void } + def visit_source_encoding_node(node); end + + sig { params(node: SourceFileNode).void } + def visit_source_file_node(node); end + + sig { params(node: SourceLineNode).void } + def visit_source_line_node(node); end + + sig { params(node: SplatNode).void } + def visit_splat_node(node); end + + sig { params(node: StatementsNode).void } + def visit_statements_node(node); end + + sig { params(node: StringNode).void } + def visit_string_node(node); end + + sig { params(node: SuperNode).void } + def visit_super_node(node); end + + sig { params(node: SymbolNode).void } + def visit_symbol_node(node); end + + sig { params(node: TrueNode).void } + def visit_true_node(node); end + + sig { params(node: UndefNode).void } + def visit_undef_node(node); end + + sig { params(node: UnlessNode).void } + def visit_unless_node(node); end + + sig { params(node: UntilNode).void } + def visit_until_node(node); end + + sig { params(node: WhenNode).void } + def visit_when_node(node); end + + sig { params(node: WhileNode).void } + def visit_while_node(node); end + + sig { params(node: XStringNode).void } + def visit_x_string_node(node); end + + sig { params(node: YieldNode).void } + def visit_yield_node(node); end + + # Compose a header for the given node. + sig { params(name: String, node: Node).returns(String) } + private def inspect_node(name, node); end + + # Compose a string representing the given inner location field. + sig { params(location: ::T.nilable(Location)).returns(String) } + private def inspect_location(location); end + end +end diff --git a/rbi/generated/prism/lex_compat.rbi b/rbi/generated/prism/lex_compat.rbi new file mode 100644 index 0000000000..ca479b7225 --- /dev/null +++ b/rbi/generated/prism/lex_compat.rbi @@ -0,0 +1,155 @@ +# typed: true + +module Prism + module Translation + class Ripper + EXPR_NONE = T.let(nil, Integer) + EXPR_BEG = T.let(nil, Integer) + EXPR_MID = T.let(nil, Integer) + EXPR_END = T.let(nil, Integer) + EXPR_CLASS = T.let(nil, Integer) + EXPR_VALUE = T.let(nil, Integer) + EXPR_ARG = T.let(nil, Integer) + EXPR_CMDARG = T.let(nil, Integer) + EXPR_ENDARG = T.let(nil, Integer) + EXPR_ENDFN = T.let(nil, Integer) + + class Lexer < Ripper + class State + sig { params(value: Integer).returns(State) } + def self.[](value); end + end + end + end + end + + # 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 + # 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. + sig { returns(T::Array[[[Integer, Integer], Symbol, String, ::T.untyped]]) } + attr_reader :value + + # Create a new lex compat result object with the given values. + sig { params(value: T::Array[[[Integer, Integer], Symbol, String, ::T.untyped]], comments: T::Array[Comment], magic_comments: T::Array[MagicComment], data_loc: ::T.nilable(Location), errors: T::Array[ParseError], warnings: T::Array[ParseWarning], continuable: T::Boolean, source: Source).void } + def initialize(value, comments, magic_comments, data_loc, errors, warnings, continuable, source); end + + # Implement the hash pattern matching interface for Result. + sig { params(keys: ::T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, ::T.untyped]) } + def deconstruct_keys(keys); 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 = T.let(nil, ::T.untyped) + + # 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 + # 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 + sig { returns(T::Array[[[Integer, Integer], Symbol, String, ::T.untyped]]) } + attr_reader :tokens + + sig { void } + def initialize; end + + sig { params(token: [[Integer, Integer], Symbol, String, ::T.untyped]).void } + def <<(token); end + + sig { returns(T::Array[[[Integer, Integer], Symbol, String, ::T.untyped]]) } + def to_a; 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 + sig { returns(T::Boolean) } + attr_reader :split + + sig { returns(T::Array[[[Integer, Integer], Symbol, String, ::T.untyped]]) } + attr_reader :tokens + + sig { params(split: T::Boolean).void } + def initialize(split); end + + sig { params(token: [[Integer, Integer], Symbol, String, ::T.untyped]).void } + def <<(token); end + + sig { returns(T::Array[[[Integer, Integer], Symbol, String, ::T.untyped]]) } + def to_a; 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 + TAB_WIDTH = T.let(nil, Integer) + + sig { returns(T::Array[[[Integer, Integer], Symbol, String, ::T.untyped]]) } + attr_reader :tokens + + sig { returns(T::Boolean) } + attr_reader :dedent_next + + sig { returns(::T.nilable(Integer)) } + attr_reader :dedent + + sig { returns(Integer) } + attr_reader :embexpr_balance + + sig { void } + def initialize; 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. + # + sig { params(token: [[Integer, Integer], Symbol, String, ::T.untyped]).void } + def <<(token); end + + sig { returns(T::Array[[[Integer, Integer], Symbol, String, ::T.untyped]]) } + def to_a; end + end + + # Here we will split between the two types of heredocs and return the + # object that will store their tokens. + sig { params(opening: [[Integer, Integer], Symbol, String, ::T.untyped]).returns(::T.any(PlainHeredoc, DashHeredoc, DedentingHeredoc)) } + def self.build(opening); end + end + + # 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 = T.let(nil, ::T.untyped) + + sig { returns(T::Hash[Symbol, ::T.untyped]) } + attr_reader :options + + sig { params(source: String, options: ::T.untyped).void } + def initialize(source, **options); end + + sig { returns(Result) } + def result; end + + sig { params(tokens: T::Array[[[Integer, Integer], Symbol, String, ::T.untyped]], source: Source, data_loc: ::T.nilable(Location), bom: T::Boolean, eof_token: ::T.nilable(Token)).returns(T::Array[[[Integer, Integer], Symbol, String, ::T.untyped]]) } + private def post_process_tokens(tokens, source, data_loc, bom, eof_token); end + end +end diff --git a/rbi/generated/prism/mutation_compiler.rbi b/rbi/generated/prism/mutation_compiler.rbi new file mode 100644 index 0000000000..21be124fed --- /dev/null +++ b/rbi/generated/prism/mutation_compiler.rbi @@ -0,0 +1,464 @@ +# typed: true + +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 + sig { params(arg0: AliasGlobalVariableNode).returns(::T.nilable(Node)) } + def visit_alias_global_variable_node(arg0); end + + sig { params(arg0: AliasMethodNode).returns(::T.nilable(Node)) } + def visit_alias_method_node(arg0); end + + sig { params(arg0: AlternationPatternNode).returns(::T.nilable(Node)) } + def visit_alternation_pattern_node(arg0); end + + sig { params(arg0: AndNode).returns(::T.nilable(Node)) } + def visit_and_node(arg0); end + + sig { params(arg0: ArgumentsNode).returns(::T.nilable(Node)) } + def visit_arguments_node(arg0); end + + sig { params(arg0: ArrayNode).returns(::T.nilable(Node)) } + def visit_array_node(arg0); end + + sig { params(arg0: ArrayPatternNode).returns(::T.nilable(Node)) } + def visit_array_pattern_node(arg0); end + + sig { params(arg0: AssocNode).returns(::T.nilable(Node)) } + def visit_assoc_node(arg0); end + + sig { params(arg0: AssocSplatNode).returns(::T.nilable(Node)) } + def visit_assoc_splat_node(arg0); end + + sig { params(arg0: BackReferenceReadNode).returns(::T.nilable(Node)) } + def visit_back_reference_read_node(arg0); end + + sig { params(arg0: BeginNode).returns(::T.nilable(Node)) } + def visit_begin_node(arg0); end + + sig { params(arg0: BlockArgumentNode).returns(::T.nilable(Node)) } + def visit_block_argument_node(arg0); end + + sig { params(arg0: BlockLocalVariableNode).returns(::T.nilable(Node)) } + def visit_block_local_variable_node(arg0); end + + sig { params(arg0: BlockNode).returns(::T.nilable(Node)) } + def visit_block_node(arg0); end + + sig { params(arg0: BlockParameterNode).returns(::T.nilable(Node)) } + def visit_block_parameter_node(arg0); end + + sig { params(arg0: BlockParametersNode).returns(::T.nilable(Node)) } + def visit_block_parameters_node(arg0); end + + sig { params(arg0: BreakNode).returns(::T.nilable(Node)) } + def visit_break_node(arg0); end + + sig { params(arg0: CallAndWriteNode).returns(::T.nilable(Node)) } + def visit_call_and_write_node(arg0); end + + sig { params(arg0: CallNode).returns(::T.nilable(Node)) } + def visit_call_node(arg0); end + + sig { params(arg0: CallOperatorWriteNode).returns(::T.nilable(Node)) } + def visit_call_operator_write_node(arg0); end + + sig { params(arg0: CallOrWriteNode).returns(::T.nilable(Node)) } + def visit_call_or_write_node(arg0); end + + sig { params(arg0: CallTargetNode).returns(::T.nilable(Node)) } + def visit_call_target_node(arg0); end + + sig { params(arg0: CapturePatternNode).returns(::T.nilable(Node)) } + def visit_capture_pattern_node(arg0); end + + sig { params(arg0: CaseMatchNode).returns(::T.nilable(Node)) } + def visit_case_match_node(arg0); end + + sig { params(arg0: CaseNode).returns(::T.nilable(Node)) } + def visit_case_node(arg0); end + + sig { params(arg0: ClassNode).returns(::T.nilable(Node)) } + def visit_class_node(arg0); end + + sig { params(arg0: ClassVariableAndWriteNode).returns(::T.nilable(Node)) } + def visit_class_variable_and_write_node(arg0); end + + sig { params(arg0: ClassVariableOperatorWriteNode).returns(::T.nilable(Node)) } + def visit_class_variable_operator_write_node(arg0); end + + sig { params(arg0: ClassVariableOrWriteNode).returns(::T.nilable(Node)) } + def visit_class_variable_or_write_node(arg0); end + + sig { params(arg0: ClassVariableReadNode).returns(::T.nilable(Node)) } + def visit_class_variable_read_node(arg0); end + + sig { params(arg0: ClassVariableTargetNode).returns(::T.nilable(Node)) } + def visit_class_variable_target_node(arg0); end + + sig { params(arg0: ClassVariableWriteNode).returns(::T.nilable(Node)) } + def visit_class_variable_write_node(arg0); end + + sig { params(arg0: ConstantAndWriteNode).returns(::T.nilable(Node)) } + def visit_constant_and_write_node(arg0); end + + sig { params(arg0: ConstantOperatorWriteNode).returns(::T.nilable(Node)) } + def visit_constant_operator_write_node(arg0); end + + sig { params(arg0: ConstantOrWriteNode).returns(::T.nilable(Node)) } + def visit_constant_or_write_node(arg0); end + + sig { params(arg0: ConstantPathAndWriteNode).returns(::T.nilable(Node)) } + def visit_constant_path_and_write_node(arg0); end + + sig { params(arg0: ConstantPathNode).returns(::T.nilable(Node)) } + def visit_constant_path_node(arg0); end + + sig { params(arg0: ConstantPathOperatorWriteNode).returns(::T.nilable(Node)) } + def visit_constant_path_operator_write_node(arg0); end + + sig { params(arg0: ConstantPathOrWriteNode).returns(::T.nilable(Node)) } + def visit_constant_path_or_write_node(arg0); end + + sig { params(arg0: ConstantPathTargetNode).returns(::T.nilable(Node)) } + def visit_constant_path_target_node(arg0); end + + sig { params(arg0: ConstantPathWriteNode).returns(::T.nilable(Node)) } + def visit_constant_path_write_node(arg0); end + + sig { params(arg0: ConstantReadNode).returns(::T.nilable(Node)) } + def visit_constant_read_node(arg0); end + + sig { params(arg0: ConstantTargetNode).returns(::T.nilable(Node)) } + def visit_constant_target_node(arg0); end + + sig { params(arg0: ConstantWriteNode).returns(::T.nilable(Node)) } + def visit_constant_write_node(arg0); end + + sig { params(arg0: DefNode).returns(::T.nilable(Node)) } + def visit_def_node(arg0); end + + sig { params(arg0: DefinedNode).returns(::T.nilable(Node)) } + def visit_defined_node(arg0); end + + sig { params(arg0: ElseNode).returns(::T.nilable(Node)) } + def visit_else_node(arg0); end + + sig { params(arg0: EmbeddedStatementsNode).returns(::T.nilable(Node)) } + def visit_embedded_statements_node(arg0); end + + sig { params(arg0: EmbeddedVariableNode).returns(::T.nilable(Node)) } + def visit_embedded_variable_node(arg0); end + + sig { params(arg0: EnsureNode).returns(::T.nilable(Node)) } + def visit_ensure_node(arg0); end + + sig { params(arg0: FalseNode).returns(::T.nilable(Node)) } + def visit_false_node(arg0); end + + sig { params(arg0: FindPatternNode).returns(::T.nilable(Node)) } + def visit_find_pattern_node(arg0); end + + sig { params(arg0: FlipFlopNode).returns(::T.nilable(Node)) } + def visit_flip_flop_node(arg0); end + + sig { params(arg0: FloatNode).returns(::T.nilable(Node)) } + def visit_float_node(arg0); end + + sig { params(arg0: ForNode).returns(::T.nilable(Node)) } + def visit_for_node(arg0); end + + sig { params(arg0: ForwardingArgumentsNode).returns(::T.nilable(Node)) } + def visit_forwarding_arguments_node(arg0); end + + sig { params(arg0: ForwardingParameterNode).returns(::T.nilable(Node)) } + def visit_forwarding_parameter_node(arg0); end + + sig { params(arg0: ForwardingSuperNode).returns(::T.nilable(Node)) } + def visit_forwarding_super_node(arg0); end + + sig { params(arg0: GlobalVariableAndWriteNode).returns(::T.nilable(Node)) } + def visit_global_variable_and_write_node(arg0); end + + sig { params(arg0: GlobalVariableOperatorWriteNode).returns(::T.nilable(Node)) } + def visit_global_variable_operator_write_node(arg0); end + + sig { params(arg0: GlobalVariableOrWriteNode).returns(::T.nilable(Node)) } + def visit_global_variable_or_write_node(arg0); end + + sig { params(arg0: GlobalVariableReadNode).returns(::T.nilable(Node)) } + def visit_global_variable_read_node(arg0); end + + sig { params(arg0: GlobalVariableTargetNode).returns(::T.nilable(Node)) } + def visit_global_variable_target_node(arg0); end + + sig { params(arg0: GlobalVariableWriteNode).returns(::T.nilable(Node)) } + def visit_global_variable_write_node(arg0); end + + sig { params(arg0: HashNode).returns(::T.nilable(Node)) } + def visit_hash_node(arg0); end + + sig { params(arg0: HashPatternNode).returns(::T.nilable(Node)) } + def visit_hash_pattern_node(arg0); end + + sig { params(arg0: IfNode).returns(::T.nilable(Node)) } + def visit_if_node(arg0); end + + sig { params(arg0: ImaginaryNode).returns(::T.nilable(Node)) } + def visit_imaginary_node(arg0); end + + sig { params(arg0: ImplicitNode).returns(::T.nilable(Node)) } + def visit_implicit_node(arg0); end + + sig { params(arg0: ImplicitRestNode).returns(::T.nilable(Node)) } + def visit_implicit_rest_node(arg0); end + + sig { params(arg0: InNode).returns(::T.nilable(Node)) } + def visit_in_node(arg0); end + + sig { params(arg0: IndexAndWriteNode).returns(::T.nilable(Node)) } + def visit_index_and_write_node(arg0); end + + sig { params(arg0: IndexOperatorWriteNode).returns(::T.nilable(Node)) } + def visit_index_operator_write_node(arg0); end + + sig { params(arg0: IndexOrWriteNode).returns(::T.nilable(Node)) } + def visit_index_or_write_node(arg0); end + + sig { params(arg0: IndexTargetNode).returns(::T.nilable(Node)) } + def visit_index_target_node(arg0); end + + sig { params(arg0: InstanceVariableAndWriteNode).returns(::T.nilable(Node)) } + def visit_instance_variable_and_write_node(arg0); end + + sig { params(arg0: InstanceVariableOperatorWriteNode).returns(::T.nilable(Node)) } + def visit_instance_variable_operator_write_node(arg0); end + + sig { params(arg0: InstanceVariableOrWriteNode).returns(::T.nilable(Node)) } + def visit_instance_variable_or_write_node(arg0); end + + sig { params(arg0: InstanceVariableReadNode).returns(::T.nilable(Node)) } + def visit_instance_variable_read_node(arg0); end + + sig { params(arg0: InstanceVariableTargetNode).returns(::T.nilable(Node)) } + def visit_instance_variable_target_node(arg0); end + + sig { params(arg0: InstanceVariableWriteNode).returns(::T.nilable(Node)) } + def visit_instance_variable_write_node(arg0); end + + sig { params(arg0: IntegerNode).returns(::T.nilable(Node)) } + def visit_integer_node(arg0); end + + sig { params(arg0: InterpolatedMatchLastLineNode).returns(::T.nilable(Node)) } + def visit_interpolated_match_last_line_node(arg0); end + + sig { params(arg0: InterpolatedRegularExpressionNode).returns(::T.nilable(Node)) } + def visit_interpolated_regular_expression_node(arg0); end + + sig { params(arg0: InterpolatedStringNode).returns(::T.nilable(Node)) } + def visit_interpolated_string_node(arg0); end + + sig { params(arg0: InterpolatedSymbolNode).returns(::T.nilable(Node)) } + def visit_interpolated_symbol_node(arg0); end + + sig { params(arg0: InterpolatedXStringNode).returns(::T.nilable(Node)) } + def visit_interpolated_x_string_node(arg0); end + + sig { params(arg0: ItLocalVariableReadNode).returns(::T.nilable(Node)) } + def visit_it_local_variable_read_node(arg0); end + + sig { params(arg0: ItParametersNode).returns(::T.nilable(Node)) } + def visit_it_parameters_node(arg0); end + + sig { params(arg0: KeywordHashNode).returns(::T.nilable(Node)) } + def visit_keyword_hash_node(arg0); end + + sig { params(arg0: KeywordRestParameterNode).returns(::T.nilable(Node)) } + def visit_keyword_rest_parameter_node(arg0); end + + sig { params(arg0: LambdaNode).returns(::T.nilable(Node)) } + def visit_lambda_node(arg0); end + + sig { params(arg0: LocalVariableAndWriteNode).returns(::T.nilable(Node)) } + def visit_local_variable_and_write_node(arg0); end + + sig { params(arg0: LocalVariableOperatorWriteNode).returns(::T.nilable(Node)) } + def visit_local_variable_operator_write_node(arg0); end + + sig { params(arg0: LocalVariableOrWriteNode).returns(::T.nilable(Node)) } + def visit_local_variable_or_write_node(arg0); end + + sig { params(arg0: LocalVariableReadNode).returns(::T.nilable(Node)) } + def visit_local_variable_read_node(arg0); end + + sig { params(arg0: LocalVariableTargetNode).returns(::T.nilable(Node)) } + def visit_local_variable_target_node(arg0); end + + sig { params(arg0: LocalVariableWriteNode).returns(::T.nilable(Node)) } + def visit_local_variable_write_node(arg0); end + + sig { params(arg0: MatchLastLineNode).returns(::T.nilable(Node)) } + def visit_match_last_line_node(arg0); end + + sig { params(arg0: MatchPredicateNode).returns(::T.nilable(Node)) } + def visit_match_predicate_node(arg0); end + + sig { params(arg0: MatchRequiredNode).returns(::T.nilable(Node)) } + def visit_match_required_node(arg0); end + + sig { params(arg0: MatchWriteNode).returns(::T.nilable(Node)) } + def visit_match_write_node(arg0); end + + sig { params(arg0: MissingNode).returns(::T.nilable(Node)) } + def visit_missing_node(arg0); end + + sig { params(arg0: ModuleNode).returns(::T.nilable(Node)) } + def visit_module_node(arg0); end + + sig { params(arg0: MultiTargetNode).returns(::T.nilable(Node)) } + def visit_multi_target_node(arg0); end + + sig { params(arg0: MultiWriteNode).returns(::T.nilable(Node)) } + def visit_multi_write_node(arg0); end + + sig { params(arg0: NextNode).returns(::T.nilable(Node)) } + def visit_next_node(arg0); end + + sig { params(arg0: NilNode).returns(::T.nilable(Node)) } + def visit_nil_node(arg0); end + + sig { params(arg0: NoBlockParameterNode).returns(::T.nilable(Node)) } + def visit_no_block_parameter_node(arg0); end + + sig { params(arg0: NoKeywordsParameterNode).returns(::T.nilable(Node)) } + def visit_no_keywords_parameter_node(arg0); end + + sig { params(arg0: NumberedParametersNode).returns(::T.nilable(Node)) } + def visit_numbered_parameters_node(arg0); end + + sig { params(arg0: NumberedReferenceReadNode).returns(::T.nilable(Node)) } + def visit_numbered_reference_read_node(arg0); end + + sig { params(arg0: OptionalKeywordParameterNode).returns(::T.nilable(Node)) } + def visit_optional_keyword_parameter_node(arg0); end + + sig { params(arg0: OptionalParameterNode).returns(::T.nilable(Node)) } + def visit_optional_parameter_node(arg0); end + + sig { params(arg0: OrNode).returns(::T.nilable(Node)) } + def visit_or_node(arg0); end + + sig { params(arg0: ParametersNode).returns(::T.nilable(Node)) } + def visit_parameters_node(arg0); end + + sig { params(arg0: ParenthesesNode).returns(::T.nilable(Node)) } + def visit_parentheses_node(arg0); end + + sig { params(arg0: PinnedExpressionNode).returns(::T.nilable(Node)) } + def visit_pinned_expression_node(arg0); end + + sig { params(arg0: PinnedVariableNode).returns(::T.nilable(Node)) } + def visit_pinned_variable_node(arg0); end + + sig { params(arg0: PostExecutionNode).returns(::T.nilable(Node)) } + def visit_post_execution_node(arg0); end + + sig { params(arg0: PreExecutionNode).returns(::T.nilable(Node)) } + def visit_pre_execution_node(arg0); end + + sig { params(arg0: ProgramNode).returns(::T.nilable(Node)) } + def visit_program_node(arg0); end + + sig { params(arg0: RangeNode).returns(::T.nilable(Node)) } + def visit_range_node(arg0); end + + sig { params(arg0: RationalNode).returns(::T.nilable(Node)) } + def visit_rational_node(arg0); end + + sig { params(arg0: RedoNode).returns(::T.nilable(Node)) } + def visit_redo_node(arg0); end + + sig { params(arg0: RegularExpressionNode).returns(::T.nilable(Node)) } + def visit_regular_expression_node(arg0); end + + sig { params(arg0: RequiredKeywordParameterNode).returns(::T.nilable(Node)) } + def visit_required_keyword_parameter_node(arg0); end + + sig { params(arg0: RequiredParameterNode).returns(::T.nilable(Node)) } + def visit_required_parameter_node(arg0); end + + sig { params(arg0: RescueModifierNode).returns(::T.nilable(Node)) } + def visit_rescue_modifier_node(arg0); end + + sig { params(arg0: RescueNode).returns(::T.nilable(Node)) } + def visit_rescue_node(arg0); end + + sig { params(arg0: RestParameterNode).returns(::T.nilable(Node)) } + def visit_rest_parameter_node(arg0); end + + sig { params(arg0: RetryNode).returns(::T.nilable(Node)) } + def visit_retry_node(arg0); end + + sig { params(arg0: ReturnNode).returns(::T.nilable(Node)) } + def visit_return_node(arg0); end + + sig { params(arg0: SelfNode).returns(::T.nilable(Node)) } + def visit_self_node(arg0); end + + sig { params(arg0: ShareableConstantNode).returns(::T.nilable(Node)) } + def visit_shareable_constant_node(arg0); end + + sig { params(arg0: SingletonClassNode).returns(::T.nilable(Node)) } + def visit_singleton_class_node(arg0); end + + sig { params(arg0: SourceEncodingNode).returns(::T.nilable(Node)) } + def visit_source_encoding_node(arg0); end + + sig { params(arg0: SourceFileNode).returns(::T.nilable(Node)) } + def visit_source_file_node(arg0); end + + sig { params(arg0: SourceLineNode).returns(::T.nilable(Node)) } + def visit_source_line_node(arg0); end + + sig { params(arg0: SplatNode).returns(::T.nilable(Node)) } + def visit_splat_node(arg0); end + + sig { params(arg0: StatementsNode).returns(::T.nilable(Node)) } + def visit_statements_node(arg0); end + + sig { params(arg0: StringNode).returns(::T.nilable(Node)) } + def visit_string_node(arg0); end + + sig { params(arg0: SuperNode).returns(::T.nilable(Node)) } + def visit_super_node(arg0); end + + sig { params(arg0: SymbolNode).returns(::T.nilable(Node)) } + def visit_symbol_node(arg0); end + + sig { params(arg0: TrueNode).returns(::T.nilable(Node)) } + def visit_true_node(arg0); end + + sig { params(arg0: UndefNode).returns(::T.nilable(Node)) } + def visit_undef_node(arg0); end + + sig { params(arg0: UnlessNode).returns(::T.nilable(Node)) } + def visit_unless_node(arg0); end + + sig { params(arg0: UntilNode).returns(::T.nilable(Node)) } + def visit_until_node(arg0); end + + sig { params(arg0: WhenNode).returns(::T.nilable(Node)) } + def visit_when_node(arg0); end + + sig { params(arg0: WhileNode).returns(::T.nilable(Node)) } + def visit_while_node(arg0); end + + sig { params(arg0: XStringNode).returns(::T.nilable(Node)) } + def visit_x_string_node(arg0); end + + sig { params(arg0: YieldNode).returns(::T.nilable(Node)) } + def visit_yield_node(arg0); end + end +end diff --git a/rbi/generated/prism/node.rbi b/rbi/generated/prism/node.rbi new file mode 100644 index 0000000000..3b62926afb --- /dev/null +++ b/rbi/generated/prism/node.rbi @@ -0,0 +1,14280 @@ +# typed: true + +module Prism + # This represents a node in the tree. It is the parent class of all of the + # various node types. + class Node + abstract! + + # A pointer to the source that this node was created from. + sig { returns(Source) } + attr_reader :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. + sig { returns(Integer) } + attr_reader :node_id + + # Save this node using a saved source so that it can be retrieved later. + sig { params(repository: ::T.untyped).returns(Relocation::Entry) } + def save(repository); end + + # A Location instance that represents the location of this node in the + # source. + sig { returns(Location) } + def location; end + + # Save the location using a saved source so that it can be retrieved later. + sig { params(repository: ::T.untyped).returns(Relocation::Entry) } + def save_location(repository); end + + # Delegates to [`start_line`](rdoc-ref:Location#start_line) of the associated location object. + sig { returns(Integer) } + def start_line; end + + # Delegates to [`end_line`](rdoc-ref:Location#end_line) of the associated location object. + sig { returns(Integer) } + def end_line; end + + # Delegates to [`start_offset`](rdoc-ref:Location#start_offset) of the associated location object. + sig { returns(Integer) } + def start_offset; end + + # Delegates to [`end_offset`](rdoc-ref:Location#end_offset) of the associated location object. + sig { returns(Integer) } + def end_offset; end + + # Delegates to [`start_character_offset`](rdoc-ref:Location#start_character_offset) + # of the associated location object. + sig { returns(Integer) } + def start_character_offset; end + + # Delegates to [`end_character_offset`](rdoc-ref:Location#end_character_offset) + # of the associated location object. + sig { returns(Integer) } + def end_character_offset; end + + # Delegates to [`cached_start_code_units_offset`](rdoc-ref:Location#cached_start_code_units_offset) + # of the associated location object. + sig { params(cache: ::T.untyped).returns(Integer) } + def cached_start_code_units_offset(cache); end + + # Delegates to [`cached_end_code_units_offset`](rdoc-ref:Location#cached_end_code_units_offset) + # of the associated location object. + sig { params(cache: ::T.untyped).returns(Integer) } + def cached_end_code_units_offset(cache); end + + # Delegates to [`start_column`](rdoc-ref:Location#start_column) of the associated location object. + sig { returns(Integer) } + def start_column; end + + # Delegates to [`end_column`](rdoc-ref:Location#end_column) of the associated location object. + sig { returns(Integer) } + def end_column; end + + # Delegates to [`start_character_column`](rdoc-ref:Location#start_character_column) + # of the associated location object. + sig { returns(Integer) } + def start_character_column; end + + # Delegates to [`end_character_column`](rdoc-ref:Location#end_character_column) + # of the associated location object. + sig { returns(Integer) } + def end_character_column; end + + # Delegates to [`cached_start_code_units_column`](rdoc-ref:Location#cached_start_code_units_column) + # of the associated location object. + sig { params(cache: ::T.untyped).returns(Integer) } + def cached_start_code_units_column(cache); end + + # Delegates to [`cached_end_code_units_column`](rdoc-ref:Location#cached_end_code_units_column) + # of the associated location object. + sig { params(cache: ::T.untyped).returns(Integer) } + def cached_end_code_units_column(cache); end + + # Delegates to [`leading_comments`](rdoc-ref:Location#leading_comments) of the associated location object. + sig { returns(T::Array[Comment]) } + def leading_comments; end + + # Delegates to [`trailing_comments`](rdoc-ref:Location#trailing_comments) of the associated location object. + sig { returns(T::Array[Comment]) } + def trailing_comments; end + + # Delegates to [`comments`](rdoc-ref:Location#comments) of the associated location object. + sig { returns(T::Array[Comment]) } + def comments; end + + # Returns all of the lines of the source code associated with this node. + sig { returns(T::Array[String]) } + def source_lines; end + + # An alias for source_lines, used to mimic the API from + # RubyVM::AbstractSyntaxTree to make it easier to migrate. + sig { returns(T::Array[String]) } + def script_lines; end + + # Slice the location of the node from the source. + sig { returns(String) } + def 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. + sig { returns(String) } + def 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. + sig { returns(Integer) } + attr_reader :flags + + # Returns true if the node has the newline flag set. + sig { returns(T::Boolean) } + def newline?; end + + # Returns true if the node has the static literal flag set. + sig { returns(T::Boolean) } + def static_literal?; end + + # Similar to inspect, but respects the current level of indentation given by + # the pretty print object. + sig { params(q: PP).void } + def pretty_print(q); end + + # Convert this node into a graphviz dot graph string. + sig { returns(String) } + def 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. + sig { params(line: Integer, column: Integer).returns(T::Array[Node]) } + def tunnel(line, column); 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 } + sig { params(blk: ::T.proc.params(arg0: Node).returns(T::Boolean)).returns(::T.nilable(Node)) } + def breadth_first_search(&blk); end + + sig { params(blk: ::T.proc.params(arg0: Node).returns(T::Boolean)).returns(::T.nilable(Node)) } + def find(&blk); end + + # 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) } + sig { params(blk: ::T.proc.params(arg0: Node).returns(T::Boolean)).returns(T::Array[Node]) } + def breadth_first_search_all(&blk); end + + sig { params(blk: ::T.proc.params(arg0: Node).returns(T::Boolean)).returns(T::Array[Node]) } + def find_all(&blk); end + + # Returns a list of the fields that exist for this node class. Fields + # describe the structure of the node. This kind of reflection is useful for + # things like recursively visiting each node _and_ field in the tree. + sig { returns(T::Array[Reflection::Field]) } + def self.fields; end + + # Accepts a visitor and calls back into the specialized visit function. + sig { abstract.params(visitor: Visitor).returns(::T.untyped) } + def accept(visitor); end + + # Returns an array of child nodes, including `nil`s in the place of optional + # nodes that were not present. + sig { abstract.returns(T::Array[::T.nilable(Node)]) } + def child_nodes; end + + sig { abstract.returns(T::Array[::T.nilable(Node)]) } + def deconstruct; end + + # 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. + sig { abstract.returns(T::Enumerator[Node]) } + sig { abstract.params(blk: ::T.proc.params(arg0: Node).void).void } + def each_child_node(&blk); end + + # Returns an array of child nodes, excluding any `nil`s in the place of + # optional nodes that were not present. + sig { abstract.returns(T::Array[Node]) } + def compact_child_nodes; end + + # Returns an array of child nodes and locations that could potentially have + # comments attached to them. + sig { abstract.returns(T::Array[::T.any(Node, Location)]) } + def comment_targets; end + + # Returns a string representation of the node. + sig { abstract.returns(String) } + def 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. + sig { abstract.returns(Symbol) } + def type; 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. + sig { abstract.returns(Symbol) } + def self.type; 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. + sig { params(source: Source, node_id: Integer, location: Location, flags: Integer, new_name: ::T.any(GlobalVariableReadNode, BackReferenceReadNode, NumberedReferenceReadNode), old_name: ::T.any(GlobalVariableReadNode, BackReferenceReadNode, NumberedReferenceReadNode, SymbolNode, MissingNode), keyword_loc: Location).void } + def initialize(source, node_id, location, flags, new_name, old_name, keyword_loc); end + + # See Node.accept. + sig { override.params(visitor: Visitor).returns(::T.untyped) } + def accept(visitor); end + + # See Node.child_nodes. + sig { override.returns(T::Array[::T.nilable(Node)]) } + def child_nodes; end + + # See Node.each_child_node. + sig { override.returns(T::Enumerator[Node]) } + sig { override.params(blk: ::T.proc.params(arg0: Node).void).void } + def each_child_node(&blk); end + + # See Node.compact_child_nodes. + sig { override.returns(T::Array[Node]) } + def compact_child_nodes; end + + # See Node.comment_targets. + sig { override.returns(T::Array[::T.any(Node, Location)]) } + def comment_targets; end + + # Creates a copy of self with the given fields, using self as the template. + sig { params(node_id: Integer, location: Location, flags: Integer, new_name: ::T.any(GlobalVariableReadNode, BackReferenceReadNode, NumberedReferenceReadNode), old_name: ::T.any(GlobalVariableReadNode, BackReferenceReadNode, NumberedReferenceReadNode, SymbolNode, MissingNode), keyword_loc: Location).returns(AliasGlobalVariableNode) } + def copy(node_id: T.unsafe(nil), location: T.unsafe(nil), flags: T.unsafe(nil), new_name: T.unsafe(nil), old_name: T.unsafe(nil), keyword_loc: T.unsafe(nil)); end + + sig { override.returns(T::Array[::T.nilable(Node)]) } + def deconstruct; end + + sig { params(keys: ::T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, ::T.untyped]) } + def deconstruct_keys(keys); end + + # See `Node#type`. + sig { override.returns(Symbol) } + def type; end + + # See `Node.type`. + sig { override.returns(Symbol) } + def self.type; end + + sig { override.returns(String) } + def inspect; end + + # Represents the new name of the global variable that can be used after aliasing. + # + # alias $foo $bar + # ^^^^ + sig { returns(::T.any(GlobalVariableReadNode, BackReferenceReadNode, NumberedReferenceReadNode)) } + def new_name; end + + # Represents the old name of the global variable that can be used before aliasing. + # + # alias $foo $bar + # ^^^^ + sig { returns(::T.any(GlobalVariableReadNode, BackReferenceReadNode, NumberedReferenceReadNode, SymbolNode, MissingNode)) } + def old_name; end + + # The Location of the `alias` keyword. + # + # alias $foo $bar + # ^^^^^ + sig { returns(Location) } + def keyword_loc; end + + # Save the keyword_loc location using the given saved source so that + # it can be retrieved later. + sig { params(repository: ::T.untyped).returns(Relocation::Entry) } + def save_keyword_loc(repository); end + + # Slice the location of keyword_loc from the source. + sig { returns(String) } + def keyword; end + + sig { params(other: ::T.untyped).returns(::T.nilable(T::Boolean)) } + def ===(other); end + end + + # Represents the use of the `alias` keyword to alias a method. + # + # alias foo bar + # ^^^^^^^^^^^^^ + class AliasMethodNode < Node + # Initialize a new AliasMethodNode node. + sig { params(source: Source, node_id: Integer, location: Location, flags: Integer, new_name: ::T.any(SymbolNode, InterpolatedSymbolNode), old_name: ::T.any(SymbolNode, InterpolatedSymbolNode, GlobalVariableReadNode, MissingNode), keyword_loc: Location).void } + def initialize(source, node_id, location, flags, new_name, old_name, keyword_loc); end + + # See Node.accept. + sig { override.params(visitor: Visitor).returns(::T.untyped) } + def accept(visitor); end + + # See Node.child_nodes. + sig { override.returns(T::Array[::T.nilable(Node)]) } + def child_nodes; end + + # See Node.each_child_node. + sig { override.returns(T::Enumerator[Node]) } + sig { override.params(blk: ::T.proc.params(arg0: Node).void).void } + def each_child_node(&blk); end + + # See Node.compact_child_nodes. + sig { override.returns(T::Array[Node]) } + def compact_child_nodes; end + + # See Node.comment_targets. + sig { override.returns(T::Array[::T.any(Node, Location)]) } + def comment_targets; end + + # Creates a copy of self with the given fields, using self as the template. + sig { params(node_id: Integer, location: Location, flags: Integer, new_name: ::T.any(SymbolNode, InterpolatedSymbolNode), old_name: ::T.any(SymbolNode, InterpolatedSymbolNode, GlobalVariableReadNode, MissingNode), keyword_loc: Location).returns(AliasMethodNode) } + def copy(node_id: T.unsafe(nil), location: T.unsafe(nil), flags: T.unsafe(nil), new_name: T.unsafe(nil), old_name: T.unsafe(nil), keyword_loc: T.unsafe(nil)); end + + sig { override.returns(T::Array[::T.nilable(Node)]) } + def deconstruct; end + + sig { params(keys: ::T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, ::T.untyped]) } + def deconstruct_keys(keys); end + + # See `Node#type`. + sig { override.returns(Symbol) } + def type; end + + # See `Node.type`. + sig { override.returns(Symbol) } + def self.type; end + + sig { override.returns(String) } + def inspect; end + + # Represents the new name of the method that will be aliased. + # + # alias foo bar + # ^^^ + # + # alias :foo :bar + # ^^^^ + # + # alias :"#{foo}" :"#{bar}" + # ^^^^^^^^^ + sig { returns(::T.any(SymbolNode, InterpolatedSymbolNode)) } + def new_name; end + + # Represents the old name of the method that will be aliased. + # + # alias foo bar + # ^^^ + # + # alias :foo :bar + # ^^^^ + # + # alias :"#{foo}" :"#{bar}" + # ^^^^^^^^^ + sig { returns(::T.any(SymbolNode, InterpolatedSymbolNode, GlobalVariableReadNode, MissingNode)) } + def old_name; end + + # Represents the Location of the `alias` keyword. + # + # alias foo bar + # ^^^^^ + sig { returns(Location) } + def keyword_loc; end + + # Save the keyword_loc location using the given saved source so that + # it can be retrieved later. + sig { params(repository: ::T.untyped).returns(Relocation::Entry) } + def save_keyword_loc(repository); end + + # Slice the location of keyword_loc from the source. + sig { returns(String) } + def keyword; end + + sig { params(other: ::T.untyped).returns(::T.nilable(T::Boolean)) } + def ===(other); end + end + + # Represents an alternation pattern in pattern matching. + # + # foo => bar | baz + # ^^^^^^^^^ + class AlternationPatternNode < Node + # Initialize a new AlternationPatternNode node. + sig { params(source: Source, node_id: Integer, location: Location, flags: Integer, left: Node, right: Node, operator_loc: Location).void } + def initialize(source, node_id, location, flags, left, right, operator_loc); end + + # See Node.accept. + sig { override.params(visitor: Visitor).returns(::T.untyped) } + def accept(visitor); end + + # See Node.child_nodes. + sig { override.returns(T::Array[::T.nilable(Node)]) } + def child_nodes; end + + # See Node.each_child_node. + sig { override.returns(T::Enumerator[Node]) } + sig { override.params(blk: ::T.proc.params(arg0: Node).void).void } + def each_child_node(&blk); end + + # See Node.compact_child_nodes. + sig { override.returns(T::Array[Node]) } + def compact_child_nodes; end + + # See Node.comment_targets. + sig { override.returns(T::Array[::T.any(Node, Location)]) } + def comment_targets; end + + # Creates a copy of self with the given fields, using self as the template. + sig { params(node_id: Integer, location: Location, flags: Integer, left: Node, right: Node, operator_loc: Location).returns(AlternationPatternNode) } + def copy(node_id: T.unsafe(nil), location: T.unsafe(nil), flags: T.unsafe(nil), left: T.unsafe(nil), right: T.unsafe(nil), operator_loc: T.unsafe(nil)); end + + sig { override.returns(T::Array[::T.nilable(Node)]) } + def deconstruct; end + + sig { params(keys: ::T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, ::T.untyped]) } + def deconstruct_keys(keys); end + + # See `Node#type`. + sig { override.returns(Symbol) } + def type; end + + # See `Node.type`. + sig { override.returns(Symbol) } + def self.type; end + + sig { override.returns(String) } + def inspect; end + + # Represents the left side of the expression. + # + # foo => bar | baz + # ^^^ + sig { returns(Node) } + def left; end + + # Represents the right side of the expression. + # + # foo => bar | baz + # ^^^ + sig { returns(Node) } + def right; end + + # Represents the alternation operator Location. + # + # foo => bar | baz + # ^ + sig { returns(Location) } + def operator_loc; end + + # Save the operator_loc location using the given saved source so that + # it can be retrieved later. + sig { params(repository: ::T.untyped).returns(Relocation::Entry) } + def save_operator_loc(repository); end + + # Slice the location of operator_loc from the source. + sig { returns(String) } + def operator; end + + sig { params(other: ::T.untyped).returns(::T.nilable(T::Boolean)) } + def ===(other); end + end + + # Represents the use of the `&&` operator or the `and` keyword. + # + # left and right + # ^^^^^^^^^^^^^^ + class AndNode < Node + # Initialize a new AndNode node. + sig { params(source: Source, node_id: Integer, location: Location, flags: Integer, left: Node, right: Node, operator_loc: Location).void } + def initialize(source, node_id, location, flags, left, right, operator_loc); end + + # See Node.accept. + sig { override.params(visitor: Visitor).returns(::T.untyped) } + def accept(visitor); end + + # See Node.child_nodes. + sig { override.returns(T::Array[::T.nilable(Node)]) } + def child_nodes; end + + # See Node.each_child_node. + sig { override.returns(T::Enumerator[Node]) } + sig { override.params(blk: ::T.proc.params(arg0: Node).void).void } + def each_child_node(&blk); end + + # See Node.compact_child_nodes. + sig { override.returns(T::Array[Node]) } + def compact_child_nodes; end + + # See Node.comment_targets. + sig { override.returns(T::Array[::T.any(Node, Location)]) } + def comment_targets; end + + # Creates a copy of self with the given fields, using self as the template. + sig { params(node_id: Integer, location: Location, flags: Integer, left: Node, right: Node, operator_loc: Location).returns(AndNode) } + def copy(node_id: T.unsafe(nil), location: T.unsafe(nil), flags: T.unsafe(nil), left: T.unsafe(nil), right: T.unsafe(nil), operator_loc: T.unsafe(nil)); end + + sig { override.returns(T::Array[::T.nilable(Node)]) } + def deconstruct; end + + sig { params(keys: ::T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, ::T.untyped]) } + def deconstruct_keys(keys); end + + # See `Node#type`. + sig { override.returns(Symbol) } + def type; end + + # See `Node.type`. + sig { override.returns(Symbol) } + def self.type; end + + sig { override.returns(String) } + def inspect; 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 + # ^ + sig { returns(Node) } + def left; end + + # Represents the right side of the expression. + # + # left && right + # ^^^^^ + # + # 1 and 2 + # ^ + sig { returns(Node) } + def right; end + + # The Location of the `and` keyword or the `&&` operator. + # + # left and right + # ^^^ + sig { returns(Location) } + def operator_loc; end + + # Save the operator_loc location using the given saved source so that + # it can be retrieved later. + sig { params(repository: ::T.untyped).returns(Relocation::Entry) } + def save_operator_loc(repository); end + + # Slice the location of operator_loc from the source. + sig { returns(String) } + def operator; end + + sig { params(other: ::T.untyped).returns(::T.nilable(T::Boolean)) } + def ===(other); 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. + sig { params(source: Source, node_id: Integer, location: Location, flags: Integer, arguments: T::Array[Node]).void } + def initialize(source, node_id, location, flags, arguments); end + + # See Node.accept. + sig { override.params(visitor: Visitor).returns(::T.untyped) } + def accept(visitor); end + + # See Node.child_nodes. + sig { override.returns(T::Array[::T.nilable(Node)]) } + def child_nodes; end + + # See Node.each_child_node. + sig { override.returns(T::Enumerator[Node]) } + sig { override.params(blk: ::T.proc.params(arg0: Node).void).void } + def each_child_node(&blk); end + + # See Node.compact_child_nodes. + sig { override.returns(T::Array[Node]) } + def compact_child_nodes; end + + # See Node.comment_targets. + sig { override.returns(T::Array[::T.any(Node, Location)]) } + def comment_targets; end + + # Creates a copy of self with the given fields, using self as the template. + sig { params(node_id: Integer, location: Location, flags: Integer, arguments: T::Array[Node]).returns(ArgumentsNode) } + def copy(node_id: T.unsafe(nil), location: T.unsafe(nil), flags: T.unsafe(nil), arguments: T.unsafe(nil)); end + + sig { override.returns(T::Array[::T.nilable(Node)]) } + def deconstruct; end + + sig { params(keys: ::T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, ::T.untyped]) } + def deconstruct_keys(keys); end + + # See `Node#type`. + sig { override.returns(Symbol) } + def type; end + + # See `Node.type`. + sig { override.returns(Symbol) } + def self.type; end + + sig { override.returns(String) } + def inspect; end + + # if the arguments contain forwarding + sig { returns(T::Boolean) } + def contains_forwarding?; end + + # if the arguments contain keywords + sig { returns(T::Boolean) } + def contains_keywords?; end + + # if the arguments contain a keyword splat + sig { returns(T::Boolean) } + def contains_keyword_splat?; end + + # if the arguments contain a splat + sig { returns(T::Boolean) } + def contains_splat?; end + + # if the arguments contain multiple splats + sig { returns(T::Boolean) } + def 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) + # ^^^^^^^^ + sig { returns(T::Array[Node]) } + def arguments; end + + sig { params(other: ::T.untyped).returns(::T.nilable(T::Boolean)) } + def ===(other); 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. + sig { params(source: Source, node_id: Integer, location: Location, flags: Integer, elements: T::Array[Node], opening_loc: ::T.nilable(Location), closing_loc: ::T.nilable(Location)).void } + def initialize(source, node_id, location, flags, elements, opening_loc, closing_loc); end + + # See Node.accept. + sig { override.params(visitor: Visitor).returns(::T.untyped) } + def accept(visitor); end + + # See Node.child_nodes. + sig { override.returns(T::Array[::T.nilable(Node)]) } + def child_nodes; end + + # See Node.each_child_node. + sig { override.returns(T::Enumerator[Node]) } + sig { override.params(blk: ::T.proc.params(arg0: Node).void).void } + def each_child_node(&blk); end + + # See Node.compact_child_nodes. + sig { override.returns(T::Array[Node]) } + def compact_child_nodes; end + + # See Node.comment_targets. + sig { override.returns(T::Array[::T.any(Node, Location)]) } + def comment_targets; end + + # Creates a copy of self with the given fields, using self as the template. + sig { params(node_id: Integer, location: Location, flags: Integer, elements: T::Array[Node], opening_loc: ::T.nilable(Location), closing_loc: ::T.nilable(Location)).returns(ArrayNode) } + def copy(node_id: T.unsafe(nil), location: T.unsafe(nil), flags: T.unsafe(nil), elements: T.unsafe(nil), opening_loc: T.unsafe(nil), closing_loc: T.unsafe(nil)); end + + sig { override.returns(T::Array[::T.nilable(Node)]) } + def deconstruct; end + + sig { params(keys: ::T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, ::T.untyped]) } + def deconstruct_keys(keys); end + + # See `Node#type`. + sig { override.returns(Symbol) } + def type; end + + # See `Node.type`. + sig { override.returns(Symbol) } + def self.type; end + + sig { override.returns(String) } + def inspect; end + + # if array contains splat nodes + sig { returns(T::Boolean) } + def 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. + sig { returns(T::Array[Node]) } + def elements; end + + # 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 + sig { returns(::T.nilable(Location)) } + def opening_loc; end + + # Save the opening_loc location using the given saved source so that + # it can be retrieved later. + sig { params(repository: ::T.untyped).returns(::T.nilable(Relocation::Entry)) } + def save_opening_loc(repository); 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 + sig { returns(::T.nilable(Location)) } + def closing_loc; end + + # Save the closing_loc location using the given saved source so that + # it can be retrieved later. + sig { params(repository: ::T.untyped).returns(::T.nilable(Relocation::Entry)) } + def save_closing_loc(repository); end + + # Slice the location of opening_loc from the source. + sig { returns(::T.nilable(String)) } + def opening; end + + # Slice the location of closing_loc from the source. + sig { returns(::T.nilable(String)) } + def closing; end + + sig { params(other: ::T.untyped).returns(::T.nilable(T::Boolean)) } + def ===(other); 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. + sig { params(source: Source, node_id: Integer, location: Location, flags: Integer, constant: ::T.nilable(::T.any(ConstantPathNode, ConstantReadNode)), requireds: T::Array[Node], rest: ::T.nilable(Node), posts: T::Array[Node], opening_loc: ::T.nilable(Location), closing_loc: ::T.nilable(Location)).void } + def initialize(source, node_id, location, flags, constant, requireds, rest, posts, opening_loc, closing_loc); end + + # See Node.accept. + sig { override.params(visitor: Visitor).returns(::T.untyped) } + def accept(visitor); end + + # See Node.child_nodes. + sig { override.returns(T::Array[::T.nilable(Node)]) } + def child_nodes; end + + # See Node.each_child_node. + sig { override.returns(T::Enumerator[Node]) } + sig { override.params(blk: ::T.proc.params(arg0: Node).void).void } + def each_child_node(&blk); end + + # See Node.compact_child_nodes. + sig { override.returns(T::Array[Node]) } + def compact_child_nodes; end + + # See Node.comment_targets. + sig { override.returns(T::Array[::T.any(Node, Location)]) } + def comment_targets; end + + # Creates a copy of self with the given fields, using self as the template. + sig { params(node_id: Integer, location: Location, flags: Integer, constant: ::T.nilable(::T.any(ConstantPathNode, ConstantReadNode)), requireds: T::Array[Node], rest: ::T.nilable(Node), posts: T::Array[Node], opening_loc: ::T.nilable(Location), closing_loc: ::T.nilable(Location)).returns(ArrayPatternNode) } + def copy(node_id: T.unsafe(nil), location: T.unsafe(nil), flags: T.unsafe(nil), constant: T.unsafe(nil), requireds: T.unsafe(nil), rest: T.unsafe(nil), posts: T.unsafe(nil), opening_loc: T.unsafe(nil), closing_loc: T.unsafe(nil)); end + + sig { override.returns(T::Array[::T.nilable(Node)]) } + def deconstruct; end + + sig { params(keys: ::T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, ::T.untyped]) } + def deconstruct_keys(keys); end + + # See `Node#type`. + sig { override.returns(Symbol) } + def type; end + + # See `Node.type`. + sig { override.returns(Symbol) } + def self.type; end + + sig { override.returns(String) } + def inspect; end + + # Represents the optional constant preceding the Array + # + # foo in Bar[] + # ^^^ + # + # foo in Bar[1, 2, 3] + # ^^^ + # + # foo in Bar::Baz[1, 2, 3] + # ^^^^^^^^ + sig { returns(::T.nilable(::T.any(ConstantPathNode, ConstantReadNode))) } + def constant; end + + # Represents the required elements of the array pattern. + # + # foo in [1, 2] + # ^ ^ + sig { returns(T::Array[Node]) } + def requireds; end + + # Represents the rest element of the array pattern. + # + # foo in *bar + # ^^^^ + sig { returns(::T.nilable(Node)) } + def rest; end + + # Represents the elements after the rest element of the array pattern. + # + # foo in *bar, baz + # ^^^ + sig { returns(T::Array[Node]) } + def posts; end + + # Represents the opening Location of the array pattern. + # + # foo in [1, 2] + # ^ + sig { returns(::T.nilable(Location)) } + def opening_loc; end + + # Save the opening_loc location using the given saved source so that + # it can be retrieved later. + sig { params(repository: ::T.untyped).returns(::T.nilable(Relocation::Entry)) } + def save_opening_loc(repository); end + + # Represents the closing Location of the array pattern. + # + # foo in [1, 2] + # ^ + sig { returns(::T.nilable(Location)) } + def closing_loc; end + + # Save the closing_loc location using the given saved source so that + # it can be retrieved later. + sig { params(repository: ::T.untyped).returns(::T.nilable(Relocation::Entry)) } + def save_closing_loc(repository); end + + # Slice the location of opening_loc from the source. + sig { returns(::T.nilable(String)) } + def opening; end + + # Slice the location of closing_loc from the source. + sig { returns(::T.nilable(String)) } + def closing; end + + sig { params(other: ::T.untyped).returns(::T.nilable(T::Boolean)) } + def ===(other); end + end + + # Represents a hash key/value pair. + # + # { a => b } + # ^^^^^^ + class AssocNode < Node + # Initialize a new AssocNode node. + sig { params(source: Source, node_id: Integer, location: Location, flags: Integer, key: Node, value: Node, operator_loc: ::T.nilable(Location)).void } + def initialize(source, node_id, location, flags, key, value, operator_loc); end + + # See Node.accept. + sig { override.params(visitor: Visitor).returns(::T.untyped) } + def accept(visitor); end + + # See Node.child_nodes. + sig { override.returns(T::Array[::T.nilable(Node)]) } + def child_nodes; end + + # See Node.each_child_node. + sig { override.returns(T::Enumerator[Node]) } + sig { override.params(blk: ::T.proc.params(arg0: Node).void).void } + def each_child_node(&blk); end + + # See Node.compact_child_nodes. + sig { override.returns(T::Array[Node]) } + def compact_child_nodes; end + + # See Node.comment_targets. + sig { override.returns(T::Array[::T.any(Node, Location)]) } + def comment_targets; end + + # Creates a copy of self with the given fields, using self as the template. + sig { params(node_id: Integer, location: Location, flags: Integer, key: Node, value: Node, operator_loc: ::T.nilable(Location)).returns(AssocNode) } + def copy(node_id: T.unsafe(nil), location: T.unsafe(nil), flags: T.unsafe(nil), key: T.unsafe(nil), value: T.unsafe(nil), operator_loc: T.unsafe(nil)); end + + sig { override.returns(T::Array[::T.nilable(Node)]) } + def deconstruct; end + + sig { params(keys: ::T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, ::T.untyped]) } + def deconstruct_keys(keys); end + + # See `Node#type`. + sig { override.returns(Symbol) } + def type; end + + # See `Node.type`. + sig { override.returns(Symbol) } + def self.type; end + + sig { override.returns(String) } + def inspect; 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 } + # ^^^^^^^^^^ + sig { returns(Node) } + def key; end + + # 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 } + # ^ + sig { returns(Node) } + def value; end + + # The Location of the `=>` operator, if present. + # + # { foo => bar } + # ^^ + sig { returns(::T.nilable(Location)) } + def operator_loc; end + + # Save the operator_loc location using the given saved source so that + # it can be retrieved later. + sig { params(repository: ::T.untyped).returns(::T.nilable(Relocation::Entry)) } + def save_operator_loc(repository); end + + # Slice the location of operator_loc from the source. + sig { returns(::T.nilable(String)) } + def operator; end + + sig { params(other: ::T.untyped).returns(::T.nilable(T::Boolean)) } + def ===(other); end + end + + # Represents a splat in a hash literal. + # + # { **foo } + # ^^^^^ + class AssocSplatNode < Node + # Initialize a new AssocSplatNode node. + sig { params(source: Source, node_id: Integer, location: Location, flags: Integer, value: ::T.nilable(Node), operator_loc: Location).void } + def initialize(source, node_id, location, flags, value, operator_loc); end + + # See Node.accept. + sig { override.params(visitor: Visitor).returns(::T.untyped) } + def accept(visitor); end + + # See Node.child_nodes. + sig { override.returns(T::Array[::T.nilable(Node)]) } + def child_nodes; end + + # See Node.each_child_node. + sig { override.returns(T::Enumerator[Node]) } + sig { override.params(blk: ::T.proc.params(arg0: Node).void).void } + def each_child_node(&blk); end + + # See Node.compact_child_nodes. + sig { override.returns(T::Array[Node]) } + def compact_child_nodes; end + + # See Node.comment_targets. + sig { override.returns(T::Array[::T.any(Node, Location)]) } + def comment_targets; end + + # Creates a copy of self with the given fields, using self as the template. + sig { params(node_id: Integer, location: Location, flags: Integer, value: ::T.nilable(Node), operator_loc: Location).returns(AssocSplatNode) } + def copy(node_id: T.unsafe(nil), location: T.unsafe(nil), flags: T.unsafe(nil), value: T.unsafe(nil), operator_loc: T.unsafe(nil)); end + + sig { override.returns(T::Array[::T.nilable(Node)]) } + def deconstruct; end + + sig { params(keys: ::T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, ::T.untyped]) } + def deconstruct_keys(keys); end + + # See `Node#type`. + sig { override.returns(Symbol) } + def type; end + + # See `Node.type`. + sig { override.returns(Symbol) } + def self.type; end + + sig { override.returns(String) } + def inspect; end + + # The value to be splatted, if present. Will be missing when keyword rest argument forwarding is used. + # + # { **foo } + # ^^^ + sig { returns(::T.nilable(Node)) } + def value; end + + # The Location of the `**` operator. + # + # { **x } + # ^^ + sig { returns(Location) } + def operator_loc; end + + # Save the operator_loc location using the given saved source so that + # it can be retrieved later. + sig { params(repository: ::T.untyped).returns(Relocation::Entry) } + def save_operator_loc(repository); end + + # Slice the location of operator_loc from the source. + sig { returns(String) } + def operator; end + + sig { params(other: ::T.untyped).returns(::T.nilable(T::Boolean)) } + def ===(other); end + end + + # Represents reading a reference to a field in the previous match. + # + # $' + # ^^ + class BackReferenceReadNode < Node + # Initialize a new BackReferenceReadNode node. + sig { params(source: Source, node_id: Integer, location: Location, flags: Integer, name: Symbol).void } + def initialize(source, node_id, location, flags, name); end + + # See Node.accept. + sig { override.params(visitor: Visitor).returns(::T.untyped) } + def accept(visitor); end + + # See Node.child_nodes. + sig { override.returns(T::Array[::T.nilable(Node)]) } + def child_nodes; end + + # See Node.each_child_node. + sig { override.returns(T::Enumerator[Node]) } + sig { override.params(blk: ::T.proc.params(arg0: Node).void).void } + def each_child_node(&blk); end + + # See Node.compact_child_nodes. + sig { override.returns(T::Array[Node]) } + def compact_child_nodes; end + + # See Node.comment_targets. + sig { override.returns(T::Array[::T.any(Node, Location)]) } + def comment_targets; end + + # Creates a copy of self with the given fields, using self as the template. + sig { params(node_id: Integer, location: Location, flags: Integer, name: Symbol).returns(BackReferenceReadNode) } + def copy(node_id: T.unsafe(nil), location: T.unsafe(nil), flags: T.unsafe(nil), name: T.unsafe(nil)); end + + sig { override.returns(T::Array[::T.nilable(Node)]) } + def deconstruct; end + + sig { params(keys: ::T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, ::T.untyped]) } + def deconstruct_keys(keys); end + + # See `Node#type`. + sig { override.returns(Symbol) } + def type; end + + # See `Node.type`. + sig { override.returns(Symbol) } + def self.type; end + + sig { override.returns(String) } + def inspect; end + + # The name of the back-reference variable, including the leading `$`. + # + # $& # name `:$&` + # + # $+ # name `:$+` + sig { returns(Symbol) } + def name; end + + sig { params(other: ::T.untyped).returns(::T.nilable(T::Boolean)) } + def ===(other); end + end + + # Represents a begin statement. + # + # begin + # foo + # end + # ^^^^^ + class BeginNode < Node + # Initialize a new BeginNode node. + sig { params(source: Source, node_id: Integer, location: Location, flags: Integer, begin_keyword_loc: ::T.nilable(Location), statements: ::T.nilable(StatementsNode), rescue_clause: ::T.nilable(RescueNode), else_clause: ::T.nilable(ElseNode), ensure_clause: ::T.nilable(EnsureNode), end_keyword_loc: ::T.nilable(Location)).void } + def initialize(source, node_id, location, flags, begin_keyword_loc, statements, rescue_clause, else_clause, ensure_clause, end_keyword_loc); end + + # See Node.accept. + sig { override.params(visitor: Visitor).returns(::T.untyped) } + def accept(visitor); end + + # See Node.child_nodes. + sig { override.returns(T::Array[::T.nilable(Node)]) } + def child_nodes; end + + # See Node.each_child_node. + sig { override.returns(T::Enumerator[Node]) } + sig { override.params(blk: ::T.proc.params(arg0: Node).void).void } + def each_child_node(&blk); end + + # See Node.compact_child_nodes. + sig { override.returns(T::Array[Node]) } + def compact_child_nodes; end + + # See Node.comment_targets. + sig { override.returns(T::Array[::T.any(Node, Location)]) } + def comment_targets; end + + # Creates a copy of self with the given fields, using self as the template. + sig { params(node_id: Integer, location: Location, flags: Integer, begin_keyword_loc: ::T.nilable(Location), statements: ::T.nilable(StatementsNode), rescue_clause: ::T.nilable(RescueNode), else_clause: ::T.nilable(ElseNode), ensure_clause: ::T.nilable(EnsureNode), end_keyword_loc: ::T.nilable(Location)).returns(BeginNode) } + def copy(node_id: T.unsafe(nil), location: T.unsafe(nil), flags: T.unsafe(nil), begin_keyword_loc: T.unsafe(nil), statements: T.unsafe(nil), rescue_clause: T.unsafe(nil), else_clause: T.unsafe(nil), ensure_clause: T.unsafe(nil), end_keyword_loc: T.unsafe(nil)); end + + sig { override.returns(T::Array[::T.nilable(Node)]) } + def deconstruct; end + + sig { params(keys: ::T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, ::T.untyped]) } + def deconstruct_keys(keys); end + + # See `Node#type`. + sig { override.returns(Symbol) } + def type; end + + # See `Node.type`. + sig { override.returns(Symbol) } + def self.type; end + + sig { override.returns(String) } + def inspect; end + + # Represents the Location of the `begin` keyword. + # + # begin x end + # ^^^^^ + sig { returns(::T.nilable(Location)) } + def begin_keyword_loc; end + + # Save the begin_keyword_loc location using the given saved source so that + # it can be retrieved later. + sig { params(repository: ::T.untyped).returns(::T.nilable(Relocation::Entry)) } + def save_begin_keyword_loc(repository); end + + # Represents the statements within the begin block. + # + # begin x end + # ^ + sig { returns(::T.nilable(StatementsNode)) } + def statements; end + + # Represents the rescue clause within the begin block. + # + # begin x; rescue y; end + # ^^^^^^^^ + sig { returns(::T.nilable(RescueNode)) } + def rescue_clause; end + + # Represents the else clause within the begin block. + # + # begin x; rescue y; else z; end + # ^^^^^^^^^^^ + sig { returns(::T.nilable(ElseNode)) } + def else_clause; end + + # Represents the ensure clause within the begin block. + # + # begin x; ensure y; end + # ^^^^^^^^ + sig { returns(::T.nilable(EnsureNode)) } + def ensure_clause; end + + # Represents the Location of the `end` keyword. + # + # begin x end + # ^^^ + sig { returns(::T.nilable(Location)) } + def end_keyword_loc; end + + # Save the end_keyword_loc location using the given saved source so that + # it can be retrieved later. + sig { params(repository: ::T.untyped).returns(::T.nilable(Relocation::Entry)) } + def save_end_keyword_loc(repository); end + + # Slice the location of begin_keyword_loc from the source. + sig { returns(::T.nilable(String)) } + def begin_keyword; end + + # Slice the location of end_keyword_loc from the source. + sig { returns(::T.nilable(String)) } + def end_keyword; end + + sig { params(other: ::T.untyped).returns(::T.nilable(T::Boolean)) } + def ===(other); end + end + + # Represents a block argument using `&`. + # + # bar(&args) + # ^^^^^ + class BlockArgumentNode < Node + # Initialize a new BlockArgumentNode node. + sig { params(source: Source, node_id: Integer, location: Location, flags: Integer, expression: ::T.nilable(Node), operator_loc: Location).void } + def initialize(source, node_id, location, flags, expression, operator_loc); end + + # See Node.accept. + sig { override.params(visitor: Visitor).returns(::T.untyped) } + def accept(visitor); end + + # See Node.child_nodes. + sig { override.returns(T::Array[::T.nilable(Node)]) } + def child_nodes; end + + # See Node.each_child_node. + sig { override.returns(T::Enumerator[Node]) } + sig { override.params(blk: ::T.proc.params(arg0: Node).void).void } + def each_child_node(&blk); end + + # See Node.compact_child_nodes. + sig { override.returns(T::Array[Node]) } + def compact_child_nodes; end + + # See Node.comment_targets. + sig { override.returns(T::Array[::T.any(Node, Location)]) } + def comment_targets; end + + # Creates a copy of self with the given fields, using self as the template. + sig { params(node_id: Integer, location: Location, flags: Integer, expression: ::T.nilable(Node), operator_loc: Location).returns(BlockArgumentNode) } + def copy(node_id: T.unsafe(nil), location: T.unsafe(nil), flags: T.unsafe(nil), expression: T.unsafe(nil), operator_loc: T.unsafe(nil)); end + + sig { override.returns(T::Array[::T.nilable(Node)]) } + def deconstruct; end + + sig { params(keys: ::T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, ::T.untyped]) } + def deconstruct_keys(keys); end + + # See `Node#type`. + sig { override.returns(Symbol) } + def type; end + + # See `Node.type`. + sig { override.returns(Symbol) } + def self.type; end + + sig { override.returns(String) } + def inspect; 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) + # ^^^^ + sig { returns(::T.nilable(Node)) } + def expression; end + + # Represents the Location of the `&` operator. + # + # foo(&args) + # ^ + sig { returns(Location) } + def operator_loc; end + + # Save the operator_loc location using the given saved source so that + # it can be retrieved later. + sig { params(repository: ::T.untyped).returns(Relocation::Entry) } + def save_operator_loc(repository); end + + # Slice the location of operator_loc from the source. + sig { returns(String) } + def operator; end + + sig { params(other: ::T.untyped).returns(::T.nilable(T::Boolean)) } + def ===(other); end + end + + # Represents a block local variable. + # + # a { |; b| } + # ^ + class BlockLocalVariableNode < Node + # Initialize a new BlockLocalVariableNode node. + sig { params(source: Source, node_id: Integer, location: Location, flags: Integer, name: Symbol).void } + def initialize(source, node_id, location, flags, name); end + + # See Node.accept. + sig { override.params(visitor: Visitor).returns(::T.untyped) } + def accept(visitor); end + + # See Node.child_nodes. + sig { override.returns(T::Array[::T.nilable(Node)]) } + def child_nodes; end + + # See Node.each_child_node. + sig { override.returns(T::Enumerator[Node]) } + sig { override.params(blk: ::T.proc.params(arg0: Node).void).void } + def each_child_node(&blk); end + + # See Node.compact_child_nodes. + sig { override.returns(T::Array[Node]) } + def compact_child_nodes; end + + # See Node.comment_targets. + sig { override.returns(T::Array[::T.any(Node, Location)]) } + def comment_targets; end + + # Creates a copy of self with the given fields, using self as the template. + sig { params(node_id: Integer, location: Location, flags: Integer, name: Symbol).returns(BlockLocalVariableNode) } + def copy(node_id: T.unsafe(nil), location: T.unsafe(nil), flags: T.unsafe(nil), name: T.unsafe(nil)); end + + sig { override.returns(T::Array[::T.nilable(Node)]) } + def deconstruct; end + + sig { params(keys: ::T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, ::T.untyped]) } + def deconstruct_keys(keys); end + + # See `Node#type`. + sig { override.returns(Symbol) } + def type; end + + # See `Node.type`. + sig { override.returns(Symbol) } + def self.type; end + + sig { override.returns(String) } + def inspect; end + + # a parameter name that has been repeated in the method signature + sig { returns(T::Boolean) } + def repeated_parameter?; end + + # The name of the block local variable. + # + # a { |; b| } # name `:b` + # ^ + sig { returns(Symbol) } + def name; end + + sig { params(other: ::T.untyped).returns(::T.nilable(T::Boolean)) } + def ===(other); end + end + + # Represents a block of ruby code. + # + # [1, 2, 3].each { |i| puts x } + # ^^^^^^^^^^^^^^ + class BlockNode < Node + # Initialize a new BlockNode node. + sig { params(source: Source, node_id: Integer, location: Location, flags: Integer, locals: T::Array[Symbol], parameters: ::T.nilable(::T.any(BlockParametersNode, NumberedParametersNode, ItParametersNode)), body: ::T.nilable(::T.any(StatementsNode, BeginNode)), opening_loc: Location, closing_loc: Location).void } + def initialize(source, node_id, location, flags, locals, parameters, body, opening_loc, closing_loc); end + + # See Node.accept. + sig { override.params(visitor: Visitor).returns(::T.untyped) } + def accept(visitor); end + + # See Node.child_nodes. + sig { override.returns(T::Array[::T.nilable(Node)]) } + def child_nodes; end + + # See Node.each_child_node. + sig { override.returns(T::Enumerator[Node]) } + sig { override.params(blk: ::T.proc.params(arg0: Node).void).void } + def each_child_node(&blk); end + + # See Node.compact_child_nodes. + sig { override.returns(T::Array[Node]) } + def compact_child_nodes; end + + # See Node.comment_targets. + sig { override.returns(T::Array[::T.any(Node, Location)]) } + def comment_targets; end + + # Creates a copy of self with the given fields, using self as the template. + sig { params(node_id: Integer, location: Location, flags: Integer, locals: T::Array[Symbol], parameters: ::T.nilable(::T.any(BlockParametersNode, NumberedParametersNode, ItParametersNode)), body: ::T.nilable(::T.any(StatementsNode, BeginNode)), opening_loc: Location, closing_loc: Location).returns(BlockNode) } + def copy(node_id: T.unsafe(nil), location: T.unsafe(nil), flags: T.unsafe(nil), locals: T.unsafe(nil), parameters: T.unsafe(nil), body: T.unsafe(nil), opening_loc: T.unsafe(nil), closing_loc: T.unsafe(nil)); end + + sig { override.returns(T::Array[::T.nilable(Node)]) } + def deconstruct; end + + sig { params(keys: ::T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, ::T.untyped]) } + def deconstruct_keys(keys); end + + # See `Node#type`. + sig { override.returns(Symbol) } + def type; end + + # See `Node.type`. + sig { override.returns(Symbol) } + def self.type; end + + sig { override.returns(String) } + def inspect; end + + # The local variables declared in the block. + # + # [1, 2, 3].each { |i| puts x } # locals: [:i] + # ^ + sig { returns(T::Array[Symbol]) } + def locals; end + + # The parameters of the block. + # + # [1, 2, 3].each { |i| puts x } + # ^^^ + # [1, 2, 3].each { puts _1 } + # ^^^^^^^^^^^ + # [1, 2, 3].each { puts it } + # ^^^^^^^^^^^ + sig { returns(::T.nilable(::T.any(BlockParametersNode, NumberedParametersNode, ItParametersNode))) } + def parameters; end + + # The body of the block. + # + # [1, 2, 3].each { |i| puts x } + # ^^^^^^ + sig { returns(::T.nilable(::T.any(StatementsNode, BeginNode))) } + def body; end + + # Represents the Location of the opening `{` or `do`. + # + # [1, 2, 3].each { |i| puts x } + # ^ + sig { returns(Location) } + def opening_loc; end + + # Save the opening_loc location using the given saved source so that + # it can be retrieved later. + sig { params(repository: ::T.untyped).returns(Relocation::Entry) } + def save_opening_loc(repository); end + + # Represents the Location of the closing `}` or `end`. + # + # [1, 2, 3].each { |i| puts x } + # ^ + sig { returns(Location) } + def closing_loc; end + + # Save the closing_loc location using the given saved source so that + # it can be retrieved later. + sig { params(repository: ::T.untyped).returns(Relocation::Entry) } + def save_closing_loc(repository); end + + # Slice the location of opening_loc from the source. + sig { returns(String) } + def opening; end + + # Slice the location of closing_loc from the source. + sig { returns(String) } + def closing; end + + sig { params(other: ::T.untyped).returns(::T.nilable(T::Boolean)) } + def ===(other); 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. + sig { params(source: Source, node_id: Integer, location: Location, flags: Integer, name: ::T.nilable(Symbol), name_loc: ::T.nilable(Location), operator_loc: Location).void } + def initialize(source, node_id, location, flags, name, name_loc, operator_loc); end + + # See Node.accept. + sig { override.params(visitor: Visitor).returns(::T.untyped) } + def accept(visitor); end + + # See Node.child_nodes. + sig { override.returns(T::Array[::T.nilable(Node)]) } + def child_nodes; end + + # See Node.each_child_node. + sig { override.returns(T::Enumerator[Node]) } + sig { override.params(blk: ::T.proc.params(arg0: Node).void).void } + def each_child_node(&blk); end + + # See Node.compact_child_nodes. + sig { override.returns(T::Array[Node]) } + def compact_child_nodes; end + + # See Node.comment_targets. + sig { override.returns(T::Array[::T.any(Node, Location)]) } + def comment_targets; end + + # Creates a copy of self with the given fields, using self as the template. + sig { params(node_id: Integer, location: Location, flags: Integer, name: ::T.nilable(Symbol), name_loc: ::T.nilable(Location), operator_loc: Location).returns(BlockParameterNode) } + def copy(node_id: T.unsafe(nil), location: T.unsafe(nil), flags: T.unsafe(nil), name: T.unsafe(nil), name_loc: T.unsafe(nil), operator_loc: T.unsafe(nil)); end + + sig { override.returns(T::Array[::T.nilable(Node)]) } + def deconstruct; end + + sig { params(keys: ::T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, ::T.untyped]) } + def deconstruct_keys(keys); end + + # See `Node#type`. + sig { override.returns(Symbol) } + def type; end + + # See `Node.type`. + sig { override.returns(Symbol) } + def self.type; end + + sig { override.returns(String) } + def inspect; end + + # a parameter name that has been repeated in the method signature + sig { returns(T::Boolean) } + def repeated_parameter?; end + + # The name of the block parameter. + # + # def a(&b) # name `:b` + # ^ + # end + sig { returns(::T.nilable(Symbol)) } + def name; end + + # Represents the Location of the block parameter name. + # + # def a(&b) + # ^ + sig { returns(::T.nilable(Location)) } + def name_loc; end + + # Save the name_loc location using the given saved source so that + # it can be retrieved later. + sig { params(repository: ::T.untyped).returns(::T.nilable(Relocation::Entry)) } + def save_name_loc(repository); end + + # Represents the Location of the `&` operator. + # + # def a(&b) + # ^ + # end + sig { returns(Location) } + def operator_loc; end + + # Save the operator_loc location using the given saved source so that + # it can be retrieved later. + sig { params(repository: ::T.untyped).returns(Relocation::Entry) } + def save_operator_loc(repository); end + + # Slice the location of operator_loc from the source. + sig { returns(String) } + def operator; end + + sig { params(other: ::T.untyped).returns(::T.nilable(T::Boolean)) } + def ===(other); 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. + sig { params(source: Source, node_id: Integer, location: Location, flags: Integer, parameters: ::T.nilable(ParametersNode), locals: T::Array[BlockLocalVariableNode], opening_loc: ::T.nilable(Location), closing_loc: ::T.nilable(Location)).void } + def initialize(source, node_id, location, flags, parameters, locals, opening_loc, closing_loc); end + + # See Node.accept. + sig { override.params(visitor: Visitor).returns(::T.untyped) } + def accept(visitor); end + + # See Node.child_nodes. + sig { override.returns(T::Array[::T.nilable(Node)]) } + def child_nodes; end + + # See Node.each_child_node. + sig { override.returns(T::Enumerator[Node]) } + sig { override.params(blk: ::T.proc.params(arg0: Node).void).void } + def each_child_node(&blk); end + + # See Node.compact_child_nodes. + sig { override.returns(T::Array[Node]) } + def compact_child_nodes; end + + # See Node.comment_targets. + sig { override.returns(T::Array[::T.any(Node, Location)]) } + def comment_targets; end + + # Creates a copy of self with the given fields, using self as the template. + sig { params(node_id: Integer, location: Location, flags: Integer, parameters: ::T.nilable(ParametersNode), locals: T::Array[BlockLocalVariableNode], opening_loc: ::T.nilable(Location), closing_loc: ::T.nilable(Location)).returns(BlockParametersNode) } + def copy(node_id: T.unsafe(nil), location: T.unsafe(nil), flags: T.unsafe(nil), parameters: T.unsafe(nil), locals: T.unsafe(nil), opening_loc: T.unsafe(nil), closing_loc: T.unsafe(nil)); end + + sig { override.returns(T::Array[::T.nilable(Node)]) } + def deconstruct; end + + sig { params(keys: ::T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, ::T.untyped]) } + def deconstruct_keys(keys); end + + # See `Node#type`. + sig { override.returns(Symbol) } + def type; end + + # See `Node.type`. + sig { override.returns(Symbol) } + def self.type; end + + sig { override.returns(String) } + def inspect; end + + # Represents the parameters of the block. + # + # -> (a, b = 1; local) { } + # ^^^^^^^^ + # + # foo do |a, b = 1; local| + # ^^^^^^^^ + # end + sig { returns(::T.nilable(ParametersNode)) } + def parameters; end + + # Represents the local variables of the block. + # + # -> (a, b = 1; local) { } + # ^^^^^ + # + # foo do |a, b = 1; local| + # ^^^^^ + # end + sig { returns(T::Array[BlockLocalVariableNode]) } + def locals; end + + # Represents the opening Location of the block parameters. + # + # -> (a, b = 1; local) { } + # ^ + # + # foo do |a, b = 1; local| + # ^ + # end + sig { returns(::T.nilable(Location)) } + def opening_loc; end + + # Save the opening_loc location using the given saved source so that + # it can be retrieved later. + sig { params(repository: ::T.untyped).returns(::T.nilable(Relocation::Entry)) } + def save_opening_loc(repository); end + + # Represents the closing Location of the block parameters. + # + # -> (a, b = 1; local) { } + # ^ + # + # foo do |a, b = 1; local| + # ^ + # end + sig { returns(::T.nilable(Location)) } + def closing_loc; end + + # Save the closing_loc location using the given saved source so that + # it can be retrieved later. + sig { params(repository: ::T.untyped).returns(::T.nilable(Relocation::Entry)) } + def save_closing_loc(repository); end + + # Slice the location of opening_loc from the source. + sig { returns(::T.nilable(String)) } + def opening; end + + # Slice the location of closing_loc from the source. + sig { returns(::T.nilable(String)) } + def closing; end + + sig { params(other: ::T.untyped).returns(::T.nilable(T::Boolean)) } + def ===(other); end + end + + # Represents the use of the `break` keyword. + # + # break foo + # ^^^^^^^^^ + class BreakNode < Node + # Initialize a new BreakNode node. + sig { params(source: Source, node_id: Integer, location: Location, flags: Integer, arguments: ::T.nilable(ArgumentsNode), keyword_loc: Location).void } + def initialize(source, node_id, location, flags, arguments, keyword_loc); end + + # See Node.accept. + sig { override.params(visitor: Visitor).returns(::T.untyped) } + def accept(visitor); end + + # See Node.child_nodes. + sig { override.returns(T::Array[::T.nilable(Node)]) } + def child_nodes; end + + # See Node.each_child_node. + sig { override.returns(T::Enumerator[Node]) } + sig { override.params(blk: ::T.proc.params(arg0: Node).void).void } + def each_child_node(&blk); end + + # See Node.compact_child_nodes. + sig { override.returns(T::Array[Node]) } + def compact_child_nodes; end + + # See Node.comment_targets. + sig { override.returns(T::Array[::T.any(Node, Location)]) } + def comment_targets; end + + # Creates a copy of self with the given fields, using self as the template. + sig { params(node_id: Integer, location: Location, flags: Integer, arguments: ::T.nilable(ArgumentsNode), keyword_loc: Location).returns(BreakNode) } + def copy(node_id: T.unsafe(nil), location: T.unsafe(nil), flags: T.unsafe(nil), arguments: T.unsafe(nil), keyword_loc: T.unsafe(nil)); end + + sig { override.returns(T::Array[::T.nilable(Node)]) } + def deconstruct; end + + sig { params(keys: ::T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, ::T.untyped]) } + def deconstruct_keys(keys); end + + # See `Node#type`. + sig { override.returns(Symbol) } + def type; end + + # See `Node.type`. + sig { override.returns(Symbol) } + def self.type; end + + sig { override.returns(String) } + def inspect; 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 + # ^^^ + sig { returns(::T.nilable(ArgumentsNode)) } + def arguments; end + + # The Location of the `break` keyword. + # + # break foo + # ^^^^^ + sig { returns(Location) } + def keyword_loc; end + + # Save the keyword_loc location using the given saved source so that + # it can be retrieved later. + sig { params(repository: ::T.untyped).returns(Relocation::Entry) } + def save_keyword_loc(repository); end + + # Slice the location of keyword_loc from the source. + sig { returns(String) } + def keyword; end + + sig { params(other: ::T.untyped).returns(::T.nilable(T::Boolean)) } + def ===(other); end + end + + # Represents the use of the `&&=` operator on a call. + # + # foo.bar &&= value + # ^^^^^^^^^^^^^^^^^ + class CallAndWriteNode < Node + # Initialize a new CallAndWriteNode node. + sig { params(source: Source, node_id: Integer, location: Location, flags: Integer, receiver: ::T.nilable(Node), call_operator_loc: ::T.nilable(Location), message_loc: ::T.nilable(Location), read_name: Symbol, write_name: Symbol, operator_loc: Location, value: Node).void } + def initialize(source, node_id, location, flags, receiver, call_operator_loc, message_loc, read_name, write_name, operator_loc, value); end + + # See Node.accept. + sig { override.params(visitor: Visitor).returns(::T.untyped) } + def accept(visitor); end + + # See Node.child_nodes. + sig { override.returns(T::Array[::T.nilable(Node)]) } + def child_nodes; end + + # See Node.each_child_node. + sig { override.returns(T::Enumerator[Node]) } + sig { override.params(blk: ::T.proc.params(arg0: Node).void).void } + def each_child_node(&blk); end + + # See Node.compact_child_nodes. + sig { override.returns(T::Array[Node]) } + def compact_child_nodes; end + + # See Node.comment_targets. + sig { override.returns(T::Array[::T.any(Node, Location)]) } + def comment_targets; end + + # Creates a copy of self with the given fields, using self as the template. + sig { params(node_id: Integer, location: Location, flags: Integer, receiver: ::T.nilable(Node), call_operator_loc: ::T.nilable(Location), message_loc: ::T.nilable(Location), read_name: Symbol, write_name: Symbol, operator_loc: Location, value: Node).returns(CallAndWriteNode) } + def copy(node_id: T.unsafe(nil), location: T.unsafe(nil), flags: T.unsafe(nil), receiver: T.unsafe(nil), call_operator_loc: T.unsafe(nil), message_loc: T.unsafe(nil), read_name: T.unsafe(nil), write_name: T.unsafe(nil), operator_loc: T.unsafe(nil), value: T.unsafe(nil)); end + + sig { override.returns(T::Array[::T.nilable(Node)]) } + def deconstruct; end + + sig { params(keys: ::T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, ::T.untyped]) } + def deconstruct_keys(keys); end + + # See `Node#type`. + sig { override.returns(Symbol) } + def type; end + + # See `Node.type`. + sig { override.returns(Symbol) } + def self.type; end + + sig { override.returns(String) } + def inspect; end + + # &. operator + sig { returns(T::Boolean) } + def safe_navigation?; end + + # a call that could have been a local variable + sig { returns(T::Boolean) } + def variable_call?; end + + # a call that is an attribute write, so the value being written should be returned + sig { returns(T::Boolean) } + def attribute_write?; end + + # a call that ignores method visibility + sig { returns(T::Boolean) } + def 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 + # ^^^ + sig { returns(::T.nilable(Node)) } + def receiver; end + + # Represents the Location of the call operator. + # + # foo.bar &&= value + # ^ + sig { returns(::T.nilable(Location)) } + def call_operator_loc; end + + # Save the call_operator_loc location using the given saved source so that + # it can be retrieved later. + sig { params(repository: ::T.untyped).returns(::T.nilable(Relocation::Entry)) } + def save_call_operator_loc(repository); end + + # Represents the Location of the message. + # + # foo.bar &&= value + # ^^^ + sig { returns(::T.nilable(Location)) } + def message_loc; end + + # Save the message_loc location using the given saved source so that + # it can be retrieved later. + sig { params(repository: ::T.untyped).returns(::T.nilable(Relocation::Entry)) } + def save_message_loc(repository); end + + # Represents the name of the method being called. + # + # foo.bar &&= value # read_name `:bar` + # ^^^ + sig { returns(Symbol) } + def read_name; end + + # Represents the name of the method being written to. + # + # foo.bar &&= value # write_name `:bar=` + # ^^^ + sig { returns(Symbol) } + def write_name; end + + # Represents the Location of the operator. + # + # foo.bar &&= value + # ^^^ + sig { returns(Location) } + def operator_loc; end + + # Save the operator_loc location using the given saved source so that + # it can be retrieved later. + sig { params(repository: ::T.untyped).returns(Relocation::Entry) } + def save_operator_loc(repository); end + + # Represents the value being assigned. + # + # foo.bar &&= value + # ^^^^^ + sig { returns(Node) } + def value; end + + # Slice the location of call_operator_loc from the source. + sig { returns(::T.nilable(String)) } + def call_operator; end + + # Slice the location of message_loc from the source. + sig { returns(::T.nilable(String)) } + def message; end + + # Slice the location of operator_loc from the source. + sig { returns(String) } + def operator; end + + sig { params(other: ::T.untyped).returns(::T.nilable(T::Boolean)) } + def ===(other); 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. + sig { params(source: Source, node_id: Integer, location: Location, flags: Integer, receiver: ::T.nilable(Node), call_operator_loc: ::T.nilable(Location), name: Symbol, message_loc: ::T.nilable(Location), opening_loc: ::T.nilable(Location), arguments: ::T.nilable(ArgumentsNode), closing_loc: ::T.nilable(Location), equal_loc: ::T.nilable(Location), block: ::T.nilable(::T.any(BlockNode, 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 + + # See Node.accept. + sig { override.params(visitor: Visitor).returns(::T.untyped) } + def accept(visitor); end + + # See Node.child_nodes. + sig { override.returns(T::Array[::T.nilable(Node)]) } + def child_nodes; end + + # See Node.each_child_node. + sig { override.returns(T::Enumerator[Node]) } + sig { override.params(blk: ::T.proc.params(arg0: Node).void).void } + def each_child_node(&blk); end + + # See Node.compact_child_nodes. + sig { override.returns(T::Array[Node]) } + def compact_child_nodes; end + + # See Node.comment_targets. + sig { override.returns(T::Array[::T.any(Node, Location)]) } + def comment_targets; end + + # Creates a copy of self with the given fields, using self as the template. + sig { params(node_id: Integer, location: Location, flags: Integer, receiver: ::T.nilable(Node), call_operator_loc: ::T.nilable(Location), name: Symbol, message_loc: ::T.nilable(Location), opening_loc: ::T.nilable(Location), arguments: ::T.nilable(ArgumentsNode), closing_loc: ::T.nilable(Location), equal_loc: ::T.nilable(Location), block: ::T.nilable(::T.any(BlockNode, BlockArgumentNode))).returns(CallNode) } + def copy(node_id: T.unsafe(nil), location: T.unsafe(nil), flags: T.unsafe(nil), receiver: T.unsafe(nil), call_operator_loc: T.unsafe(nil), name: T.unsafe(nil), message_loc: T.unsafe(nil), opening_loc: T.unsafe(nil), arguments: T.unsafe(nil), closing_loc: T.unsafe(nil), equal_loc: T.unsafe(nil), block: T.unsafe(nil)); end + + sig { override.returns(T::Array[::T.nilable(Node)]) } + def deconstruct; end + + sig { params(keys: ::T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, ::T.untyped]) } + def deconstruct_keys(keys); end + + # See `Node#type`. + sig { override.returns(Symbol) } + def type; end + + # See `Node.type`. + sig { override.returns(Symbol) } + def self.type; end + + sig { override.returns(String) } + def inspect; end + + # &. operator + sig { returns(T::Boolean) } + def safe_navigation?; end + + # a call that could have been a local variable + sig { returns(T::Boolean) } + def variable_call?; end + + # a call that is an attribute write, so the value being written should be returned + sig { returns(T::Boolean) } + def attribute_write?; end + + # a call that ignores method visibility + sig { returns(T::Boolean) } + def 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 + # ^^^ + sig { returns(::T.nilable(Node)) } + def receiver; end + + # Represents the Location of the call operator. + # + # foo.bar + # ^ + # + # foo&.bar + # ^^ + sig { returns(::T.nilable(Location)) } + def call_operator_loc; end + + # Save the call_operator_loc location using the given saved source so that + # it can be retrieved later. + sig { params(repository: ::T.untyped).returns(::T.nilable(Relocation::Entry)) } + def save_call_operator_loc(repository); end + + # Represents the name of the method being called. + # + # foo.bar # name `:foo` + # ^^^ + sig { returns(Symbol) } + def name; end + + # Represents the Location of the message. + # + # foo.bar + # ^^^ + sig { returns(::T.nilable(Location)) } + def message_loc; end + + # Save the message_loc location using the given saved source so that + # it can be retrieved later. + sig { params(repository: ::T.untyped).returns(::T.nilable(Relocation::Entry)) } + def save_message_loc(repository); end + + # Represents the Location of the left parenthesis. + # + # foo(bar) + # ^ + sig { returns(::T.nilable(Location)) } + def opening_loc; end + + # Save the opening_loc location using the given saved source so that + # it can be retrieved later. + sig { params(repository: ::T.untyped).returns(::T.nilable(Relocation::Entry)) } + def save_opening_loc(repository); 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) + # ^^^ + sig { returns(::T.nilable(ArgumentsNode)) } + def arguments; end + + # Represents the Location of the right parenthesis. + # + # foo(bar) + # ^ + sig { returns(::T.nilable(Location)) } + def closing_loc; end + + # Save the closing_loc location using the given saved source so that + # it can be retrieved later. + sig { params(repository: ::T.untyped).returns(::T.nilable(Relocation::Entry)) } + def save_closing_loc(repository); end + + # Represents the Location of the equal sign, in the case that this is an attribute write. + # + # foo.bar = value + # ^ + # + # foo[bar] = value + # ^ + sig { returns(::T.nilable(Location)) } + def equal_loc; end + + # Save the equal_loc location using the given saved source so that + # it can be retrieved later. + sig { params(repository: ::T.untyped).returns(::T.nilable(Relocation::Entry)) } + def save_equal_loc(repository); end + + # Represents the block that is being passed to the method. + # + # foo { |a| a } + # ^^^^^^^^^ + sig { returns(::T.nilable(::T.any(BlockNode, BlockArgumentNode))) } + def block; end + + # Slice the location of call_operator_loc from the source. + sig { returns(::T.nilable(String)) } + def call_operator; end + + # Slice the location of message_loc from the source. + sig { returns(::T.nilable(String)) } + def message; end + + # Slice the location of opening_loc from the source. + sig { returns(::T.nilable(String)) } + def opening; end + + # Slice the location of closing_loc from the source. + sig { returns(::T.nilable(String)) } + def closing; end + + # Slice the location of equal_loc from the source. + sig { returns(::T.nilable(String)) } + def equal; end + + sig { params(other: ::T.untyped).returns(::T.nilable(T::Boolean)) } + def ===(other); end + end + + # Represents the use of an assignment operator on a call. + # + # foo.bar += baz + # ^^^^^^^^^^^^^^ + class CallOperatorWriteNode < Node + # Initialize a new CallOperatorWriteNode node. + sig { params(source: Source, node_id: Integer, location: Location, flags: Integer, receiver: ::T.nilable(Node), call_operator_loc: ::T.nilable(Location), message_loc: ::T.nilable(Location), read_name: Symbol, write_name: Symbol, binary_operator: Symbol, binary_operator_loc: Location, value: 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 + + # See Node.accept. + sig { override.params(visitor: Visitor).returns(::T.untyped) } + def accept(visitor); end + + # See Node.child_nodes. + sig { override.returns(T::Array[::T.nilable(Node)]) } + def child_nodes; end + + # See Node.each_child_node. + sig { override.returns(T::Enumerator[Node]) } + sig { override.params(blk: ::T.proc.params(arg0: Node).void).void } + def each_child_node(&blk); end + + # See Node.compact_child_nodes. + sig { override.returns(T::Array[Node]) } + def compact_child_nodes; end + + # See Node.comment_targets. + sig { override.returns(T::Array[::T.any(Node, Location)]) } + def comment_targets; end + + # Creates a copy of self with the given fields, using self as the template. + sig { params(node_id: Integer, location: Location, flags: Integer, receiver: ::T.nilable(Node), call_operator_loc: ::T.nilable(Location), message_loc: ::T.nilable(Location), read_name: Symbol, write_name: Symbol, binary_operator: Symbol, binary_operator_loc: Location, value: Node).returns(CallOperatorWriteNode) } + def copy(node_id: T.unsafe(nil), location: T.unsafe(nil), flags: T.unsafe(nil), receiver: T.unsafe(nil), call_operator_loc: T.unsafe(nil), message_loc: T.unsafe(nil), read_name: T.unsafe(nil), write_name: T.unsafe(nil), binary_operator: T.unsafe(nil), binary_operator_loc: T.unsafe(nil), value: T.unsafe(nil)); end + + sig { override.returns(T::Array[::T.nilable(Node)]) } + def deconstruct; end + + sig { params(keys: ::T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, ::T.untyped]) } + def deconstruct_keys(keys); end + + # See `Node#type`. + sig { override.returns(Symbol) } + def type; end + + # See `Node.type`. + sig { override.returns(Symbol) } + def self.type; end + + sig { override.returns(String) } + def inspect; end + + # &. operator + sig { returns(T::Boolean) } + def safe_navigation?; end + + # a call that could have been a local variable + sig { returns(T::Boolean) } + def variable_call?; end + + # a call that is an attribute write, so the value being written should be returned + sig { returns(T::Boolean) } + def attribute_write?; end + + # a call that ignores method visibility + sig { returns(T::Boolean) } + def 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 + # ^^^ + sig { returns(::T.nilable(Node)) } + def receiver; end + + # Represents the Location of the call operator. + # + # foo.bar += value + # ^ + sig { returns(::T.nilable(Location)) } + def call_operator_loc; end + + # Save the call_operator_loc location using the given saved source so that + # it can be retrieved later. + sig { params(repository: ::T.untyped).returns(::T.nilable(Relocation::Entry)) } + def save_call_operator_loc(repository); end + + # Represents the Location of the message. + # + # foo.bar += value + # ^^^ + sig { returns(::T.nilable(Location)) } + def message_loc; end + + # Save the message_loc location using the given saved source so that + # it can be retrieved later. + sig { params(repository: ::T.untyped).returns(::T.nilable(Relocation::Entry)) } + def save_message_loc(repository); end + + # Represents the name of the method being called. + # + # foo.bar += value # read_name `:bar` + # ^^^ + sig { returns(Symbol) } + def read_name; end + + # Represents the name of the method being written to. + # + # foo.bar += value # write_name `:bar=` + # ^^^ + sig { returns(Symbol) } + def write_name; end + + # Represents the binary operator being used. + # + # foo.bar += value # binary_operator `:+` + # ^ + sig { returns(Symbol) } + def binary_operator; end + + # Represents the Location of the binary operator. + # + # foo.bar += value + # ^^ + sig { returns(Location) } + def binary_operator_loc; end + + # Save the binary_operator_loc location using the given saved source so that + # it can be retrieved later. + sig { params(repository: ::T.untyped).returns(Relocation::Entry) } + def save_binary_operator_loc(repository); end + + # Represents the value being assigned. + # + # foo.bar += value + # ^^^^^ + sig { returns(Node) } + def value; end + + # Slice the location of call_operator_loc from the source. + sig { returns(::T.nilable(String)) } + def call_operator; end + + # Slice the location of message_loc from the source. + sig { returns(::T.nilable(String)) } + def message; end + + sig { params(other: ::T.untyped).returns(::T.nilable(T::Boolean)) } + def ===(other); end + end + + # Represents the use of the `||=` operator on a call. + # + # foo.bar ||= value + # ^^^^^^^^^^^^^^^^^ + class CallOrWriteNode < Node + # Initialize a new CallOrWriteNode node. + sig { params(source: Source, node_id: Integer, location: Location, flags: Integer, receiver: ::T.nilable(Node), call_operator_loc: ::T.nilable(Location), message_loc: ::T.nilable(Location), read_name: Symbol, write_name: Symbol, operator_loc: Location, value: Node).void } + def initialize(source, node_id, location, flags, receiver, call_operator_loc, message_loc, read_name, write_name, operator_loc, value); end + + # See Node.accept. + sig { override.params(visitor: Visitor).returns(::T.untyped) } + def accept(visitor); end + + # See Node.child_nodes. + sig { override.returns(T::Array[::T.nilable(Node)]) } + def child_nodes; end + + # See Node.each_child_node. + sig { override.returns(T::Enumerator[Node]) } + sig { override.params(blk: ::T.proc.params(arg0: Node).void).void } + def each_child_node(&blk); end + + # See Node.compact_child_nodes. + sig { override.returns(T::Array[Node]) } + def compact_child_nodes; end + + # See Node.comment_targets. + sig { override.returns(T::Array[::T.any(Node, Location)]) } + def comment_targets; end + + # Creates a copy of self with the given fields, using self as the template. + sig { params(node_id: Integer, location: Location, flags: Integer, receiver: ::T.nilable(Node), call_operator_loc: ::T.nilable(Location), message_loc: ::T.nilable(Location), read_name: Symbol, write_name: Symbol, operator_loc: Location, value: Node).returns(CallOrWriteNode) } + def copy(node_id: T.unsafe(nil), location: T.unsafe(nil), flags: T.unsafe(nil), receiver: T.unsafe(nil), call_operator_loc: T.unsafe(nil), message_loc: T.unsafe(nil), read_name: T.unsafe(nil), write_name: T.unsafe(nil), operator_loc: T.unsafe(nil), value: T.unsafe(nil)); end + + sig { override.returns(T::Array[::T.nilable(Node)]) } + def deconstruct; end + + sig { params(keys: ::T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, ::T.untyped]) } + def deconstruct_keys(keys); end + + # See `Node#type`. + sig { override.returns(Symbol) } + def type; end + + # See `Node.type`. + sig { override.returns(Symbol) } + def self.type; end + + sig { override.returns(String) } + def inspect; end + + # &. operator + sig { returns(T::Boolean) } + def safe_navigation?; end + + # a call that could have been a local variable + sig { returns(T::Boolean) } + def variable_call?; end + + # a call that is an attribute write, so the value being written should be returned + sig { returns(T::Boolean) } + def attribute_write?; end + + # a call that ignores method visibility + sig { returns(T::Boolean) } + def 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 + # ^^^ + sig { returns(::T.nilable(Node)) } + def receiver; end + + # Represents the Location of the call operator. + # + # foo.bar ||= value + # ^ + sig { returns(::T.nilable(Location)) } + def call_operator_loc; end + + # Save the call_operator_loc location using the given saved source so that + # it can be retrieved later. + sig { params(repository: ::T.untyped).returns(::T.nilable(Relocation::Entry)) } + def save_call_operator_loc(repository); end + + # Represents the Location of the message. + # + # foo.bar ||= value + # ^^^ + sig { returns(::T.nilable(Location)) } + def message_loc; end + + # Save the message_loc location using the given saved source so that + # it can be retrieved later. + sig { params(repository: ::T.untyped).returns(::T.nilable(Relocation::Entry)) } + def save_message_loc(repository); end + + # Represents the name of the method being called. + # + # foo.bar ||= value # read_name `:bar` + # ^^^ + sig { returns(Symbol) } + def read_name; end + + # Represents the name of the method being written to. + # + # foo.bar ||= value # write_name `:bar=` + # ^^^ + sig { returns(Symbol) } + def write_name; end + + # Represents the Location of the operator. + # + # foo.bar ||= value + # ^^^ + sig { returns(Location) } + def operator_loc; end + + # Save the operator_loc location using the given saved source so that + # it can be retrieved later. + sig { params(repository: ::T.untyped).returns(Relocation::Entry) } + def save_operator_loc(repository); end + + # Represents the value being assigned. + # + # foo.bar ||= value + # ^^^^^ + sig { returns(Node) } + def value; end + + # Slice the location of call_operator_loc from the source. + sig { returns(::T.nilable(String)) } + def call_operator; end + + # Slice the location of message_loc from the source. + sig { returns(::T.nilable(String)) } + def message; end + + # Slice the location of operator_loc from the source. + sig { returns(String) } + def operator; end + + sig { params(other: ::T.untyped).returns(::T.nilable(T::Boolean)) } + def ===(other); 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. + sig { params(source: Source, node_id: Integer, location: Location, flags: Integer, receiver: Node, call_operator_loc: Location, name: Symbol, message_loc: Location).void } + def initialize(source, node_id, location, flags, receiver, call_operator_loc, name, message_loc); end + + # See Node.accept. + sig { override.params(visitor: Visitor).returns(::T.untyped) } + def accept(visitor); end + + # See Node.child_nodes. + sig { override.returns(T::Array[::T.nilable(Node)]) } + def child_nodes; end + + # See Node.each_child_node. + sig { override.returns(T::Enumerator[Node]) } + sig { override.params(blk: ::T.proc.params(arg0: Node).void).void } + def each_child_node(&blk); end + + # See Node.compact_child_nodes. + sig { override.returns(T::Array[Node]) } + def compact_child_nodes; end + + # See Node.comment_targets. + sig { override.returns(T::Array[::T.any(Node, Location)]) } + def comment_targets; end + + # Creates a copy of self with the given fields, using self as the template. + sig { params(node_id: Integer, location: Location, flags: Integer, receiver: Node, call_operator_loc: Location, name: Symbol, message_loc: Location).returns(CallTargetNode) } + def copy(node_id: T.unsafe(nil), location: T.unsafe(nil), flags: T.unsafe(nil), receiver: T.unsafe(nil), call_operator_loc: T.unsafe(nil), name: T.unsafe(nil), message_loc: T.unsafe(nil)); end + + sig { override.returns(T::Array[::T.nilable(Node)]) } + def deconstruct; end + + sig { params(keys: ::T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, ::T.untyped]) } + def deconstruct_keys(keys); end + + # See `Node#type`. + sig { override.returns(Symbol) } + def type; end + + # See `Node.type`. + sig { override.returns(Symbol) } + def self.type; end + + sig { override.returns(String) } + def inspect; end + + # &. operator + sig { returns(T::Boolean) } + def safe_navigation?; end + + # a call that could have been a local variable + sig { returns(T::Boolean) } + def variable_call?; end + + # a call that is an attribute write, so the value being written should be returned + sig { returns(T::Boolean) } + def attribute_write?; end + + # a call that ignores method visibility + sig { returns(T::Boolean) } + def 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 + # ^^^ + sig { returns(Node) } + def receiver; end + + # Represents the Location of the call operator. + # + # foo.bar = 1 + # ^ + sig { returns(Location) } + def call_operator_loc; end + + # Save the call_operator_loc location using the given saved source so that + # it can be retrieved later. + sig { params(repository: ::T.untyped).returns(Relocation::Entry) } + def save_call_operator_loc(repository); end + + # Represents the name of the method being called. + # + # foo.bar = 1 # name `:foo` + # ^^^ + sig { returns(Symbol) } + def name; end + + # Represents the Location of the message. + # + # foo.bar = 1 + # ^^^ + sig { returns(Location) } + def message_loc; end + + # Save the message_loc location using the given saved source so that + # it can be retrieved later. + sig { params(repository: ::T.untyped).returns(Relocation::Entry) } + def save_message_loc(repository); end + + # Slice the location of call_operator_loc from the source. + sig { returns(String) } + def call_operator; end + + # Slice the location of message_loc from the source. + sig { returns(String) } + def message; end + + sig { params(other: ::T.untyped).returns(::T.nilable(T::Boolean)) } + def ===(other); end + end + + # Represents assigning to a local variable in pattern matching. + # + # foo => [bar => baz] + # ^^^^^^^^^^ + class CapturePatternNode < Node + # Initialize a new CapturePatternNode node. + sig { params(source: Source, node_id: Integer, location: Location, flags: Integer, value: Node, target: LocalVariableTargetNode, operator_loc: Location).void } + def initialize(source, node_id, location, flags, value, target, operator_loc); end + + # See Node.accept. + sig { override.params(visitor: Visitor).returns(::T.untyped) } + def accept(visitor); end + + # See Node.child_nodes. + sig { override.returns(T::Array[::T.nilable(Node)]) } + def child_nodes; end + + # See Node.each_child_node. + sig { override.returns(T::Enumerator[Node]) } + sig { override.params(blk: ::T.proc.params(arg0: Node).void).void } + def each_child_node(&blk); end + + # See Node.compact_child_nodes. + sig { override.returns(T::Array[Node]) } + def compact_child_nodes; end + + # See Node.comment_targets. + sig { override.returns(T::Array[::T.any(Node, Location)]) } + def comment_targets; end + + # Creates a copy of self with the given fields, using self as the template. + sig { params(node_id: Integer, location: Location, flags: Integer, value: Node, target: LocalVariableTargetNode, operator_loc: Location).returns(CapturePatternNode) } + def copy(node_id: T.unsafe(nil), location: T.unsafe(nil), flags: T.unsafe(nil), value: T.unsafe(nil), target: T.unsafe(nil), operator_loc: T.unsafe(nil)); end + + sig { override.returns(T::Array[::T.nilable(Node)]) } + def deconstruct; end + + sig { params(keys: ::T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, ::T.untyped]) } + def deconstruct_keys(keys); end + + # See `Node#type`. + sig { override.returns(Symbol) } + def type; end + + # See `Node.type`. + sig { override.returns(Symbol) } + def self.type; end + + sig { override.returns(String) } + def inspect; end + + # Represents the value to capture. + # + # foo => bar + # ^^^ + sig { returns(Node) } + def value; end + + # Represents the target of the capture. + # + # foo => bar + # ^^^ + sig { returns(LocalVariableTargetNode) } + def target; end + + # Represents the Location of the `=>` operator. + # + # foo => bar + # ^^ + sig { returns(Location) } + def operator_loc; end + + # Save the operator_loc location using the given saved source so that + # it can be retrieved later. + sig { params(repository: ::T.untyped).returns(Relocation::Entry) } + def save_operator_loc(repository); end + + # Slice the location of operator_loc from the source. + sig { returns(String) } + def operator; end + + sig { params(other: ::T.untyped).returns(::T.nilable(T::Boolean)) } + def ===(other); 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. + sig { params(source: Source, node_id: Integer, location: Location, flags: Integer, predicate: ::T.nilable(Node), conditions: T::Array[InNode], else_clause: ::T.nilable(ElseNode), case_keyword_loc: Location, end_keyword_loc: Location).void } + def initialize(source, node_id, location, flags, predicate, conditions, else_clause, case_keyword_loc, end_keyword_loc); end + + # See Node.accept. + sig { override.params(visitor: Visitor).returns(::T.untyped) } + def accept(visitor); end + + # See Node.child_nodes. + sig { override.returns(T::Array[::T.nilable(Node)]) } + def child_nodes; end + + # See Node.each_child_node. + sig { override.returns(T::Enumerator[Node]) } + sig { override.params(blk: ::T.proc.params(arg0: Node).void).void } + def each_child_node(&blk); end + + # See Node.compact_child_nodes. + sig { override.returns(T::Array[Node]) } + def compact_child_nodes; end + + # See Node.comment_targets. + sig { override.returns(T::Array[::T.any(Node, Location)]) } + def comment_targets; end + + # Creates a copy of self with the given fields, using self as the template. + sig { params(node_id: Integer, location: Location, flags: Integer, predicate: ::T.nilable(Node), conditions: T::Array[InNode], else_clause: ::T.nilable(ElseNode), case_keyword_loc: Location, end_keyword_loc: Location).returns(CaseMatchNode) } + def copy(node_id: T.unsafe(nil), location: T.unsafe(nil), flags: T.unsafe(nil), predicate: T.unsafe(nil), conditions: T.unsafe(nil), else_clause: T.unsafe(nil), case_keyword_loc: T.unsafe(nil), end_keyword_loc: T.unsafe(nil)); end + + sig { override.returns(T::Array[::T.nilable(Node)]) } + def deconstruct; end + + sig { params(keys: ::T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, ::T.untyped]) } + def deconstruct_keys(keys); end + + # See `Node#type`. + sig { override.returns(Symbol) } + def type; end + + # See `Node.type`. + sig { override.returns(Symbol) } + def self.type; end + + sig { override.returns(String) } + def inspect; 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 + # ^^^^ + sig { returns(::T.nilable(Node)) } + def predicate; end + + # Represents the conditions of the case match. + # + # case true; in false; end + # ^^^^^^^^ + sig { returns(T::Array[InNode]) } + def conditions; end + + # Represents the else clause of the case match. + # + # case true; in false; else; end + # ^^^^^^^^^ + sig { returns(::T.nilable(ElseNode)) } + def else_clause; end + + # Represents the Location of the `case` keyword. + # + # case true; in false; end + # ^^^^ + sig { returns(Location) } + def case_keyword_loc; end + + # Save the case_keyword_loc location using the given saved source so that + # it can be retrieved later. + sig { params(repository: ::T.untyped).returns(Relocation::Entry) } + def save_case_keyword_loc(repository); end + + # Represents the Location of the `end` keyword. + # + # case true; in false; end + # ^^^ + sig { returns(Location) } + def end_keyword_loc; end + + # Save the end_keyword_loc location using the given saved source so that + # it can be retrieved later. + sig { params(repository: ::T.untyped).returns(Relocation::Entry) } + def save_end_keyword_loc(repository); end + + # Slice the location of case_keyword_loc from the source. + sig { returns(String) } + def case_keyword; end + + # Slice the location of end_keyword_loc from the source. + sig { returns(String) } + def end_keyword; end + + sig { params(other: ::T.untyped).returns(::T.nilable(T::Boolean)) } + def ===(other); end + end + + # Represents the use of a case statement. + # + # case true + # when false + # end + # ^^^^^^^^^^ + class CaseNode < Node + # Initialize a new CaseNode node. + sig { params(source: Source, node_id: Integer, location: Location, flags: Integer, predicate: ::T.nilable(Node), conditions: T::Array[WhenNode], else_clause: ::T.nilable(ElseNode), case_keyword_loc: Location, end_keyword_loc: Location).void } + def initialize(source, node_id, location, flags, predicate, conditions, else_clause, case_keyword_loc, end_keyword_loc); end + + # See Node.accept. + sig { override.params(visitor: Visitor).returns(::T.untyped) } + def accept(visitor); end + + # See Node.child_nodes. + sig { override.returns(T::Array[::T.nilable(Node)]) } + def child_nodes; end + + # See Node.each_child_node. + sig { override.returns(T::Enumerator[Node]) } + sig { override.params(blk: ::T.proc.params(arg0: Node).void).void } + def each_child_node(&blk); end + + # See Node.compact_child_nodes. + sig { override.returns(T::Array[Node]) } + def compact_child_nodes; end + + # See Node.comment_targets. + sig { override.returns(T::Array[::T.any(Node, Location)]) } + def comment_targets; end + + # Creates a copy of self with the given fields, using self as the template. + sig { params(node_id: Integer, location: Location, flags: Integer, predicate: ::T.nilable(Node), conditions: T::Array[WhenNode], else_clause: ::T.nilable(ElseNode), case_keyword_loc: Location, end_keyword_loc: Location).returns(CaseNode) } + def copy(node_id: T.unsafe(nil), location: T.unsafe(nil), flags: T.unsafe(nil), predicate: T.unsafe(nil), conditions: T.unsafe(nil), else_clause: T.unsafe(nil), case_keyword_loc: T.unsafe(nil), end_keyword_loc: T.unsafe(nil)); end + + sig { override.returns(T::Array[::T.nilable(Node)]) } + def deconstruct; end + + sig { params(keys: ::T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, ::T.untyped]) } + def deconstruct_keys(keys); end + + # See `Node#type`. + sig { override.returns(Symbol) } + def type; end + + # See `Node.type`. + sig { override.returns(Symbol) } + def self.type; end + + sig { override.returns(String) } + def inspect; 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 + # ^^^^ + sig { returns(::T.nilable(Node)) } + def predicate; end + + # Represents the conditions of the case statement. + # + # case true; when false; end + # ^^^^^^^^^^ + sig { returns(T::Array[WhenNode]) } + def conditions; end + + # Represents the else clause of the case statement. + # + # case true; when false; else; end + # ^^^^^^^^^ + sig { returns(::T.nilable(ElseNode)) } + def else_clause; end + + # Represents the Location of the `case` keyword. + # + # case true; when false; end + # ^^^^ + sig { returns(Location) } + def case_keyword_loc; end + + # Save the case_keyword_loc location using the given saved source so that + # it can be retrieved later. + sig { params(repository: ::T.untyped).returns(Relocation::Entry) } + def save_case_keyword_loc(repository); end + + # Represents the Location of the `end` keyword. + # + # case true; when false; end + # ^^^ + sig { returns(Location) } + def end_keyword_loc; end + + # Save the end_keyword_loc location using the given saved source so that + # it can be retrieved later. + sig { params(repository: ::T.untyped).returns(Relocation::Entry) } + def save_end_keyword_loc(repository); end + + # Slice the location of case_keyword_loc from the source. + sig { returns(String) } + def case_keyword; end + + # Slice the location of end_keyword_loc from the source. + sig { returns(String) } + def end_keyword; end + + sig { params(other: ::T.untyped).returns(::T.nilable(T::Boolean)) } + def ===(other); end + end + + # Represents a class declaration involving the `class` keyword. + # + # class Foo end + # ^^^^^^^^^^^^^ + class ClassNode < Node + # Initialize a new ClassNode node. + sig { params(source: Source, node_id: Integer, location: Location, flags: Integer, locals: T::Array[Symbol], class_keyword_loc: Location, constant_path: ::T.any(ConstantReadNode, ConstantPathNode, CallNode), inheritance_operator_loc: ::T.nilable(Location), superclass: ::T.nilable(Node), body: ::T.nilable(::T.any(StatementsNode, BeginNode)), end_keyword_loc: 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 + + # See Node.accept. + sig { override.params(visitor: Visitor).returns(::T.untyped) } + def accept(visitor); end + + # See Node.child_nodes. + sig { override.returns(T::Array[::T.nilable(Node)]) } + def child_nodes; end + + # See Node.each_child_node. + sig { override.returns(T::Enumerator[Node]) } + sig { override.params(blk: ::T.proc.params(arg0: Node).void).void } + def each_child_node(&blk); end + + # See Node.compact_child_nodes. + sig { override.returns(T::Array[Node]) } + def compact_child_nodes; end + + # See Node.comment_targets. + sig { override.returns(T::Array[::T.any(Node, Location)]) } + def comment_targets; end + + # Creates a copy of self with the given fields, using self as the template. + sig { params(node_id: Integer, location: Location, flags: Integer, locals: T::Array[Symbol], class_keyword_loc: Location, constant_path: ::T.any(ConstantReadNode, ConstantPathNode, CallNode), inheritance_operator_loc: ::T.nilable(Location), superclass: ::T.nilable(Node), body: ::T.nilable(::T.any(StatementsNode, BeginNode)), end_keyword_loc: Location, name: Symbol).returns(ClassNode) } + def copy(node_id: T.unsafe(nil), location: T.unsafe(nil), flags: T.unsafe(nil), locals: T.unsafe(nil), class_keyword_loc: T.unsafe(nil), constant_path: T.unsafe(nil), inheritance_operator_loc: T.unsafe(nil), superclass: T.unsafe(nil), body: T.unsafe(nil), end_keyword_loc: T.unsafe(nil), name: T.unsafe(nil)); end + + sig { override.returns(T::Array[::T.nilable(Node)]) } + def deconstruct; end + + sig { params(keys: ::T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, ::T.untyped]) } + def deconstruct_keys(keys); end + + # See `Node#type`. + sig { override.returns(Symbol) } + def type; end + + # See `Node.type`. + sig { override.returns(Symbol) } + def self.type; end + + sig { override.returns(String) } + def inspect; end + + # Returns the `locals` attribute. + sig { returns(T::Array[Symbol]) } + def locals; end + + # Represents the Location of the `class` keyword. + # + # class Foo end + # ^^^^^ + sig { returns(Location) } + def class_keyword_loc; end + + # Save the class_keyword_loc location using the given saved source so that + # it can be retrieved later. + sig { params(repository: ::T.untyped).returns(Relocation::Entry) } + def save_class_keyword_loc(repository); end + + # Returns the `constant_path` attribute. + sig { returns(::T.any(ConstantReadNode, ConstantPathNode, CallNode)) } + def constant_path; end + + # Represents the Location of the `<` operator. + # + # class Foo < Bar + # ^ + sig { returns(::T.nilable(Location)) } + def inheritance_operator_loc; end + + # Save the inheritance_operator_loc location using the given saved source so that + # it can be retrieved later. + sig { params(repository: ::T.untyped).returns(::T.nilable(Relocation::Entry)) } + def save_inheritance_operator_loc(repository); end + + # Represents the superclass of the class. + # + # class Foo < Bar + # ^^^ + sig { returns(::T.nilable(Node)) } + def superclass; end + + # Represents the body of the class. + # + # class Foo; bar; end + # ^^^ + sig { returns(::T.nilable(::T.any(StatementsNode, BeginNode))) } + def body; end + + # Represents the Location of the `end` keyword. + # + # class Foo end + # ^^^ + sig { returns(Location) } + def end_keyword_loc; end + + # Save the end_keyword_loc location using the given saved source so that + # it can be retrieved later. + sig { params(repository: ::T.untyped).returns(Relocation::Entry) } + def save_end_keyword_loc(repository); end + + # The name of the class. + # + # class Foo end # name `:Foo` + sig { returns(Symbol) } + def name; end + + # Slice the location of class_keyword_loc from the source. + sig { returns(String) } + def class_keyword; end + + # Slice the location of inheritance_operator_loc from the source. + sig { returns(::T.nilable(String)) } + def inheritance_operator; end + + # Slice the location of end_keyword_loc from the source. + sig { returns(String) } + def end_keyword; end + + sig { params(other: ::T.untyped).returns(::T.nilable(T::Boolean)) } + def ===(other); end + end + + # Represents the use of the `&&=` operator for assignment to a class variable. + # + # @@target &&= value + # ^^^^^^^^^^^^^^^^^^ + class ClassVariableAndWriteNode < Node + # Initialize a new ClassVariableAndWriteNode node. + sig { params(source: Source, node_id: Integer, location: Location, flags: Integer, name: Symbol, name_loc: Location, operator_loc: Location, value: Node).void } + def initialize(source, node_id, location, flags, name, name_loc, operator_loc, value); end + + # See Node.accept. + sig { override.params(visitor: Visitor).returns(::T.untyped) } + def accept(visitor); end + + # See Node.child_nodes. + sig { override.returns(T::Array[::T.nilable(Node)]) } + def child_nodes; end + + # See Node.each_child_node. + sig { override.returns(T::Enumerator[Node]) } + sig { override.params(blk: ::T.proc.params(arg0: Node).void).void } + def each_child_node(&blk); end + + # See Node.compact_child_nodes. + sig { override.returns(T::Array[Node]) } + def compact_child_nodes; end + + # See Node.comment_targets. + sig { override.returns(T::Array[::T.any(Node, Location)]) } + def comment_targets; end + + # Creates a copy of self with the given fields, using self as the template. + sig { params(node_id: Integer, location: Location, flags: Integer, name: Symbol, name_loc: Location, operator_loc: Location, value: Node).returns(ClassVariableAndWriteNode) } + def copy(node_id: T.unsafe(nil), location: T.unsafe(nil), flags: T.unsafe(nil), name: T.unsafe(nil), name_loc: T.unsafe(nil), operator_loc: T.unsafe(nil), value: T.unsafe(nil)); end + + sig { override.returns(T::Array[::T.nilable(Node)]) } + def deconstruct; end + + sig { params(keys: ::T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, ::T.untyped]) } + def deconstruct_keys(keys); end + + # See `Node#type`. + sig { override.returns(Symbol) } + def type; end + + # See `Node.type`. + sig { override.returns(Symbol) } + def self.type; end + + sig { override.returns(String) } + def inspect; 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` + # ^^^^^^^^ + sig { returns(Symbol) } + def name; end + + # Represents the Location of the variable name. + # + # @@target &&= value + # ^^^^^^^^ + sig { returns(Location) } + def name_loc; end + + # Save the name_loc location using the given saved source so that + # it can be retrieved later. + sig { params(repository: ::T.untyped).returns(Relocation::Entry) } + def save_name_loc(repository); end + + # Represents the Location of the `&&=` operator. + # + # @@target &&= value + # ^^^ + sig { returns(Location) } + def operator_loc; end + + # Save the operator_loc location using the given saved source so that + # it can be retrieved later. + sig { params(repository: ::T.untyped).returns(Relocation::Entry) } + def save_operator_loc(repository); 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 + # ^^^^^ + sig { returns(Node) } + def value; end + + # Slice the location of operator_loc from the source. + sig { returns(String) } + def operator; end + + sig { params(other: ::T.untyped).returns(::T.nilable(T::Boolean)) } + def ===(other); end + end + + # Represents assigning to a class variable using an operator that isn't `=`. + # + # @@target += value + # ^^^^^^^^^^^^^^^^^ + class ClassVariableOperatorWriteNode < Node + # Initialize a new ClassVariableOperatorWriteNode node. + sig { params(source: Source, node_id: Integer, location: Location, flags: Integer, name: Symbol, name_loc: Location, binary_operator_loc: Location, value: Node, binary_operator: Symbol).void } + def initialize(source, node_id, location, flags, name, name_loc, binary_operator_loc, value, binary_operator); end + + # See Node.accept. + sig { override.params(visitor: Visitor).returns(::T.untyped) } + def accept(visitor); end + + # See Node.child_nodes. + sig { override.returns(T::Array[::T.nilable(Node)]) } + def child_nodes; end + + # See Node.each_child_node. + sig { override.returns(T::Enumerator[Node]) } + sig { override.params(blk: ::T.proc.params(arg0: Node).void).void } + def each_child_node(&blk); end + + # See Node.compact_child_nodes. + sig { override.returns(T::Array[Node]) } + def compact_child_nodes; end + + # See Node.comment_targets. + sig { override.returns(T::Array[::T.any(Node, Location)]) } + def comment_targets; end + + # Creates a copy of self with the given fields, using self as the template. + sig { params(node_id: Integer, location: Location, flags: Integer, name: Symbol, name_loc: Location, binary_operator_loc: Location, value: Node, binary_operator: Symbol).returns(ClassVariableOperatorWriteNode) } + def copy(node_id: T.unsafe(nil), location: T.unsafe(nil), flags: T.unsafe(nil), name: T.unsafe(nil), name_loc: T.unsafe(nil), binary_operator_loc: T.unsafe(nil), value: T.unsafe(nil), binary_operator: T.unsafe(nil)); end + + sig { override.returns(T::Array[::T.nilable(Node)]) } + def deconstruct; end + + sig { params(keys: ::T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, ::T.untyped]) } + def deconstruct_keys(keys); end + + # See `Node#type`. + sig { override.returns(Symbol) } + def type; end + + # See `Node.type`. + sig { override.returns(Symbol) } + def self.type; end + + sig { override.returns(String) } + def inspect; end + + # Returns the `name` attribute. + sig { returns(Symbol) } + def name; end + + # Returns the Location represented by `name_loc`. + sig { returns(Location) } + def name_loc; end + + # Save the name_loc location using the given saved source so that + # it can be retrieved later. + sig { params(repository: ::T.untyped).returns(Relocation::Entry) } + def save_name_loc(repository); end + + # Returns the Location represented by `binary_operator_loc`. + sig { returns(Location) } + def binary_operator_loc; end + + # Save the binary_operator_loc location using the given saved source so that + # it can be retrieved later. + sig { params(repository: ::T.untyped).returns(Relocation::Entry) } + def save_binary_operator_loc(repository); end + + # Returns the `value` attribute. + sig { returns(Node) } + def value; end + + # Returns the `binary_operator` attribute. + sig { returns(Symbol) } + def binary_operator; end + + sig { params(other: ::T.untyped).returns(::T.nilable(T::Boolean)) } + def ===(other); end + end + + # Represents the use of the `||=` operator for assignment to a class variable. + # + # @@target ||= value + # ^^^^^^^^^^^^^^^^^^ + class ClassVariableOrWriteNode < Node + # Initialize a new ClassVariableOrWriteNode node. + sig { params(source: Source, node_id: Integer, location: Location, flags: Integer, name: Symbol, name_loc: Location, operator_loc: Location, value: Node).void } + def initialize(source, node_id, location, flags, name, name_loc, operator_loc, value); end + + # See Node.accept. + sig { override.params(visitor: Visitor).returns(::T.untyped) } + def accept(visitor); end + + # See Node.child_nodes. + sig { override.returns(T::Array[::T.nilable(Node)]) } + def child_nodes; end + + # See Node.each_child_node. + sig { override.returns(T::Enumerator[Node]) } + sig { override.params(blk: ::T.proc.params(arg0: Node).void).void } + def each_child_node(&blk); end + + # See Node.compact_child_nodes. + sig { override.returns(T::Array[Node]) } + def compact_child_nodes; end + + # See Node.comment_targets. + sig { override.returns(T::Array[::T.any(Node, Location)]) } + def comment_targets; end + + # Creates a copy of self with the given fields, using self as the template. + sig { params(node_id: Integer, location: Location, flags: Integer, name: Symbol, name_loc: Location, operator_loc: Location, value: Node).returns(ClassVariableOrWriteNode) } + def copy(node_id: T.unsafe(nil), location: T.unsafe(nil), flags: T.unsafe(nil), name: T.unsafe(nil), name_loc: T.unsafe(nil), operator_loc: T.unsafe(nil), value: T.unsafe(nil)); end + + sig { override.returns(T::Array[::T.nilable(Node)]) } + def deconstruct; end + + sig { params(keys: ::T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, ::T.untyped]) } + def deconstruct_keys(keys); end + + # See `Node#type`. + sig { override.returns(Symbol) } + def type; end + + # See `Node.type`. + sig { override.returns(Symbol) } + def self.type; end + + sig { override.returns(String) } + def inspect; end + + # Returns the `name` attribute. + sig { returns(Symbol) } + def name; end + + # Returns the Location represented by `name_loc`. + sig { returns(Location) } + def name_loc; end + + # Save the name_loc location using the given saved source so that + # it can be retrieved later. + sig { params(repository: ::T.untyped).returns(Relocation::Entry) } + def save_name_loc(repository); end + + # Returns the Location represented by `operator_loc`. + sig { returns(Location) } + def operator_loc; end + + # Save the operator_loc location using the given saved source so that + # it can be retrieved later. + sig { params(repository: ::T.untyped).returns(Relocation::Entry) } + def save_operator_loc(repository); end + + # Returns the `value` attribute. + sig { returns(Node) } + def value; end + + # Slice the location of operator_loc from the source. + sig { returns(String) } + def operator; end + + sig { params(other: ::T.untyped).returns(::T.nilable(T::Boolean)) } + def ===(other); end + end + + # Represents referencing a class variable. + # + # @@foo + # ^^^^^ + class ClassVariableReadNode < Node + # Initialize a new ClassVariableReadNode node. + sig { params(source: Source, node_id: Integer, location: Location, flags: Integer, name: Symbol).void } + def initialize(source, node_id, location, flags, name); end + + # See Node.accept. + sig { override.params(visitor: Visitor).returns(::T.untyped) } + def accept(visitor); end + + # See Node.child_nodes. + sig { override.returns(T::Array[::T.nilable(Node)]) } + def child_nodes; end + + # See Node.each_child_node. + sig { override.returns(T::Enumerator[Node]) } + sig { override.params(blk: ::T.proc.params(arg0: Node).void).void } + def each_child_node(&blk); end + + # See Node.compact_child_nodes. + sig { override.returns(T::Array[Node]) } + def compact_child_nodes; end + + # See Node.comment_targets. + sig { override.returns(T::Array[::T.any(Node, Location)]) } + def comment_targets; end + + # Creates a copy of self with the given fields, using self as the template. + sig { params(node_id: Integer, location: Location, flags: Integer, name: Symbol).returns(ClassVariableReadNode) } + def copy(node_id: T.unsafe(nil), location: T.unsafe(nil), flags: T.unsafe(nil), name: T.unsafe(nil)); end + + sig { override.returns(T::Array[::T.nilable(Node)]) } + def deconstruct; end + + sig { params(keys: ::T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, ::T.untyped]) } + def deconstruct_keys(keys); end + + # See `Node#type`. + sig { override.returns(Symbol) } + def type; end + + # See `Node.type`. + sig { override.returns(Symbol) } + def self.type; end + + sig { override.returns(String) } + def inspect; 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` + sig { returns(Symbol) } + def name; end + + sig { params(other: ::T.untyped).returns(::T.nilable(T::Boolean)) } + def ===(other); 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. + sig { params(source: Source, node_id: Integer, location: Location, flags: Integer, name: Symbol).void } + def initialize(source, node_id, location, flags, name); end + + # See Node.accept. + sig { override.params(visitor: Visitor).returns(::T.untyped) } + def accept(visitor); end + + # See Node.child_nodes. + sig { override.returns(T::Array[::T.nilable(Node)]) } + def child_nodes; end + + # See Node.each_child_node. + sig { override.returns(T::Enumerator[Node]) } + sig { override.params(blk: ::T.proc.params(arg0: Node).void).void } + def each_child_node(&blk); end + + # See Node.compact_child_nodes. + sig { override.returns(T::Array[Node]) } + def compact_child_nodes; end + + # See Node.comment_targets. + sig { override.returns(T::Array[::T.any(Node, Location)]) } + def comment_targets; end + + # Creates a copy of self with the given fields, using self as the template. + sig { params(node_id: Integer, location: Location, flags: Integer, name: Symbol).returns(ClassVariableTargetNode) } + def copy(node_id: T.unsafe(nil), location: T.unsafe(nil), flags: T.unsafe(nil), name: T.unsafe(nil)); end + + sig { override.returns(T::Array[::T.nilable(Node)]) } + def deconstruct; end + + sig { params(keys: ::T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, ::T.untyped]) } + def deconstruct_keys(keys); end + + # See `Node#type`. + sig { override.returns(Symbol) } + def type; end + + # See `Node.type`. + sig { override.returns(Symbol) } + def self.type; end + + sig { override.returns(String) } + def inspect; end + + # Returns the `name` attribute. + sig { returns(Symbol) } + def name; end + + sig { params(other: ::T.untyped).returns(::T.nilable(T::Boolean)) } + def ===(other); end + end + + # Represents writing to a class variable. + # + # @@foo = 1 + # ^^^^^^^^^ + class ClassVariableWriteNode < Node + # Initialize a new ClassVariableWriteNode node. + sig { params(source: Source, node_id: Integer, location: Location, flags: Integer, name: Symbol, name_loc: Location, value: Node, operator_loc: Location).void } + def initialize(source, node_id, location, flags, name, name_loc, value, operator_loc); end + + # See Node.accept. + sig { override.params(visitor: Visitor).returns(::T.untyped) } + def accept(visitor); end + + # See Node.child_nodes. + sig { override.returns(T::Array[::T.nilable(Node)]) } + def child_nodes; end + + # See Node.each_child_node. + sig { override.returns(T::Enumerator[Node]) } + sig { override.params(blk: ::T.proc.params(arg0: Node).void).void } + def each_child_node(&blk); end + + # See Node.compact_child_nodes. + sig { override.returns(T::Array[Node]) } + def compact_child_nodes; end + + # See Node.comment_targets. + sig { override.returns(T::Array[::T.any(Node, Location)]) } + def comment_targets; end + + # Creates a copy of self with the given fields, using self as the template. + sig { params(node_id: Integer, location: Location, flags: Integer, name: Symbol, name_loc: Location, value: Node, operator_loc: Location).returns(ClassVariableWriteNode) } + def copy(node_id: T.unsafe(nil), location: T.unsafe(nil), flags: T.unsafe(nil), name: T.unsafe(nil), name_loc: T.unsafe(nil), value: T.unsafe(nil), operator_loc: T.unsafe(nil)); end + + sig { override.returns(T::Array[::T.nilable(Node)]) } + def deconstruct; end + + sig { params(keys: ::T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, ::T.untyped]) } + def deconstruct_keys(keys); end + + # See `Node#type`. + sig { override.returns(Symbol) } + def type; end + + # See `Node.type`. + sig { override.returns(Symbol) } + def self.type; end + + sig { override.returns(String) } + def inspect; 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` + sig { returns(Symbol) } + def name; end + + # The Location of the variable name. + # + # @@foo = :bar + # ^^^^^ + sig { returns(Location) } + def name_loc; end + + # Save the name_loc location using the given saved source so that + # it can be retrieved later. + sig { params(repository: ::T.untyped).returns(Relocation::Entry) } + def save_name_loc(repository); 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 + # ^^^ + sig { returns(Node) } + def value; end + + # The Location of the `=` operator. + # + # @@foo = :bar + # ^ + sig { returns(Location) } + def operator_loc; end + + # Save the operator_loc location using the given saved source so that + # it can be retrieved later. + sig { params(repository: ::T.untyped).returns(Relocation::Entry) } + def save_operator_loc(repository); end + + # Slice the location of operator_loc from the source. + sig { returns(String) } + def operator; end + + sig { params(other: ::T.untyped).returns(::T.nilable(T::Boolean)) } + def ===(other); end + end + + # Represents the use of the `&&=` operator for assignment to a constant. + # + # Target &&= value + # ^^^^^^^^^^^^^^^^ + class ConstantAndWriteNode < Node + # Initialize a new ConstantAndWriteNode node. + sig { params(source: Source, node_id: Integer, location: Location, flags: Integer, name: Symbol, name_loc: Location, operator_loc: Location, value: Node).void } + def initialize(source, node_id, location, flags, name, name_loc, operator_loc, value); end + + # See Node.accept. + sig { override.params(visitor: Visitor).returns(::T.untyped) } + def accept(visitor); end + + # See Node.child_nodes. + sig { override.returns(T::Array[::T.nilable(Node)]) } + def child_nodes; end + + # See Node.each_child_node. + sig { override.returns(T::Enumerator[Node]) } + sig { override.params(blk: ::T.proc.params(arg0: Node).void).void } + def each_child_node(&blk); end + + # See Node.compact_child_nodes. + sig { override.returns(T::Array[Node]) } + def compact_child_nodes; end + + # See Node.comment_targets. + sig { override.returns(T::Array[::T.any(Node, Location)]) } + def comment_targets; end + + # Creates a copy of self with the given fields, using self as the template. + sig { params(node_id: Integer, location: Location, flags: Integer, name: Symbol, name_loc: Location, operator_loc: Location, value: Node).returns(ConstantAndWriteNode) } + def copy(node_id: T.unsafe(nil), location: T.unsafe(nil), flags: T.unsafe(nil), name: T.unsafe(nil), name_loc: T.unsafe(nil), operator_loc: T.unsafe(nil), value: T.unsafe(nil)); end + + sig { override.returns(T::Array[::T.nilable(Node)]) } + def deconstruct; end + + sig { params(keys: ::T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, ::T.untyped]) } + def deconstruct_keys(keys); end + + # See `Node#type`. + sig { override.returns(Symbol) } + def type; end + + # See `Node.type`. + sig { override.returns(Symbol) } + def self.type; end + + sig { override.returns(String) } + def inspect; end + + # Returns the `name` attribute. + sig { returns(Symbol) } + def name; end + + # Returns the Location represented by `name_loc`. + sig { returns(Location) } + def name_loc; end + + # Save the name_loc location using the given saved source so that + # it can be retrieved later. + sig { params(repository: ::T.untyped).returns(Relocation::Entry) } + def save_name_loc(repository); end + + # Returns the Location represented by `operator_loc`. + sig { returns(Location) } + def operator_loc; end + + # Save the operator_loc location using the given saved source so that + # it can be retrieved later. + sig { params(repository: ::T.untyped).returns(Relocation::Entry) } + def save_operator_loc(repository); end + + # Returns the `value` attribute. + sig { returns(Node) } + def value; end + + # Slice the location of operator_loc from the source. + sig { returns(String) } + def operator; end + + sig { params(other: ::T.untyped).returns(::T.nilable(T::Boolean)) } + def ===(other); end + end + + # Represents assigning to a constant using an operator that isn't `=`. + # + # Target += value + # ^^^^^^^^^^^^^^^ + class ConstantOperatorWriteNode < Node + # Initialize a new ConstantOperatorWriteNode node. + sig { params(source: Source, node_id: Integer, location: Location, flags: Integer, name: Symbol, name_loc: Location, binary_operator_loc: Location, value: Node, binary_operator: Symbol).void } + def initialize(source, node_id, location, flags, name, name_loc, binary_operator_loc, value, binary_operator); end + + # See Node.accept. + sig { override.params(visitor: Visitor).returns(::T.untyped) } + def accept(visitor); end + + # See Node.child_nodes. + sig { override.returns(T::Array[::T.nilable(Node)]) } + def child_nodes; end + + # See Node.each_child_node. + sig { override.returns(T::Enumerator[Node]) } + sig { override.params(blk: ::T.proc.params(arg0: Node).void).void } + def each_child_node(&blk); end + + # See Node.compact_child_nodes. + sig { override.returns(T::Array[Node]) } + def compact_child_nodes; end + + # See Node.comment_targets. + sig { override.returns(T::Array[::T.any(Node, Location)]) } + def comment_targets; end + + # Creates a copy of self with the given fields, using self as the template. + sig { params(node_id: Integer, location: Location, flags: Integer, name: Symbol, name_loc: Location, binary_operator_loc: Location, value: Node, binary_operator: Symbol).returns(ConstantOperatorWriteNode) } + def copy(node_id: T.unsafe(nil), location: T.unsafe(nil), flags: T.unsafe(nil), name: T.unsafe(nil), name_loc: T.unsafe(nil), binary_operator_loc: T.unsafe(nil), value: T.unsafe(nil), binary_operator: T.unsafe(nil)); end + + sig { override.returns(T::Array[::T.nilable(Node)]) } + def deconstruct; end + + sig { params(keys: ::T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, ::T.untyped]) } + def deconstruct_keys(keys); end + + # See `Node#type`. + sig { override.returns(Symbol) } + def type; end + + # See `Node.type`. + sig { override.returns(Symbol) } + def self.type; end + + sig { override.returns(String) } + def inspect; end + + # Returns the `name` attribute. + sig { returns(Symbol) } + def name; end + + # Returns the Location represented by `name_loc`. + sig { returns(Location) } + def name_loc; end + + # Save the name_loc location using the given saved source so that + # it can be retrieved later. + sig { params(repository: ::T.untyped).returns(Relocation::Entry) } + def save_name_loc(repository); end + + # Returns the Location represented by `binary_operator_loc`. + sig { returns(Location) } + def binary_operator_loc; end + + # Save the binary_operator_loc location using the given saved source so that + # it can be retrieved later. + sig { params(repository: ::T.untyped).returns(Relocation::Entry) } + def save_binary_operator_loc(repository); end + + # Returns the `value` attribute. + sig { returns(Node) } + def value; end + + # Returns the `binary_operator` attribute. + sig { returns(Symbol) } + def binary_operator; end + + sig { params(other: ::T.untyped).returns(::T.nilable(T::Boolean)) } + def ===(other); end + end + + # Represents the use of the `||=` operator for assignment to a constant. + # + # Target ||= value + # ^^^^^^^^^^^^^^^^ + class ConstantOrWriteNode < Node + # Initialize a new ConstantOrWriteNode node. + sig { params(source: Source, node_id: Integer, location: Location, flags: Integer, name: Symbol, name_loc: Location, operator_loc: Location, value: Node).void } + def initialize(source, node_id, location, flags, name, name_loc, operator_loc, value); end + + # See Node.accept. + sig { override.params(visitor: Visitor).returns(::T.untyped) } + def accept(visitor); end + + # See Node.child_nodes. + sig { override.returns(T::Array[::T.nilable(Node)]) } + def child_nodes; end + + # See Node.each_child_node. + sig { override.returns(T::Enumerator[Node]) } + sig { override.params(blk: ::T.proc.params(arg0: Node).void).void } + def each_child_node(&blk); end + + # See Node.compact_child_nodes. + sig { override.returns(T::Array[Node]) } + def compact_child_nodes; end + + # See Node.comment_targets. + sig { override.returns(T::Array[::T.any(Node, Location)]) } + def comment_targets; end + + # Creates a copy of self with the given fields, using self as the template. + sig { params(node_id: Integer, location: Location, flags: Integer, name: Symbol, name_loc: Location, operator_loc: Location, value: Node).returns(ConstantOrWriteNode) } + def copy(node_id: T.unsafe(nil), location: T.unsafe(nil), flags: T.unsafe(nil), name: T.unsafe(nil), name_loc: T.unsafe(nil), operator_loc: T.unsafe(nil), value: T.unsafe(nil)); end + + sig { override.returns(T::Array[::T.nilable(Node)]) } + def deconstruct; end + + sig { params(keys: ::T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, ::T.untyped]) } + def deconstruct_keys(keys); end + + # See `Node#type`. + sig { override.returns(Symbol) } + def type; end + + # See `Node.type`. + sig { override.returns(Symbol) } + def self.type; end + + sig { override.returns(String) } + def inspect; end + + # Returns the `name` attribute. + sig { returns(Symbol) } + def name; end + + # Returns the Location represented by `name_loc`. + sig { returns(Location) } + def name_loc; end + + # Save the name_loc location using the given saved source so that + # it can be retrieved later. + sig { params(repository: ::T.untyped).returns(Relocation::Entry) } + def save_name_loc(repository); end + + # Returns the Location represented by `operator_loc`. + sig { returns(Location) } + def operator_loc; end + + # Save the operator_loc location using the given saved source so that + # it can be retrieved later. + sig { params(repository: ::T.untyped).returns(Relocation::Entry) } + def save_operator_loc(repository); end + + # Returns the `value` attribute. + sig { returns(Node) } + def value; end + + # Slice the location of operator_loc from the source. + sig { returns(String) } + def operator; end + + sig { params(other: ::T.untyped).returns(::T.nilable(T::Boolean)) } + def ===(other); 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. + sig { params(source: Source, node_id: Integer, location: Location, flags: Integer, target: ConstantPathNode, operator_loc: Location, value: Node).void } + def initialize(source, node_id, location, flags, target, operator_loc, value); end + + # See Node.accept. + sig { override.params(visitor: Visitor).returns(::T.untyped) } + def accept(visitor); end + + # See Node.child_nodes. + sig { override.returns(T::Array[::T.nilable(Node)]) } + def child_nodes; end + + # See Node.each_child_node. + sig { override.returns(T::Enumerator[Node]) } + sig { override.params(blk: ::T.proc.params(arg0: Node).void).void } + def each_child_node(&blk); end + + # See Node.compact_child_nodes. + sig { override.returns(T::Array[Node]) } + def compact_child_nodes; end + + # See Node.comment_targets. + sig { override.returns(T::Array[::T.any(Node, Location)]) } + def comment_targets; end + + # Creates a copy of self with the given fields, using self as the template. + sig { params(node_id: Integer, location: Location, flags: Integer, target: ConstantPathNode, operator_loc: Location, value: Node).returns(ConstantPathAndWriteNode) } + def copy(node_id: T.unsafe(nil), location: T.unsafe(nil), flags: T.unsafe(nil), target: T.unsafe(nil), operator_loc: T.unsafe(nil), value: T.unsafe(nil)); end + + sig { override.returns(T::Array[::T.nilable(Node)]) } + def deconstruct; end + + sig { params(keys: ::T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, ::T.untyped]) } + def deconstruct_keys(keys); end + + # See `Node#type`. + sig { override.returns(Symbol) } + def type; end + + # See `Node.type`. + sig { override.returns(Symbol) } + def self.type; end + + sig { override.returns(String) } + def inspect; end + + # Returns the `target` attribute. + sig { returns(ConstantPathNode) } + def target; end + + # Returns the Location represented by `operator_loc`. + sig { returns(Location) } + def operator_loc; end + + # Save the operator_loc location using the given saved source so that + # it can be retrieved later. + sig { params(repository: ::T.untyped).returns(Relocation::Entry) } + def save_operator_loc(repository); end + + # Returns the `value` attribute. + sig { returns(Node) } + def value; end + + # Slice the location of operator_loc from the source. + sig { returns(String) } + def operator; end + + sig { params(other: ::T.untyped).returns(::T.nilable(T::Boolean)) } + def ===(other); end + end + + # Represents accessing a constant through a path of `::` operators. + # + # Foo::Bar + # ^^^^^^^^ + class ConstantPathNode < Node + # Initialize a new ConstantPathNode node. + sig { params(source: Source, node_id: Integer, location: Location, flags: Integer, parent: ::T.nilable(Node), name: ::T.nilable(Symbol), delimiter_loc: Location, name_loc: Location).void } + def initialize(source, node_id, location, flags, parent, name, delimiter_loc, name_loc); end + + # See Node.accept. + sig { override.params(visitor: Visitor).returns(::T.untyped) } + def accept(visitor); end + + # See Node.child_nodes. + sig { override.returns(T::Array[::T.nilable(Node)]) } + def child_nodes; end + + # See Node.each_child_node. + sig { override.returns(T::Enumerator[Node]) } + sig { override.params(blk: ::T.proc.params(arg0: Node).void).void } + def each_child_node(&blk); end + + # See Node.compact_child_nodes. + sig { override.returns(T::Array[Node]) } + def compact_child_nodes; end + + # See Node.comment_targets. + sig { override.returns(T::Array[::T.any(Node, Location)]) } + def comment_targets; end + + # Creates a copy of self with the given fields, using self as the template. + sig { params(node_id: Integer, location: Location, flags: Integer, parent: ::T.nilable(Node), name: ::T.nilable(Symbol), delimiter_loc: Location, name_loc: Location).returns(ConstantPathNode) } + def copy(node_id: T.unsafe(nil), location: T.unsafe(nil), flags: T.unsafe(nil), parent: T.unsafe(nil), name: T.unsafe(nil), delimiter_loc: T.unsafe(nil), name_loc: T.unsafe(nil)); end + + sig { override.returns(T::Array[::T.nilable(Node)]) } + def deconstruct; end + + sig { params(keys: ::T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, ::T.untyped]) } + def deconstruct_keys(keys); end + + # See `Node#type`. + sig { override.returns(Symbol) } + def type; end + + # See `Node.type`. + sig { override.returns(Symbol) } + def self.type; end + + sig { override.returns(String) } + def inspect; 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 + # ^^^ + sig { returns(::T.nilable(Node)) } + def parent; end + + # The name of the constant being accessed. This could be `nil` in the event of a syntax error. + sig { returns(::T.nilable(Symbol)) } + def name; end + + # The Location of the `::` delimiter. + # + # ::Foo + # ^^ + # + # One::Two + # ^^ + sig { returns(Location) } + def delimiter_loc; end + + # Save the delimiter_loc location using the given saved source so that + # it can be retrieved later. + sig { params(repository: ::T.untyped).returns(Relocation::Entry) } + def save_delimiter_loc(repository); end + + # The Location of the name of the constant. + # + # ::Foo + # ^^^ + # + # One::Two + # ^^^ + sig { returns(Location) } + def name_loc; end + + # Save the name_loc location using the given saved source so that + # it can be retrieved later. + sig { params(repository: ::T.untyped).returns(Relocation::Entry) } + def save_name_loc(repository); end + + # Slice the location of delimiter_loc from the source. + sig { returns(String) } + def delimiter; end + + sig { params(other: ::T.untyped).returns(::T.nilable(T::Boolean)) } + def ===(other); 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. + sig { params(source: Source, node_id: Integer, location: Location, flags: Integer, target: ConstantPathNode, binary_operator_loc: Location, value: Node, binary_operator: Symbol).void } + def initialize(source, node_id, location, flags, target, binary_operator_loc, value, binary_operator); end + + # See Node.accept. + sig { override.params(visitor: Visitor).returns(::T.untyped) } + def accept(visitor); end + + # See Node.child_nodes. + sig { override.returns(T::Array[::T.nilable(Node)]) } + def child_nodes; end + + # See Node.each_child_node. + sig { override.returns(T::Enumerator[Node]) } + sig { override.params(blk: ::T.proc.params(arg0: Node).void).void } + def each_child_node(&blk); end + + # See Node.compact_child_nodes. + sig { override.returns(T::Array[Node]) } + def compact_child_nodes; end + + # See Node.comment_targets. + sig { override.returns(T::Array[::T.any(Node, Location)]) } + def comment_targets; end + + # Creates a copy of self with the given fields, using self as the template. + sig { params(node_id: Integer, location: Location, flags: Integer, target: ConstantPathNode, binary_operator_loc: Location, value: Node, binary_operator: Symbol).returns(ConstantPathOperatorWriteNode) } + def copy(node_id: T.unsafe(nil), location: T.unsafe(nil), flags: T.unsafe(nil), target: T.unsafe(nil), binary_operator_loc: T.unsafe(nil), value: T.unsafe(nil), binary_operator: T.unsafe(nil)); end + + sig { override.returns(T::Array[::T.nilable(Node)]) } + def deconstruct; end + + sig { params(keys: ::T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, ::T.untyped]) } + def deconstruct_keys(keys); end + + # See `Node#type`. + sig { override.returns(Symbol) } + def type; end + + # See `Node.type`. + sig { override.returns(Symbol) } + def self.type; end + + sig { override.returns(String) } + def inspect; end + + # Returns the `target` attribute. + sig { returns(ConstantPathNode) } + def target; end + + # Returns the Location represented by `binary_operator_loc`. + sig { returns(Location) } + def binary_operator_loc; end + + # Save the binary_operator_loc location using the given saved source so that + # it can be retrieved later. + sig { params(repository: ::T.untyped).returns(Relocation::Entry) } + def save_binary_operator_loc(repository); end + + # Returns the `value` attribute. + sig { returns(Node) } + def value; end + + # Returns the `binary_operator` attribute. + sig { returns(Symbol) } + def binary_operator; end + + sig { params(other: ::T.untyped).returns(::T.nilable(T::Boolean)) } + def ===(other); 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. + sig { params(source: Source, node_id: Integer, location: Location, flags: Integer, target: ConstantPathNode, operator_loc: Location, value: Node).void } + def initialize(source, node_id, location, flags, target, operator_loc, value); end + + # See Node.accept. + sig { override.params(visitor: Visitor).returns(::T.untyped) } + def accept(visitor); end + + # See Node.child_nodes. + sig { override.returns(T::Array[::T.nilable(Node)]) } + def child_nodes; end + + # See Node.each_child_node. + sig { override.returns(T::Enumerator[Node]) } + sig { override.params(blk: ::T.proc.params(arg0: Node).void).void } + def each_child_node(&blk); end + + # See Node.compact_child_nodes. + sig { override.returns(T::Array[Node]) } + def compact_child_nodes; end + + # See Node.comment_targets. + sig { override.returns(T::Array[::T.any(Node, Location)]) } + def comment_targets; end + + # Creates a copy of self with the given fields, using self as the template. + sig { params(node_id: Integer, location: Location, flags: Integer, target: ConstantPathNode, operator_loc: Location, value: Node).returns(ConstantPathOrWriteNode) } + def copy(node_id: T.unsafe(nil), location: T.unsafe(nil), flags: T.unsafe(nil), target: T.unsafe(nil), operator_loc: T.unsafe(nil), value: T.unsafe(nil)); end + + sig { override.returns(T::Array[::T.nilable(Node)]) } + def deconstruct; end + + sig { params(keys: ::T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, ::T.untyped]) } + def deconstruct_keys(keys); end + + # See `Node#type`. + sig { override.returns(Symbol) } + def type; end + + # See `Node.type`. + sig { override.returns(Symbol) } + def self.type; end + + sig { override.returns(String) } + def inspect; end + + # Returns the `target` attribute. + sig { returns(ConstantPathNode) } + def target; end + + # Returns the Location represented by `operator_loc`. + sig { returns(Location) } + def operator_loc; end + + # Save the operator_loc location using the given saved source so that + # it can be retrieved later. + sig { params(repository: ::T.untyped).returns(Relocation::Entry) } + def save_operator_loc(repository); end + + # Returns the `value` attribute. + sig { returns(Node) } + def value; end + + # Slice the location of operator_loc from the source. + sig { returns(String) } + def operator; end + + sig { params(other: ::T.untyped).returns(::T.nilable(T::Boolean)) } + def ===(other); 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. + sig { params(source: Source, node_id: Integer, location: Location, flags: Integer, parent: ::T.nilable(Node), name: ::T.nilable(Symbol), delimiter_loc: Location, name_loc: Location).void } + def initialize(source, node_id, location, flags, parent, name, delimiter_loc, name_loc); end + + # See Node.accept. + sig { override.params(visitor: Visitor).returns(::T.untyped) } + def accept(visitor); end + + # See Node.child_nodes. + sig { override.returns(T::Array[::T.nilable(Node)]) } + def child_nodes; end + + # See Node.each_child_node. + sig { override.returns(T::Enumerator[Node]) } + sig { override.params(blk: ::T.proc.params(arg0: Node).void).void } + def each_child_node(&blk); end + + # See Node.compact_child_nodes. + sig { override.returns(T::Array[Node]) } + def compact_child_nodes; end + + # See Node.comment_targets. + sig { override.returns(T::Array[::T.any(Node, Location)]) } + def comment_targets; end + + # Creates a copy of self with the given fields, using self as the template. + sig { params(node_id: Integer, location: Location, flags: Integer, parent: ::T.nilable(Node), name: ::T.nilable(Symbol), delimiter_loc: Location, name_loc: Location).returns(ConstantPathTargetNode) } + def copy(node_id: T.unsafe(nil), location: T.unsafe(nil), flags: T.unsafe(nil), parent: T.unsafe(nil), name: T.unsafe(nil), delimiter_loc: T.unsafe(nil), name_loc: T.unsafe(nil)); end + + sig { override.returns(T::Array[::T.nilable(Node)]) } + def deconstruct; end + + sig { params(keys: ::T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, ::T.untyped]) } + def deconstruct_keys(keys); end + + # See `Node#type`. + sig { override.returns(Symbol) } + def type; end + + # See `Node.type`. + sig { override.returns(Symbol) } + def self.type; end + + sig { override.returns(String) } + def inspect; end + + # Returns the `parent` attribute. + sig { returns(::T.nilable(Node)) } + def parent; end + + # Returns the `name` attribute. + sig { returns(::T.nilable(Symbol)) } + def name; end + + # Returns the Location represented by `delimiter_loc`. + sig { returns(Location) } + def delimiter_loc; end + + # Save the delimiter_loc location using the given saved source so that + # it can be retrieved later. + sig { params(repository: ::T.untyped).returns(Relocation::Entry) } + def save_delimiter_loc(repository); end + + # Returns the Location represented by `name_loc`. + sig { returns(Location) } + def name_loc; end + + # Save the name_loc location using the given saved source so that + # it can be retrieved later. + sig { params(repository: ::T.untyped).returns(Relocation::Entry) } + def save_name_loc(repository); end + + # Slice the location of delimiter_loc from the source. + sig { returns(String) } + def delimiter; end + + sig { params(other: ::T.untyped).returns(::T.nilable(T::Boolean)) } + def ===(other); end + end + + # Represents writing to a constant path. + # + # ::Foo = 1 + # ^^^^^^^^^ + # + # Foo::Bar = 1 + # ^^^^^^^^^^^^ + # + # ::Foo::Bar = 1 + # ^^^^^^^^^^^^^^ + class ConstantPathWriteNode < Node + # Initialize a new ConstantPathWriteNode node. + sig { params(source: Source, node_id: Integer, location: Location, flags: Integer, target: ConstantPathNode, operator_loc: Location, value: Node).void } + def initialize(source, node_id, location, flags, target, operator_loc, value); end + + # See Node.accept. + sig { override.params(visitor: Visitor).returns(::T.untyped) } + def accept(visitor); end + + # See Node.child_nodes. + sig { override.returns(T::Array[::T.nilable(Node)]) } + def child_nodes; end + + # See Node.each_child_node. + sig { override.returns(T::Enumerator[Node]) } + sig { override.params(blk: ::T.proc.params(arg0: Node).void).void } + def each_child_node(&blk); end + + # See Node.compact_child_nodes. + sig { override.returns(T::Array[Node]) } + def compact_child_nodes; end + + # See Node.comment_targets. + sig { override.returns(T::Array[::T.any(Node, Location)]) } + def comment_targets; end + + # Creates a copy of self with the given fields, using self as the template. + sig { params(node_id: Integer, location: Location, flags: Integer, target: ConstantPathNode, operator_loc: Location, value: Node).returns(ConstantPathWriteNode) } + def copy(node_id: T.unsafe(nil), location: T.unsafe(nil), flags: T.unsafe(nil), target: T.unsafe(nil), operator_loc: T.unsafe(nil), value: T.unsafe(nil)); end + + sig { override.returns(T::Array[::T.nilable(Node)]) } + def deconstruct; end + + sig { params(keys: ::T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, ::T.untyped]) } + def deconstruct_keys(keys); end + + # See `Node#type`. + sig { override.returns(Symbol) } + def type; end + + # See `Node.type`. + sig { override.returns(Symbol) } + def self.type; end + + sig { override.returns(String) } + def inspect; end + + # A node representing the constant path being written to. + # + # Foo::Bar = 1 + # ^^^^^^^^ + # + # ::Foo = :abc + # ^^^^^ + sig { returns(ConstantPathNode) } + def target; end + + # The Location of the `=` operator. + # + # ::ABC = 123 + # ^ + sig { returns(Location) } + def operator_loc; end + + # Save the operator_loc location using the given saved source so that + # it can be retrieved later. + sig { params(repository: ::T.untyped).returns(Relocation::Entry) } + def save_operator_loc(repository); 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 + # ^^^^ + sig { returns(Node) } + def value; end + + # Slice the location of operator_loc from the source. + sig { returns(String) } + def operator; end + + sig { params(other: ::T.untyped).returns(::T.nilable(T::Boolean)) } + def ===(other); end + end + + # Represents referencing a constant. + # + # Foo + # ^^^ + class ConstantReadNode < Node + # Initialize a new ConstantReadNode node. + sig { params(source: Source, node_id: Integer, location: Location, flags: Integer, name: Symbol).void } + def initialize(source, node_id, location, flags, name); end + + # See Node.accept. + sig { override.params(visitor: Visitor).returns(::T.untyped) } + def accept(visitor); end + + # See Node.child_nodes. + sig { override.returns(T::Array[::T.nilable(Node)]) } + def child_nodes; end + + # See Node.each_child_node. + sig { override.returns(T::Enumerator[Node]) } + sig { override.params(blk: ::T.proc.params(arg0: Node).void).void } + def each_child_node(&blk); end + + # See Node.compact_child_nodes. + sig { override.returns(T::Array[Node]) } + def compact_child_nodes; end + + # See Node.comment_targets. + sig { override.returns(T::Array[::T.any(Node, Location)]) } + def comment_targets; end + + # Creates a copy of self with the given fields, using self as the template. + sig { params(node_id: Integer, location: Location, flags: Integer, name: Symbol).returns(ConstantReadNode) } + def copy(node_id: T.unsafe(nil), location: T.unsafe(nil), flags: T.unsafe(nil), name: T.unsafe(nil)); end + + sig { override.returns(T::Array[::T.nilable(Node)]) } + def deconstruct; end + + sig { params(keys: ::T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, ::T.untyped]) } + def deconstruct_keys(keys); end + + # See `Node#type`. + sig { override.returns(Symbol) } + def type; end + + # See `Node.type`. + sig { override.returns(Symbol) } + def self.type; end + + sig { override.returns(String) } + def inspect; 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` + sig { returns(Symbol) } + def name; end + + sig { params(other: ::T.untyped).returns(::T.nilable(T::Boolean)) } + def ===(other); 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. + sig { params(source: Source, node_id: Integer, location: Location, flags: Integer, name: Symbol).void } + def initialize(source, node_id, location, flags, name); end + + # See Node.accept. + sig { override.params(visitor: Visitor).returns(::T.untyped) } + def accept(visitor); end + + # See Node.child_nodes. + sig { override.returns(T::Array[::T.nilable(Node)]) } + def child_nodes; end + + # See Node.each_child_node. + sig { override.returns(T::Enumerator[Node]) } + sig { override.params(blk: ::T.proc.params(arg0: Node).void).void } + def each_child_node(&blk); end + + # See Node.compact_child_nodes. + sig { override.returns(T::Array[Node]) } + def compact_child_nodes; end + + # See Node.comment_targets. + sig { override.returns(T::Array[::T.any(Node, Location)]) } + def comment_targets; end + + # Creates a copy of self with the given fields, using self as the template. + sig { params(node_id: Integer, location: Location, flags: Integer, name: Symbol).returns(ConstantTargetNode) } + def copy(node_id: T.unsafe(nil), location: T.unsafe(nil), flags: T.unsafe(nil), name: T.unsafe(nil)); end + + sig { override.returns(T::Array[::T.nilable(Node)]) } + def deconstruct; end + + sig { params(keys: ::T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, ::T.untyped]) } + def deconstruct_keys(keys); end + + # See `Node#type`. + sig { override.returns(Symbol) } + def type; end + + # See `Node.type`. + sig { override.returns(Symbol) } + def self.type; end + + sig { override.returns(String) } + def inspect; end + + # Returns the `name` attribute. + sig { returns(Symbol) } + def name; end + + sig { params(other: ::T.untyped).returns(::T.nilable(T::Boolean)) } + def ===(other); end + end + + # Represents writing to a constant. + # + # Foo = 1 + # ^^^^^^^ + class ConstantWriteNode < Node + # Initialize a new ConstantWriteNode node. + sig { params(source: Source, node_id: Integer, location: Location, flags: Integer, name: Symbol, name_loc: Location, value: Node, operator_loc: Location).void } + def initialize(source, node_id, location, flags, name, name_loc, value, operator_loc); end + + # See Node.accept. + sig { override.params(visitor: Visitor).returns(::T.untyped) } + def accept(visitor); end + + # See Node.child_nodes. + sig { override.returns(T::Array[::T.nilable(Node)]) } + def child_nodes; end + + # See Node.each_child_node. + sig { override.returns(T::Enumerator[Node]) } + sig { override.params(blk: ::T.proc.params(arg0: Node).void).void } + def each_child_node(&blk); end + + # See Node.compact_child_nodes. + sig { override.returns(T::Array[Node]) } + def compact_child_nodes; end + + # See Node.comment_targets. + sig { override.returns(T::Array[::T.any(Node, Location)]) } + def comment_targets; end + + # Creates a copy of self with the given fields, using self as the template. + sig { params(node_id: Integer, location: Location, flags: Integer, name: Symbol, name_loc: Location, value: Node, operator_loc: Location).returns(ConstantWriteNode) } + def copy(node_id: T.unsafe(nil), location: T.unsafe(nil), flags: T.unsafe(nil), name: T.unsafe(nil), name_loc: T.unsafe(nil), value: T.unsafe(nil), operator_loc: T.unsafe(nil)); end + + sig { override.returns(T::Array[::T.nilable(Node)]) } + def deconstruct; end + + sig { params(keys: ::T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, ::T.untyped]) } + def deconstruct_keys(keys); end + + # See `Node#type`. + sig { override.returns(Symbol) } + def type; end + + # See `Node.type`. + sig { override.returns(Symbol) } + def self.type; end + + sig { override.returns(String) } + def inspect; 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` + sig { returns(Symbol) } + def name; end + + # The Location of the constant name. + # + # FOO = 1 + # ^^^ + sig { returns(Location) } + def name_loc; end + + # Save the name_loc location using the given saved source so that + # it can be retrieved later. + sig { params(repository: ::T.untyped).returns(Relocation::Entry) } + def save_name_loc(repository); 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 + # ^^^^^^^^^ + sig { returns(Node) } + def value; end + + # The Location of the `=` operator. + # + # FOO = :bar + # ^ + sig { returns(Location) } + def operator_loc; end + + # Save the operator_loc location using the given saved source so that + # it can be retrieved later. + sig { params(repository: ::T.untyped).returns(Relocation::Entry) } + def save_operator_loc(repository); end + + # Slice the location of operator_loc from the source. + sig { returns(String) } + def operator; end + + sig { params(other: ::T.untyped).returns(::T.nilable(T::Boolean)) } + def ===(other); end + end + + # Represents a method definition. + # + # def method + # end + # ^^^^^^^^^^ + class DefNode < Node + # Initialize a new DefNode node. + sig { params(source: Source, node_id: Integer, location: Location, flags: Integer, name: Symbol, name_loc: Location, receiver: ::T.nilable(Node), parameters: ::T.nilable(ParametersNode), body: ::T.nilable(::T.any(StatementsNode, BeginNode)), locals: T::Array[Symbol], def_keyword_loc: Location, operator_loc: ::T.nilable(Location), lparen_loc: ::T.nilable(Location), rparen_loc: ::T.nilable(Location), equal_loc: ::T.nilable(Location), end_keyword_loc: ::T.nilable(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 + + # See Node.accept. + sig { override.params(visitor: Visitor).returns(::T.untyped) } + def accept(visitor); end + + # See Node.child_nodes. + sig { override.returns(T::Array[::T.nilable(Node)]) } + def child_nodes; end + + # See Node.each_child_node. + sig { override.returns(T::Enumerator[Node]) } + sig { override.params(blk: ::T.proc.params(arg0: Node).void).void } + def each_child_node(&blk); end + + # See Node.compact_child_nodes. + sig { override.returns(T::Array[Node]) } + def compact_child_nodes; end + + # See Node.comment_targets. + sig { override.returns(T::Array[::T.any(Node, Location)]) } + def comment_targets; end + + # Creates a copy of self with the given fields, using self as the template. + sig { params(node_id: Integer, location: Location, flags: Integer, name: Symbol, name_loc: Location, receiver: ::T.nilable(Node), parameters: ::T.nilable(ParametersNode), body: ::T.nilable(::T.any(StatementsNode, BeginNode)), locals: T::Array[Symbol], def_keyword_loc: Location, operator_loc: ::T.nilable(Location), lparen_loc: ::T.nilable(Location), rparen_loc: ::T.nilable(Location), equal_loc: ::T.nilable(Location), end_keyword_loc: ::T.nilable(Location)).returns(DefNode) } + def copy(node_id: T.unsafe(nil), location: T.unsafe(nil), flags: T.unsafe(nil), name: T.unsafe(nil), name_loc: T.unsafe(nil), receiver: T.unsafe(nil), parameters: T.unsafe(nil), body: T.unsafe(nil), locals: T.unsafe(nil), def_keyword_loc: T.unsafe(nil), operator_loc: T.unsafe(nil), lparen_loc: T.unsafe(nil), rparen_loc: T.unsafe(nil), equal_loc: T.unsafe(nil), end_keyword_loc: T.unsafe(nil)); end + + sig { override.returns(T::Array[::T.nilable(Node)]) } + def deconstruct; end + + sig { params(keys: ::T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, ::T.untyped]) } + def deconstruct_keys(keys); end + + # See `Node#type`. + sig { override.returns(Symbol) } + def type; end + + # See `Node.type`. + sig { override.returns(Symbol) } + def self.type; end + + sig { override.returns(String) } + def inspect; end + + # Returns the `name` attribute. + sig { returns(Symbol) } + def name; end + + # Returns the Location represented by `name_loc`. + sig { returns(Location) } + def name_loc; end + + # Save the name_loc location using the given saved source so that + # it can be retrieved later. + sig { params(repository: ::T.untyped).returns(Relocation::Entry) } + def save_name_loc(repository); end + + # Returns the `receiver` attribute. + sig { returns(::T.nilable(Node)) } + def receiver; end + + # Returns the `parameters` attribute. + sig { returns(::T.nilable(ParametersNode)) } + def parameters; end + + # Returns the `body` attribute. + sig { returns(::T.nilable(::T.any(StatementsNode, BeginNode))) } + def body; end + + # Returns the `locals` attribute. + sig { returns(T::Array[Symbol]) } + def locals; end + + # Returns the Location represented by `def_keyword_loc`. + sig { returns(Location) } + def def_keyword_loc; end + + # Save the def_keyword_loc location using the given saved source so that + # it can be retrieved later. + sig { params(repository: ::T.untyped).returns(Relocation::Entry) } + def save_def_keyword_loc(repository); end + + # Returns the Location represented by `operator_loc`. + sig { returns(::T.nilable(Location)) } + def operator_loc; end + + # Save the operator_loc location using the given saved source so that + # it can be retrieved later. + sig { params(repository: ::T.untyped).returns(::T.nilable(Relocation::Entry)) } + def save_operator_loc(repository); end + + # Returns the Location represented by `lparen_loc`. + sig { returns(::T.nilable(Location)) } + def lparen_loc; end + + # Save the lparen_loc location using the given saved source so that + # it can be retrieved later. + sig { params(repository: ::T.untyped).returns(::T.nilable(Relocation::Entry)) } + def save_lparen_loc(repository); end + + # Returns the Location represented by `rparen_loc`. + sig { returns(::T.nilable(Location)) } + def rparen_loc; end + + # Save the rparen_loc location using the given saved source so that + # it can be retrieved later. + sig { params(repository: ::T.untyped).returns(::T.nilable(Relocation::Entry)) } + def save_rparen_loc(repository); end + + # Returns the Location represented by `equal_loc`. + sig { returns(::T.nilable(Location)) } + def equal_loc; end + + # Save the equal_loc location using the given saved source so that + # it can be retrieved later. + sig { params(repository: ::T.untyped).returns(::T.nilable(Relocation::Entry)) } + def save_equal_loc(repository); end + + # Returns the Location represented by `end_keyword_loc`. + sig { returns(::T.nilable(Location)) } + def end_keyword_loc; end + + # Save the end_keyword_loc location using the given saved source so that + # it can be retrieved later. + sig { params(repository: ::T.untyped).returns(::T.nilable(Relocation::Entry)) } + def save_end_keyword_loc(repository); end + + # Slice the location of def_keyword_loc from the source. + sig { returns(String) } + def def_keyword; end + + # Slice the location of operator_loc from the source. + sig { returns(::T.nilable(String)) } + def operator; end + + # Slice the location of lparen_loc from the source. + sig { returns(::T.nilable(String)) } + def lparen; end + + # Slice the location of rparen_loc from the source. + sig { returns(::T.nilable(String)) } + def rparen; end + + # Slice the location of equal_loc from the source. + sig { returns(::T.nilable(String)) } + def equal; end + + # Slice the location of end_keyword_loc from the source. + sig { returns(::T.nilable(String)) } + def end_keyword; end + + sig { params(other: ::T.untyped).returns(::T.nilable(T::Boolean)) } + def ===(other); end + end + + # Represents the use of the `defined?` keyword. + # + # defined?(a) + # ^^^^^^^^^^^ + class DefinedNode < Node + # Initialize a new DefinedNode node. + sig { params(source: Source, node_id: Integer, location: Location, flags: Integer, lparen_loc: ::T.nilable(Location), value: Node, rparen_loc: ::T.nilable(Location), keyword_loc: Location).void } + def initialize(source, node_id, location, flags, lparen_loc, value, rparen_loc, keyword_loc); end + + # See Node.accept. + sig { override.params(visitor: Visitor).returns(::T.untyped) } + def accept(visitor); end + + # See Node.child_nodes. + sig { override.returns(T::Array[::T.nilable(Node)]) } + def child_nodes; end + + # See Node.each_child_node. + sig { override.returns(T::Enumerator[Node]) } + sig { override.params(blk: ::T.proc.params(arg0: Node).void).void } + def each_child_node(&blk); end + + # See Node.compact_child_nodes. + sig { override.returns(T::Array[Node]) } + def compact_child_nodes; end + + # See Node.comment_targets. + sig { override.returns(T::Array[::T.any(Node, Location)]) } + def comment_targets; end + + # Creates a copy of self with the given fields, using self as the template. + sig { params(node_id: Integer, location: Location, flags: Integer, lparen_loc: ::T.nilable(Location), value: Node, rparen_loc: ::T.nilable(Location), keyword_loc: Location).returns(DefinedNode) } + def copy(node_id: T.unsafe(nil), location: T.unsafe(nil), flags: T.unsafe(nil), lparen_loc: T.unsafe(nil), value: T.unsafe(nil), rparen_loc: T.unsafe(nil), keyword_loc: T.unsafe(nil)); end + + sig { override.returns(T::Array[::T.nilable(Node)]) } + def deconstruct; end + + sig { params(keys: ::T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, ::T.untyped]) } + def deconstruct_keys(keys); end + + # See `Node#type`. + sig { override.returns(Symbol) } + def type; end + + # See `Node.type`. + sig { override.returns(Symbol) } + def self.type; end + + sig { override.returns(String) } + def inspect; end + + # Returns the Location represented by `lparen_loc`. + sig { returns(::T.nilable(Location)) } + def lparen_loc; end + + # Save the lparen_loc location using the given saved source so that + # it can be retrieved later. + sig { params(repository: ::T.untyped).returns(::T.nilable(Relocation::Entry)) } + def save_lparen_loc(repository); end + + # Returns the `value` attribute. + sig { returns(Node) } + def value; end + + # Returns the Location represented by `rparen_loc`. + sig { returns(::T.nilable(Location)) } + def rparen_loc; end + + # Save the rparen_loc location using the given saved source so that + # it can be retrieved later. + sig { params(repository: ::T.untyped).returns(::T.nilable(Relocation::Entry)) } + def save_rparen_loc(repository); end + + # Returns the Location represented by `keyword_loc`. + sig { returns(Location) } + def keyword_loc; end + + # Save the keyword_loc location using the given saved source so that + # it can be retrieved later. + sig { params(repository: ::T.untyped).returns(Relocation::Entry) } + def save_keyword_loc(repository); end + + # Slice the location of lparen_loc from the source. + sig { returns(::T.nilable(String)) } + def lparen; end + + # Slice the location of rparen_loc from the source. + sig { returns(::T.nilable(String)) } + def rparen; end + + # Slice the location of keyword_loc from the source. + sig { returns(String) } + def keyword; end + + sig { params(other: ::T.untyped).returns(::T.nilable(T::Boolean)) } + def ===(other); 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. + sig { params(source: Source, node_id: Integer, location: Location, flags: Integer, else_keyword_loc: Location, statements: ::T.nilable(StatementsNode), end_keyword_loc: ::T.nilable(Location)).void } + def initialize(source, node_id, location, flags, else_keyword_loc, statements, end_keyword_loc); end + + # See Node.accept. + sig { override.params(visitor: Visitor).returns(::T.untyped) } + def accept(visitor); end + + # See Node.child_nodes. + sig { override.returns(T::Array[::T.nilable(Node)]) } + def child_nodes; end + + # See Node.each_child_node. + sig { override.returns(T::Enumerator[Node]) } + sig { override.params(blk: ::T.proc.params(arg0: Node).void).void } + def each_child_node(&blk); end + + # See Node.compact_child_nodes. + sig { override.returns(T::Array[Node]) } + def compact_child_nodes; end + + # See Node.comment_targets. + sig { override.returns(T::Array[::T.any(Node, Location)]) } + def comment_targets; end + + # Creates a copy of self with the given fields, using self as the template. + sig { params(node_id: Integer, location: Location, flags: Integer, else_keyword_loc: Location, statements: ::T.nilable(StatementsNode), end_keyword_loc: ::T.nilable(Location)).returns(ElseNode) } + def copy(node_id: T.unsafe(nil), location: T.unsafe(nil), flags: T.unsafe(nil), else_keyword_loc: T.unsafe(nil), statements: T.unsafe(nil), end_keyword_loc: T.unsafe(nil)); end + + sig { override.returns(T::Array[::T.nilable(Node)]) } + def deconstruct; end + + sig { params(keys: ::T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, ::T.untyped]) } + def deconstruct_keys(keys); end + + # See `Node#type`. + sig { override.returns(Symbol) } + def type; end + + # See `Node.type`. + sig { override.returns(Symbol) } + def self.type; end + + sig { override.returns(String) } + def inspect; end + + # Returns the Location represented by `else_keyword_loc`. + sig { returns(Location) } + def else_keyword_loc; end + + # Save the else_keyword_loc location using the given saved source so that + # it can be retrieved later. + sig { params(repository: ::T.untyped).returns(Relocation::Entry) } + def save_else_keyword_loc(repository); end + + # Returns the `statements` attribute. + sig { returns(::T.nilable(StatementsNode)) } + def statements; end + + # Returns the Location represented by `end_keyword_loc`. + sig { returns(::T.nilable(Location)) } + def end_keyword_loc; end + + # Save the end_keyword_loc location using the given saved source so that + # it can be retrieved later. + sig { params(repository: ::T.untyped).returns(::T.nilable(Relocation::Entry)) } + def save_end_keyword_loc(repository); end + + # Slice the location of else_keyword_loc from the source. + sig { returns(String) } + def else_keyword; end + + # Slice the location of end_keyword_loc from the source. + sig { returns(::T.nilable(String)) } + def end_keyword; end + + sig { params(other: ::T.untyped).returns(::T.nilable(T::Boolean)) } + def ===(other); end + end + + # Represents an interpolated set of statements. + # + # "foo #{bar}" + # ^^^^^^ + class EmbeddedStatementsNode < Node + # Initialize a new EmbeddedStatementsNode node. + sig { params(source: Source, node_id: Integer, location: Location, flags: Integer, opening_loc: Location, statements: ::T.nilable(StatementsNode), closing_loc: Location).void } + def initialize(source, node_id, location, flags, opening_loc, statements, closing_loc); end + + # See Node.accept. + sig { override.params(visitor: Visitor).returns(::T.untyped) } + def accept(visitor); end + + # See Node.child_nodes. + sig { override.returns(T::Array[::T.nilable(Node)]) } + def child_nodes; end + + # See Node.each_child_node. + sig { override.returns(T::Enumerator[Node]) } + sig { override.params(blk: ::T.proc.params(arg0: Node).void).void } + def each_child_node(&blk); end + + # See Node.compact_child_nodes. + sig { override.returns(T::Array[Node]) } + def compact_child_nodes; end + + # See Node.comment_targets. + sig { override.returns(T::Array[::T.any(Node, Location)]) } + def comment_targets; end + + # Creates a copy of self with the given fields, using self as the template. + sig { params(node_id: Integer, location: Location, flags: Integer, opening_loc: Location, statements: ::T.nilable(StatementsNode), closing_loc: Location).returns(EmbeddedStatementsNode) } + def copy(node_id: T.unsafe(nil), location: T.unsafe(nil), flags: T.unsafe(nil), opening_loc: T.unsafe(nil), statements: T.unsafe(nil), closing_loc: T.unsafe(nil)); end + + sig { override.returns(T::Array[::T.nilable(Node)]) } + def deconstruct; end + + sig { params(keys: ::T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, ::T.untyped]) } + def deconstruct_keys(keys); end + + # See `Node#type`. + sig { override.returns(Symbol) } + def type; end + + # See `Node.type`. + sig { override.returns(Symbol) } + def self.type; end + + sig { override.returns(String) } + def inspect; end + + # Returns the Location represented by `opening_loc`. + sig { returns(Location) } + def opening_loc; end + + # Save the opening_loc location using the given saved source so that + # it can be retrieved later. + sig { params(repository: ::T.untyped).returns(Relocation::Entry) } + def save_opening_loc(repository); end + + # Returns the `statements` attribute. + sig { returns(::T.nilable(StatementsNode)) } + def statements; end + + # Returns the Location represented by `closing_loc`. + sig { returns(Location) } + def closing_loc; end + + # Save the closing_loc location using the given saved source so that + # it can be retrieved later. + sig { params(repository: ::T.untyped).returns(Relocation::Entry) } + def save_closing_loc(repository); end + + # Slice the location of opening_loc from the source. + sig { returns(String) } + def opening; end + + # Slice the location of closing_loc from the source. + sig { returns(String) } + def closing; end + + sig { params(other: ::T.untyped).returns(::T.nilable(T::Boolean)) } + def ===(other); end + end + + # Represents an interpolated variable. + # + # "foo #@bar" + # ^^^^^ + class EmbeddedVariableNode < Node + # Initialize a new EmbeddedVariableNode node. + sig { params(source: Source, node_id: Integer, location: Location, flags: Integer, operator_loc: Location, variable: ::T.any(InstanceVariableReadNode, ClassVariableReadNode, GlobalVariableReadNode, BackReferenceReadNode, NumberedReferenceReadNode)).void } + def initialize(source, node_id, location, flags, operator_loc, variable); end + + # See Node.accept. + sig { override.params(visitor: Visitor).returns(::T.untyped) } + def accept(visitor); end + + # See Node.child_nodes. + sig { override.returns(T::Array[::T.nilable(Node)]) } + def child_nodes; end + + # See Node.each_child_node. + sig { override.returns(T::Enumerator[Node]) } + sig { override.params(blk: ::T.proc.params(arg0: Node).void).void } + def each_child_node(&blk); end + + # See Node.compact_child_nodes. + sig { override.returns(T::Array[Node]) } + def compact_child_nodes; end + + # See Node.comment_targets. + sig { override.returns(T::Array[::T.any(Node, Location)]) } + def comment_targets; end + + # Creates a copy of self with the given fields, using self as the template. + sig { params(node_id: Integer, location: Location, flags: Integer, operator_loc: Location, variable: ::T.any(InstanceVariableReadNode, ClassVariableReadNode, GlobalVariableReadNode, BackReferenceReadNode, NumberedReferenceReadNode)).returns(EmbeddedVariableNode) } + def copy(node_id: T.unsafe(nil), location: T.unsafe(nil), flags: T.unsafe(nil), operator_loc: T.unsafe(nil), variable: T.unsafe(nil)); end + + sig { override.returns(T::Array[::T.nilable(Node)]) } + def deconstruct; end + + sig { params(keys: ::T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, ::T.untyped]) } + def deconstruct_keys(keys); end + + # See `Node#type`. + sig { override.returns(Symbol) } + def type; end + + # See `Node.type`. + sig { override.returns(Symbol) } + def self.type; end + + sig { override.returns(String) } + def inspect; end + + # Returns the Location represented by `operator_loc`. + sig { returns(Location) } + def operator_loc; end + + # Save the operator_loc location using the given saved source so that + # it can be retrieved later. + sig { params(repository: ::T.untyped).returns(Relocation::Entry) } + def save_operator_loc(repository); end + + # Returns the `variable` attribute. + sig { returns(::T.any(InstanceVariableReadNode, ClassVariableReadNode, GlobalVariableReadNode, BackReferenceReadNode, NumberedReferenceReadNode)) } + def variable; end + + # Slice the location of operator_loc from the source. + sig { returns(String) } + def operator; end + + sig { params(other: ::T.untyped).returns(::T.nilable(T::Boolean)) } + def ===(other); end + end + + # Represents an `ensure` clause in a `begin` statement. + # + # begin + # foo + # ensure + # ^^^^^^ + # bar + # end + class EnsureNode < Node + # Initialize a new EnsureNode node. + sig { params(source: Source, node_id: Integer, location: Location, flags: Integer, ensure_keyword_loc: Location, statements: ::T.nilable(StatementsNode), end_keyword_loc: Location).void } + def initialize(source, node_id, location, flags, ensure_keyword_loc, statements, end_keyword_loc); end + + # See Node.accept. + sig { override.params(visitor: Visitor).returns(::T.untyped) } + def accept(visitor); end + + # See Node.child_nodes. + sig { override.returns(T::Array[::T.nilable(Node)]) } + def child_nodes; end + + # See Node.each_child_node. + sig { override.returns(T::Enumerator[Node]) } + sig { override.params(blk: ::T.proc.params(arg0: Node).void).void } + def each_child_node(&blk); end + + # See Node.compact_child_nodes. + sig { override.returns(T::Array[Node]) } + def compact_child_nodes; end + + # See Node.comment_targets. + sig { override.returns(T::Array[::T.any(Node, Location)]) } + def comment_targets; end + + # Creates a copy of self with the given fields, using self as the template. + sig { params(node_id: Integer, location: Location, flags: Integer, ensure_keyword_loc: Location, statements: ::T.nilable(StatementsNode), end_keyword_loc: Location).returns(EnsureNode) } + def copy(node_id: T.unsafe(nil), location: T.unsafe(nil), flags: T.unsafe(nil), ensure_keyword_loc: T.unsafe(nil), statements: T.unsafe(nil), end_keyword_loc: T.unsafe(nil)); end + + sig { override.returns(T::Array[::T.nilable(Node)]) } + def deconstruct; end + + sig { params(keys: ::T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, ::T.untyped]) } + def deconstruct_keys(keys); end + + # See `Node#type`. + sig { override.returns(Symbol) } + def type; end + + # See `Node.type`. + sig { override.returns(Symbol) } + def self.type; end + + sig { override.returns(String) } + def inspect; end + + # Returns the Location represented by `ensure_keyword_loc`. + sig { returns(Location) } + def ensure_keyword_loc; end + + # Save the ensure_keyword_loc location using the given saved source so that + # it can be retrieved later. + sig { params(repository: ::T.untyped).returns(Relocation::Entry) } + def save_ensure_keyword_loc(repository); end + + # Returns the `statements` attribute. + sig { returns(::T.nilable(StatementsNode)) } + def statements; end + + # Returns the Location represented by `end_keyword_loc`. + sig { returns(Location) } + def end_keyword_loc; end + + # Save the end_keyword_loc location using the given saved source so that + # it can be retrieved later. + sig { params(repository: ::T.untyped).returns(Relocation::Entry) } + def save_end_keyword_loc(repository); end + + # Slice the location of ensure_keyword_loc from the source. + sig { returns(String) } + def ensure_keyword; end + + # Slice the location of end_keyword_loc from the source. + sig { returns(String) } + def end_keyword; end + + sig { params(other: ::T.untyped).returns(::T.nilable(T::Boolean)) } + def ===(other); end + end + + # Represents the use of the literal `false` keyword. + # + # false + # ^^^^^ + class FalseNode < Node + # Initialize a new FalseNode node. + sig { params(source: Source, node_id: Integer, location: Location, flags: Integer).void } + def initialize(source, node_id, location, flags); end + + # See Node.accept. + sig { override.params(visitor: Visitor).returns(::T.untyped) } + def accept(visitor); end + + # See Node.child_nodes. + sig { override.returns(T::Array[::T.nilable(Node)]) } + def child_nodes; end + + # See Node.each_child_node. + sig { override.returns(T::Enumerator[Node]) } + sig { override.params(blk: ::T.proc.params(arg0: Node).void).void } + def each_child_node(&blk); end + + # See Node.compact_child_nodes. + sig { override.returns(T::Array[Node]) } + def compact_child_nodes; end + + # See Node.comment_targets. + sig { override.returns(T::Array[::T.any(Node, Location)]) } + def comment_targets; end + + # Creates a copy of self with the given fields, using self as the template. + sig { params(node_id: Integer, location: Location, flags: Integer).returns(FalseNode) } + def copy(node_id: T.unsafe(nil), location: T.unsafe(nil), flags: T.unsafe(nil)); end + + sig { override.returns(T::Array[::T.nilable(Node)]) } + def deconstruct; end + + sig { params(keys: ::T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, ::T.untyped]) } + def deconstruct_keys(keys); end + + # See `Node#type`. + sig { override.returns(Symbol) } + def type; end + + # See `Node.type`. + sig { override.returns(Symbol) } + def self.type; end + + sig { override.returns(String) } + def inspect; end + + sig { params(other: ::T.untyped).returns(::T.nilable(T::Boolean)) } + def ===(other); 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. + sig { params(source: Source, node_id: Integer, location: Location, flags: Integer, constant: ::T.nilable(::T.any(ConstantPathNode, ConstantReadNode)), left: SplatNode, requireds: T::Array[Node], right: ::T.any(SplatNode, MissingNode), opening_loc: ::T.nilable(Location), closing_loc: ::T.nilable(Location)).void } + def initialize(source, node_id, location, flags, constant, left, requireds, right, opening_loc, closing_loc); end + + # See Node.accept. + sig { override.params(visitor: Visitor).returns(::T.untyped) } + def accept(visitor); end + + # See Node.child_nodes. + sig { override.returns(T::Array[::T.nilable(Node)]) } + def child_nodes; end + + # See Node.each_child_node. + sig { override.returns(T::Enumerator[Node]) } + sig { override.params(blk: ::T.proc.params(arg0: Node).void).void } + def each_child_node(&blk); end + + # See Node.compact_child_nodes. + sig { override.returns(T::Array[Node]) } + def compact_child_nodes; end + + # See Node.comment_targets. + sig { override.returns(T::Array[::T.any(Node, Location)]) } + def comment_targets; end + + # Creates a copy of self with the given fields, using self as the template. + sig { params(node_id: Integer, location: Location, flags: Integer, constant: ::T.nilable(::T.any(ConstantPathNode, ConstantReadNode)), left: SplatNode, requireds: T::Array[Node], right: ::T.any(SplatNode, MissingNode), opening_loc: ::T.nilable(Location), closing_loc: ::T.nilable(Location)).returns(FindPatternNode) } + def copy(node_id: T.unsafe(nil), location: T.unsafe(nil), flags: T.unsafe(nil), constant: T.unsafe(nil), left: T.unsafe(nil), requireds: T.unsafe(nil), right: T.unsafe(nil), opening_loc: T.unsafe(nil), closing_loc: T.unsafe(nil)); end + + sig { override.returns(T::Array[::T.nilable(Node)]) } + def deconstruct; end + + sig { params(keys: ::T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, ::T.untyped]) } + def deconstruct_keys(keys); end + + # See `Node#type`. + sig { override.returns(Symbol) } + def type; end + + # See `Node.type`. + sig { override.returns(Symbol) } + def self.type; end + + sig { override.returns(String) } + def inspect; end + + # Represents the optional constant preceding the pattern + # + # foo in Foo(*bar, baz, *qux) + # ^^^ + sig { returns(::T.nilable(::T.any(ConstantPathNode, ConstantReadNode))) } + def constant; end + + # Represents the first wildcard node in the pattern. + # + # foo in *bar, baz, *qux + # ^^^^ + # + # foo in Foo(*bar, baz, *qux) + # ^^^^ + sig { returns(SplatNode) } + def left; end + + # Represents the nodes in between the wildcards. + # + # foo in *bar, baz, *qux + # ^^^ + # + # foo in Foo(*bar, baz, 1, *qux) + # ^^^^^^ + sig { returns(T::Array[Node]) } + def requireds; end + + # Represents the second wildcard node in the pattern. + # + # foo in *bar, baz, *qux + # ^^^^ + # + # foo in Foo(*bar, baz, *qux) + # ^^^^ + sig { returns(::T.any(SplatNode, MissingNode)) } + def right; end + + # The Location of the opening brace. + # + # foo in [*bar, baz, *qux] + # ^ + # + # foo in Foo(*bar, baz, *qux) + # ^ + sig { returns(::T.nilable(Location)) } + def opening_loc; end + + # Save the opening_loc location using the given saved source so that + # it can be retrieved later. + sig { params(repository: ::T.untyped).returns(::T.nilable(Relocation::Entry)) } + def save_opening_loc(repository); end + + # The Location of the closing brace. + # + # foo in [*bar, baz, *qux] + # ^ + # + # foo in Foo(*bar, baz, *qux) + # ^ + sig { returns(::T.nilable(Location)) } + def closing_loc; end + + # Save the closing_loc location using the given saved source so that + # it can be retrieved later. + sig { params(repository: ::T.untyped).returns(::T.nilable(Relocation::Entry)) } + def save_closing_loc(repository); end + + # Slice the location of opening_loc from the source. + sig { returns(::T.nilable(String)) } + def opening; end + + # Slice the location of closing_loc from the source. + sig { returns(::T.nilable(String)) } + def closing; end + + sig { params(other: ::T.untyped).returns(::T.nilable(T::Boolean)) } + def ===(other); 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. + sig { params(source: Source, node_id: Integer, location: Location, flags: Integer, left: ::T.nilable(Node), right: ::T.nilable(Node), operator_loc: Location).void } + def initialize(source, node_id, location, flags, left, right, operator_loc); end + + # See Node.accept. + sig { override.params(visitor: Visitor).returns(::T.untyped) } + def accept(visitor); end + + # See Node.child_nodes. + sig { override.returns(T::Array[::T.nilable(Node)]) } + def child_nodes; end + + # See Node.each_child_node. + sig { override.returns(T::Enumerator[Node]) } + sig { override.params(blk: ::T.proc.params(arg0: Node).void).void } + def each_child_node(&blk); end + + # See Node.compact_child_nodes. + sig { override.returns(T::Array[Node]) } + def compact_child_nodes; end + + # See Node.comment_targets. + sig { override.returns(T::Array[::T.any(Node, Location)]) } + def comment_targets; end + + # Creates a copy of self with the given fields, using self as the template. + sig { params(node_id: Integer, location: Location, flags: Integer, left: ::T.nilable(Node), right: ::T.nilable(Node), operator_loc: Location).returns(FlipFlopNode) } + def copy(node_id: T.unsafe(nil), location: T.unsafe(nil), flags: T.unsafe(nil), left: T.unsafe(nil), right: T.unsafe(nil), operator_loc: T.unsafe(nil)); end + + sig { override.returns(T::Array[::T.nilable(Node)]) } + def deconstruct; end + + sig { params(keys: ::T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, ::T.untyped]) } + def deconstruct_keys(keys); end + + # See `Node#type`. + sig { override.returns(Symbol) } + def type; end + + # See `Node.type`. + sig { override.returns(Symbol) } + def self.type; end + + sig { override.returns(String) } + def inspect; end + + # ... operator + sig { returns(T::Boolean) } + def exclude_end?; end + + # Returns the `left` attribute. + sig { returns(::T.nilable(Node)) } + def left; end + + # Returns the `right` attribute. + sig { returns(::T.nilable(Node)) } + def right; end + + # Returns the Location represented by `operator_loc`. + sig { returns(Location) } + def operator_loc; end + + # Save the operator_loc location using the given saved source so that + # it can be retrieved later. + sig { params(repository: ::T.untyped).returns(Relocation::Entry) } + def save_operator_loc(repository); end + + # Slice the location of operator_loc from the source. + sig { returns(String) } + def operator; end + + sig { params(other: ::T.untyped).returns(::T.nilable(T::Boolean)) } + def ===(other); end + end + + # Represents a floating point number literal. + # + # 1.0 + # ^^^ + class FloatNode < Node + # Initialize a new FloatNode node. + sig { params(source: Source, node_id: Integer, location: Location, flags: Integer, value: Float).void } + def initialize(source, node_id, location, flags, value); end + + # See Node.accept. + sig { override.params(visitor: Visitor).returns(::T.untyped) } + def accept(visitor); end + + # See Node.child_nodes. + sig { override.returns(T::Array[::T.nilable(Node)]) } + def child_nodes; end + + # See Node.each_child_node. + sig { override.returns(T::Enumerator[Node]) } + sig { override.params(blk: ::T.proc.params(arg0: Node).void).void } + def each_child_node(&blk); end + + # See Node.compact_child_nodes. + sig { override.returns(T::Array[Node]) } + def compact_child_nodes; end + + # See Node.comment_targets. + sig { override.returns(T::Array[::T.any(Node, Location)]) } + def comment_targets; end + + # Creates a copy of self with the given fields, using self as the template. + sig { params(node_id: Integer, location: Location, flags: Integer, value: Float).returns(FloatNode) } + def copy(node_id: T.unsafe(nil), location: T.unsafe(nil), flags: T.unsafe(nil), value: T.unsafe(nil)); end + + sig { override.returns(T::Array[::T.nilable(Node)]) } + def deconstruct; end + + sig { params(keys: ::T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, ::T.untyped]) } + def deconstruct_keys(keys); end + + # See `Node#type`. + sig { override.returns(Symbol) } + def type; end + + # See `Node.type`. + sig { override.returns(Symbol) } + def self.type; end + + sig { override.returns(String) } + def inspect; end + + # The value of the floating point number as a Float. + sig { returns(Float) } + def value; end + + sig { params(other: ::T.untyped).returns(::T.nilable(T::Boolean)) } + def ===(other); end + end + + # Represents the use of the `for` keyword. + # + # for i in a end + # ^^^^^^^^^^^^^^ + class ForNode < Node + # Initialize a new ForNode node. + sig { params(source: Source, node_id: Integer, location: Location, flags: Integer, index: ::T.any(LocalVariableTargetNode, InstanceVariableTargetNode, ClassVariableTargetNode, GlobalVariableTargetNode, ConstantTargetNode, ConstantPathTargetNode, CallTargetNode, IndexTargetNode, MultiTargetNode, BackReferenceReadNode, NumberedReferenceReadNode, MissingNode), collection: Node, statements: ::T.nilable(StatementsNode), for_keyword_loc: Location, in_keyword_loc: Location, do_keyword_loc: ::T.nilable(Location), end_keyword_loc: 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 + + # See Node.accept. + sig { override.params(visitor: Visitor).returns(::T.untyped) } + def accept(visitor); end + + # See Node.child_nodes. + sig { override.returns(T::Array[::T.nilable(Node)]) } + def child_nodes; end + + # See Node.each_child_node. + sig { override.returns(T::Enumerator[Node]) } + sig { override.params(blk: ::T.proc.params(arg0: Node).void).void } + def each_child_node(&blk); end + + # See Node.compact_child_nodes. + sig { override.returns(T::Array[Node]) } + def compact_child_nodes; end + + # See Node.comment_targets. + sig { override.returns(T::Array[::T.any(Node, Location)]) } + def comment_targets; end + + # Creates a copy of self with the given fields, using self as the template. + sig { params(node_id: Integer, location: Location, flags: Integer, index: ::T.any(LocalVariableTargetNode, InstanceVariableTargetNode, ClassVariableTargetNode, GlobalVariableTargetNode, ConstantTargetNode, ConstantPathTargetNode, CallTargetNode, IndexTargetNode, MultiTargetNode, BackReferenceReadNode, NumberedReferenceReadNode, MissingNode), collection: Node, statements: ::T.nilable(StatementsNode), for_keyword_loc: Location, in_keyword_loc: Location, do_keyword_loc: ::T.nilable(Location), end_keyword_loc: Location).returns(ForNode) } + def copy(node_id: T.unsafe(nil), location: T.unsafe(nil), flags: T.unsafe(nil), index: T.unsafe(nil), collection: T.unsafe(nil), statements: T.unsafe(nil), for_keyword_loc: T.unsafe(nil), in_keyword_loc: T.unsafe(nil), do_keyword_loc: T.unsafe(nil), end_keyword_loc: T.unsafe(nil)); end + + sig { override.returns(T::Array[::T.nilable(Node)]) } + def deconstruct; end + + sig { params(keys: ::T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, ::T.untyped]) } + def deconstruct_keys(keys); end + + # See `Node#type`. + sig { override.returns(Symbol) } + def type; end + + # See `Node.type`. + sig { override.returns(Symbol) } + def self.type; end + + sig { override.returns(String) } + def inspect; end + + # The index expression for `for` loops. + # + # for i in a end + # ^ + sig { returns(::T.any(LocalVariableTargetNode, InstanceVariableTargetNode, ClassVariableTargetNode, GlobalVariableTargetNode, ConstantTargetNode, ConstantPathTargetNode, CallTargetNode, IndexTargetNode, MultiTargetNode, BackReferenceReadNode, NumberedReferenceReadNode, MissingNode)) } + def index; end + + # The collection to iterate over. + # + # for i in a end + # ^ + sig { returns(Node) } + def collection; end + + # Represents the body of statements to execute for each iteration of the loop. + # + # for i in a + # foo(i) + # ^^^^^^ + # end + sig { returns(::T.nilable(StatementsNode)) } + def statements; end + + # The Location of the `for` keyword. + # + # for i in a end + # ^^^ + sig { returns(Location) } + def for_keyword_loc; end + + # Save the for_keyword_loc location using the given saved source so that + # it can be retrieved later. + sig { params(repository: ::T.untyped).returns(Relocation::Entry) } + def save_for_keyword_loc(repository); end + + # The Location of the `in` keyword. + # + # for i in a end + # ^^ + sig { returns(Location) } + def in_keyword_loc; end + + # Save the in_keyword_loc location using the given saved source so that + # it can be retrieved later. + sig { params(repository: ::T.untyped).returns(Relocation::Entry) } + def save_in_keyword_loc(repository); end + + # The Location of the `do` keyword, if present. + # + # for i in a do end + # ^^ + sig { returns(::T.nilable(Location)) } + def do_keyword_loc; end + + # Save the do_keyword_loc location using the given saved source so that + # it can be retrieved later. + sig { params(repository: ::T.untyped).returns(::T.nilable(Relocation::Entry)) } + def save_do_keyword_loc(repository); end + + # The Location of the `end` keyword. + # + # for i in a end + # ^^^ + sig { returns(Location) } + def end_keyword_loc; end + + # Save the end_keyword_loc location using the given saved source so that + # it can be retrieved later. + sig { params(repository: ::T.untyped).returns(Relocation::Entry) } + def save_end_keyword_loc(repository); end + + # Slice the location of for_keyword_loc from the source. + sig { returns(String) } + def for_keyword; end + + # Slice the location of in_keyword_loc from the source. + sig { returns(String) } + def in_keyword; end + + # Slice the location of do_keyword_loc from the source. + sig { returns(::T.nilable(String)) } + def do_keyword; end + + # Slice the location of end_keyword_loc from the source. + sig { returns(String) } + def end_keyword; end + + sig { params(other: ::T.untyped).returns(::T.nilable(T::Boolean)) } + def ===(other); end + end + + # Represents forwarding all arguments to this method to another method. + # + # def foo(...) + # bar(...) + # ^^^ + # end + class ForwardingArgumentsNode < Node + # Initialize a new ForwardingArgumentsNode node. + sig { params(source: Source, node_id: Integer, location: Location, flags: Integer).void } + def initialize(source, node_id, location, flags); end + + # See Node.accept. + sig { override.params(visitor: Visitor).returns(::T.untyped) } + def accept(visitor); end + + # See Node.child_nodes. + sig { override.returns(T::Array[::T.nilable(Node)]) } + def child_nodes; end + + # See Node.each_child_node. + sig { override.returns(T::Enumerator[Node]) } + sig { override.params(blk: ::T.proc.params(arg0: Node).void).void } + def each_child_node(&blk); end + + # See Node.compact_child_nodes. + sig { override.returns(T::Array[Node]) } + def compact_child_nodes; end + + # See Node.comment_targets. + sig { override.returns(T::Array[::T.any(Node, Location)]) } + def comment_targets; end + + # Creates a copy of self with the given fields, using self as the template. + sig { params(node_id: Integer, location: Location, flags: Integer).returns(ForwardingArgumentsNode) } + def copy(node_id: T.unsafe(nil), location: T.unsafe(nil), flags: T.unsafe(nil)); end + + sig { override.returns(T::Array[::T.nilable(Node)]) } + def deconstruct; end + + sig { params(keys: ::T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, ::T.untyped]) } + def deconstruct_keys(keys); end + + # See `Node#type`. + sig { override.returns(Symbol) } + def type; end + + # See `Node.type`. + sig { override.returns(Symbol) } + def self.type; end + + sig { override.returns(String) } + def inspect; end + + sig { params(other: ::T.untyped).returns(::T.nilable(T::Boolean)) } + def ===(other); 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. + sig { params(source: Source, node_id: Integer, location: Location, flags: Integer).void } + def initialize(source, node_id, location, flags); end + + # See Node.accept. + sig { override.params(visitor: Visitor).returns(::T.untyped) } + def accept(visitor); end + + # See Node.child_nodes. + sig { override.returns(T::Array[::T.nilable(Node)]) } + def child_nodes; end + + # See Node.each_child_node. + sig { override.returns(T::Enumerator[Node]) } + sig { override.params(blk: ::T.proc.params(arg0: Node).void).void } + def each_child_node(&blk); end + + # See Node.compact_child_nodes. + sig { override.returns(T::Array[Node]) } + def compact_child_nodes; end + + # See Node.comment_targets. + sig { override.returns(T::Array[::T.any(Node, Location)]) } + def comment_targets; end + + # Creates a copy of self with the given fields, using self as the template. + sig { params(node_id: Integer, location: Location, flags: Integer).returns(ForwardingParameterNode) } + def copy(node_id: T.unsafe(nil), location: T.unsafe(nil), flags: T.unsafe(nil)); end + + sig { override.returns(T::Array[::T.nilable(Node)]) } + def deconstruct; end + + sig { params(keys: ::T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, ::T.untyped]) } + def deconstruct_keys(keys); end + + # See `Node#type`. + sig { override.returns(Symbol) } + def type; end + + # See `Node.type`. + sig { override.returns(Symbol) } + def self.type; end + + sig { override.returns(String) } + def inspect; end + + sig { params(other: ::T.untyped).returns(::T.nilable(T::Boolean)) } + def ===(other); 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. + sig { params(source: Source, node_id: Integer, location: Location, flags: Integer, block: ::T.nilable(BlockNode)).void } + def initialize(source, node_id, location, flags, block); end + + # See Node.accept. + sig { override.params(visitor: Visitor).returns(::T.untyped) } + def accept(visitor); end + + # See Node.child_nodes. + sig { override.returns(T::Array[::T.nilable(Node)]) } + def child_nodes; end + + # See Node.each_child_node. + sig { override.returns(T::Enumerator[Node]) } + sig { override.params(blk: ::T.proc.params(arg0: Node).void).void } + def each_child_node(&blk); end + + # See Node.compact_child_nodes. + sig { override.returns(T::Array[Node]) } + def compact_child_nodes; end + + # See Node.comment_targets. + sig { override.returns(T::Array[::T.any(Node, Location)]) } + def comment_targets; end + + # Creates a copy of self with the given fields, using self as the template. + sig { params(node_id: Integer, location: Location, flags: Integer, block: ::T.nilable(BlockNode)).returns(ForwardingSuperNode) } + def copy(node_id: T.unsafe(nil), location: T.unsafe(nil), flags: T.unsafe(nil), block: T.unsafe(nil)); end + + sig { override.returns(T::Array[::T.nilable(Node)]) } + def deconstruct; end + + sig { params(keys: ::T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, ::T.untyped]) } + def deconstruct_keys(keys); end + + # See `Node#type`. + sig { override.returns(Symbol) } + def type; end + + # See `Node.type`. + sig { override.returns(Symbol) } + def self.type; end + + sig { override.returns(String) } + def inspect; end + + # All other arguments are forwarded as normal, except the original block is replaced with the new block. + sig { returns(::T.nilable(BlockNode)) } + def block; end + + sig { params(other: ::T.untyped).returns(::T.nilable(T::Boolean)) } + def ===(other); end + end + + # Represents the use of the `&&=` operator for assignment to a global variable. + # + # $target &&= value + # ^^^^^^^^^^^^^^^^^ + class GlobalVariableAndWriteNode < Node + # Initialize a new GlobalVariableAndWriteNode node. + sig { params(source: Source, node_id: Integer, location: Location, flags: Integer, name: Symbol, name_loc: Location, operator_loc: Location, value: Node).void } + def initialize(source, node_id, location, flags, name, name_loc, operator_loc, value); end + + # See Node.accept. + sig { override.params(visitor: Visitor).returns(::T.untyped) } + def accept(visitor); end + + # See Node.child_nodes. + sig { override.returns(T::Array[::T.nilable(Node)]) } + def child_nodes; end + + # See Node.each_child_node. + sig { override.returns(T::Enumerator[Node]) } + sig { override.params(blk: ::T.proc.params(arg0: Node).void).void } + def each_child_node(&blk); end + + # See Node.compact_child_nodes. + sig { override.returns(T::Array[Node]) } + def compact_child_nodes; end + + # See Node.comment_targets. + sig { override.returns(T::Array[::T.any(Node, Location)]) } + def comment_targets; end + + # Creates a copy of self with the given fields, using self as the template. + sig { params(node_id: Integer, location: Location, flags: Integer, name: Symbol, name_loc: Location, operator_loc: Location, value: Node).returns(GlobalVariableAndWriteNode) } + def copy(node_id: T.unsafe(nil), location: T.unsafe(nil), flags: T.unsafe(nil), name: T.unsafe(nil), name_loc: T.unsafe(nil), operator_loc: T.unsafe(nil), value: T.unsafe(nil)); end + + sig { override.returns(T::Array[::T.nilable(Node)]) } + def deconstruct; end + + sig { params(keys: ::T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, ::T.untyped]) } + def deconstruct_keys(keys); end + + # See `Node#type`. + sig { override.returns(Symbol) } + def type; end + + # See `Node.type`. + sig { override.returns(Symbol) } + def self.type; end + + sig { override.returns(String) } + def inspect; end + + # Returns the `name` attribute. + sig { returns(Symbol) } + def name; end + + # Returns the Location represented by `name_loc`. + sig { returns(Location) } + def name_loc; end + + # Save the name_loc location using the given saved source so that + # it can be retrieved later. + sig { params(repository: ::T.untyped).returns(Relocation::Entry) } + def save_name_loc(repository); end + + # Returns the Location represented by `operator_loc`. + sig { returns(Location) } + def operator_loc; end + + # Save the operator_loc location using the given saved source so that + # it can be retrieved later. + sig { params(repository: ::T.untyped).returns(Relocation::Entry) } + def save_operator_loc(repository); end + + # Returns the `value` attribute. + sig { returns(Node) } + def value; end + + # Slice the location of operator_loc from the source. + sig { returns(String) } + def operator; end + + sig { params(other: ::T.untyped).returns(::T.nilable(T::Boolean)) } + def ===(other); end + end + + # Represents assigning to a global variable using an operator that isn't `=`. + # + # $target += value + # ^^^^^^^^^^^^^^^^ + class GlobalVariableOperatorWriteNode < Node + # Initialize a new GlobalVariableOperatorWriteNode node. + sig { params(source: Source, node_id: Integer, location: Location, flags: Integer, name: Symbol, name_loc: Location, binary_operator_loc: Location, value: Node, binary_operator: Symbol).void } + def initialize(source, node_id, location, flags, name, name_loc, binary_operator_loc, value, binary_operator); end + + # See Node.accept. + sig { override.params(visitor: Visitor).returns(::T.untyped) } + def accept(visitor); end + + # See Node.child_nodes. + sig { override.returns(T::Array[::T.nilable(Node)]) } + def child_nodes; end + + # See Node.each_child_node. + sig { override.returns(T::Enumerator[Node]) } + sig { override.params(blk: ::T.proc.params(arg0: Node).void).void } + def each_child_node(&blk); end + + # See Node.compact_child_nodes. + sig { override.returns(T::Array[Node]) } + def compact_child_nodes; end + + # See Node.comment_targets. + sig { override.returns(T::Array[::T.any(Node, Location)]) } + def comment_targets; end + + # Creates a copy of self with the given fields, using self as the template. + sig { params(node_id: Integer, location: Location, flags: Integer, name: Symbol, name_loc: Location, binary_operator_loc: Location, value: Node, binary_operator: Symbol).returns(GlobalVariableOperatorWriteNode) } + def copy(node_id: T.unsafe(nil), location: T.unsafe(nil), flags: T.unsafe(nil), name: T.unsafe(nil), name_loc: T.unsafe(nil), binary_operator_loc: T.unsafe(nil), value: T.unsafe(nil), binary_operator: T.unsafe(nil)); end + + sig { override.returns(T::Array[::T.nilable(Node)]) } + def deconstruct; end + + sig { params(keys: ::T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, ::T.untyped]) } + def deconstruct_keys(keys); end + + # See `Node#type`. + sig { override.returns(Symbol) } + def type; end + + # See `Node.type`. + sig { override.returns(Symbol) } + def self.type; end + + sig { override.returns(String) } + def inspect; end + + # Returns the `name` attribute. + sig { returns(Symbol) } + def name; end + + # Returns the Location represented by `name_loc`. + sig { returns(Location) } + def name_loc; end + + # Save the name_loc location using the given saved source so that + # it can be retrieved later. + sig { params(repository: ::T.untyped).returns(Relocation::Entry) } + def save_name_loc(repository); end + + # Returns the Location represented by `binary_operator_loc`. + sig { returns(Location) } + def binary_operator_loc; end + + # Save the binary_operator_loc location using the given saved source so that + # it can be retrieved later. + sig { params(repository: ::T.untyped).returns(Relocation::Entry) } + def save_binary_operator_loc(repository); end + + # Returns the `value` attribute. + sig { returns(Node) } + def value; end + + # Returns the `binary_operator` attribute. + sig { returns(Symbol) } + def binary_operator; end + + sig { params(other: ::T.untyped).returns(::T.nilable(T::Boolean)) } + def ===(other); end + end + + # Represents the use of the `||=` operator for assignment to a global variable. + # + # $target ||= value + # ^^^^^^^^^^^^^^^^^ + class GlobalVariableOrWriteNode < Node + # Initialize a new GlobalVariableOrWriteNode node. + sig { params(source: Source, node_id: Integer, location: Location, flags: Integer, name: Symbol, name_loc: Location, operator_loc: Location, value: Node).void } + def initialize(source, node_id, location, flags, name, name_loc, operator_loc, value); end + + # See Node.accept. + sig { override.params(visitor: Visitor).returns(::T.untyped) } + def accept(visitor); end + + # See Node.child_nodes. + sig { override.returns(T::Array[::T.nilable(Node)]) } + def child_nodes; end + + # See Node.each_child_node. + sig { override.returns(T::Enumerator[Node]) } + sig { override.params(blk: ::T.proc.params(arg0: Node).void).void } + def each_child_node(&blk); end + + # See Node.compact_child_nodes. + sig { override.returns(T::Array[Node]) } + def compact_child_nodes; end + + # See Node.comment_targets. + sig { override.returns(T::Array[::T.any(Node, Location)]) } + def comment_targets; end + + # Creates a copy of self with the given fields, using self as the template. + sig { params(node_id: Integer, location: Location, flags: Integer, name: Symbol, name_loc: Location, operator_loc: Location, value: Node).returns(GlobalVariableOrWriteNode) } + def copy(node_id: T.unsafe(nil), location: T.unsafe(nil), flags: T.unsafe(nil), name: T.unsafe(nil), name_loc: T.unsafe(nil), operator_loc: T.unsafe(nil), value: T.unsafe(nil)); end + + sig { override.returns(T::Array[::T.nilable(Node)]) } + def deconstruct; end + + sig { params(keys: ::T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, ::T.untyped]) } + def deconstruct_keys(keys); end + + # See `Node#type`. + sig { override.returns(Symbol) } + def type; end + + # See `Node.type`. + sig { override.returns(Symbol) } + def self.type; end + + sig { override.returns(String) } + def inspect; end + + # Returns the `name` attribute. + sig { returns(Symbol) } + def name; end + + # Returns the Location represented by `name_loc`. + sig { returns(Location) } + def name_loc; end + + # Save the name_loc location using the given saved source so that + # it can be retrieved later. + sig { params(repository: ::T.untyped).returns(Relocation::Entry) } + def save_name_loc(repository); end + + # Returns the Location represented by `operator_loc`. + sig { returns(Location) } + def operator_loc; end + + # Save the operator_loc location using the given saved source so that + # it can be retrieved later. + sig { params(repository: ::T.untyped).returns(Relocation::Entry) } + def save_operator_loc(repository); end + + # Returns the `value` attribute. + sig { returns(Node) } + def value; end + + # Slice the location of operator_loc from the source. + sig { returns(String) } + def operator; end + + sig { params(other: ::T.untyped).returns(::T.nilable(T::Boolean)) } + def ===(other); end + end + + # Represents referencing a global variable. + # + # $foo + # ^^^^ + class GlobalVariableReadNode < Node + # Initialize a new GlobalVariableReadNode node. + sig { params(source: Source, node_id: Integer, location: Location, flags: Integer, name: Symbol).void } + def initialize(source, node_id, location, flags, name); end + + # See Node.accept. + sig { override.params(visitor: Visitor).returns(::T.untyped) } + def accept(visitor); end + + # See Node.child_nodes. + sig { override.returns(T::Array[::T.nilable(Node)]) } + def child_nodes; end + + # See Node.each_child_node. + sig { override.returns(T::Enumerator[Node]) } + sig { override.params(blk: ::T.proc.params(arg0: Node).void).void } + def each_child_node(&blk); end + + # See Node.compact_child_nodes. + sig { override.returns(T::Array[Node]) } + def compact_child_nodes; end + + # See Node.comment_targets. + sig { override.returns(T::Array[::T.any(Node, Location)]) } + def comment_targets; end + + # Creates a copy of self with the given fields, using self as the template. + sig { params(node_id: Integer, location: Location, flags: Integer, name: Symbol).returns(GlobalVariableReadNode) } + def copy(node_id: T.unsafe(nil), location: T.unsafe(nil), flags: T.unsafe(nil), name: T.unsafe(nil)); end + + sig { override.returns(T::Array[::T.nilable(Node)]) } + def deconstruct; end + + sig { params(keys: ::T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, ::T.untyped]) } + def deconstruct_keys(keys); end + + # See `Node#type`. + sig { override.returns(Symbol) } + def type; end + + # See `Node.type`. + sig { override.returns(Symbol) } + def self.type; end + + sig { override.returns(String) } + def inspect; 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` + sig { returns(Symbol) } + def name; end + + sig { params(other: ::T.untyped).returns(::T.nilable(T::Boolean)) } + def ===(other); 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. + sig { params(source: Source, node_id: Integer, location: Location, flags: Integer, name: Symbol).void } + def initialize(source, node_id, location, flags, name); end + + # See Node.accept. + sig { override.params(visitor: Visitor).returns(::T.untyped) } + def accept(visitor); end + + # See Node.child_nodes. + sig { override.returns(T::Array[::T.nilable(Node)]) } + def child_nodes; end + + # See Node.each_child_node. + sig { override.returns(T::Enumerator[Node]) } + sig { override.params(blk: ::T.proc.params(arg0: Node).void).void } + def each_child_node(&blk); end + + # See Node.compact_child_nodes. + sig { override.returns(T::Array[Node]) } + def compact_child_nodes; end + + # See Node.comment_targets. + sig { override.returns(T::Array[::T.any(Node, Location)]) } + def comment_targets; end + + # Creates a copy of self with the given fields, using self as the template. + sig { params(node_id: Integer, location: Location, flags: Integer, name: Symbol).returns(GlobalVariableTargetNode) } + def copy(node_id: T.unsafe(nil), location: T.unsafe(nil), flags: T.unsafe(nil), name: T.unsafe(nil)); end + + sig { override.returns(T::Array[::T.nilable(Node)]) } + def deconstruct; end + + sig { params(keys: ::T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, ::T.untyped]) } + def deconstruct_keys(keys); end + + # See `Node#type`. + sig { override.returns(Symbol) } + def type; end + + # See `Node.type`. + sig { override.returns(Symbol) } + def self.type; end + + sig { override.returns(String) } + def inspect; end + + # Returns the `name` attribute. + sig { returns(Symbol) } + def name; end + + sig { params(other: ::T.untyped).returns(::T.nilable(T::Boolean)) } + def ===(other); end + end + + # Represents writing to a global variable. + # + # $foo = 1 + # ^^^^^^^^ + class GlobalVariableWriteNode < Node + # Initialize a new GlobalVariableWriteNode node. + sig { params(source: Source, node_id: Integer, location: Location, flags: Integer, name: Symbol, name_loc: Location, value: Node, operator_loc: Location).void } + def initialize(source, node_id, location, flags, name, name_loc, value, operator_loc); end + + # See Node.accept. + sig { override.params(visitor: Visitor).returns(::T.untyped) } + def accept(visitor); end + + # See Node.child_nodes. + sig { override.returns(T::Array[::T.nilable(Node)]) } + def child_nodes; end + + # See Node.each_child_node. + sig { override.returns(T::Enumerator[Node]) } + sig { override.params(blk: ::T.proc.params(arg0: Node).void).void } + def each_child_node(&blk); end + + # See Node.compact_child_nodes. + sig { override.returns(T::Array[Node]) } + def compact_child_nodes; end + + # See Node.comment_targets. + sig { override.returns(T::Array[::T.any(Node, Location)]) } + def comment_targets; end + + # Creates a copy of self with the given fields, using self as the template. + sig { params(node_id: Integer, location: Location, flags: Integer, name: Symbol, name_loc: Location, value: Node, operator_loc: Location).returns(GlobalVariableWriteNode) } + def copy(node_id: T.unsafe(nil), location: T.unsafe(nil), flags: T.unsafe(nil), name: T.unsafe(nil), name_loc: T.unsafe(nil), value: T.unsafe(nil), operator_loc: T.unsafe(nil)); end + + sig { override.returns(T::Array[::T.nilable(Node)]) } + def deconstruct; end + + sig { params(keys: ::T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, ::T.untyped]) } + def deconstruct_keys(keys); end + + # See `Node#type`. + sig { override.returns(Symbol) } + def type; end + + # See `Node.type`. + sig { override.returns(Symbol) } + def self.type; end + + sig { override.returns(String) } + def inspect; 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` + sig { returns(Symbol) } + def name; end + + # The Location of the global variable's name. + # + # $foo = :bar + # ^^^^ + sig { returns(Location) } + def name_loc; end + + # Save the name_loc location using the given saved source so that + # it can be retrieved later. + sig { params(repository: ::T.untyped).returns(Relocation::Entry) } + def save_name_loc(repository); 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 + # ^^^ + sig { returns(Node) } + def value; end + + # The Location of the `=` operator. + # + # $foo = :bar + # ^ + sig { returns(Location) } + def operator_loc; end + + # Save the operator_loc location using the given saved source so that + # it can be retrieved later. + sig { params(repository: ::T.untyped).returns(Relocation::Entry) } + def save_operator_loc(repository); end + + # Slice the location of operator_loc from the source. + sig { returns(String) } + def operator; end + + sig { params(other: ::T.untyped).returns(::T.nilable(T::Boolean)) } + def ===(other); end + end + + # Represents a hash literal. + # + # { a => b } + # ^^^^^^^^^^ + class HashNode < Node + # Initialize a new HashNode node. + sig { params(source: Source, node_id: Integer, location: Location, flags: Integer, opening_loc: Location, elements: T::Array[::T.any(AssocNode, AssocSplatNode)], closing_loc: Location).void } + def initialize(source, node_id, location, flags, opening_loc, elements, closing_loc); end + + # See Node.accept. + sig { override.params(visitor: Visitor).returns(::T.untyped) } + def accept(visitor); end + + # See Node.child_nodes. + sig { override.returns(T::Array[::T.nilable(Node)]) } + def child_nodes; end + + # See Node.each_child_node. + sig { override.returns(T::Enumerator[Node]) } + sig { override.params(blk: ::T.proc.params(arg0: Node).void).void } + def each_child_node(&blk); end + + # See Node.compact_child_nodes. + sig { override.returns(T::Array[Node]) } + def compact_child_nodes; end + + # See Node.comment_targets. + sig { override.returns(T::Array[::T.any(Node, Location)]) } + def comment_targets; end + + # Creates a copy of self with the given fields, using self as the template. + sig { params(node_id: Integer, location: Location, flags: Integer, opening_loc: Location, elements: T::Array[::T.any(AssocNode, AssocSplatNode)], closing_loc: Location).returns(HashNode) } + def copy(node_id: T.unsafe(nil), location: T.unsafe(nil), flags: T.unsafe(nil), opening_loc: T.unsafe(nil), elements: T.unsafe(nil), closing_loc: T.unsafe(nil)); end + + sig { override.returns(T::Array[::T.nilable(Node)]) } + def deconstruct; end + + sig { params(keys: ::T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, ::T.untyped]) } + def deconstruct_keys(keys); end + + # See `Node#type`. + sig { override.returns(Symbol) } + def type; end + + # See `Node.type`. + sig { override.returns(Symbol) } + def self.type; end + + sig { override.returns(String) } + def inspect; end + + # The Location of the opening brace. + # + # { a => b } + # ^ + sig { returns(Location) } + def opening_loc; end + + # Save the opening_loc location using the given saved source so that + # it can be retrieved later. + sig { params(repository: ::T.untyped).returns(Relocation::Entry) } + def save_opening_loc(repository); end + + # The elements of the hash. These can be either `AssocNode`s or `AssocSplatNode`s. + # + # { a: b } + # ^^^^ + # + # { **foo } + # ^^^^^ + sig { returns(T::Array[::T.any(AssocNode, AssocSplatNode)]) } + def elements; end + + # The Location of the closing brace. + # + # { a => b } + # ^ + sig { returns(Location) } + def closing_loc; end + + # Save the closing_loc location using the given saved source so that + # it can be retrieved later. + sig { params(repository: ::T.untyped).returns(Relocation::Entry) } + def save_closing_loc(repository); end + + # Slice the location of opening_loc from the source. + sig { returns(String) } + def opening; end + + # Slice the location of closing_loc from the source. + sig { returns(String) } + def closing; end + + sig { params(other: ::T.untyped).returns(::T.nilable(T::Boolean)) } + def ===(other); 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. + sig { params(source: Source, node_id: Integer, location: Location, flags: Integer, constant: ::T.nilable(::T.any(ConstantPathNode, ConstantReadNode)), elements: T::Array[AssocNode], rest: ::T.nilable(::T.any(AssocSplatNode, NoKeywordsParameterNode)), opening_loc: ::T.nilable(Location), closing_loc: ::T.nilable(Location)).void } + def initialize(source, node_id, location, flags, constant, elements, rest, opening_loc, closing_loc); end + + # See Node.accept. + sig { override.params(visitor: Visitor).returns(::T.untyped) } + def accept(visitor); end + + # See Node.child_nodes. + sig { override.returns(T::Array[::T.nilable(Node)]) } + def child_nodes; end + + # See Node.each_child_node. + sig { override.returns(T::Enumerator[Node]) } + sig { override.params(blk: ::T.proc.params(arg0: Node).void).void } + def each_child_node(&blk); end + + # See Node.compact_child_nodes. + sig { override.returns(T::Array[Node]) } + def compact_child_nodes; end + + # See Node.comment_targets. + sig { override.returns(T::Array[::T.any(Node, Location)]) } + def comment_targets; end + + # Creates a copy of self with the given fields, using self as the template. + sig { params(node_id: Integer, location: Location, flags: Integer, constant: ::T.nilable(::T.any(ConstantPathNode, ConstantReadNode)), elements: T::Array[AssocNode], rest: ::T.nilable(::T.any(AssocSplatNode, NoKeywordsParameterNode)), opening_loc: ::T.nilable(Location), closing_loc: ::T.nilable(Location)).returns(HashPatternNode) } + def copy(node_id: T.unsafe(nil), location: T.unsafe(nil), flags: T.unsafe(nil), constant: T.unsafe(nil), elements: T.unsafe(nil), rest: T.unsafe(nil), opening_loc: T.unsafe(nil), closing_loc: T.unsafe(nil)); end + + sig { override.returns(T::Array[::T.nilable(Node)]) } + def deconstruct; end + + sig { params(keys: ::T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, ::T.untyped]) } + def deconstruct_keys(keys); end + + # See `Node#type`. + sig { override.returns(Symbol) } + def type; end + + # See `Node.type`. + sig { override.returns(Symbol) } + def self.type; end + + sig { override.returns(String) } + def inspect; end + + # Represents the optional constant preceding the Hash. + # + # foo => Bar[a: 1, b: 2] + # ^^^ + # + # foo => Bar::Baz[a: 1, b: 2] + # ^^^^^^^^ + sig { returns(::T.nilable(::T.any(ConstantPathNode, ConstantReadNode))) } + def constant; end + + # Represents the explicit named hash keys and values. + # + # foo => { a: 1, b:, ** } + # ^^^^^^^^ + sig { returns(T::Array[AssocNode]) } + def elements; end + + # 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 } + # ^^^^^ + sig { returns(::T.nilable(::T.any(AssocSplatNode, NoKeywordsParameterNode))) } + def rest; end + + # The Location of the opening brace. + # + # foo => { a: 1 } + # ^ + # + # foo => Bar[a: 1] + # ^ + sig { returns(::T.nilable(Location)) } + def opening_loc; end + + # Save the opening_loc location using the given saved source so that + # it can be retrieved later. + sig { params(repository: ::T.untyped).returns(::T.nilable(Relocation::Entry)) } + def save_opening_loc(repository); end + + # The Location of the closing brace. + # + # foo => { a: 1 } + # ^ + # + # foo => Bar[a: 1] + # ^ + sig { returns(::T.nilable(Location)) } + def closing_loc; end + + # Save the closing_loc location using the given saved source so that + # it can be retrieved later. + sig { params(repository: ::T.untyped).returns(::T.nilable(Relocation::Entry)) } + def save_closing_loc(repository); end + + # Slice the location of opening_loc from the source. + sig { returns(::T.nilable(String)) } + def opening; end + + # Slice the location of closing_loc from the source. + sig { returns(::T.nilable(String)) } + def closing; end + + sig { params(other: ::T.untyped).returns(::T.nilable(T::Boolean)) } + def ===(other); 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. + sig { params(source: Source, node_id: Integer, location: Location, flags: Integer, if_keyword_loc: ::T.nilable(Location), predicate: Node, then_keyword_loc: ::T.nilable(Location), statements: ::T.nilable(StatementsNode), subsequent: ::T.nilable(::T.any(ElseNode, IfNode)), end_keyword_loc: ::T.nilable(Location)).void } + def initialize(source, node_id, location, flags, if_keyword_loc, predicate, then_keyword_loc, statements, subsequent, end_keyword_loc); end + + # See Node.accept. + sig { override.params(visitor: Visitor).returns(::T.untyped) } + def accept(visitor); end + + # See Node.child_nodes. + sig { override.returns(T::Array[::T.nilable(Node)]) } + def child_nodes; end + + # See Node.each_child_node. + sig { override.returns(T::Enumerator[Node]) } + sig { override.params(blk: ::T.proc.params(arg0: Node).void).void } + def each_child_node(&blk); end + + # See Node.compact_child_nodes. + sig { override.returns(T::Array[Node]) } + def compact_child_nodes; end + + # See Node.comment_targets. + sig { override.returns(T::Array[::T.any(Node, Location)]) } + def comment_targets; end + + # Creates a copy of self with the given fields, using self as the template. + sig { params(node_id: Integer, location: Location, flags: Integer, if_keyword_loc: ::T.nilable(Location), predicate: Node, then_keyword_loc: ::T.nilable(Location), statements: ::T.nilable(StatementsNode), subsequent: ::T.nilable(::T.any(ElseNode, IfNode)), end_keyword_loc: ::T.nilable(Location)).returns(IfNode) } + def copy(node_id: T.unsafe(nil), location: T.unsafe(nil), flags: T.unsafe(nil), if_keyword_loc: T.unsafe(nil), predicate: T.unsafe(nil), then_keyword_loc: T.unsafe(nil), statements: T.unsafe(nil), subsequent: T.unsafe(nil), end_keyword_loc: T.unsafe(nil)); end + + sig { override.returns(T::Array[::T.nilable(Node)]) } + def deconstruct; end + + sig { params(keys: ::T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, ::T.untyped]) } + def deconstruct_keys(keys); end + + # See `Node#type`. + sig { override.returns(Symbol) } + def type; end + + # See `Node.type`. + sig { override.returns(Symbol) } + def self.type; end + + sig { override.returns(String) } + def inspect; 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. + sig { returns(::T.nilable(Location)) } + def if_keyword_loc; end + + # Save the if_keyword_loc location using the given saved source so that + # it can be retrieved later. + sig { params(repository: ::T.untyped).returns(::T.nilable(Relocation::Entry)) } + def save_if_keyword_loc(repository); end + + # The node for the condition the `IfNode` is testing. + # + # if foo + # ^^^ + # bar + # end + # + # bar if foo + # ^^^ + # + # foo ? bar : baz + # ^^^ + sig { returns(Node) } + def predicate; end + + # The Location of the `then` keyword (if present) or the `?` in a ternary expression, `nil` otherwise. + # + # if foo then bar end + # ^^^^ + # + # a ? b : c + # ^ + sig { returns(::T.nilable(Location)) } + def then_keyword_loc; end + + # Save the then_keyword_loc location using the given saved source so that + # it can be retrieved later. + sig { params(repository: ::T.untyped).returns(::T.nilable(Relocation::Entry)) } + def save_then_keyword_loc(repository); 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 + sig { returns(::T.nilable(StatementsNode)) } + def statements; end + + # 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 + # ^^^^^^^^^^^^ + sig { returns(::T.nilable(::T.any(ElseNode, IfNode))) } + def subsequent; end + + # The Location of the `end` keyword if present, `nil` otherwise. + # + # if foo + # bar + # end + # ^^^ + sig { returns(::T.nilable(Location)) } + def end_keyword_loc; end + + # Save the end_keyword_loc location using the given saved source so that + # it can be retrieved later. + sig { params(repository: ::T.untyped).returns(::T.nilable(Relocation::Entry)) } + def save_end_keyword_loc(repository); end + + # Slice the location of if_keyword_loc from the source. + sig { returns(::T.nilable(String)) } + def if_keyword; end + + # Slice the location of then_keyword_loc from the source. + sig { returns(::T.nilable(String)) } + def then_keyword; end + + # Slice the location of end_keyword_loc from the source. + sig { returns(::T.nilable(String)) } + def end_keyword; end + + sig { params(other: ::T.untyped).returns(::T.nilable(T::Boolean)) } + def ===(other); end + end + + # Represents an imaginary number literal. + # + # 1.0i + # ^^^^ + class ImaginaryNode < Node + # Initialize a new ImaginaryNode node. + sig { params(source: Source, node_id: Integer, location: Location, flags: Integer, numeric: ::T.any(FloatNode, IntegerNode, RationalNode)).void } + def initialize(source, node_id, location, flags, numeric); end + + # See Node.accept. + sig { override.params(visitor: Visitor).returns(::T.untyped) } + def accept(visitor); end + + # See Node.child_nodes. + sig { override.returns(T::Array[::T.nilable(Node)]) } + def child_nodes; end + + # See Node.each_child_node. + sig { override.returns(T::Enumerator[Node]) } + sig { override.params(blk: ::T.proc.params(arg0: Node).void).void } + def each_child_node(&blk); end + + # See Node.compact_child_nodes. + sig { override.returns(T::Array[Node]) } + def compact_child_nodes; end + + # See Node.comment_targets. + sig { override.returns(T::Array[::T.any(Node, Location)]) } + def comment_targets; end + + # Creates a copy of self with the given fields, using self as the template. + sig { params(node_id: Integer, location: Location, flags: Integer, numeric: ::T.any(FloatNode, IntegerNode, RationalNode)).returns(ImaginaryNode) } + def copy(node_id: T.unsafe(nil), location: T.unsafe(nil), flags: T.unsafe(nil), numeric: T.unsafe(nil)); end + + sig { override.returns(T::Array[::T.nilable(Node)]) } + def deconstruct; end + + sig { params(keys: ::T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, ::T.untyped]) } + def deconstruct_keys(keys); end + + # See `Node#type`. + sig { override.returns(Symbol) } + def type; end + + # See `Node.type`. + sig { override.returns(Symbol) } + def self.type; end + + sig { override.returns(String) } + def inspect; end + + # Returns the `numeric` attribute. + sig { returns(::T.any(FloatNode, IntegerNode, RationalNode)) } + def numeric; end + + sig { params(other: ::T.untyped).returns(::T.nilable(T::Boolean)) } + def ===(other); 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. + sig { params(source: Source, node_id: Integer, location: Location, flags: Integer, value: ::T.any(LocalVariableReadNode, CallNode, ConstantReadNode, LocalVariableTargetNode)).void } + def initialize(source, node_id, location, flags, value); end + + # See Node.accept. + sig { override.params(visitor: Visitor).returns(::T.untyped) } + def accept(visitor); end + + # See Node.child_nodes. + sig { override.returns(T::Array[::T.nilable(Node)]) } + def child_nodes; end + + # See Node.each_child_node. + sig { override.returns(T::Enumerator[Node]) } + sig { override.params(blk: ::T.proc.params(arg0: Node).void).void } + def each_child_node(&blk); end + + # See Node.compact_child_nodes. + sig { override.returns(T::Array[Node]) } + def compact_child_nodes; end + + # See Node.comment_targets. + sig { override.returns(T::Array[::T.any(Node, Location)]) } + def comment_targets; end + + # Creates a copy of self with the given fields, using self as the template. + sig { params(node_id: Integer, location: Location, flags: Integer, value: ::T.any(LocalVariableReadNode, CallNode, ConstantReadNode, LocalVariableTargetNode)).returns(ImplicitNode) } + def copy(node_id: T.unsafe(nil), location: T.unsafe(nil), flags: T.unsafe(nil), value: T.unsafe(nil)); end + + sig { override.returns(T::Array[::T.nilable(Node)]) } + def deconstruct; end + + sig { params(keys: ::T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, ::T.untyped]) } + def deconstruct_keys(keys); end + + # See `Node#type`. + sig { override.returns(Symbol) } + def type; end + + # See `Node.type`. + sig { override.returns(Symbol) } + def self.type; end + + sig { override.returns(String) } + def inspect; end + + # Returns the `value` attribute. + sig { returns(::T.any(LocalVariableReadNode, CallNode, ConstantReadNode, LocalVariableTargetNode)) } + def value; end + + sig { params(other: ::T.untyped).returns(::T.nilable(T::Boolean)) } + def ===(other); 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. + sig { params(source: Source, node_id: Integer, location: Location, flags: Integer).void } + def initialize(source, node_id, location, flags); end + + # See Node.accept. + sig { override.params(visitor: Visitor).returns(::T.untyped) } + def accept(visitor); end + + # See Node.child_nodes. + sig { override.returns(T::Array[::T.nilable(Node)]) } + def child_nodes; end + + # See Node.each_child_node. + sig { override.returns(T::Enumerator[Node]) } + sig { override.params(blk: ::T.proc.params(arg0: Node).void).void } + def each_child_node(&blk); end + + # See Node.compact_child_nodes. + sig { override.returns(T::Array[Node]) } + def compact_child_nodes; end + + # See Node.comment_targets. + sig { override.returns(T::Array[::T.any(Node, Location)]) } + def comment_targets; end + + # Creates a copy of self with the given fields, using self as the template. + sig { params(node_id: Integer, location: Location, flags: Integer).returns(ImplicitRestNode) } + def copy(node_id: T.unsafe(nil), location: T.unsafe(nil), flags: T.unsafe(nil)); end + + sig { override.returns(T::Array[::T.nilable(Node)]) } + def deconstruct; end + + sig { params(keys: ::T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, ::T.untyped]) } + def deconstruct_keys(keys); end + + # See `Node#type`. + sig { override.returns(Symbol) } + def type; end + + # See `Node.type`. + sig { override.returns(Symbol) } + def self.type; end + + sig { override.returns(String) } + def inspect; end + + sig { params(other: ::T.untyped).returns(::T.nilable(T::Boolean)) } + def ===(other); 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. + sig { params(source: Source, node_id: Integer, location: Location, flags: Integer, pattern: Node, statements: ::T.nilable(StatementsNode), in_loc: Location, then_loc: ::T.nilable(Location)).void } + def initialize(source, node_id, location, flags, pattern, statements, in_loc, then_loc); end + + # See Node.accept. + sig { override.params(visitor: Visitor).returns(::T.untyped) } + def accept(visitor); end + + # See Node.child_nodes. + sig { override.returns(T::Array[::T.nilable(Node)]) } + def child_nodes; end + + # See Node.each_child_node. + sig { override.returns(T::Enumerator[Node]) } + sig { override.params(blk: ::T.proc.params(arg0: Node).void).void } + def each_child_node(&blk); end + + # See Node.compact_child_nodes. + sig { override.returns(T::Array[Node]) } + def compact_child_nodes; end + + # See Node.comment_targets. + sig { override.returns(T::Array[::T.any(Node, Location)]) } + def comment_targets; end + + # Creates a copy of self with the given fields, using self as the template. + sig { params(node_id: Integer, location: Location, flags: Integer, pattern: Node, statements: ::T.nilable(StatementsNode), in_loc: Location, then_loc: ::T.nilable(Location)).returns(InNode) } + def copy(node_id: T.unsafe(nil), location: T.unsafe(nil), flags: T.unsafe(nil), pattern: T.unsafe(nil), statements: T.unsafe(nil), in_loc: T.unsafe(nil), then_loc: T.unsafe(nil)); end + + sig { override.returns(T::Array[::T.nilable(Node)]) } + def deconstruct; end + + sig { params(keys: ::T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, ::T.untyped]) } + def deconstruct_keys(keys); end + + # See `Node#type`. + sig { override.returns(Symbol) } + def type; end + + # See `Node.type`. + sig { override.returns(Symbol) } + def self.type; end + + sig { override.returns(String) } + def inspect; end + + # Returns the `pattern` attribute. + sig { returns(Node) } + def pattern; end + + # Returns the `statements` attribute. + sig { returns(::T.nilable(StatementsNode)) } + def statements; end + + # Returns the Location represented by `in_loc`. + sig { returns(Location) } + def in_loc; end + + # Save the in_loc location using the given saved source so that + # it can be retrieved later. + sig { params(repository: ::T.untyped).returns(Relocation::Entry) } + def save_in_loc(repository); end + + # Returns the Location represented by `then_loc`. + sig { returns(::T.nilable(Location)) } + def then_loc; end + + # Save the then_loc location using the given saved source so that + # it can be retrieved later. + sig { params(repository: ::T.untyped).returns(::T.nilable(Relocation::Entry)) } + def save_then_loc(repository); end + + # Slice the location of in_loc from the source. + sig { returns(String) } + def in; end + + # Slice the location of then_loc from the source. + sig { returns(::T.nilable(String)) } + def then; end + + sig { params(other: ::T.untyped).returns(::T.nilable(T::Boolean)) } + def ===(other); 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. + sig { params(source: Source, node_id: Integer, location: Location, flags: Integer, receiver: ::T.nilable(Node), call_operator_loc: ::T.nilable(Location), opening_loc: Location, arguments: ::T.nilable(ArgumentsNode), closing_loc: Location, block: ::T.nilable(BlockArgumentNode), operator_loc: Location, value: Node).void } + def initialize(source, node_id, location, flags, receiver, call_operator_loc, opening_loc, arguments, closing_loc, block, operator_loc, value); end + + # See Node.accept. + sig { override.params(visitor: Visitor).returns(::T.untyped) } + def accept(visitor); end + + # See Node.child_nodes. + sig { override.returns(T::Array[::T.nilable(Node)]) } + def child_nodes; end + + # See Node.each_child_node. + sig { override.returns(T::Enumerator[Node]) } + sig { override.params(blk: ::T.proc.params(arg0: Node).void).void } + def each_child_node(&blk); end + + # See Node.compact_child_nodes. + sig { override.returns(T::Array[Node]) } + def compact_child_nodes; end + + # See Node.comment_targets. + sig { override.returns(T::Array[::T.any(Node, Location)]) } + def comment_targets; end + + # Creates a copy of self with the given fields, using self as the template. + sig { params(node_id: Integer, location: Location, flags: Integer, receiver: ::T.nilable(Node), call_operator_loc: ::T.nilable(Location), opening_loc: Location, arguments: ::T.nilable(ArgumentsNode), closing_loc: Location, block: ::T.nilable(BlockArgumentNode), operator_loc: Location, value: Node).returns(IndexAndWriteNode) } + def copy(node_id: T.unsafe(nil), location: T.unsafe(nil), flags: T.unsafe(nil), receiver: T.unsafe(nil), call_operator_loc: T.unsafe(nil), opening_loc: T.unsafe(nil), arguments: T.unsafe(nil), closing_loc: T.unsafe(nil), block: T.unsafe(nil), operator_loc: T.unsafe(nil), value: T.unsafe(nil)); end + + sig { override.returns(T::Array[::T.nilable(Node)]) } + def deconstruct; end + + sig { params(keys: ::T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, ::T.untyped]) } + def deconstruct_keys(keys); end + + # See `Node#type`. + sig { override.returns(Symbol) } + def type; end + + # See `Node.type`. + sig { override.returns(Symbol) } + def self.type; end + + sig { override.returns(String) } + def inspect; end + + # &. operator + sig { returns(T::Boolean) } + def safe_navigation?; end + + # a call that could have been a local variable + sig { returns(T::Boolean) } + def variable_call?; end + + # a call that is an attribute write, so the value being written should be returned + sig { returns(T::Boolean) } + def attribute_write?; end + + # a call that ignores method visibility + sig { returns(T::Boolean) } + def ignore_visibility?; end + + # Returns the `receiver` attribute. + sig { returns(::T.nilable(Node)) } + def receiver; end + + # Returns the Location represented by `call_operator_loc`. + sig { returns(::T.nilable(Location)) } + def call_operator_loc; end + + # Save the call_operator_loc location using the given saved source so that + # it can be retrieved later. + sig { params(repository: ::T.untyped).returns(::T.nilable(Relocation::Entry)) } + def save_call_operator_loc(repository); end + + # Returns the Location represented by `opening_loc`. + sig { returns(Location) } + def opening_loc; end + + # Save the opening_loc location using the given saved source so that + # it can be retrieved later. + sig { params(repository: ::T.untyped).returns(Relocation::Entry) } + def save_opening_loc(repository); end + + # Returns the `arguments` attribute. + sig { returns(::T.nilable(ArgumentsNode)) } + def arguments; end + + # Returns the Location represented by `closing_loc`. + sig { returns(Location) } + def closing_loc; end + + # Save the closing_loc location using the given saved source so that + # it can be retrieved later. + sig { params(repository: ::T.untyped).returns(Relocation::Entry) } + def save_closing_loc(repository); end + + # Returns the `block` attribute. + sig { returns(::T.nilable(BlockArgumentNode)) } + def block; end + + # Returns the Location represented by `operator_loc`. + sig { returns(Location) } + def operator_loc; end + + # Save the operator_loc location using the given saved source so that + # it can be retrieved later. + sig { params(repository: ::T.untyped).returns(Relocation::Entry) } + def save_operator_loc(repository); end + + # Returns the `value` attribute. + sig { returns(Node) } + def value; end + + # Slice the location of call_operator_loc from the source. + sig { returns(::T.nilable(String)) } + def call_operator; end + + # Slice the location of opening_loc from the source. + sig { returns(String) } + def opening; end + + # Slice the location of closing_loc from the source. + sig { returns(String) } + def closing; end + + # Slice the location of operator_loc from the source. + sig { returns(String) } + def operator; end + + sig { params(other: ::T.untyped).returns(::T.nilable(T::Boolean)) } + def ===(other); 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. + sig { params(source: Source, node_id: Integer, location: Location, flags: Integer, receiver: ::T.nilable(Node), call_operator_loc: ::T.nilable(Location), opening_loc: Location, arguments: ::T.nilable(ArgumentsNode), closing_loc: Location, block: ::T.nilable(BlockArgumentNode), binary_operator: Symbol, binary_operator_loc: Location, value: 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 + + # See Node.accept. + sig { override.params(visitor: Visitor).returns(::T.untyped) } + def accept(visitor); end + + # See Node.child_nodes. + sig { override.returns(T::Array[::T.nilable(Node)]) } + def child_nodes; end + + # See Node.each_child_node. + sig { override.returns(T::Enumerator[Node]) } + sig { override.params(blk: ::T.proc.params(arg0: Node).void).void } + def each_child_node(&blk); end + + # See Node.compact_child_nodes. + sig { override.returns(T::Array[Node]) } + def compact_child_nodes; end + + # See Node.comment_targets. + sig { override.returns(T::Array[::T.any(Node, Location)]) } + def comment_targets; end + + # Creates a copy of self with the given fields, using self as the template. + sig { params(node_id: Integer, location: Location, flags: Integer, receiver: ::T.nilable(Node), call_operator_loc: ::T.nilable(Location), opening_loc: Location, arguments: ::T.nilable(ArgumentsNode), closing_loc: Location, block: ::T.nilable(BlockArgumentNode), binary_operator: Symbol, binary_operator_loc: Location, value: Node).returns(IndexOperatorWriteNode) } + def copy(node_id: T.unsafe(nil), location: T.unsafe(nil), flags: T.unsafe(nil), receiver: T.unsafe(nil), call_operator_loc: T.unsafe(nil), opening_loc: T.unsafe(nil), arguments: T.unsafe(nil), closing_loc: T.unsafe(nil), block: T.unsafe(nil), binary_operator: T.unsafe(nil), binary_operator_loc: T.unsafe(nil), value: T.unsafe(nil)); end + + sig { override.returns(T::Array[::T.nilable(Node)]) } + def deconstruct; end + + sig { params(keys: ::T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, ::T.untyped]) } + def deconstruct_keys(keys); end + + # See `Node#type`. + sig { override.returns(Symbol) } + def type; end + + # See `Node.type`. + sig { override.returns(Symbol) } + def self.type; end + + sig { override.returns(String) } + def inspect; end + + # &. operator + sig { returns(T::Boolean) } + def safe_navigation?; end + + # a call that could have been a local variable + sig { returns(T::Boolean) } + def variable_call?; end + + # a call that is an attribute write, so the value being written should be returned + sig { returns(T::Boolean) } + def attribute_write?; end + + # a call that ignores method visibility + sig { returns(T::Boolean) } + def ignore_visibility?; end + + # Returns the `receiver` attribute. + sig { returns(::T.nilable(Node)) } + def receiver; end + + # Returns the Location represented by `call_operator_loc`. + sig { returns(::T.nilable(Location)) } + def call_operator_loc; end + + # Save the call_operator_loc location using the given saved source so that + # it can be retrieved later. + sig { params(repository: ::T.untyped).returns(::T.nilable(Relocation::Entry)) } + def save_call_operator_loc(repository); end + + # Returns the Location represented by `opening_loc`. + sig { returns(Location) } + def opening_loc; end + + # Save the opening_loc location using the given saved source so that + # it can be retrieved later. + sig { params(repository: ::T.untyped).returns(Relocation::Entry) } + def save_opening_loc(repository); end + + # Returns the `arguments` attribute. + sig { returns(::T.nilable(ArgumentsNode)) } + def arguments; end + + # Returns the Location represented by `closing_loc`. + sig { returns(Location) } + def closing_loc; end + + # Save the closing_loc location using the given saved source so that + # it can be retrieved later. + sig { params(repository: ::T.untyped).returns(Relocation::Entry) } + def save_closing_loc(repository); end + + # Returns the `block` attribute. + sig { returns(::T.nilable(BlockArgumentNode)) } + def block; end + + # Returns the `binary_operator` attribute. + sig { returns(Symbol) } + def binary_operator; end + + # Returns the Location represented by `binary_operator_loc`. + sig { returns(Location) } + def binary_operator_loc; end + + # Save the binary_operator_loc location using the given saved source so that + # it can be retrieved later. + sig { params(repository: ::T.untyped).returns(Relocation::Entry) } + def save_binary_operator_loc(repository); end + + # Returns the `value` attribute. + sig { returns(Node) } + def value; end + + # Slice the location of call_operator_loc from the source. + sig { returns(::T.nilable(String)) } + def call_operator; end + + # Slice the location of opening_loc from the source. + sig { returns(String) } + def opening; end + + # Slice the location of closing_loc from the source. + sig { returns(String) } + def closing; end + + sig { params(other: ::T.untyped).returns(::T.nilable(T::Boolean)) } + def ===(other); end + end + + # Represents the use of the `||=` operator on a call to `[]`. + # + # foo.bar[baz] ||= value + # ^^^^^^^^^^^^^^^^^^^^^^ + class IndexOrWriteNode < Node + # Initialize a new IndexOrWriteNode node. + sig { params(source: Source, node_id: Integer, location: Location, flags: Integer, receiver: ::T.nilable(Node), call_operator_loc: ::T.nilable(Location), opening_loc: Location, arguments: ::T.nilable(ArgumentsNode), closing_loc: Location, block: ::T.nilable(BlockArgumentNode), operator_loc: Location, value: Node).void } + def initialize(source, node_id, location, flags, receiver, call_operator_loc, opening_loc, arguments, closing_loc, block, operator_loc, value); end + + # See Node.accept. + sig { override.params(visitor: Visitor).returns(::T.untyped) } + def accept(visitor); end + + # See Node.child_nodes. + sig { override.returns(T::Array[::T.nilable(Node)]) } + def child_nodes; end + + # See Node.each_child_node. + sig { override.returns(T::Enumerator[Node]) } + sig { override.params(blk: ::T.proc.params(arg0: Node).void).void } + def each_child_node(&blk); end + + # See Node.compact_child_nodes. + sig { override.returns(T::Array[Node]) } + def compact_child_nodes; end + + # See Node.comment_targets. + sig { override.returns(T::Array[::T.any(Node, Location)]) } + def comment_targets; end + + # Creates a copy of self with the given fields, using self as the template. + sig { params(node_id: Integer, location: Location, flags: Integer, receiver: ::T.nilable(Node), call_operator_loc: ::T.nilable(Location), opening_loc: Location, arguments: ::T.nilable(ArgumentsNode), closing_loc: Location, block: ::T.nilable(BlockArgumentNode), operator_loc: Location, value: Node).returns(IndexOrWriteNode) } + def copy(node_id: T.unsafe(nil), location: T.unsafe(nil), flags: T.unsafe(nil), receiver: T.unsafe(nil), call_operator_loc: T.unsafe(nil), opening_loc: T.unsafe(nil), arguments: T.unsafe(nil), closing_loc: T.unsafe(nil), block: T.unsafe(nil), operator_loc: T.unsafe(nil), value: T.unsafe(nil)); end + + sig { override.returns(T::Array[::T.nilable(Node)]) } + def deconstruct; end + + sig { params(keys: ::T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, ::T.untyped]) } + def deconstruct_keys(keys); end + + # See `Node#type`. + sig { override.returns(Symbol) } + def type; end + + # See `Node.type`. + sig { override.returns(Symbol) } + def self.type; end + + sig { override.returns(String) } + def inspect; end + + # &. operator + sig { returns(T::Boolean) } + def safe_navigation?; end + + # a call that could have been a local variable + sig { returns(T::Boolean) } + def variable_call?; end + + # a call that is an attribute write, so the value being written should be returned + sig { returns(T::Boolean) } + def attribute_write?; end + + # a call that ignores method visibility + sig { returns(T::Boolean) } + def ignore_visibility?; end + + # Returns the `receiver` attribute. + sig { returns(::T.nilable(Node)) } + def receiver; end + + # Returns the Location represented by `call_operator_loc`. + sig { returns(::T.nilable(Location)) } + def call_operator_loc; end + + # Save the call_operator_loc location using the given saved source so that + # it can be retrieved later. + sig { params(repository: ::T.untyped).returns(::T.nilable(Relocation::Entry)) } + def save_call_operator_loc(repository); end + + # Returns the Location represented by `opening_loc`. + sig { returns(Location) } + def opening_loc; end + + # Save the opening_loc location using the given saved source so that + # it can be retrieved later. + sig { params(repository: ::T.untyped).returns(Relocation::Entry) } + def save_opening_loc(repository); end + + # Returns the `arguments` attribute. + sig { returns(::T.nilable(ArgumentsNode)) } + def arguments; end + + # Returns the Location represented by `closing_loc`. + sig { returns(Location) } + def closing_loc; end + + # Save the closing_loc location using the given saved source so that + # it can be retrieved later. + sig { params(repository: ::T.untyped).returns(Relocation::Entry) } + def save_closing_loc(repository); end + + # Returns the `block` attribute. + sig { returns(::T.nilable(BlockArgumentNode)) } + def block; end + + # Returns the Location represented by `operator_loc`. + sig { returns(Location) } + def operator_loc; end + + # Save the operator_loc location using the given saved source so that + # it can be retrieved later. + sig { params(repository: ::T.untyped).returns(Relocation::Entry) } + def save_operator_loc(repository); end + + # Returns the `value` attribute. + sig { returns(Node) } + def value; end + + # Slice the location of call_operator_loc from the source. + sig { returns(::T.nilable(String)) } + def call_operator; end + + # Slice the location of opening_loc from the source. + sig { returns(String) } + def opening; end + + # Slice the location of closing_loc from the source. + sig { returns(String) } + def closing; end + + # Slice the location of operator_loc from the source. + sig { returns(String) } + def operator; end + + sig { params(other: ::T.untyped).returns(::T.nilable(T::Boolean)) } + def ===(other); 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. + sig { params(source: Source, node_id: Integer, location: Location, flags: Integer, receiver: Node, opening_loc: Location, arguments: ::T.nilable(ArgumentsNode), closing_loc: Location, block: ::T.nilable(BlockArgumentNode)).void } + def initialize(source, node_id, location, flags, receiver, opening_loc, arguments, closing_loc, block); end + + # See Node.accept. + sig { override.params(visitor: Visitor).returns(::T.untyped) } + def accept(visitor); end + + # See Node.child_nodes. + sig { override.returns(T::Array[::T.nilable(Node)]) } + def child_nodes; end + + # See Node.each_child_node. + sig { override.returns(T::Enumerator[Node]) } + sig { override.params(blk: ::T.proc.params(arg0: Node).void).void } + def each_child_node(&blk); end + + # See Node.compact_child_nodes. + sig { override.returns(T::Array[Node]) } + def compact_child_nodes; end + + # See Node.comment_targets. + sig { override.returns(T::Array[::T.any(Node, Location)]) } + def comment_targets; end + + # Creates a copy of self with the given fields, using self as the template. + sig { params(node_id: Integer, location: Location, flags: Integer, receiver: Node, opening_loc: Location, arguments: ::T.nilable(ArgumentsNode), closing_loc: Location, block: ::T.nilable(BlockArgumentNode)).returns(IndexTargetNode) } + def copy(node_id: T.unsafe(nil), location: T.unsafe(nil), flags: T.unsafe(nil), receiver: T.unsafe(nil), opening_loc: T.unsafe(nil), arguments: T.unsafe(nil), closing_loc: T.unsafe(nil), block: T.unsafe(nil)); end + + sig { override.returns(T::Array[::T.nilable(Node)]) } + def deconstruct; end + + sig { params(keys: ::T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, ::T.untyped]) } + def deconstruct_keys(keys); end + + # See `Node#type`. + sig { override.returns(Symbol) } + def type; end + + # See `Node.type`. + sig { override.returns(Symbol) } + def self.type; end + + sig { override.returns(String) } + def inspect; end + + # &. operator + sig { returns(T::Boolean) } + def safe_navigation?; end + + # a call that could have been a local variable + sig { returns(T::Boolean) } + def variable_call?; end + + # a call that is an attribute write, so the value being written should be returned + sig { returns(T::Boolean) } + def attribute_write?; end + + # a call that ignores method visibility + sig { returns(T::Boolean) } + def ignore_visibility?; end + + # Returns the `receiver` attribute. + sig { returns(Node) } + def receiver; end + + # Returns the Location represented by `opening_loc`. + sig { returns(Location) } + def opening_loc; end + + # Save the opening_loc location using the given saved source so that + # it can be retrieved later. + sig { params(repository: ::T.untyped).returns(Relocation::Entry) } + def save_opening_loc(repository); end + + # Returns the `arguments` attribute. + sig { returns(::T.nilable(ArgumentsNode)) } + def arguments; end + + # Returns the Location represented by `closing_loc`. + sig { returns(Location) } + def closing_loc; end + + # Save the closing_loc location using the given saved source so that + # it can be retrieved later. + sig { params(repository: ::T.untyped).returns(Relocation::Entry) } + def save_closing_loc(repository); end + + # Returns the `block` attribute. + sig { returns(::T.nilable(BlockArgumentNode)) } + def block; end + + # Slice the location of opening_loc from the source. + sig { returns(String) } + def opening; end + + # Slice the location of closing_loc from the source. + sig { returns(String) } + def closing; end + + sig { params(other: ::T.untyped).returns(::T.nilable(T::Boolean)) } + def ===(other); end + end + + # Represents the use of the `&&=` operator for assignment to an instance variable. + # + # @target &&= value + # ^^^^^^^^^^^^^^^^^ + class InstanceVariableAndWriteNode < Node + # Initialize a new InstanceVariableAndWriteNode node. + sig { params(source: Source, node_id: Integer, location: Location, flags: Integer, name: Symbol, name_loc: Location, operator_loc: Location, value: Node).void } + def initialize(source, node_id, location, flags, name, name_loc, operator_loc, value); end + + # See Node.accept. + sig { override.params(visitor: Visitor).returns(::T.untyped) } + def accept(visitor); end + + # See Node.child_nodes. + sig { override.returns(T::Array[::T.nilable(Node)]) } + def child_nodes; end + + # See Node.each_child_node. + sig { override.returns(T::Enumerator[Node]) } + sig { override.params(blk: ::T.proc.params(arg0: Node).void).void } + def each_child_node(&blk); end + + # See Node.compact_child_nodes. + sig { override.returns(T::Array[Node]) } + def compact_child_nodes; end + + # See Node.comment_targets. + sig { override.returns(T::Array[::T.any(Node, Location)]) } + def comment_targets; end + + # Creates a copy of self with the given fields, using self as the template. + sig { params(node_id: Integer, location: Location, flags: Integer, name: Symbol, name_loc: Location, operator_loc: Location, value: Node).returns(InstanceVariableAndWriteNode) } + def copy(node_id: T.unsafe(nil), location: T.unsafe(nil), flags: T.unsafe(nil), name: T.unsafe(nil), name_loc: T.unsafe(nil), operator_loc: T.unsafe(nil), value: T.unsafe(nil)); end + + sig { override.returns(T::Array[::T.nilable(Node)]) } + def deconstruct; end + + sig { params(keys: ::T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, ::T.untyped]) } + def deconstruct_keys(keys); end + + # See `Node#type`. + sig { override.returns(Symbol) } + def type; end + + # See `Node.type`. + sig { override.returns(Symbol) } + def self.type; end + + sig { override.returns(String) } + def inspect; end + + # Returns the `name` attribute. + sig { returns(Symbol) } + def name; end + + # Returns the Location represented by `name_loc`. + sig { returns(Location) } + def name_loc; end + + # Save the name_loc location using the given saved source so that + # it can be retrieved later. + sig { params(repository: ::T.untyped).returns(Relocation::Entry) } + def save_name_loc(repository); end + + # Returns the Location represented by `operator_loc`. + sig { returns(Location) } + def operator_loc; end + + # Save the operator_loc location using the given saved source so that + # it can be retrieved later. + sig { params(repository: ::T.untyped).returns(Relocation::Entry) } + def save_operator_loc(repository); end + + # Returns the `value` attribute. + sig { returns(Node) } + def value; end + + # Slice the location of operator_loc from the source. + sig { returns(String) } + def operator; end + + sig { params(other: ::T.untyped).returns(::T.nilable(T::Boolean)) } + def ===(other); end + end + + # Represents assigning to an instance variable using an operator that isn't `=`. + # + # @target += value + # ^^^^^^^^^^^^^^^^ + class InstanceVariableOperatorWriteNode < Node + # Initialize a new InstanceVariableOperatorWriteNode node. + sig { params(source: Source, node_id: Integer, location: Location, flags: Integer, name: Symbol, name_loc: Location, binary_operator_loc: Location, value: Node, binary_operator: Symbol).void } + def initialize(source, node_id, location, flags, name, name_loc, binary_operator_loc, value, binary_operator); end + + # See Node.accept. + sig { override.params(visitor: Visitor).returns(::T.untyped) } + def accept(visitor); end + + # See Node.child_nodes. + sig { override.returns(T::Array[::T.nilable(Node)]) } + def child_nodes; end + + # See Node.each_child_node. + sig { override.returns(T::Enumerator[Node]) } + sig { override.params(blk: ::T.proc.params(arg0: Node).void).void } + def each_child_node(&blk); end + + # See Node.compact_child_nodes. + sig { override.returns(T::Array[Node]) } + def compact_child_nodes; end + + # See Node.comment_targets. + sig { override.returns(T::Array[::T.any(Node, Location)]) } + def comment_targets; end + + # Creates a copy of self with the given fields, using self as the template. + sig { params(node_id: Integer, location: Location, flags: Integer, name: Symbol, name_loc: Location, binary_operator_loc: Location, value: Node, binary_operator: Symbol).returns(InstanceVariableOperatorWriteNode) } + def copy(node_id: T.unsafe(nil), location: T.unsafe(nil), flags: T.unsafe(nil), name: T.unsafe(nil), name_loc: T.unsafe(nil), binary_operator_loc: T.unsafe(nil), value: T.unsafe(nil), binary_operator: T.unsafe(nil)); end + + sig { override.returns(T::Array[::T.nilable(Node)]) } + def deconstruct; end + + sig { params(keys: ::T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, ::T.untyped]) } + def deconstruct_keys(keys); end + + # See `Node#type`. + sig { override.returns(Symbol) } + def type; end + + # See `Node.type`. + sig { override.returns(Symbol) } + def self.type; end + + sig { override.returns(String) } + def inspect; end + + # Returns the `name` attribute. + sig { returns(Symbol) } + def name; end + + # Returns the Location represented by `name_loc`. + sig { returns(Location) } + def name_loc; end + + # Save the name_loc location using the given saved source so that + # it can be retrieved later. + sig { params(repository: ::T.untyped).returns(Relocation::Entry) } + def save_name_loc(repository); end + + # Returns the Location represented by `binary_operator_loc`. + sig { returns(Location) } + def binary_operator_loc; end + + # Save the binary_operator_loc location using the given saved source so that + # it can be retrieved later. + sig { params(repository: ::T.untyped).returns(Relocation::Entry) } + def save_binary_operator_loc(repository); end + + # Returns the `value` attribute. + sig { returns(Node) } + def value; end + + # Returns the `binary_operator` attribute. + sig { returns(Symbol) } + def binary_operator; end + + sig { params(other: ::T.untyped).returns(::T.nilable(T::Boolean)) } + def ===(other); end + end + + # Represents the use of the `||=` operator for assignment to an instance variable. + # + # @target ||= value + # ^^^^^^^^^^^^^^^^^ + class InstanceVariableOrWriteNode < Node + # Initialize a new InstanceVariableOrWriteNode node. + sig { params(source: Source, node_id: Integer, location: Location, flags: Integer, name: Symbol, name_loc: Location, operator_loc: Location, value: Node).void } + def initialize(source, node_id, location, flags, name, name_loc, operator_loc, value); end + + # See Node.accept. + sig { override.params(visitor: Visitor).returns(::T.untyped) } + def accept(visitor); end + + # See Node.child_nodes. + sig { override.returns(T::Array[::T.nilable(Node)]) } + def child_nodes; end + + # See Node.each_child_node. + sig { override.returns(T::Enumerator[Node]) } + sig { override.params(blk: ::T.proc.params(arg0: Node).void).void } + def each_child_node(&blk); end + + # See Node.compact_child_nodes. + sig { override.returns(T::Array[Node]) } + def compact_child_nodes; end + + # See Node.comment_targets. + sig { override.returns(T::Array[::T.any(Node, Location)]) } + def comment_targets; end + + # Creates a copy of self with the given fields, using self as the template. + sig { params(node_id: Integer, location: Location, flags: Integer, name: Symbol, name_loc: Location, operator_loc: Location, value: Node).returns(InstanceVariableOrWriteNode) } + def copy(node_id: T.unsafe(nil), location: T.unsafe(nil), flags: T.unsafe(nil), name: T.unsafe(nil), name_loc: T.unsafe(nil), operator_loc: T.unsafe(nil), value: T.unsafe(nil)); end + + sig { override.returns(T::Array[::T.nilable(Node)]) } + def deconstruct; end + + sig { params(keys: ::T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, ::T.untyped]) } + def deconstruct_keys(keys); end + + # See `Node#type`. + sig { override.returns(Symbol) } + def type; end + + # See `Node.type`. + sig { override.returns(Symbol) } + def self.type; end + + sig { override.returns(String) } + def inspect; end + + # Returns the `name` attribute. + sig { returns(Symbol) } + def name; end + + # Returns the Location represented by `name_loc`. + sig { returns(Location) } + def name_loc; end + + # Save the name_loc location using the given saved source so that + # it can be retrieved later. + sig { params(repository: ::T.untyped).returns(Relocation::Entry) } + def save_name_loc(repository); end + + # Returns the Location represented by `operator_loc`. + sig { returns(Location) } + def operator_loc; end + + # Save the operator_loc location using the given saved source so that + # it can be retrieved later. + sig { params(repository: ::T.untyped).returns(Relocation::Entry) } + def save_operator_loc(repository); end + + # Returns the `value` attribute. + sig { returns(Node) } + def value; end + + # Slice the location of operator_loc from the source. + sig { returns(String) } + def operator; end + + sig { params(other: ::T.untyped).returns(::T.nilable(T::Boolean)) } + def ===(other); end + end + + # Represents referencing an instance variable. + # + # @foo + # ^^^^ + class InstanceVariableReadNode < Node + # Initialize a new InstanceVariableReadNode node. + sig { params(source: Source, node_id: Integer, location: Location, flags: Integer, name: Symbol).void } + def initialize(source, node_id, location, flags, name); end + + # See Node.accept. + sig { override.params(visitor: Visitor).returns(::T.untyped) } + def accept(visitor); end + + # See Node.child_nodes. + sig { override.returns(T::Array[::T.nilable(Node)]) } + def child_nodes; end + + # See Node.each_child_node. + sig { override.returns(T::Enumerator[Node]) } + sig { override.params(blk: ::T.proc.params(arg0: Node).void).void } + def each_child_node(&blk); end + + # See Node.compact_child_nodes. + sig { override.returns(T::Array[Node]) } + def compact_child_nodes; end + + # See Node.comment_targets. + sig { override.returns(T::Array[::T.any(Node, Location)]) } + def comment_targets; end + + # Creates a copy of self with the given fields, using self as the template. + sig { params(node_id: Integer, location: Location, flags: Integer, name: Symbol).returns(InstanceVariableReadNode) } + def copy(node_id: T.unsafe(nil), location: T.unsafe(nil), flags: T.unsafe(nil), name: T.unsafe(nil)); end + + sig { override.returns(T::Array[::T.nilable(Node)]) } + def deconstruct; end + + sig { params(keys: ::T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, ::T.untyped]) } + def deconstruct_keys(keys); end + + # See `Node#type`. + sig { override.returns(Symbol) } + def type; end + + # See `Node.type`. + sig { override.returns(Symbol) } + def self.type; end + + sig { override.returns(String) } + def inspect; 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` + sig { returns(Symbol) } + def name; end + + sig { params(other: ::T.untyped).returns(::T.nilable(T::Boolean)) } + def ===(other); 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. + sig { params(source: Source, node_id: Integer, location: Location, flags: Integer, name: Symbol).void } + def initialize(source, node_id, location, flags, name); end + + # See Node.accept. + sig { override.params(visitor: Visitor).returns(::T.untyped) } + def accept(visitor); end + + # See Node.child_nodes. + sig { override.returns(T::Array[::T.nilable(Node)]) } + def child_nodes; end + + # See Node.each_child_node. + sig { override.returns(T::Enumerator[Node]) } + sig { override.params(blk: ::T.proc.params(arg0: Node).void).void } + def each_child_node(&blk); end + + # See Node.compact_child_nodes. + sig { override.returns(T::Array[Node]) } + def compact_child_nodes; end + + # See Node.comment_targets. + sig { override.returns(T::Array[::T.any(Node, Location)]) } + def comment_targets; end + + # Creates a copy of self with the given fields, using self as the template. + sig { params(node_id: Integer, location: Location, flags: Integer, name: Symbol).returns(InstanceVariableTargetNode) } + def copy(node_id: T.unsafe(nil), location: T.unsafe(nil), flags: T.unsafe(nil), name: T.unsafe(nil)); end + + sig { override.returns(T::Array[::T.nilable(Node)]) } + def deconstruct; end + + sig { params(keys: ::T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, ::T.untyped]) } + def deconstruct_keys(keys); end + + # See `Node#type`. + sig { override.returns(Symbol) } + def type; end + + # See `Node.type`. + sig { override.returns(Symbol) } + def self.type; end + + sig { override.returns(String) } + def inspect; end + + # Returns the `name` attribute. + sig { returns(Symbol) } + def name; end + + sig { params(other: ::T.untyped).returns(::T.nilable(T::Boolean)) } + def ===(other); end + end + + # Represents writing to an instance variable. + # + # @foo = 1 + # ^^^^^^^^ + class InstanceVariableWriteNode < Node + # Initialize a new InstanceVariableWriteNode node. + sig { params(source: Source, node_id: Integer, location: Location, flags: Integer, name: Symbol, name_loc: Location, value: Node, operator_loc: Location).void } + def initialize(source, node_id, location, flags, name, name_loc, value, operator_loc); end + + # See Node.accept. + sig { override.params(visitor: Visitor).returns(::T.untyped) } + def accept(visitor); end + + # See Node.child_nodes. + sig { override.returns(T::Array[::T.nilable(Node)]) } + def child_nodes; end + + # See Node.each_child_node. + sig { override.returns(T::Enumerator[Node]) } + sig { override.params(blk: ::T.proc.params(arg0: Node).void).void } + def each_child_node(&blk); end + + # See Node.compact_child_nodes. + sig { override.returns(T::Array[Node]) } + def compact_child_nodes; end + + # See Node.comment_targets. + sig { override.returns(T::Array[::T.any(Node, Location)]) } + def comment_targets; end + + # Creates a copy of self with the given fields, using self as the template. + sig { params(node_id: Integer, location: Location, flags: Integer, name: Symbol, name_loc: Location, value: Node, operator_loc: Location).returns(InstanceVariableWriteNode) } + def copy(node_id: T.unsafe(nil), location: T.unsafe(nil), flags: T.unsafe(nil), name: T.unsafe(nil), name_loc: T.unsafe(nil), value: T.unsafe(nil), operator_loc: T.unsafe(nil)); end + + sig { override.returns(T::Array[::T.nilable(Node)]) } + def deconstruct; end + + sig { params(keys: ::T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, ::T.untyped]) } + def deconstruct_keys(keys); end + + # See `Node#type`. + sig { override.returns(Symbol) } + def type; end + + # See `Node.type`. + sig { override.returns(Symbol) } + def self.type; end + + sig { override.returns(String) } + def inspect; 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` + sig { returns(Symbol) } + def name; end + + # The Location of the variable name. + # + # @_x = 1 + # ^^^ + sig { returns(Location) } + def name_loc; end + + # Save the name_loc location using the given saved source so that + # it can be retrieved later. + sig { params(repository: ::T.untyped).returns(Relocation::Entry) } + def save_name_loc(repository); 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 + # ^^^^ + sig { returns(Node) } + def value; end + + # The Location of the `=` operator. + # + # @x = y + # ^ + sig { returns(Location) } + def operator_loc; end + + # Save the operator_loc location using the given saved source so that + # it can be retrieved later. + sig { params(repository: ::T.untyped).returns(Relocation::Entry) } + def save_operator_loc(repository); end + + # Slice the location of operator_loc from the source. + sig { returns(String) } + def operator; end + + sig { params(other: ::T.untyped).returns(::T.nilable(T::Boolean)) } + def ===(other); end + end + + # Represents an integer number literal. + # + # 1 + # ^ + class IntegerNode < Node + # Initialize a new IntegerNode node. + sig { params(source: Source, node_id: Integer, location: Location, flags: Integer, value: Integer).void } + def initialize(source, node_id, location, flags, value); end + + # See Node.accept. + sig { override.params(visitor: Visitor).returns(::T.untyped) } + def accept(visitor); end + + # See Node.child_nodes. + sig { override.returns(T::Array[::T.nilable(Node)]) } + def child_nodes; end + + # See Node.each_child_node. + sig { override.returns(T::Enumerator[Node]) } + sig { override.params(blk: ::T.proc.params(arg0: Node).void).void } + def each_child_node(&blk); end + + # See Node.compact_child_nodes. + sig { override.returns(T::Array[Node]) } + def compact_child_nodes; end + + # See Node.comment_targets. + sig { override.returns(T::Array[::T.any(Node, Location)]) } + def comment_targets; end + + # Creates a copy of self with the given fields, using self as the template. + sig { params(node_id: Integer, location: Location, flags: Integer, value: Integer).returns(IntegerNode) } + def copy(node_id: T.unsafe(nil), location: T.unsafe(nil), flags: T.unsafe(nil), value: T.unsafe(nil)); end + + sig { override.returns(T::Array[::T.nilable(Node)]) } + def deconstruct; end + + sig { params(keys: ::T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, ::T.untyped]) } + def deconstruct_keys(keys); end + + # See `Node#type`. + sig { override.returns(Symbol) } + def type; end + + # See `Node.type`. + sig { override.returns(Symbol) } + def self.type; end + + sig { override.returns(String) } + def inspect; end + + # 0b prefix + sig { returns(T::Boolean) } + def binary?; end + + # 0d or no prefix + sig { returns(T::Boolean) } + def decimal?; end + + # 0o or 0 prefix + sig { returns(T::Boolean) } + def octal?; end + + # 0x prefix + sig { returns(T::Boolean) } + def hexadecimal?; end + + # The value of the integer literal as a number. + sig { returns(Integer) } + def value; end + + sig { params(other: ::T.untyped).returns(::T.nilable(T::Boolean)) } + def ===(other); 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. + sig { params(source: Source, node_id: Integer, location: Location, flags: Integer, opening_loc: Location, parts: T::Array[::T.any(StringNode, EmbeddedStatementsNode, EmbeddedVariableNode)], closing_loc: Location).void } + def initialize(source, node_id, location, flags, opening_loc, parts, closing_loc); end + + # See Node.accept. + sig { override.params(visitor: Visitor).returns(::T.untyped) } + def accept(visitor); end + + # See Node.child_nodes. + sig { override.returns(T::Array[::T.nilable(Node)]) } + def child_nodes; end + + # See Node.each_child_node. + sig { override.returns(T::Enumerator[Node]) } + sig { override.params(blk: ::T.proc.params(arg0: Node).void).void } + def each_child_node(&blk); end + + # See Node.compact_child_nodes. + sig { override.returns(T::Array[Node]) } + def compact_child_nodes; end + + # See Node.comment_targets. + sig { override.returns(T::Array[::T.any(Node, Location)]) } + def comment_targets; end + + # Creates a copy of self with the given fields, using self as the template. + sig { params(node_id: Integer, location: Location, flags: Integer, opening_loc: Location, parts: T::Array[::T.any(StringNode, EmbeddedStatementsNode, EmbeddedVariableNode)], closing_loc: Location).returns(InterpolatedMatchLastLineNode) } + def copy(node_id: T.unsafe(nil), location: T.unsafe(nil), flags: T.unsafe(nil), opening_loc: T.unsafe(nil), parts: T.unsafe(nil), closing_loc: T.unsafe(nil)); end + + sig { override.returns(T::Array[::T.nilable(Node)]) } + def deconstruct; end + + sig { params(keys: ::T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, ::T.untyped]) } + def deconstruct_keys(keys); end + + # See `Node#type`. + sig { override.returns(Symbol) } + def type; end + + # See `Node.type`. + sig { override.returns(Symbol) } + def self.type; end + + sig { override.returns(String) } + def inspect; end + + # i - ignores the case of characters when matching + sig { returns(T::Boolean) } + def ignore_case?; end + + # x - ignores whitespace and allows comments in regular expressions + sig { returns(T::Boolean) } + def extended?; end + + # m - allows $ to match the end of lines within strings + sig { returns(T::Boolean) } + def multi_line?; end + + # o - only interpolates values into the regular expression once + sig { returns(T::Boolean) } + def once?; end + + # e - forces the EUC-JP encoding + sig { returns(T::Boolean) } + def euc_jp?; end + + # n - forces the ASCII-8BIT encoding + sig { returns(T::Boolean) } + def ascii_8bit?; end + + # s - forces the Windows-31J encoding + sig { returns(T::Boolean) } + def windows_31j?; end + + # u - forces the UTF-8 encoding + sig { returns(T::Boolean) } + def utf_8?; end + + # internal bytes forced the encoding to UTF-8 + sig { returns(T::Boolean) } + def forced_utf8_encoding?; end + + # internal bytes forced the encoding to binary + sig { returns(T::Boolean) } + def forced_binary_encoding?; end + + # internal bytes forced the encoding to US-ASCII + sig { returns(T::Boolean) } + def forced_us_ascii_encoding?; end + + # Returns the Location represented by `opening_loc`. + sig { returns(Location) } + def opening_loc; end + + # Save the opening_loc location using the given saved source so that + # it can be retrieved later. + sig { params(repository: ::T.untyped).returns(Relocation::Entry) } + def save_opening_loc(repository); end + + # Returns the `parts` attribute. + sig { returns(T::Array[::T.any(StringNode, EmbeddedStatementsNode, EmbeddedVariableNode)]) } + def parts; end + + # Returns the Location represented by `closing_loc`. + sig { returns(Location) } + def closing_loc; end + + # Save the closing_loc location using the given saved source so that + # it can be retrieved later. + sig { params(repository: ::T.untyped).returns(Relocation::Entry) } + def save_closing_loc(repository); end + + # Slice the location of opening_loc from the source. + sig { returns(String) } + def opening; end + + # Slice the location of closing_loc from the source. + sig { returns(String) } + def closing; end + + sig { params(other: ::T.untyped).returns(::T.nilable(T::Boolean)) } + def ===(other); end + end + + # Represents a regular expression literal that contains interpolation. + # + # /foo #{bar} baz/ + # ^^^^^^^^^^^^^^^^ + class InterpolatedRegularExpressionNode < Node + # Initialize a new InterpolatedRegularExpressionNode node. + sig { params(source: Source, node_id: Integer, location: Location, flags: Integer, opening_loc: Location, parts: T::Array[::T.any(StringNode, EmbeddedStatementsNode, EmbeddedVariableNode)], closing_loc: Location).void } + def initialize(source, node_id, location, flags, opening_loc, parts, closing_loc); end + + # See Node.accept. + sig { override.params(visitor: Visitor).returns(::T.untyped) } + def accept(visitor); end + + # See Node.child_nodes. + sig { override.returns(T::Array[::T.nilable(Node)]) } + def child_nodes; end + + # See Node.each_child_node. + sig { override.returns(T::Enumerator[Node]) } + sig { override.params(blk: ::T.proc.params(arg0: Node).void).void } + def each_child_node(&blk); end + + # See Node.compact_child_nodes. + sig { override.returns(T::Array[Node]) } + def compact_child_nodes; end + + # See Node.comment_targets. + sig { override.returns(T::Array[::T.any(Node, Location)]) } + def comment_targets; end + + # Creates a copy of self with the given fields, using self as the template. + sig { params(node_id: Integer, location: Location, flags: Integer, opening_loc: Location, parts: T::Array[::T.any(StringNode, EmbeddedStatementsNode, EmbeddedVariableNode)], closing_loc: Location).returns(InterpolatedRegularExpressionNode) } + def copy(node_id: T.unsafe(nil), location: T.unsafe(nil), flags: T.unsafe(nil), opening_loc: T.unsafe(nil), parts: T.unsafe(nil), closing_loc: T.unsafe(nil)); end + + sig { override.returns(T::Array[::T.nilable(Node)]) } + def deconstruct; end + + sig { params(keys: ::T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, ::T.untyped]) } + def deconstruct_keys(keys); end + + # See `Node#type`. + sig { override.returns(Symbol) } + def type; end + + # See `Node.type`. + sig { override.returns(Symbol) } + def self.type; end + + sig { override.returns(String) } + def inspect; end + + # i - ignores the case of characters when matching + sig { returns(T::Boolean) } + def ignore_case?; end + + # x - ignores whitespace and allows comments in regular expressions + sig { returns(T::Boolean) } + def extended?; end + + # m - allows $ to match the end of lines within strings + sig { returns(T::Boolean) } + def multi_line?; end + + # o - only interpolates values into the regular expression once + sig { returns(T::Boolean) } + def once?; end + + # e - forces the EUC-JP encoding + sig { returns(T::Boolean) } + def euc_jp?; end + + # n - forces the ASCII-8BIT encoding + sig { returns(T::Boolean) } + def ascii_8bit?; end + + # s - forces the Windows-31J encoding + sig { returns(T::Boolean) } + def windows_31j?; end + + # u - forces the UTF-8 encoding + sig { returns(T::Boolean) } + def utf_8?; end + + # internal bytes forced the encoding to UTF-8 + sig { returns(T::Boolean) } + def forced_utf8_encoding?; end + + # internal bytes forced the encoding to binary + sig { returns(T::Boolean) } + def forced_binary_encoding?; end + + # internal bytes forced the encoding to US-ASCII + sig { returns(T::Boolean) } + def forced_us_ascii_encoding?; end + + # Returns the Location represented by `opening_loc`. + sig { returns(Location) } + def opening_loc; end + + # Save the opening_loc location using the given saved source so that + # it can be retrieved later. + sig { params(repository: ::T.untyped).returns(Relocation::Entry) } + def save_opening_loc(repository); end + + # Returns the `parts` attribute. + sig { returns(T::Array[::T.any(StringNode, EmbeddedStatementsNode, EmbeddedVariableNode)]) } + def parts; end + + # Returns the Location represented by `closing_loc`. + sig { returns(Location) } + def closing_loc; end + + # Save the closing_loc location using the given saved source so that + # it can be retrieved later. + sig { params(repository: ::T.untyped).returns(Relocation::Entry) } + def save_closing_loc(repository); end + + # Slice the location of opening_loc from the source. + sig { returns(String) } + def opening; end + + # Slice the location of closing_loc from the source. + sig { returns(String) } + def closing; end + + sig { params(other: ::T.untyped).returns(::T.nilable(T::Boolean)) } + def ===(other); end + end + + # Represents a string literal that contains interpolation. + # + # "foo #{bar} baz" + # ^^^^^^^^^^^^^^^^ + class InterpolatedStringNode < Node + # Initialize a new InterpolatedStringNode node. + sig { params(source: Source, node_id: Integer, location: Location, flags: Integer, opening_loc: ::T.nilable(Location), parts: T::Array[::T.any(StringNode, EmbeddedStatementsNode, EmbeddedVariableNode, InterpolatedStringNode, XStringNode, InterpolatedXStringNode, SymbolNode, InterpolatedSymbolNode)], closing_loc: ::T.nilable(Location)).void } + def initialize(source, node_id, location, flags, opening_loc, parts, closing_loc); end + + # See Node.accept. + sig { override.params(visitor: Visitor).returns(::T.untyped) } + def accept(visitor); end + + # See Node.child_nodes. + sig { override.returns(T::Array[::T.nilable(Node)]) } + def child_nodes; end + + # See Node.each_child_node. + sig { override.returns(T::Enumerator[Node]) } + sig { override.params(blk: ::T.proc.params(arg0: Node).void).void } + def each_child_node(&blk); end + + # See Node.compact_child_nodes. + sig { override.returns(T::Array[Node]) } + def compact_child_nodes; end + + # See Node.comment_targets. + sig { override.returns(T::Array[::T.any(Node, Location)]) } + def comment_targets; end + + # Creates a copy of self with the given fields, using self as the template. + sig { params(node_id: Integer, location: Location, flags: Integer, opening_loc: ::T.nilable(Location), parts: T::Array[::T.any(StringNode, EmbeddedStatementsNode, EmbeddedVariableNode, InterpolatedStringNode, XStringNode, InterpolatedXStringNode, SymbolNode, InterpolatedSymbolNode)], closing_loc: ::T.nilable(Location)).returns(InterpolatedStringNode) } + def copy(node_id: T.unsafe(nil), location: T.unsafe(nil), flags: T.unsafe(nil), opening_loc: T.unsafe(nil), parts: T.unsafe(nil), closing_loc: T.unsafe(nil)); end + + sig { override.returns(T::Array[::T.nilable(Node)]) } + def deconstruct; end + + sig { params(keys: ::T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, ::T.untyped]) } + def deconstruct_keys(keys); end + + # See `Node#type`. + sig { override.returns(Symbol) } + def type; end + + # See `Node.type`. + sig { override.returns(Symbol) } + def self.type; end + + sig { override.returns(String) } + def inspect; end + + # frozen by virtue of a `frozen_string_literal: true` comment or `--enable-frozen-string-literal`; only for adjacent string literals like `'a' 'b'` + sig { returns(T::Boolean) } + def frozen?; end + + # mutable by virtue of a `frozen_string_literal: false` comment or `--disable-frozen-string-literal`; only for adjacent string literals like `'a' 'b'` + sig { returns(T::Boolean) } + def mutable?; end + + # Returns the Location represented by `opening_loc`. + sig { returns(::T.nilable(Location)) } + def opening_loc; end + + # Save the opening_loc location using the given saved source so that + # it can be retrieved later. + sig { params(repository: ::T.untyped).returns(::T.nilable(Relocation::Entry)) } + def save_opening_loc(repository); end + + # Returns the `parts` attribute. + sig { returns(T::Array[::T.any(StringNode, EmbeddedStatementsNode, EmbeddedVariableNode, InterpolatedStringNode, XStringNode, InterpolatedXStringNode, SymbolNode, InterpolatedSymbolNode)]) } + def parts; end + + # Returns the Location represented by `closing_loc`. + sig { returns(::T.nilable(Location)) } + def closing_loc; end + + # Save the closing_loc location using the given saved source so that + # it can be retrieved later. + sig { params(repository: ::T.untyped).returns(::T.nilable(Relocation::Entry)) } + def save_closing_loc(repository); end + + # Slice the location of opening_loc from the source. + sig { returns(::T.nilable(String)) } + def opening; end + + # Slice the location of closing_loc from the source. + sig { returns(::T.nilable(String)) } + def closing; end + + sig { params(other: ::T.untyped).returns(::T.nilable(T::Boolean)) } + def ===(other); end + end + + # Represents a symbol literal that contains interpolation. + # + # :"foo #{bar} baz" + # ^^^^^^^^^^^^^^^^^ + class InterpolatedSymbolNode < Node + # Initialize a new InterpolatedSymbolNode node. + sig { params(source: Source, node_id: Integer, location: Location, flags: Integer, opening_loc: ::T.nilable(Location), parts: T::Array[::T.any(StringNode, EmbeddedStatementsNode, EmbeddedVariableNode)], closing_loc: ::T.nilable(Location)).void } + def initialize(source, node_id, location, flags, opening_loc, parts, closing_loc); end + + # See Node.accept. + sig { override.params(visitor: Visitor).returns(::T.untyped) } + def accept(visitor); end + + # See Node.child_nodes. + sig { override.returns(T::Array[::T.nilable(Node)]) } + def child_nodes; end + + # See Node.each_child_node. + sig { override.returns(T::Enumerator[Node]) } + sig { override.params(blk: ::T.proc.params(arg0: Node).void).void } + def each_child_node(&blk); end + + # See Node.compact_child_nodes. + sig { override.returns(T::Array[Node]) } + def compact_child_nodes; end + + # See Node.comment_targets. + sig { override.returns(T::Array[::T.any(Node, Location)]) } + def comment_targets; end + + # Creates a copy of self with the given fields, using self as the template. + sig { params(node_id: Integer, location: Location, flags: Integer, opening_loc: ::T.nilable(Location), parts: T::Array[::T.any(StringNode, EmbeddedStatementsNode, EmbeddedVariableNode)], closing_loc: ::T.nilable(Location)).returns(InterpolatedSymbolNode) } + def copy(node_id: T.unsafe(nil), location: T.unsafe(nil), flags: T.unsafe(nil), opening_loc: T.unsafe(nil), parts: T.unsafe(nil), closing_loc: T.unsafe(nil)); end + + sig { override.returns(T::Array[::T.nilable(Node)]) } + def deconstruct; end + + sig { params(keys: ::T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, ::T.untyped]) } + def deconstruct_keys(keys); end + + # See `Node#type`. + sig { override.returns(Symbol) } + def type; end + + # See `Node.type`. + sig { override.returns(Symbol) } + def self.type; end + + sig { override.returns(String) } + def inspect; end + + # Returns the Location represented by `opening_loc`. + sig { returns(::T.nilable(Location)) } + def opening_loc; end + + # Save the opening_loc location using the given saved source so that + # it can be retrieved later. + sig { params(repository: ::T.untyped).returns(::T.nilable(Relocation::Entry)) } + def save_opening_loc(repository); end + + # Returns the `parts` attribute. + sig { returns(T::Array[::T.any(StringNode, EmbeddedStatementsNode, EmbeddedVariableNode)]) } + def parts; end + + # Returns the Location represented by `closing_loc`. + sig { returns(::T.nilable(Location)) } + def closing_loc; end + + # Save the closing_loc location using the given saved source so that + # it can be retrieved later. + sig { params(repository: ::T.untyped).returns(::T.nilable(Relocation::Entry)) } + def save_closing_loc(repository); end + + # Slice the location of opening_loc from the source. + sig { returns(::T.nilable(String)) } + def opening; end + + # Slice the location of closing_loc from the source. + sig { returns(::T.nilable(String)) } + def closing; end + + sig { params(other: ::T.untyped).returns(::T.nilable(T::Boolean)) } + def ===(other); end + end + + # Represents an xstring literal that contains interpolation. + # + # `foo #{bar} baz` + # ^^^^^^^^^^^^^^^^ + class InterpolatedXStringNode < Node + # Initialize a new InterpolatedXStringNode node. + sig { params(source: Source, node_id: Integer, location: Location, flags: Integer, opening_loc: Location, parts: T::Array[::T.any(StringNode, EmbeddedStatementsNode, EmbeddedVariableNode)], closing_loc: Location).void } + def initialize(source, node_id, location, flags, opening_loc, parts, closing_loc); end + + # See Node.accept. + sig { override.params(visitor: Visitor).returns(::T.untyped) } + def accept(visitor); end + + # See Node.child_nodes. + sig { override.returns(T::Array[::T.nilable(Node)]) } + def child_nodes; end + + # See Node.each_child_node. + sig { override.returns(T::Enumerator[Node]) } + sig { override.params(blk: ::T.proc.params(arg0: Node).void).void } + def each_child_node(&blk); end + + # See Node.compact_child_nodes. + sig { override.returns(T::Array[Node]) } + def compact_child_nodes; end + + # See Node.comment_targets. + sig { override.returns(T::Array[::T.any(Node, Location)]) } + def comment_targets; end + + # Creates a copy of self with the given fields, using self as the template. + sig { params(node_id: Integer, location: Location, flags: Integer, opening_loc: Location, parts: T::Array[::T.any(StringNode, EmbeddedStatementsNode, EmbeddedVariableNode)], closing_loc: Location).returns(InterpolatedXStringNode) } + def copy(node_id: T.unsafe(nil), location: T.unsafe(nil), flags: T.unsafe(nil), opening_loc: T.unsafe(nil), parts: T.unsafe(nil), closing_loc: T.unsafe(nil)); end + + sig { override.returns(T::Array[::T.nilable(Node)]) } + def deconstruct; end + + sig { params(keys: ::T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, ::T.untyped]) } + def deconstruct_keys(keys); end + + # See `Node#type`. + sig { override.returns(Symbol) } + def type; end + + # See `Node.type`. + sig { override.returns(Symbol) } + def self.type; end + + sig { override.returns(String) } + def inspect; end + + # Returns the Location represented by `opening_loc`. + sig { returns(Location) } + def opening_loc; end + + # Save the opening_loc location using the given saved source so that + # it can be retrieved later. + sig { params(repository: ::T.untyped).returns(Relocation::Entry) } + def save_opening_loc(repository); end + + # Returns the `parts` attribute. + sig { returns(T::Array[::T.any(StringNode, EmbeddedStatementsNode, EmbeddedVariableNode)]) } + def parts; end + + # Returns the Location represented by `closing_loc`. + sig { returns(Location) } + def closing_loc; end + + # Save the closing_loc location using the given saved source so that + # it can be retrieved later. + sig { params(repository: ::T.untyped).returns(Relocation::Entry) } + def save_closing_loc(repository); end + + # Slice the location of opening_loc from the source. + sig { returns(String) } + def opening; end + + # Slice the location of closing_loc from the source. + sig { returns(String) } + def closing; end + + sig { params(other: ::T.untyped).returns(::T.nilable(T::Boolean)) } + def ===(other); end + end + + # Represents reading from the implicit `it` local variable. + # + # -> { it } + # ^^ + class ItLocalVariableReadNode < Node + # Initialize a new ItLocalVariableReadNode node. + sig { params(source: Source, node_id: Integer, location: Location, flags: Integer).void } + def initialize(source, node_id, location, flags); end + + # See Node.accept. + sig { override.params(visitor: Visitor).returns(::T.untyped) } + def accept(visitor); end + + # See Node.child_nodes. + sig { override.returns(T::Array[::T.nilable(Node)]) } + def child_nodes; end + + # See Node.each_child_node. + sig { override.returns(T::Enumerator[Node]) } + sig { override.params(blk: ::T.proc.params(arg0: Node).void).void } + def each_child_node(&blk); end + + # See Node.compact_child_nodes. + sig { override.returns(T::Array[Node]) } + def compact_child_nodes; end + + # See Node.comment_targets. + sig { override.returns(T::Array[::T.any(Node, Location)]) } + def comment_targets; end + + # Creates a copy of self with the given fields, using self as the template. + sig { params(node_id: Integer, location: Location, flags: Integer).returns(ItLocalVariableReadNode) } + def copy(node_id: T.unsafe(nil), location: T.unsafe(nil), flags: T.unsafe(nil)); end + + sig { override.returns(T::Array[::T.nilable(Node)]) } + def deconstruct; end + + sig { params(keys: ::T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, ::T.untyped]) } + def deconstruct_keys(keys); end + + # See `Node#type`. + sig { override.returns(Symbol) } + def type; end + + # See `Node.type`. + sig { override.returns(Symbol) } + def self.type; end + + sig { override.returns(String) } + def inspect; end + + sig { params(other: ::T.untyped).returns(::T.nilable(T::Boolean)) } + def ===(other); 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. + sig { params(source: Source, node_id: Integer, location: Location, flags: Integer).void } + def initialize(source, node_id, location, flags); end + + # See Node.accept. + sig { override.params(visitor: Visitor).returns(::T.untyped) } + def accept(visitor); end + + # See Node.child_nodes. + sig { override.returns(T::Array[::T.nilable(Node)]) } + def child_nodes; end + + # See Node.each_child_node. + sig { override.returns(T::Enumerator[Node]) } + sig { override.params(blk: ::T.proc.params(arg0: Node).void).void } + def each_child_node(&blk); end + + # See Node.compact_child_nodes. + sig { override.returns(T::Array[Node]) } + def compact_child_nodes; end + + # See Node.comment_targets. + sig { override.returns(T::Array[::T.any(Node, Location)]) } + def comment_targets; end + + # Creates a copy of self with the given fields, using self as the template. + sig { params(node_id: Integer, location: Location, flags: Integer).returns(ItParametersNode) } + def copy(node_id: T.unsafe(nil), location: T.unsafe(nil), flags: T.unsafe(nil)); end + + sig { override.returns(T::Array[::T.nilable(Node)]) } + def deconstruct; end + + sig { params(keys: ::T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, ::T.untyped]) } + def deconstruct_keys(keys); end + + # See `Node#type`. + sig { override.returns(Symbol) } + def type; end + + # See `Node.type`. + sig { override.returns(Symbol) } + def self.type; end + + sig { override.returns(String) } + def inspect; end + + sig { params(other: ::T.untyped).returns(::T.nilable(T::Boolean)) } + def ===(other); end + end + + # Represents a hash literal without opening and closing braces. + # + # foo(a: b) + # ^^^^ + class KeywordHashNode < Node + # Initialize a new KeywordHashNode node. + sig { params(source: Source, node_id: Integer, location: Location, flags: Integer, elements: T::Array[::T.any(AssocNode, AssocSplatNode)]).void } + def initialize(source, node_id, location, flags, elements); end + + # See Node.accept. + sig { override.params(visitor: Visitor).returns(::T.untyped) } + def accept(visitor); end + + # See Node.child_nodes. + sig { override.returns(T::Array[::T.nilable(Node)]) } + def child_nodes; end + + # See Node.each_child_node. + sig { override.returns(T::Enumerator[Node]) } + sig { override.params(blk: ::T.proc.params(arg0: Node).void).void } + def each_child_node(&blk); end + + # See Node.compact_child_nodes. + sig { override.returns(T::Array[Node]) } + def compact_child_nodes; end + + # See Node.comment_targets. + sig { override.returns(T::Array[::T.any(Node, Location)]) } + def comment_targets; end + + # Creates a copy of self with the given fields, using self as the template. + sig { params(node_id: Integer, location: Location, flags: Integer, elements: T::Array[::T.any(AssocNode, AssocSplatNode)]).returns(KeywordHashNode) } + def copy(node_id: T.unsafe(nil), location: T.unsafe(nil), flags: T.unsafe(nil), elements: T.unsafe(nil)); end + + sig { override.returns(T::Array[::T.nilable(Node)]) } + def deconstruct; end + + sig { params(keys: ::T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, ::T.untyped]) } + def deconstruct_keys(keys); end + + # See `Node#type`. + sig { override.returns(Symbol) } + def type; end + + # See `Node.type`. + sig { override.returns(Symbol) } + def self.type; end + + sig { override.returns(String) } + def inspect; end + + # a keyword hash which only has `AssocNode` elements all with symbol keys, which means the elements can be treated as keyword arguments + sig { returns(T::Boolean) } + def symbol_keys?; end + + # Returns the `elements` attribute. + sig { returns(T::Array[::T.any(AssocNode, AssocSplatNode)]) } + def elements; end + + sig { params(other: ::T.untyped).returns(::T.nilable(T::Boolean)) } + def ===(other); 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. + sig { params(source: Source, node_id: Integer, location: Location, flags: Integer, name: ::T.nilable(Symbol), name_loc: ::T.nilable(Location), operator_loc: Location).void } + def initialize(source, node_id, location, flags, name, name_loc, operator_loc); end + + # See Node.accept. + sig { override.params(visitor: Visitor).returns(::T.untyped) } + def accept(visitor); end + + # See Node.child_nodes. + sig { override.returns(T::Array[::T.nilable(Node)]) } + def child_nodes; end + + # See Node.each_child_node. + sig { override.returns(T::Enumerator[Node]) } + sig { override.params(blk: ::T.proc.params(arg0: Node).void).void } + def each_child_node(&blk); end + + # See Node.compact_child_nodes. + sig { override.returns(T::Array[Node]) } + def compact_child_nodes; end + + # See Node.comment_targets. + sig { override.returns(T::Array[::T.any(Node, Location)]) } + def comment_targets; end + + # Creates a copy of self with the given fields, using self as the template. + sig { params(node_id: Integer, location: Location, flags: Integer, name: ::T.nilable(Symbol), name_loc: ::T.nilable(Location), operator_loc: Location).returns(KeywordRestParameterNode) } + def copy(node_id: T.unsafe(nil), location: T.unsafe(nil), flags: T.unsafe(nil), name: T.unsafe(nil), name_loc: T.unsafe(nil), operator_loc: T.unsafe(nil)); end + + sig { override.returns(T::Array[::T.nilable(Node)]) } + def deconstruct; end + + sig { params(keys: ::T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, ::T.untyped]) } + def deconstruct_keys(keys); end + + # See `Node#type`. + sig { override.returns(Symbol) } + def type; end + + # See `Node.type`. + sig { override.returns(Symbol) } + def self.type; end + + sig { override.returns(String) } + def inspect; end + + # a parameter name that has been repeated in the method signature + sig { returns(T::Boolean) } + def repeated_parameter?; end + + # Returns the `name` attribute. + sig { returns(::T.nilable(Symbol)) } + def name; end + + # Returns the Location represented by `name_loc`. + sig { returns(::T.nilable(Location)) } + def name_loc; end + + # Save the name_loc location using the given saved source so that + # it can be retrieved later. + sig { params(repository: ::T.untyped).returns(::T.nilable(Relocation::Entry)) } + def save_name_loc(repository); end + + # Returns the Location represented by `operator_loc`. + sig { returns(Location) } + def operator_loc; end + + # Save the operator_loc location using the given saved source so that + # it can be retrieved later. + sig { params(repository: ::T.untyped).returns(Relocation::Entry) } + def save_operator_loc(repository); end + + # Slice the location of operator_loc from the source. + sig { returns(String) } + def operator; end + + sig { params(other: ::T.untyped).returns(::T.nilable(T::Boolean)) } + def ===(other); end + end + + # Represents using a lambda literal (not the lambda method call). + # + # ->(value) { value * 2 } + # ^^^^^^^^^^^^^^^^^^^^^^^ + class LambdaNode < Node + # Initialize a new LambdaNode node. + sig { params(source: Source, node_id: Integer, location: Location, flags: Integer, locals: T::Array[Symbol], operator_loc: Location, opening_loc: Location, closing_loc: Location, parameters: ::T.nilable(::T.any(BlockParametersNode, NumberedParametersNode, ItParametersNode)), body: ::T.nilable(::T.any(StatementsNode, BeginNode))).void } + def initialize(source, node_id, location, flags, locals, operator_loc, opening_loc, closing_loc, parameters, body); end + + # See Node.accept. + sig { override.params(visitor: Visitor).returns(::T.untyped) } + def accept(visitor); end + + # See Node.child_nodes. + sig { override.returns(T::Array[::T.nilable(Node)]) } + def child_nodes; end + + # See Node.each_child_node. + sig { override.returns(T::Enumerator[Node]) } + sig { override.params(blk: ::T.proc.params(arg0: Node).void).void } + def each_child_node(&blk); end + + # See Node.compact_child_nodes. + sig { override.returns(T::Array[Node]) } + def compact_child_nodes; end + + # See Node.comment_targets. + sig { override.returns(T::Array[::T.any(Node, Location)]) } + def comment_targets; end + + # Creates a copy of self with the given fields, using self as the template. + sig { params(node_id: Integer, location: Location, flags: Integer, locals: T::Array[Symbol], operator_loc: Location, opening_loc: Location, closing_loc: Location, parameters: ::T.nilable(::T.any(BlockParametersNode, NumberedParametersNode, ItParametersNode)), body: ::T.nilable(::T.any(StatementsNode, BeginNode))).returns(LambdaNode) } + def copy(node_id: T.unsafe(nil), location: T.unsafe(nil), flags: T.unsafe(nil), locals: T.unsafe(nil), operator_loc: T.unsafe(nil), opening_loc: T.unsafe(nil), closing_loc: T.unsafe(nil), parameters: T.unsafe(nil), body: T.unsafe(nil)); end + + sig { override.returns(T::Array[::T.nilable(Node)]) } + def deconstruct; end + + sig { params(keys: ::T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, ::T.untyped]) } + def deconstruct_keys(keys); end + + # See `Node#type`. + sig { override.returns(Symbol) } + def type; end + + # See `Node.type`. + sig { override.returns(Symbol) } + def self.type; end + + sig { override.returns(String) } + def inspect; end + + # Returns the `locals` attribute. + sig { returns(T::Array[Symbol]) } + def locals; end + + # Returns the Location represented by `operator_loc`. + sig { returns(Location) } + def operator_loc; end + + # Save the operator_loc location using the given saved source so that + # it can be retrieved later. + sig { params(repository: ::T.untyped).returns(Relocation::Entry) } + def save_operator_loc(repository); end + + # Returns the Location represented by `opening_loc`. + sig { returns(Location) } + def opening_loc; end + + # Save the opening_loc location using the given saved source so that + # it can be retrieved later. + sig { params(repository: ::T.untyped).returns(Relocation::Entry) } + def save_opening_loc(repository); end + + # Returns the Location represented by `closing_loc`. + sig { returns(Location) } + def closing_loc; end + + # Save the closing_loc location using the given saved source so that + # it can be retrieved later. + sig { params(repository: ::T.untyped).returns(Relocation::Entry) } + def save_closing_loc(repository); end + + # Returns the `parameters` attribute. + sig { returns(::T.nilable(::T.any(BlockParametersNode, NumberedParametersNode, ItParametersNode))) } + def parameters; end + + # Returns the `body` attribute. + sig { returns(::T.nilable(::T.any(StatementsNode, BeginNode))) } + def body; end + + # Slice the location of operator_loc from the source. + sig { returns(String) } + def operator; end + + # Slice the location of opening_loc from the source. + sig { returns(String) } + def opening; end + + # Slice the location of closing_loc from the source. + sig { returns(String) } + def closing; end + + sig { params(other: ::T.untyped).returns(::T.nilable(T::Boolean)) } + def ===(other); end + end + + # Represents the use of the `&&=` operator for assignment to a local variable. + # + # target &&= value + # ^^^^^^^^^^^^^^^^ + class LocalVariableAndWriteNode < Node + # Initialize a new LocalVariableAndWriteNode node. + sig { params(source: Source, node_id: Integer, location: Location, flags: Integer, name_loc: Location, operator_loc: Location, value: Node, name: Symbol, depth: Integer).void } + def initialize(source, node_id, location, flags, name_loc, operator_loc, value, name, depth); end + + # See Node.accept. + sig { override.params(visitor: Visitor).returns(::T.untyped) } + def accept(visitor); end + + # See Node.child_nodes. + sig { override.returns(T::Array[::T.nilable(Node)]) } + def child_nodes; end + + # See Node.each_child_node. + sig { override.returns(T::Enumerator[Node]) } + sig { override.params(blk: ::T.proc.params(arg0: Node).void).void } + def each_child_node(&blk); end + + # See Node.compact_child_nodes. + sig { override.returns(T::Array[Node]) } + def compact_child_nodes; end + + # See Node.comment_targets. + sig { override.returns(T::Array[::T.any(Node, Location)]) } + def comment_targets; end + + # Creates a copy of self with the given fields, using self as the template. + sig { params(node_id: Integer, location: Location, flags: Integer, name_loc: Location, operator_loc: Location, value: Node, name: Symbol, depth: Integer).returns(LocalVariableAndWriteNode) } + def copy(node_id: T.unsafe(nil), location: T.unsafe(nil), flags: T.unsafe(nil), name_loc: T.unsafe(nil), operator_loc: T.unsafe(nil), value: T.unsafe(nil), name: T.unsafe(nil), depth: T.unsafe(nil)); end + + sig { override.returns(T::Array[::T.nilable(Node)]) } + def deconstruct; end + + sig { params(keys: ::T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, ::T.untyped]) } + def deconstruct_keys(keys); end + + # See `Node#type`. + sig { override.returns(Symbol) } + def type; end + + # See `Node.type`. + sig { override.returns(Symbol) } + def self.type; end + + sig { override.returns(String) } + def inspect; end + + # Returns the Location represented by `name_loc`. + sig { returns(Location) } + def name_loc; end + + # Save the name_loc location using the given saved source so that + # it can be retrieved later. + sig { params(repository: ::T.untyped).returns(Relocation::Entry) } + def save_name_loc(repository); end + + # Returns the Location represented by `operator_loc`. + sig { returns(Location) } + def operator_loc; end + + # Save the operator_loc location using the given saved source so that + # it can be retrieved later. + sig { params(repository: ::T.untyped).returns(Relocation::Entry) } + def save_operator_loc(repository); end + + # Returns the `value` attribute. + sig { returns(Node) } + def value; end + + # Returns the `name` attribute. + sig { returns(Symbol) } + def name; end + + # Returns the `depth` attribute. + sig { returns(Integer) } + def depth; end + + # Slice the location of operator_loc from the source. + sig { returns(String) } + def operator; end + + sig { params(other: ::T.untyped).returns(::T.nilable(T::Boolean)) } + def ===(other); end + end + + # Represents assigning to a local variable using an operator that isn't `=`. + # + # target += value + # ^^^^^^^^^^^^^^^ + class LocalVariableOperatorWriteNode < Node + # Initialize a new LocalVariableOperatorWriteNode node. + sig { params(source: Source, node_id: Integer, location: Location, flags: Integer, name_loc: Location, binary_operator_loc: Location, value: 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 + + # See Node.accept. + sig { override.params(visitor: Visitor).returns(::T.untyped) } + def accept(visitor); end + + # See Node.child_nodes. + sig { override.returns(T::Array[::T.nilable(Node)]) } + def child_nodes; end + + # See Node.each_child_node. + sig { override.returns(T::Enumerator[Node]) } + sig { override.params(blk: ::T.proc.params(arg0: Node).void).void } + def each_child_node(&blk); end + + # See Node.compact_child_nodes. + sig { override.returns(T::Array[Node]) } + def compact_child_nodes; end + + # See Node.comment_targets. + sig { override.returns(T::Array[::T.any(Node, Location)]) } + def comment_targets; end + + # Creates a copy of self with the given fields, using self as the template. + sig { params(node_id: Integer, location: Location, flags: Integer, name_loc: Location, binary_operator_loc: Location, value: Node, name: Symbol, binary_operator: Symbol, depth: Integer).returns(LocalVariableOperatorWriteNode) } + def copy(node_id: T.unsafe(nil), location: T.unsafe(nil), flags: T.unsafe(nil), name_loc: T.unsafe(nil), binary_operator_loc: T.unsafe(nil), value: T.unsafe(nil), name: T.unsafe(nil), binary_operator: T.unsafe(nil), depth: T.unsafe(nil)); end + + sig { override.returns(T::Array[::T.nilable(Node)]) } + def deconstruct; end + + sig { params(keys: ::T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, ::T.untyped]) } + def deconstruct_keys(keys); end + + # See `Node#type`. + sig { override.returns(Symbol) } + def type; end + + # See `Node.type`. + sig { override.returns(Symbol) } + def self.type; end + + sig { override.returns(String) } + def inspect; end + + # Returns the Location represented by `name_loc`. + sig { returns(Location) } + def name_loc; end + + # Save the name_loc location using the given saved source so that + # it can be retrieved later. + sig { params(repository: ::T.untyped).returns(Relocation::Entry) } + def save_name_loc(repository); end + + # Returns the Location represented by `binary_operator_loc`. + sig { returns(Location) } + def binary_operator_loc; end + + # Save the binary_operator_loc location using the given saved source so that + # it can be retrieved later. + sig { params(repository: ::T.untyped).returns(Relocation::Entry) } + def save_binary_operator_loc(repository); end + + # Returns the `value` attribute. + sig { returns(Node) } + def value; end + + # Returns the `name` attribute. + sig { returns(Symbol) } + def name; end + + # Returns the `binary_operator` attribute. + sig { returns(Symbol) } + def binary_operator; end + + # Returns the `depth` attribute. + sig { returns(Integer) } + def depth; end + + sig { params(other: ::T.untyped).returns(::T.nilable(T::Boolean)) } + def ===(other); end + end + + # Represents the use of the `||=` operator for assignment to a local variable. + # + # target ||= value + # ^^^^^^^^^^^^^^^^ + class LocalVariableOrWriteNode < Node + # Initialize a new LocalVariableOrWriteNode node. + sig { params(source: Source, node_id: Integer, location: Location, flags: Integer, name_loc: Location, operator_loc: Location, value: Node, name: Symbol, depth: Integer).void } + def initialize(source, node_id, location, flags, name_loc, operator_loc, value, name, depth); end + + # See Node.accept. + sig { override.params(visitor: Visitor).returns(::T.untyped) } + def accept(visitor); end + + # See Node.child_nodes. + sig { override.returns(T::Array[::T.nilable(Node)]) } + def child_nodes; end + + # See Node.each_child_node. + sig { override.returns(T::Enumerator[Node]) } + sig { override.params(blk: ::T.proc.params(arg0: Node).void).void } + def each_child_node(&blk); end + + # See Node.compact_child_nodes. + sig { override.returns(T::Array[Node]) } + def compact_child_nodes; end + + # See Node.comment_targets. + sig { override.returns(T::Array[::T.any(Node, Location)]) } + def comment_targets; end + + # Creates a copy of self with the given fields, using self as the template. + sig { params(node_id: Integer, location: Location, flags: Integer, name_loc: Location, operator_loc: Location, value: Node, name: Symbol, depth: Integer).returns(LocalVariableOrWriteNode) } + def copy(node_id: T.unsafe(nil), location: T.unsafe(nil), flags: T.unsafe(nil), name_loc: T.unsafe(nil), operator_loc: T.unsafe(nil), value: T.unsafe(nil), name: T.unsafe(nil), depth: T.unsafe(nil)); end + + sig { override.returns(T::Array[::T.nilable(Node)]) } + def deconstruct; end + + sig { params(keys: ::T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, ::T.untyped]) } + def deconstruct_keys(keys); end + + # See `Node#type`. + sig { override.returns(Symbol) } + def type; end + + # See `Node.type`. + sig { override.returns(Symbol) } + def self.type; end + + sig { override.returns(String) } + def inspect; end + + # Returns the Location represented by `name_loc`. + sig { returns(Location) } + def name_loc; end + + # Save the name_loc location using the given saved source so that + # it can be retrieved later. + sig { params(repository: ::T.untyped).returns(Relocation::Entry) } + def save_name_loc(repository); end + + # Returns the Location represented by `operator_loc`. + sig { returns(Location) } + def operator_loc; end + + # Save the operator_loc location using the given saved source so that + # it can be retrieved later. + sig { params(repository: ::T.untyped).returns(Relocation::Entry) } + def save_operator_loc(repository); end + + # Returns the `value` attribute. + sig { returns(Node) } + def value; end + + # Returns the `name` attribute. + sig { returns(Symbol) } + def name; end + + # Returns the `depth` attribute. + sig { returns(Integer) } + def depth; end + + # Slice the location of operator_loc from the source. + sig { returns(String) } + def operator; end + + sig { params(other: ::T.untyped).returns(::T.nilable(T::Boolean)) } + def ===(other); 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. + sig { params(source: Source, node_id: Integer, location: Location, flags: Integer, name: Symbol, depth: Integer).void } + def initialize(source, node_id, location, flags, name, depth); end + + # See Node.accept. + sig { override.params(visitor: Visitor).returns(::T.untyped) } + def accept(visitor); end + + # See Node.child_nodes. + sig { override.returns(T::Array[::T.nilable(Node)]) } + def child_nodes; end + + # See Node.each_child_node. + sig { override.returns(T::Enumerator[Node]) } + sig { override.params(blk: ::T.proc.params(arg0: Node).void).void } + def each_child_node(&blk); end + + # See Node.compact_child_nodes. + sig { override.returns(T::Array[Node]) } + def compact_child_nodes; end + + # See Node.comment_targets. + sig { override.returns(T::Array[::T.any(Node, Location)]) } + def comment_targets; end + + # Creates a copy of self with the given fields, using self as the template. + sig { params(node_id: Integer, location: Location, flags: Integer, name: Symbol, depth: Integer).returns(LocalVariableReadNode) } + def copy(node_id: T.unsafe(nil), location: T.unsafe(nil), flags: T.unsafe(nil), name: T.unsafe(nil), depth: T.unsafe(nil)); end + + sig { override.returns(T::Array[::T.nilable(Node)]) } + def deconstruct; end + + sig { params(keys: ::T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, ::T.untyped]) } + def deconstruct_keys(keys); end + + # See `Node#type`. + sig { override.returns(Symbol) } + def type; end + + # See `Node.type`. + sig { override.returns(Symbol) } + def self.type; end + + sig { override.returns(String) } + def inspect; 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` + sig { returns(Symbol) } + def name; end + + # 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). + sig { returns(Integer) } + def depth; end + + sig { params(other: ::T.untyped).returns(::T.nilable(T::Boolean)) } + def ===(other); 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. + sig { params(source: Source, node_id: Integer, location: Location, flags: Integer, name: Symbol, depth: Integer).void } + def initialize(source, node_id, location, flags, name, depth); end + + # See Node.accept. + sig { override.params(visitor: Visitor).returns(::T.untyped) } + def accept(visitor); end + + # See Node.child_nodes. + sig { override.returns(T::Array[::T.nilable(Node)]) } + def child_nodes; end + + # See Node.each_child_node. + sig { override.returns(T::Enumerator[Node]) } + sig { override.params(blk: ::T.proc.params(arg0: Node).void).void } + def each_child_node(&blk); end + + # See Node.compact_child_nodes. + sig { override.returns(T::Array[Node]) } + def compact_child_nodes; end + + # See Node.comment_targets. + sig { override.returns(T::Array[::T.any(Node, Location)]) } + def comment_targets; end + + # Creates a copy of self with the given fields, using self as the template. + sig { params(node_id: Integer, location: Location, flags: Integer, name: Symbol, depth: Integer).returns(LocalVariableTargetNode) } + def copy(node_id: T.unsafe(nil), location: T.unsafe(nil), flags: T.unsafe(nil), name: T.unsafe(nil), depth: T.unsafe(nil)); end + + sig { override.returns(T::Array[::T.nilable(Node)]) } + def deconstruct; end + + sig { params(keys: ::T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, ::T.untyped]) } + def deconstruct_keys(keys); end + + # See `Node#type`. + sig { override.returns(Symbol) } + def type; end + + # See `Node.type`. + sig { override.returns(Symbol) } + def self.type; end + + sig { override.returns(String) } + def inspect; end + + # Returns the `name` attribute. + sig { returns(Symbol) } + def name; end + + # Returns the `depth` attribute. + sig { returns(Integer) } + def depth; end + + sig { params(other: ::T.untyped).returns(::T.nilable(T::Boolean)) } + def ===(other); end + end + + # Represents writing to a local variable. + # + # foo = 1 + # ^^^^^^^ + class LocalVariableWriteNode < Node + # Initialize a new LocalVariableWriteNode node. + sig { params(source: Source, node_id: Integer, location: Location, flags: Integer, name: Symbol, depth: Integer, name_loc: Location, value: Node, operator_loc: Location).void } + def initialize(source, node_id, location, flags, name, depth, name_loc, value, operator_loc); end + + # See Node.accept. + sig { override.params(visitor: Visitor).returns(::T.untyped) } + def accept(visitor); end + + # See Node.child_nodes. + sig { override.returns(T::Array[::T.nilable(Node)]) } + def child_nodes; end + + # See Node.each_child_node. + sig { override.returns(T::Enumerator[Node]) } + sig { override.params(blk: ::T.proc.params(arg0: Node).void).void } + def each_child_node(&blk); end + + # See Node.compact_child_nodes. + sig { override.returns(T::Array[Node]) } + def compact_child_nodes; end + + # See Node.comment_targets. + sig { override.returns(T::Array[::T.any(Node, Location)]) } + def comment_targets; end + + # Creates a copy of self with the given fields, using self as the template. + sig { params(node_id: Integer, location: Location, flags: Integer, name: Symbol, depth: Integer, name_loc: Location, value: Node, operator_loc: Location).returns(LocalVariableWriteNode) } + def copy(node_id: T.unsafe(nil), location: T.unsafe(nil), flags: T.unsafe(nil), name: T.unsafe(nil), depth: T.unsafe(nil), name_loc: T.unsafe(nil), value: T.unsafe(nil), operator_loc: T.unsafe(nil)); end + + sig { override.returns(T::Array[::T.nilable(Node)]) } + def deconstruct; end + + sig { params(keys: ::T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, ::T.untyped]) } + def deconstruct_keys(keys); end + + # See `Node#type`. + sig { override.returns(Symbol) } + def type; end + + # See `Node.type`. + sig { override.returns(Symbol) } + def self.type; end + + sig { override.returns(String) } + def inspect; 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` + sig { returns(Symbol) } + def name; end + + # 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). + sig { returns(Integer) } + def depth; end + + # The Location of the variable name. + # + # foo = :bar + # ^^^ + sig { returns(Location) } + def name_loc; end + + # Save the name_loc location using the given saved source so that + # it can be retrieved later. + sig { params(repository: ::T.untyped).returns(Relocation::Entry) } + def save_name_loc(repository); 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 + sig { returns(Node) } + def value; end + + # The Location of the `=` operator. + # + # x = :y + # ^ + sig { returns(Location) } + def operator_loc; end + + # Save the operator_loc location using the given saved source so that + # it can be retrieved later. + sig { params(repository: ::T.untyped).returns(Relocation::Entry) } + def save_operator_loc(repository); end + + # Slice the location of operator_loc from the source. + sig { returns(String) } + def operator; end + + sig { params(other: ::T.untyped).returns(::T.nilable(T::Boolean)) } + def ===(other); 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. + sig { params(source: Source, node_id: Integer, location: Location, flags: Integer, opening_loc: Location, content_loc: Location, closing_loc: Location, unescaped: String).void } + def initialize(source, node_id, location, flags, opening_loc, content_loc, closing_loc, unescaped); end + + # See Node.accept. + sig { override.params(visitor: Visitor).returns(::T.untyped) } + def accept(visitor); end + + # See Node.child_nodes. + sig { override.returns(T::Array[::T.nilable(Node)]) } + def child_nodes; end + + # See Node.each_child_node. + sig { override.returns(T::Enumerator[Node]) } + sig { override.params(blk: ::T.proc.params(arg0: Node).void).void } + def each_child_node(&blk); end + + # See Node.compact_child_nodes. + sig { override.returns(T::Array[Node]) } + def compact_child_nodes; end + + # See Node.comment_targets. + sig { override.returns(T::Array[::T.any(Node, Location)]) } + def comment_targets; end + + # Creates a copy of self with the given fields, using self as the template. + sig { params(node_id: Integer, location: Location, flags: Integer, opening_loc: Location, content_loc: Location, closing_loc: Location, unescaped: String).returns(MatchLastLineNode) } + def copy(node_id: T.unsafe(nil), location: T.unsafe(nil), flags: T.unsafe(nil), opening_loc: T.unsafe(nil), content_loc: T.unsafe(nil), closing_loc: T.unsafe(nil), unescaped: T.unsafe(nil)); end + + sig { override.returns(T::Array[::T.nilable(Node)]) } + def deconstruct; end + + sig { params(keys: ::T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, ::T.untyped]) } + def deconstruct_keys(keys); end + + # See `Node#type`. + sig { override.returns(Symbol) } + def type; end + + # See `Node.type`. + sig { override.returns(Symbol) } + def self.type; end + + sig { override.returns(String) } + def inspect; end + + # i - ignores the case of characters when matching + sig { returns(T::Boolean) } + def ignore_case?; end + + # x - ignores whitespace and allows comments in regular expressions + sig { returns(T::Boolean) } + def extended?; end + + # m - allows $ to match the end of lines within strings + sig { returns(T::Boolean) } + def multi_line?; end + + # o - only interpolates values into the regular expression once + sig { returns(T::Boolean) } + def once?; end + + # e - forces the EUC-JP encoding + sig { returns(T::Boolean) } + def euc_jp?; end + + # n - forces the ASCII-8BIT encoding + sig { returns(T::Boolean) } + def ascii_8bit?; end + + # s - forces the Windows-31J encoding + sig { returns(T::Boolean) } + def windows_31j?; end + + # u - forces the UTF-8 encoding + sig { returns(T::Boolean) } + def utf_8?; end + + # internal bytes forced the encoding to UTF-8 + sig { returns(T::Boolean) } + def forced_utf8_encoding?; end + + # internal bytes forced the encoding to binary + sig { returns(T::Boolean) } + def forced_binary_encoding?; end + + # internal bytes forced the encoding to US-ASCII + sig { returns(T::Boolean) } + def forced_us_ascii_encoding?; end + + # Returns the Location represented by `opening_loc`. + sig { returns(Location) } + def opening_loc; end + + # Save the opening_loc location using the given saved source so that + # it can be retrieved later. + sig { params(repository: ::T.untyped).returns(Relocation::Entry) } + def save_opening_loc(repository); end + + # Returns the Location represented by `content_loc`. + sig { returns(Location) } + def content_loc; end + + # Save the content_loc location using the given saved source so that + # it can be retrieved later. + sig { params(repository: ::T.untyped).returns(Relocation::Entry) } + def save_content_loc(repository); end + + # Returns the Location represented by `closing_loc`. + sig { returns(Location) } + def closing_loc; end + + # Save the closing_loc location using the given saved source so that + # it can be retrieved later. + sig { params(repository: ::T.untyped).returns(Relocation::Entry) } + def save_closing_loc(repository); end + + # Returns the `unescaped` attribute. + sig { returns(String) } + def unescaped; end + + # Slice the location of opening_loc from the source. + sig { returns(String) } + def opening; end + + # Slice the location of content_loc from the source. + sig { returns(String) } + def content; end + + # Slice the location of closing_loc from the source. + sig { returns(String) } + def closing; end + + sig { params(other: ::T.untyped).returns(::T.nilable(T::Boolean)) } + def ===(other); end + end + + # Represents the use of the modifier `in` operator. + # + # foo in bar + # ^^^^^^^^^^ + class MatchPredicateNode < Node + # Initialize a new MatchPredicateNode node. + sig { params(source: Source, node_id: Integer, location: Location, flags: Integer, value: Node, pattern: Node, operator_loc: Location).void } + def initialize(source, node_id, location, flags, value, pattern, operator_loc); end + + # See Node.accept. + sig { override.params(visitor: Visitor).returns(::T.untyped) } + def accept(visitor); end + + # See Node.child_nodes. + sig { override.returns(T::Array[::T.nilable(Node)]) } + def child_nodes; end + + # See Node.each_child_node. + sig { override.returns(T::Enumerator[Node]) } + sig { override.params(blk: ::T.proc.params(arg0: Node).void).void } + def each_child_node(&blk); end + + # See Node.compact_child_nodes. + sig { override.returns(T::Array[Node]) } + def compact_child_nodes; end + + # See Node.comment_targets. + sig { override.returns(T::Array[::T.any(Node, Location)]) } + def comment_targets; end + + # Creates a copy of self with the given fields, using self as the template. + sig { params(node_id: Integer, location: Location, flags: Integer, value: Node, pattern: Node, operator_loc: Location).returns(MatchPredicateNode) } + def copy(node_id: T.unsafe(nil), location: T.unsafe(nil), flags: T.unsafe(nil), value: T.unsafe(nil), pattern: T.unsafe(nil), operator_loc: T.unsafe(nil)); end + + sig { override.returns(T::Array[::T.nilable(Node)]) } + def deconstruct; end + + sig { params(keys: ::T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, ::T.untyped]) } + def deconstruct_keys(keys); end + + # See `Node#type`. + sig { override.returns(Symbol) } + def type; end + + # See `Node.type`. + sig { override.returns(Symbol) } + def self.type; end + + sig { override.returns(String) } + def inspect; end + + # Returns the `value` attribute. + sig { returns(Node) } + def value; end + + # Returns the `pattern` attribute. + sig { returns(Node) } + def pattern; end + + # Returns the Location represented by `operator_loc`. + sig { returns(Location) } + def operator_loc; end + + # Save the operator_loc location using the given saved source so that + # it can be retrieved later. + sig { params(repository: ::T.untyped).returns(Relocation::Entry) } + def save_operator_loc(repository); end + + # Slice the location of operator_loc from the source. + sig { returns(String) } + def operator; end + + sig { params(other: ::T.untyped).returns(::T.nilable(T::Boolean)) } + def ===(other); end + end + + # Represents the use of the `=>` operator. + # + # foo => bar + # ^^^^^^^^^^ + class MatchRequiredNode < Node + # Initialize a new MatchRequiredNode node. + sig { params(source: Source, node_id: Integer, location: Location, flags: Integer, value: Node, pattern: Node, operator_loc: Location).void } + def initialize(source, node_id, location, flags, value, pattern, operator_loc); end + + # See Node.accept. + sig { override.params(visitor: Visitor).returns(::T.untyped) } + def accept(visitor); end + + # See Node.child_nodes. + sig { override.returns(T::Array[::T.nilable(Node)]) } + def child_nodes; end + + # See Node.each_child_node. + sig { override.returns(T::Enumerator[Node]) } + sig { override.params(blk: ::T.proc.params(arg0: Node).void).void } + def each_child_node(&blk); end + + # See Node.compact_child_nodes. + sig { override.returns(T::Array[Node]) } + def compact_child_nodes; end + + # See Node.comment_targets. + sig { override.returns(T::Array[::T.any(Node, Location)]) } + def comment_targets; end + + # Creates a copy of self with the given fields, using self as the template. + sig { params(node_id: Integer, location: Location, flags: Integer, value: Node, pattern: Node, operator_loc: Location).returns(MatchRequiredNode) } + def copy(node_id: T.unsafe(nil), location: T.unsafe(nil), flags: T.unsafe(nil), value: T.unsafe(nil), pattern: T.unsafe(nil), operator_loc: T.unsafe(nil)); end + + sig { override.returns(T::Array[::T.nilable(Node)]) } + def deconstruct; end + + sig { params(keys: ::T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, ::T.untyped]) } + def deconstruct_keys(keys); end + + # See `Node#type`. + sig { override.returns(Symbol) } + def type; end + + # See `Node.type`. + sig { override.returns(Symbol) } + def self.type; end + + sig { override.returns(String) } + def inspect; end + + # Represents the left-hand side of the operator. + # + # foo => bar + # ^^^ + sig { returns(Node) } + def value; end + + # 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 + sig { returns(Node) } + def pattern; end + + # The Location of the operator. + # + # foo => bar + # ^^ + sig { returns(Location) } + def operator_loc; end + + # Save the operator_loc location using the given saved source so that + # it can be retrieved later. + sig { params(repository: ::T.untyped).returns(Relocation::Entry) } + def save_operator_loc(repository); end + + # Slice the location of operator_loc from the source. + sig { returns(String) } + def operator; end + + sig { params(other: ::T.untyped).returns(::T.nilable(T::Boolean)) } + def ===(other); 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. + sig { params(source: Source, node_id: Integer, location: Location, flags: Integer, call: CallNode, targets: T::Array[LocalVariableTargetNode]).void } + def initialize(source, node_id, location, flags, call, targets); end + + # See Node.accept. + sig { override.params(visitor: Visitor).returns(::T.untyped) } + def accept(visitor); end + + # See Node.child_nodes. + sig { override.returns(T::Array[::T.nilable(Node)]) } + def child_nodes; end + + # See Node.each_child_node. + sig { override.returns(T::Enumerator[Node]) } + sig { override.params(blk: ::T.proc.params(arg0: Node).void).void } + def each_child_node(&blk); end + + # See Node.compact_child_nodes. + sig { override.returns(T::Array[Node]) } + def compact_child_nodes; end + + # See Node.comment_targets. + sig { override.returns(T::Array[::T.any(Node, Location)]) } + def comment_targets; end + + # Creates a copy of self with the given fields, using self as the template. + sig { params(node_id: Integer, location: Location, flags: Integer, call: CallNode, targets: T::Array[LocalVariableTargetNode]).returns(MatchWriteNode) } + def copy(node_id: T.unsafe(nil), location: T.unsafe(nil), flags: T.unsafe(nil), call: T.unsafe(nil), targets: T.unsafe(nil)); end + + sig { override.returns(T::Array[::T.nilable(Node)]) } + def deconstruct; end + + sig { params(keys: ::T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, ::T.untyped]) } + def deconstruct_keys(keys); end + + # See `Node#type`. + sig { override.returns(Symbol) } + def type; end + + # See `Node.type`. + sig { override.returns(Symbol) } + def self.type; end + + sig { override.returns(String) } + def inspect; end + + # Returns the `call` attribute. + sig { returns(CallNode) } + def call; end + + # Returns the `targets` attribute. + sig { returns(T::Array[LocalVariableTargetNode]) } + def targets; end + + sig { params(other: ::T.untyped).returns(::T.nilable(T::Boolean)) } + def ===(other); 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. + sig { params(source: Source, node_id: Integer, location: Location, flags: Integer).void } + def initialize(source, node_id, location, flags); end + + # See Node.accept. + sig { override.params(visitor: Visitor).returns(::T.untyped) } + def accept(visitor); end + + # See Node.child_nodes. + sig { override.returns(T::Array[::T.nilable(Node)]) } + def child_nodes; end + + # See Node.each_child_node. + sig { override.returns(T::Enumerator[Node]) } + sig { override.params(blk: ::T.proc.params(arg0: Node).void).void } + def each_child_node(&blk); end + + # See Node.compact_child_nodes. + sig { override.returns(T::Array[Node]) } + def compact_child_nodes; end + + # See Node.comment_targets. + sig { override.returns(T::Array[::T.any(Node, Location)]) } + def comment_targets; end + + # Creates a copy of self with the given fields, using self as the template. + sig { params(node_id: Integer, location: Location, flags: Integer).returns(MissingNode) } + def copy(node_id: T.unsafe(nil), location: T.unsafe(nil), flags: T.unsafe(nil)); end + + sig { override.returns(T::Array[::T.nilable(Node)]) } + def deconstruct; end + + sig { params(keys: ::T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, ::T.untyped]) } + def deconstruct_keys(keys); end + + # See `Node#type`. + sig { override.returns(Symbol) } + def type; end + + # See `Node.type`. + sig { override.returns(Symbol) } + def self.type; end + + sig { override.returns(String) } + def inspect; end + + sig { params(other: ::T.untyped).returns(::T.nilable(T::Boolean)) } + def ===(other); end + end + + # Represents a module declaration involving the `module` keyword. + # + # module Foo end + # ^^^^^^^^^^^^^^ + class ModuleNode < Node + # Initialize a new ModuleNode node. + sig { params(source: Source, node_id: Integer, location: Location, flags: Integer, locals: T::Array[Symbol], module_keyword_loc: Location, constant_path: ::T.any(ConstantReadNode, ConstantPathNode, MissingNode), body: ::T.nilable(::T.any(StatementsNode, BeginNode)), end_keyword_loc: Location, name: Symbol).void } + def initialize(source, node_id, location, flags, locals, module_keyword_loc, constant_path, body, end_keyword_loc, name); end + + # See Node.accept. + sig { override.params(visitor: Visitor).returns(::T.untyped) } + def accept(visitor); end + + # See Node.child_nodes. + sig { override.returns(T::Array[::T.nilable(Node)]) } + def child_nodes; end + + # See Node.each_child_node. + sig { override.returns(T::Enumerator[Node]) } + sig { override.params(blk: ::T.proc.params(arg0: Node).void).void } + def each_child_node(&blk); end + + # See Node.compact_child_nodes. + sig { override.returns(T::Array[Node]) } + def compact_child_nodes; end + + # See Node.comment_targets. + sig { override.returns(T::Array[::T.any(Node, Location)]) } + def comment_targets; end + + # Creates a copy of self with the given fields, using self as the template. + sig { params(node_id: Integer, location: Location, flags: Integer, locals: T::Array[Symbol], module_keyword_loc: Location, constant_path: ::T.any(ConstantReadNode, ConstantPathNode, MissingNode), body: ::T.nilable(::T.any(StatementsNode, BeginNode)), end_keyword_loc: Location, name: Symbol).returns(ModuleNode) } + def copy(node_id: T.unsafe(nil), location: T.unsafe(nil), flags: T.unsafe(nil), locals: T.unsafe(nil), module_keyword_loc: T.unsafe(nil), constant_path: T.unsafe(nil), body: T.unsafe(nil), end_keyword_loc: T.unsafe(nil), name: T.unsafe(nil)); end + + sig { override.returns(T::Array[::T.nilable(Node)]) } + def deconstruct; end + + sig { params(keys: ::T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, ::T.untyped]) } + def deconstruct_keys(keys); end + + # See `Node#type`. + sig { override.returns(Symbol) } + def type; end + + # See `Node.type`. + sig { override.returns(Symbol) } + def self.type; end + + sig { override.returns(String) } + def inspect; end + + # Returns the `locals` attribute. + sig { returns(T::Array[Symbol]) } + def locals; end + + # Returns the Location represented by `module_keyword_loc`. + sig { returns(Location) } + def module_keyword_loc; end + + # Save the module_keyword_loc location using the given saved source so that + # it can be retrieved later. + sig { params(repository: ::T.untyped).returns(Relocation::Entry) } + def save_module_keyword_loc(repository); end + + # Returns the `constant_path` attribute. + sig { returns(::T.any(ConstantReadNode, ConstantPathNode, MissingNode)) } + def constant_path; end + + # Returns the `body` attribute. + sig { returns(::T.nilable(::T.any(StatementsNode, BeginNode))) } + def body; end + + # Returns the Location represented by `end_keyword_loc`. + sig { returns(Location) } + def end_keyword_loc; end + + # Save the end_keyword_loc location using the given saved source so that + # it can be retrieved later. + sig { params(repository: ::T.untyped).returns(Relocation::Entry) } + def save_end_keyword_loc(repository); end + + # Returns the `name` attribute. + sig { returns(Symbol) } + def name; end + + # Slice the location of module_keyword_loc from the source. + sig { returns(String) } + def module_keyword; end + + # Slice the location of end_keyword_loc from the source. + sig { returns(String) } + def end_keyword; end + + sig { params(other: ::T.untyped).returns(::T.nilable(T::Boolean)) } + def ===(other); 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. + sig { params(source: Source, node_id: Integer, location: Location, flags: Integer, lefts: T::Array[::T.any(LocalVariableTargetNode, InstanceVariableTargetNode, ClassVariableTargetNode, GlobalVariableTargetNode, ConstantTargetNode, ConstantPathTargetNode, CallTargetNode, IndexTargetNode, MultiTargetNode, RequiredParameterNode, BackReferenceReadNode, NumberedReferenceReadNode)], rest: ::T.nilable(::T.any(ImplicitRestNode, SplatNode)), rights: T::Array[::T.any(LocalVariableTargetNode, InstanceVariableTargetNode, ClassVariableTargetNode, GlobalVariableTargetNode, ConstantTargetNode, ConstantPathTargetNode, CallTargetNode, IndexTargetNode, MultiTargetNode, RequiredParameterNode, BackReferenceReadNode, NumberedReferenceReadNode)], lparen_loc: ::T.nilable(Location), rparen_loc: ::T.nilable(Location)).void } + def initialize(source, node_id, location, flags, lefts, rest, rights, lparen_loc, rparen_loc); end + + # See Node.accept. + sig { override.params(visitor: Visitor).returns(::T.untyped) } + def accept(visitor); end + + # See Node.child_nodes. + sig { override.returns(T::Array[::T.nilable(Node)]) } + def child_nodes; end + + # See Node.each_child_node. + sig { override.returns(T::Enumerator[Node]) } + sig { override.params(blk: ::T.proc.params(arg0: Node).void).void } + def each_child_node(&blk); end + + # See Node.compact_child_nodes. + sig { override.returns(T::Array[Node]) } + def compact_child_nodes; end + + # See Node.comment_targets. + sig { override.returns(T::Array[::T.any(Node, Location)]) } + def comment_targets; end + + # Creates a copy of self with the given fields, using self as the template. + sig { params(node_id: Integer, location: Location, flags: Integer, lefts: T::Array[::T.any(LocalVariableTargetNode, InstanceVariableTargetNode, ClassVariableTargetNode, GlobalVariableTargetNode, ConstantTargetNode, ConstantPathTargetNode, CallTargetNode, IndexTargetNode, MultiTargetNode, RequiredParameterNode, BackReferenceReadNode, NumberedReferenceReadNode)], rest: ::T.nilable(::T.any(ImplicitRestNode, SplatNode)), rights: T::Array[::T.any(LocalVariableTargetNode, InstanceVariableTargetNode, ClassVariableTargetNode, GlobalVariableTargetNode, ConstantTargetNode, ConstantPathTargetNode, CallTargetNode, IndexTargetNode, MultiTargetNode, RequiredParameterNode, BackReferenceReadNode, NumberedReferenceReadNode)], lparen_loc: ::T.nilable(Location), rparen_loc: ::T.nilable(Location)).returns(MultiTargetNode) } + def copy(node_id: T.unsafe(nil), location: T.unsafe(nil), flags: T.unsafe(nil), lefts: T.unsafe(nil), rest: T.unsafe(nil), rights: T.unsafe(nil), lparen_loc: T.unsafe(nil), rparen_loc: T.unsafe(nil)); end + + sig { override.returns(T::Array[::T.nilable(Node)]) } + def deconstruct; end + + sig { params(keys: ::T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, ::T.untyped]) } + def deconstruct_keys(keys); end + + # See `Node#type`. + sig { override.returns(Symbol) } + def type; end + + # See `Node.type`. + sig { override.returns(Symbol) } + def self.type; end + + sig { override.returns(String) } + def inspect; 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 + # ^^^^ + sig { returns(T::Array[::T.any(LocalVariableTargetNode, InstanceVariableTargetNode, ClassVariableTargetNode, GlobalVariableTargetNode, ConstantTargetNode, ConstantPathTargetNode, CallTargetNode, IndexTargetNode, MultiTargetNode, RequiredParameterNode, BackReferenceReadNode, NumberedReferenceReadNode)]) } + def lefts; end + + # 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 + # ^ + sig { returns(::T.nilable(::T.any(ImplicitRestNode, SplatNode))) } + def rest; end + + # Represents the targets expressions after a splat node. + # + # a, (*, b, c) = 1, 2, 3, 4, 5 + # ^^^^ + sig { returns(T::Array[::T.any(LocalVariableTargetNode, InstanceVariableTargetNode, ClassVariableTargetNode, GlobalVariableTargetNode, ConstantTargetNode, ConstantPathTargetNode, CallTargetNode, IndexTargetNode, MultiTargetNode, RequiredParameterNode, BackReferenceReadNode, NumberedReferenceReadNode)]) } + def rights; end + + # The Location of the opening parenthesis. + # + # a, (b, c) = 1, 2, 3 + # ^ + sig { returns(::T.nilable(Location)) } + def lparen_loc; end + + # Save the lparen_loc location using the given saved source so that + # it can be retrieved later. + sig { params(repository: ::T.untyped).returns(::T.nilable(Relocation::Entry)) } + def save_lparen_loc(repository); end + + # The Location of the closing parenthesis. + # + # a, (b, c) = 1, 2, 3 + # ^ + sig { returns(::T.nilable(Location)) } + def rparen_loc; end + + # Save the rparen_loc location using the given saved source so that + # it can be retrieved later. + sig { params(repository: ::T.untyped).returns(::T.nilable(Relocation::Entry)) } + def save_rparen_loc(repository); end + + # Slice the location of lparen_loc from the source. + sig { returns(::T.nilable(String)) } + def lparen; end + + # Slice the location of rparen_loc from the source. + sig { returns(::T.nilable(String)) } + def rparen; end + + sig { params(other: ::T.untyped).returns(::T.nilable(T::Boolean)) } + def ===(other); end + end + + # Represents a write to a multi-target expression. + # + # a, b, c = 1, 2, 3 + # ^^^^^^^^^^^^^^^^^ + class MultiWriteNode < Node + # Initialize a new MultiWriteNode node. + sig { params(source: Source, node_id: Integer, location: Location, flags: Integer, lefts: T::Array[::T.any(LocalVariableTargetNode, InstanceVariableTargetNode, ClassVariableTargetNode, GlobalVariableTargetNode, ConstantTargetNode, ConstantPathTargetNode, CallTargetNode, IndexTargetNode, MultiTargetNode, BackReferenceReadNode, NumberedReferenceReadNode)], rest: ::T.nilable(::T.any(ImplicitRestNode, SplatNode)), rights: T::Array[::T.any(LocalVariableTargetNode, InstanceVariableTargetNode, ClassVariableTargetNode, GlobalVariableTargetNode, ConstantTargetNode, ConstantPathTargetNode, CallTargetNode, IndexTargetNode, MultiTargetNode, BackReferenceReadNode, NumberedReferenceReadNode)], lparen_loc: ::T.nilable(Location), rparen_loc: ::T.nilable(Location), operator_loc: Location, value: Node).void } + def initialize(source, node_id, location, flags, lefts, rest, rights, lparen_loc, rparen_loc, operator_loc, value); end + + # See Node.accept. + sig { override.params(visitor: Visitor).returns(::T.untyped) } + def accept(visitor); end + + # See Node.child_nodes. + sig { override.returns(T::Array[::T.nilable(Node)]) } + def child_nodes; end + + # See Node.each_child_node. + sig { override.returns(T::Enumerator[Node]) } + sig { override.params(blk: ::T.proc.params(arg0: Node).void).void } + def each_child_node(&blk); end + + # See Node.compact_child_nodes. + sig { override.returns(T::Array[Node]) } + def compact_child_nodes; end + + # See Node.comment_targets. + sig { override.returns(T::Array[::T.any(Node, Location)]) } + def comment_targets; end + + # Creates a copy of self with the given fields, using self as the template. + sig { params(node_id: Integer, location: Location, flags: Integer, lefts: T::Array[::T.any(LocalVariableTargetNode, InstanceVariableTargetNode, ClassVariableTargetNode, GlobalVariableTargetNode, ConstantTargetNode, ConstantPathTargetNode, CallTargetNode, IndexTargetNode, MultiTargetNode, BackReferenceReadNode, NumberedReferenceReadNode)], rest: ::T.nilable(::T.any(ImplicitRestNode, SplatNode)), rights: T::Array[::T.any(LocalVariableTargetNode, InstanceVariableTargetNode, ClassVariableTargetNode, GlobalVariableTargetNode, ConstantTargetNode, ConstantPathTargetNode, CallTargetNode, IndexTargetNode, MultiTargetNode, BackReferenceReadNode, NumberedReferenceReadNode)], lparen_loc: ::T.nilable(Location), rparen_loc: ::T.nilable(Location), operator_loc: Location, value: Node).returns(MultiWriteNode) } + def copy(node_id: T.unsafe(nil), location: T.unsafe(nil), flags: T.unsafe(nil), lefts: T.unsafe(nil), rest: T.unsafe(nil), rights: T.unsafe(nil), lparen_loc: T.unsafe(nil), rparen_loc: T.unsafe(nil), operator_loc: T.unsafe(nil), value: T.unsafe(nil)); end + + sig { override.returns(T::Array[::T.nilable(Node)]) } + def deconstruct; end + + sig { params(keys: ::T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, ::T.untyped]) } + def deconstruct_keys(keys); end + + # See `Node#type`. + sig { override.returns(Symbol) } + def type; end + + # See `Node.type`. + sig { override.returns(Symbol) } + def self.type; end + + sig { override.returns(String) } + def inspect; 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 + # ^^^^^^^ + sig { returns(T::Array[::T.any(LocalVariableTargetNode, InstanceVariableTargetNode, ClassVariableTargetNode, GlobalVariableTargetNode, ConstantTargetNode, ConstantPathTargetNode, CallTargetNode, IndexTargetNode, MultiTargetNode, BackReferenceReadNode, NumberedReferenceReadNode)]) } + def lefts; end + + # 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 + # ^ + sig { returns(::T.nilable(::T.any(ImplicitRestNode, SplatNode))) } + def rest; end + + # Represents the targets expressions after a splat node. + # + # a, *, b, c = 1, 2, 3, 4, 5 + # ^^^^ + sig { returns(T::Array[::T.any(LocalVariableTargetNode, InstanceVariableTargetNode, ClassVariableTargetNode, GlobalVariableTargetNode, ConstantTargetNode, ConstantPathTargetNode, CallTargetNode, IndexTargetNode, MultiTargetNode, BackReferenceReadNode, NumberedReferenceReadNode)]) } + def rights; end + + # The Location of the opening parenthesis. + # + # (a, b, c) = 1, 2, 3 + # ^ + sig { returns(::T.nilable(Location)) } + def lparen_loc; end + + # Save the lparen_loc location using the given saved source so that + # it can be retrieved later. + sig { params(repository: ::T.untyped).returns(::T.nilable(Relocation::Entry)) } + def save_lparen_loc(repository); end + + # The Location of the closing parenthesis. + # + # (a, b, c) = 1, 2, 3 + # ^ + sig { returns(::T.nilable(Location)) } + def rparen_loc; end + + # Save the rparen_loc location using the given saved source so that + # it can be retrieved later. + sig { params(repository: ::T.untyped).returns(::T.nilable(Relocation::Entry)) } + def save_rparen_loc(repository); end + + # The Location of the operator. + # + # a, b, c = 1, 2, 3 + # ^ + sig { returns(Location) } + def operator_loc; end + + # Save the operator_loc location using the given saved source so that + # it can be retrieved later. + sig { params(repository: ::T.untyped).returns(Relocation::Entry) } + def save_operator_loc(repository); 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 + # ^^^^^^^ + sig { returns(Node) } + def value; end + + # Slice the location of lparen_loc from the source. + sig { returns(::T.nilable(String)) } + def lparen; end + + # Slice the location of rparen_loc from the source. + sig { returns(::T.nilable(String)) } + def rparen; end + + # Slice the location of operator_loc from the source. + sig { returns(String) } + def operator; end + + sig { params(other: ::T.untyped).returns(::T.nilable(T::Boolean)) } + def ===(other); end + end + + # Represents the use of the `next` keyword. + # + # next 1 + # ^^^^^^ + class NextNode < Node + # Initialize a new NextNode node. + sig { params(source: Source, node_id: Integer, location: Location, flags: Integer, arguments: ::T.nilable(ArgumentsNode), keyword_loc: Location).void } + def initialize(source, node_id, location, flags, arguments, keyword_loc); end + + # See Node.accept. + sig { override.params(visitor: Visitor).returns(::T.untyped) } + def accept(visitor); end + + # See Node.child_nodes. + sig { override.returns(T::Array[::T.nilable(Node)]) } + def child_nodes; end + + # See Node.each_child_node. + sig { override.returns(T::Enumerator[Node]) } + sig { override.params(blk: ::T.proc.params(arg0: Node).void).void } + def each_child_node(&blk); end + + # See Node.compact_child_nodes. + sig { override.returns(T::Array[Node]) } + def compact_child_nodes; end + + # See Node.comment_targets. + sig { override.returns(T::Array[::T.any(Node, Location)]) } + def comment_targets; end + + # Creates a copy of self with the given fields, using self as the template. + sig { params(node_id: Integer, location: Location, flags: Integer, arguments: ::T.nilable(ArgumentsNode), keyword_loc: Location).returns(NextNode) } + def copy(node_id: T.unsafe(nil), location: T.unsafe(nil), flags: T.unsafe(nil), arguments: T.unsafe(nil), keyword_loc: T.unsafe(nil)); end + + sig { override.returns(T::Array[::T.nilable(Node)]) } + def deconstruct; end + + sig { params(keys: ::T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, ::T.untyped]) } + def deconstruct_keys(keys); end + + # See `Node#type`. + sig { override.returns(Symbol) } + def type; end + + # See `Node.type`. + sig { override.returns(Symbol) } + def self.type; end + + sig { override.returns(String) } + def inspect; end + + # Returns the `arguments` attribute. + sig { returns(::T.nilable(ArgumentsNode)) } + def arguments; end + + # Returns the Location represented by `keyword_loc`. + sig { returns(Location) } + def keyword_loc; end + + # Save the keyword_loc location using the given saved source so that + # it can be retrieved later. + sig { params(repository: ::T.untyped).returns(Relocation::Entry) } + def save_keyword_loc(repository); end + + # Slice the location of keyword_loc from the source. + sig { returns(String) } + def keyword; end + + sig { params(other: ::T.untyped).returns(::T.nilable(T::Boolean)) } + def ===(other); end + end + + # Represents the use of the `nil` keyword. + # + # nil + # ^^^ + class NilNode < Node + # Initialize a new NilNode node. + sig { params(source: Source, node_id: Integer, location: Location, flags: Integer).void } + def initialize(source, node_id, location, flags); end + + # See Node.accept. + sig { override.params(visitor: Visitor).returns(::T.untyped) } + def accept(visitor); end + + # See Node.child_nodes. + sig { override.returns(T::Array[::T.nilable(Node)]) } + def child_nodes; end + + # See Node.each_child_node. + sig { override.returns(T::Enumerator[Node]) } + sig { override.params(blk: ::T.proc.params(arg0: Node).void).void } + def each_child_node(&blk); end + + # See Node.compact_child_nodes. + sig { override.returns(T::Array[Node]) } + def compact_child_nodes; end + + # See Node.comment_targets. + sig { override.returns(T::Array[::T.any(Node, Location)]) } + def comment_targets; end + + # Creates a copy of self with the given fields, using self as the template. + sig { params(node_id: Integer, location: Location, flags: Integer).returns(NilNode) } + def copy(node_id: T.unsafe(nil), location: T.unsafe(nil), flags: T.unsafe(nil)); end + + sig { override.returns(T::Array[::T.nilable(Node)]) } + def deconstruct; end + + sig { params(keys: ::T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, ::T.untyped]) } + def deconstruct_keys(keys); end + + # See `Node#type`. + sig { override.returns(Symbol) } + def type; end + + # See `Node.type`. + sig { override.returns(Symbol) } + def self.type; end + + sig { override.returns(String) } + def inspect; end + + sig { params(other: ::T.untyped).returns(::T.nilable(T::Boolean)) } + def ===(other); end + end + + # Represents the use of `&nil` inside method arguments. + # + # def a(&nil) + # ^^^^ + # end + class NoBlockParameterNode < Node + # Initialize a new NoBlockParameterNode node. + sig { params(source: Source, node_id: Integer, location: Location, flags: Integer, operator_loc: Location, keyword_loc: Location).void } + def initialize(source, node_id, location, flags, operator_loc, keyword_loc); end + + # See Node.accept. + sig { override.params(visitor: Visitor).returns(::T.untyped) } + def accept(visitor); end + + # See Node.child_nodes. + sig { override.returns(T::Array[::T.nilable(Node)]) } + def child_nodes; end + + # See Node.each_child_node. + sig { override.returns(T::Enumerator[Node]) } + sig { override.params(blk: ::T.proc.params(arg0: Node).void).void } + def each_child_node(&blk); end + + # See Node.compact_child_nodes. + sig { override.returns(T::Array[Node]) } + def compact_child_nodes; end + + # See Node.comment_targets. + sig { override.returns(T::Array[::T.any(Node, Location)]) } + def comment_targets; end + + # Creates a copy of self with the given fields, using self as the template. + sig { params(node_id: Integer, location: Location, flags: Integer, operator_loc: Location, keyword_loc: Location).returns(NoBlockParameterNode) } + def copy(node_id: T.unsafe(nil), location: T.unsafe(nil), flags: T.unsafe(nil), operator_loc: T.unsafe(nil), keyword_loc: T.unsafe(nil)); end + + sig { override.returns(T::Array[::T.nilable(Node)]) } + def deconstruct; end + + sig { params(keys: ::T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, ::T.untyped]) } + def deconstruct_keys(keys); end + + # See `Node#type`. + sig { override.returns(Symbol) } + def type; end + + # See `Node.type`. + sig { override.returns(Symbol) } + def self.type; end + + sig { override.returns(String) } + def inspect; end + + # Returns the Location represented by `operator_loc`. + sig { returns(Location) } + def operator_loc; end + + # Save the operator_loc location using the given saved source so that + # it can be retrieved later. + sig { params(repository: ::T.untyped).returns(Relocation::Entry) } + def save_operator_loc(repository); end + + # Returns the Location represented by `keyword_loc`. + sig { returns(Location) } + def keyword_loc; end + + # Save the keyword_loc location using the given saved source so that + # it can be retrieved later. + sig { params(repository: ::T.untyped).returns(Relocation::Entry) } + def save_keyword_loc(repository); end + + # Slice the location of operator_loc from the source. + sig { returns(String) } + def operator; end + + # Slice the location of keyword_loc from the source. + sig { returns(String) } + def keyword; end + + sig { params(other: ::T.untyped).returns(::T.nilable(T::Boolean)) } + def ===(other); end + end + + # Represents the use of `**nil` inside method arguments. + # + # def a(**nil) + # ^^^^^ + # end + class NoKeywordsParameterNode < Node + # Initialize a new NoKeywordsParameterNode node. + sig { params(source: Source, node_id: Integer, location: Location, flags: Integer, operator_loc: Location, keyword_loc: Location).void } + def initialize(source, node_id, location, flags, operator_loc, keyword_loc); end + + # See Node.accept. + sig { override.params(visitor: Visitor).returns(::T.untyped) } + def accept(visitor); end + + # See Node.child_nodes. + sig { override.returns(T::Array[::T.nilable(Node)]) } + def child_nodes; end + + # See Node.each_child_node. + sig { override.returns(T::Enumerator[Node]) } + sig { override.params(blk: ::T.proc.params(arg0: Node).void).void } + def each_child_node(&blk); end + + # See Node.compact_child_nodes. + sig { override.returns(T::Array[Node]) } + def compact_child_nodes; end + + # See Node.comment_targets. + sig { override.returns(T::Array[::T.any(Node, Location)]) } + def comment_targets; end + + # Creates a copy of self with the given fields, using self as the template. + sig { params(node_id: Integer, location: Location, flags: Integer, operator_loc: Location, keyword_loc: Location).returns(NoKeywordsParameterNode) } + def copy(node_id: T.unsafe(nil), location: T.unsafe(nil), flags: T.unsafe(nil), operator_loc: T.unsafe(nil), keyword_loc: T.unsafe(nil)); end + + sig { override.returns(T::Array[::T.nilable(Node)]) } + def deconstruct; end + + sig { params(keys: ::T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, ::T.untyped]) } + def deconstruct_keys(keys); end + + # See `Node#type`. + sig { override.returns(Symbol) } + def type; end + + # See `Node.type`. + sig { override.returns(Symbol) } + def self.type; end + + sig { override.returns(String) } + def inspect; end + + # Returns the Location represented by `operator_loc`. + sig { returns(Location) } + def operator_loc; end + + # Save the operator_loc location using the given saved source so that + # it can be retrieved later. + sig { params(repository: ::T.untyped).returns(Relocation::Entry) } + def save_operator_loc(repository); end + + # Returns the Location represented by `keyword_loc`. + sig { returns(Location) } + def keyword_loc; end + + # Save the keyword_loc location using the given saved source so that + # it can be retrieved later. + sig { params(repository: ::T.untyped).returns(Relocation::Entry) } + def save_keyword_loc(repository); end + + # Slice the location of operator_loc from the source. + sig { returns(String) } + def operator; end + + # Slice the location of keyword_loc from the source. + sig { returns(String) } + def keyword; end + + sig { params(other: ::T.untyped).returns(::T.nilable(T::Boolean)) } + def ===(other); 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. + sig { params(source: Source, node_id: Integer, location: Location, flags: Integer, maximum: Integer).void } + def initialize(source, node_id, location, flags, maximum); end + + # See Node.accept. + sig { override.params(visitor: Visitor).returns(::T.untyped) } + def accept(visitor); end + + # See Node.child_nodes. + sig { override.returns(T::Array[::T.nilable(Node)]) } + def child_nodes; end + + # See Node.each_child_node. + sig { override.returns(T::Enumerator[Node]) } + sig { override.params(blk: ::T.proc.params(arg0: Node).void).void } + def each_child_node(&blk); end + + # See Node.compact_child_nodes. + sig { override.returns(T::Array[Node]) } + def compact_child_nodes; end + + # See Node.comment_targets. + sig { override.returns(T::Array[::T.any(Node, Location)]) } + def comment_targets; end + + # Creates a copy of self with the given fields, using self as the template. + sig { params(node_id: Integer, location: Location, flags: Integer, maximum: Integer).returns(NumberedParametersNode) } + def copy(node_id: T.unsafe(nil), location: T.unsafe(nil), flags: T.unsafe(nil), maximum: T.unsafe(nil)); end + + sig { override.returns(T::Array[::T.nilable(Node)]) } + def deconstruct; end + + sig { params(keys: ::T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, ::T.untyped]) } + def deconstruct_keys(keys); end + + # See `Node#type`. + sig { override.returns(Symbol) } + def type; end + + # See `Node.type`. + sig { override.returns(Symbol) } + def self.type; end + + sig { override.returns(String) } + def inspect; end + + # Returns the `maximum` attribute. + sig { returns(Integer) } + def maximum; end + + sig { params(other: ::T.untyped).returns(::T.nilable(T::Boolean)) } + def ===(other); end + end + + # Represents reading a numbered reference to a capture in the previous match. + # + # $1 + # ^^ + class NumberedReferenceReadNode < Node + # Initialize a new NumberedReferenceReadNode node. + sig { params(source: Source, node_id: Integer, location: Location, flags: Integer, number: Integer).void } + def initialize(source, node_id, location, flags, number); end + + # See Node.accept. + sig { override.params(visitor: Visitor).returns(::T.untyped) } + def accept(visitor); end + + # See Node.child_nodes. + sig { override.returns(T::Array[::T.nilable(Node)]) } + def child_nodes; end + + # See Node.each_child_node. + sig { override.returns(T::Enumerator[Node]) } + sig { override.params(blk: ::T.proc.params(arg0: Node).void).void } + def each_child_node(&blk); end + + # See Node.compact_child_nodes. + sig { override.returns(T::Array[Node]) } + def compact_child_nodes; end + + # See Node.comment_targets. + sig { override.returns(T::Array[::T.any(Node, Location)]) } + def comment_targets; end + + # Creates a copy of self with the given fields, using self as the template. + sig { params(node_id: Integer, location: Location, flags: Integer, number: Integer).returns(NumberedReferenceReadNode) } + def copy(node_id: T.unsafe(nil), location: T.unsafe(nil), flags: T.unsafe(nil), number: T.unsafe(nil)); end + + sig { override.returns(T::Array[::T.nilable(Node)]) } + def deconstruct; end + + sig { params(keys: ::T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, ::T.untyped]) } + def deconstruct_keys(keys); end + + # See `Node#type`. + sig { override.returns(Symbol) } + def type; end + + # See `Node.type`. + sig { override.returns(Symbol) } + def self.type; end + + sig { override.returns(String) } + def inspect; 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` + sig { returns(Integer) } + def number; end + + sig { params(other: ::T.untyped).returns(::T.nilable(T::Boolean)) } + def ===(other); 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. + sig { params(source: Source, node_id: Integer, location: Location, flags: Integer, name: Symbol, name_loc: Location, value: Node).void } + def initialize(source, node_id, location, flags, name, name_loc, value); end + + # See Node.accept. + sig { override.params(visitor: Visitor).returns(::T.untyped) } + def accept(visitor); end + + # See Node.child_nodes. + sig { override.returns(T::Array[::T.nilable(Node)]) } + def child_nodes; end + + # See Node.each_child_node. + sig { override.returns(T::Enumerator[Node]) } + sig { override.params(blk: ::T.proc.params(arg0: Node).void).void } + def each_child_node(&blk); end + + # See Node.compact_child_nodes. + sig { override.returns(T::Array[Node]) } + def compact_child_nodes; end + + # See Node.comment_targets. + sig { override.returns(T::Array[::T.any(Node, Location)]) } + def comment_targets; end + + # Creates a copy of self with the given fields, using self as the template. + sig { params(node_id: Integer, location: Location, flags: Integer, name: Symbol, name_loc: Location, value: Node).returns(OptionalKeywordParameterNode) } + def copy(node_id: T.unsafe(nil), location: T.unsafe(nil), flags: T.unsafe(nil), name: T.unsafe(nil), name_loc: T.unsafe(nil), value: T.unsafe(nil)); end + + sig { override.returns(T::Array[::T.nilable(Node)]) } + def deconstruct; end + + sig { params(keys: ::T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, ::T.untyped]) } + def deconstruct_keys(keys); end + + # See `Node#type`. + sig { override.returns(Symbol) } + def type; end + + # See `Node.type`. + sig { override.returns(Symbol) } + def self.type; end + + sig { override.returns(String) } + def inspect; end + + # a parameter name that has been repeated in the method signature + sig { returns(T::Boolean) } + def repeated_parameter?; end + + # Returns the `name` attribute. + sig { returns(Symbol) } + def name; end + + # Returns the Location represented by `name_loc`. + sig { returns(Location) } + def name_loc; end + + # Save the name_loc location using the given saved source so that + # it can be retrieved later. + sig { params(repository: ::T.untyped).returns(Relocation::Entry) } + def save_name_loc(repository); end + + # Returns the `value` attribute. + sig { returns(Node) } + def value; end + + sig { params(other: ::T.untyped).returns(::T.nilable(T::Boolean)) } + def ===(other); 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. + sig { params(source: Source, node_id: Integer, location: Location, flags: Integer, name: Symbol, name_loc: Location, operator_loc: Location, value: Node).void } + def initialize(source, node_id, location, flags, name, name_loc, operator_loc, value); end + + # See Node.accept. + sig { override.params(visitor: Visitor).returns(::T.untyped) } + def accept(visitor); end + + # See Node.child_nodes. + sig { override.returns(T::Array[::T.nilable(Node)]) } + def child_nodes; end + + # See Node.each_child_node. + sig { override.returns(T::Enumerator[Node]) } + sig { override.params(blk: ::T.proc.params(arg0: Node).void).void } + def each_child_node(&blk); end + + # See Node.compact_child_nodes. + sig { override.returns(T::Array[Node]) } + def compact_child_nodes; end + + # See Node.comment_targets. + sig { override.returns(T::Array[::T.any(Node, Location)]) } + def comment_targets; end + + # Creates a copy of self with the given fields, using self as the template. + sig { params(node_id: Integer, location: Location, flags: Integer, name: Symbol, name_loc: Location, operator_loc: Location, value: Node).returns(OptionalParameterNode) } + def copy(node_id: T.unsafe(nil), location: T.unsafe(nil), flags: T.unsafe(nil), name: T.unsafe(nil), name_loc: T.unsafe(nil), operator_loc: T.unsafe(nil), value: T.unsafe(nil)); end + + sig { override.returns(T::Array[::T.nilable(Node)]) } + def deconstruct; end + + sig { params(keys: ::T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, ::T.untyped]) } + def deconstruct_keys(keys); end + + # See `Node#type`. + sig { override.returns(Symbol) } + def type; end + + # See `Node.type`. + sig { override.returns(Symbol) } + def self.type; end + + sig { override.returns(String) } + def inspect; end + + # a parameter name that has been repeated in the method signature + sig { returns(T::Boolean) } + def repeated_parameter?; end + + # Returns the `name` attribute. + sig { returns(Symbol) } + def name; end + + # Returns the Location represented by `name_loc`. + sig { returns(Location) } + def name_loc; end + + # Save the name_loc location using the given saved source so that + # it can be retrieved later. + sig { params(repository: ::T.untyped).returns(Relocation::Entry) } + def save_name_loc(repository); end + + # Returns the Location represented by `operator_loc`. + sig { returns(Location) } + def operator_loc; end + + # Save the operator_loc location using the given saved source so that + # it can be retrieved later. + sig { params(repository: ::T.untyped).returns(Relocation::Entry) } + def save_operator_loc(repository); end + + # Returns the `value` attribute. + sig { returns(Node) } + def value; end + + # Slice the location of operator_loc from the source. + sig { returns(String) } + def operator; end + + sig { params(other: ::T.untyped).returns(::T.nilable(T::Boolean)) } + def ===(other); end + end + + # Represents the use of the `||` operator or the `or` keyword. + # + # left or right + # ^^^^^^^^^^^^^ + class OrNode < Node + # Initialize a new OrNode node. + sig { params(source: Source, node_id: Integer, location: Location, flags: Integer, left: Node, right: Node, operator_loc: Location).void } + def initialize(source, node_id, location, flags, left, right, operator_loc); end + + # See Node.accept. + sig { override.params(visitor: Visitor).returns(::T.untyped) } + def accept(visitor); end + + # See Node.child_nodes. + sig { override.returns(T::Array[::T.nilable(Node)]) } + def child_nodes; end + + # See Node.each_child_node. + sig { override.returns(T::Enumerator[Node]) } + sig { override.params(blk: ::T.proc.params(arg0: Node).void).void } + def each_child_node(&blk); end + + # See Node.compact_child_nodes. + sig { override.returns(T::Array[Node]) } + def compact_child_nodes; end + + # See Node.comment_targets. + sig { override.returns(T::Array[::T.any(Node, Location)]) } + def comment_targets; end + + # Creates a copy of self with the given fields, using self as the template. + sig { params(node_id: Integer, location: Location, flags: Integer, left: Node, right: Node, operator_loc: Location).returns(OrNode) } + def copy(node_id: T.unsafe(nil), location: T.unsafe(nil), flags: T.unsafe(nil), left: T.unsafe(nil), right: T.unsafe(nil), operator_loc: T.unsafe(nil)); end + + sig { override.returns(T::Array[::T.nilable(Node)]) } + def deconstruct; end + + sig { params(keys: ::T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, ::T.untyped]) } + def deconstruct_keys(keys); end + + # See `Node#type`. + sig { override.returns(Symbol) } + def type; end + + # See `Node.type`. + sig { override.returns(Symbol) } + def self.type; end + + sig { override.returns(String) } + def inspect; 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 + # ^ + sig { returns(Node) } + def left; end + + # Represents the right side of the expression. + # + # left || right + # ^^^^^ + # + # 1 or 2 + # ^ + sig { returns(Node) } + def right; end + + # The Location of the `or` keyword or the `||` operator. + # + # left or right + # ^^ + sig { returns(Location) } + def operator_loc; end + + # Save the operator_loc location using the given saved source so that + # it can be retrieved later. + sig { params(repository: ::T.untyped).returns(Relocation::Entry) } + def save_operator_loc(repository); end + + # Slice the location of operator_loc from the source. + sig { returns(String) } + def operator; end + + sig { params(other: ::T.untyped).returns(::T.nilable(T::Boolean)) } + def ===(other); 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. + sig { params(source: Source, node_id: Integer, location: Location, flags: Integer, requireds: T::Array[::T.any(RequiredParameterNode, MultiTargetNode)], optionals: T::Array[OptionalParameterNode], rest: ::T.nilable(::T.any(RestParameterNode, ImplicitRestNode)), posts: T::Array[::T.any(RequiredParameterNode, MultiTargetNode, KeywordRestParameterNode, NoKeywordsParameterNode, ForwardingParameterNode, BlockParameterNode, NoBlockParameterNode)], keywords: T::Array[::T.any(RequiredKeywordParameterNode, OptionalKeywordParameterNode)], keyword_rest: ::T.nilable(::T.any(KeywordRestParameterNode, ForwardingParameterNode, NoKeywordsParameterNode)), block: ::T.nilable(::T.any(BlockParameterNode, NoBlockParameterNode))).void } + def initialize(source, node_id, location, flags, requireds, optionals, rest, posts, keywords, keyword_rest, block); end + + # See Node.accept. + sig { override.params(visitor: Visitor).returns(::T.untyped) } + def accept(visitor); end + + # See Node.child_nodes. + sig { override.returns(T::Array[::T.nilable(Node)]) } + def child_nodes; end + + # See Node.each_child_node. + sig { override.returns(T::Enumerator[Node]) } + sig { override.params(blk: ::T.proc.params(arg0: Node).void).void } + def each_child_node(&blk); end + + # See Node.compact_child_nodes. + sig { override.returns(T::Array[Node]) } + def compact_child_nodes; end + + # See Node.comment_targets. + sig { override.returns(T::Array[::T.any(Node, Location)]) } + def comment_targets; end + + # Creates a copy of self with the given fields, using self as the template. + sig { params(node_id: Integer, location: Location, flags: Integer, requireds: T::Array[::T.any(RequiredParameterNode, MultiTargetNode)], optionals: T::Array[OptionalParameterNode], rest: ::T.nilable(::T.any(RestParameterNode, ImplicitRestNode)), posts: T::Array[::T.any(RequiredParameterNode, MultiTargetNode, KeywordRestParameterNode, NoKeywordsParameterNode, ForwardingParameterNode, BlockParameterNode, NoBlockParameterNode)], keywords: T::Array[::T.any(RequiredKeywordParameterNode, OptionalKeywordParameterNode)], keyword_rest: ::T.nilable(::T.any(KeywordRestParameterNode, ForwardingParameterNode, NoKeywordsParameterNode)), block: ::T.nilable(::T.any(BlockParameterNode, NoBlockParameterNode))).returns(ParametersNode) } + def copy(node_id: T.unsafe(nil), location: T.unsafe(nil), flags: T.unsafe(nil), requireds: T.unsafe(nil), optionals: T.unsafe(nil), rest: T.unsafe(nil), posts: T.unsafe(nil), keywords: T.unsafe(nil), keyword_rest: T.unsafe(nil), block: T.unsafe(nil)); end + + sig { override.returns(T::Array[::T.nilable(Node)]) } + def deconstruct; end + + sig { params(keys: ::T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, ::T.untyped]) } + def deconstruct_keys(keys); end + + # See `Node#type`. + sig { override.returns(Symbol) } + def type; end + + # See `Node.type`. + sig { override.returns(Symbol) } + def self.type; end + + sig { override.returns(String) } + def inspect; end + + # Returns the `requireds` attribute. + sig { returns(T::Array[::T.any(RequiredParameterNode, MultiTargetNode)]) } + def requireds; end + + # Returns the `optionals` attribute. + sig { returns(T::Array[OptionalParameterNode]) } + def optionals; end + + # Returns the `rest` attribute. + sig { returns(::T.nilable(::T.any(RestParameterNode, ImplicitRestNode))) } + def rest; end + + # Returns the `posts` attribute. + sig { returns(T::Array[::T.any(RequiredParameterNode, MultiTargetNode, KeywordRestParameterNode, NoKeywordsParameterNode, ForwardingParameterNode, BlockParameterNode, NoBlockParameterNode)]) } + def posts; end + + # Returns the `keywords` attribute. + sig { returns(T::Array[::T.any(RequiredKeywordParameterNode, OptionalKeywordParameterNode)]) } + def keywords; end + + # Returns the `keyword_rest` attribute. + sig { returns(::T.nilable(::T.any(KeywordRestParameterNode, ForwardingParameterNode, NoKeywordsParameterNode))) } + def keyword_rest; end + + # Returns the `block` attribute. + sig { returns(::T.nilable(::T.any(BlockParameterNode, NoBlockParameterNode))) } + def block; end + + sig { params(other: ::T.untyped).returns(::T.nilable(T::Boolean)) } + def ===(other); end + end + + # Represents a parenthesized expression + # + # (10 + 34) + # ^^^^^^^^^ + class ParenthesesNode < Node + # Initialize a new ParenthesesNode node. + sig { params(source: Source, node_id: Integer, location: Location, flags: Integer, body: ::T.nilable(Node), opening_loc: Location, closing_loc: Location).void } + def initialize(source, node_id, location, flags, body, opening_loc, closing_loc); end + + # See Node.accept. + sig { override.params(visitor: Visitor).returns(::T.untyped) } + def accept(visitor); end + + # See Node.child_nodes. + sig { override.returns(T::Array[::T.nilable(Node)]) } + def child_nodes; end + + # See Node.each_child_node. + sig { override.returns(T::Enumerator[Node]) } + sig { override.params(blk: ::T.proc.params(arg0: Node).void).void } + def each_child_node(&blk); end + + # See Node.compact_child_nodes. + sig { override.returns(T::Array[Node]) } + def compact_child_nodes; end + + # See Node.comment_targets. + sig { override.returns(T::Array[::T.any(Node, Location)]) } + def comment_targets; end + + # Creates a copy of self with the given fields, using self as the template. + sig { params(node_id: Integer, location: Location, flags: Integer, body: ::T.nilable(Node), opening_loc: Location, closing_loc: Location).returns(ParenthesesNode) } + def copy(node_id: T.unsafe(nil), location: T.unsafe(nil), flags: T.unsafe(nil), body: T.unsafe(nil), opening_loc: T.unsafe(nil), closing_loc: T.unsafe(nil)); end + + sig { override.returns(T::Array[::T.nilable(Node)]) } + def deconstruct; end + + sig { params(keys: ::T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, ::T.untyped]) } + def deconstruct_keys(keys); end + + # See `Node#type`. + sig { override.returns(Symbol) } + def type; end + + # See `Node.type`. + sig { override.returns(Symbol) } + def self.type; end + + sig { override.returns(String) } + def inspect; end + + # parentheses that contain multiple potentially void statements + sig { returns(T::Boolean) } + def multiple_statements?; end + + # Returns the `body` attribute. + sig { returns(::T.nilable(Node)) } + def body; end + + # Returns the Location represented by `opening_loc`. + sig { returns(Location) } + def opening_loc; end + + # Save the opening_loc location using the given saved source so that + # it can be retrieved later. + sig { params(repository: ::T.untyped).returns(Relocation::Entry) } + def save_opening_loc(repository); end + + # Returns the Location represented by `closing_loc`. + sig { returns(Location) } + def closing_loc; end + + # Save the closing_loc location using the given saved source so that + # it can be retrieved later. + sig { params(repository: ::T.untyped).returns(Relocation::Entry) } + def save_closing_loc(repository); end + + # Slice the location of opening_loc from the source. + sig { returns(String) } + def opening; end + + # Slice the location of closing_loc from the source. + sig { returns(String) } + def closing; end + + sig { params(other: ::T.untyped).returns(::T.nilable(T::Boolean)) } + def ===(other); 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. + sig { params(source: Source, node_id: Integer, location: Location, flags: Integer, expression: Node, operator_loc: Location, lparen_loc: Location, rparen_loc: Location).void } + def initialize(source, node_id, location, flags, expression, operator_loc, lparen_loc, rparen_loc); end + + # See Node.accept. + sig { override.params(visitor: Visitor).returns(::T.untyped) } + def accept(visitor); end + + # See Node.child_nodes. + sig { override.returns(T::Array[::T.nilable(Node)]) } + def child_nodes; end + + # See Node.each_child_node. + sig { override.returns(T::Enumerator[Node]) } + sig { override.params(blk: ::T.proc.params(arg0: Node).void).void } + def each_child_node(&blk); end + + # See Node.compact_child_nodes. + sig { override.returns(T::Array[Node]) } + def compact_child_nodes; end + + # See Node.comment_targets. + sig { override.returns(T::Array[::T.any(Node, Location)]) } + def comment_targets; end + + # Creates a copy of self with the given fields, using self as the template. + sig { params(node_id: Integer, location: Location, flags: Integer, expression: Node, operator_loc: Location, lparen_loc: Location, rparen_loc: Location).returns(PinnedExpressionNode) } + def copy(node_id: T.unsafe(nil), location: T.unsafe(nil), flags: T.unsafe(nil), expression: T.unsafe(nil), operator_loc: T.unsafe(nil), lparen_loc: T.unsafe(nil), rparen_loc: T.unsafe(nil)); end + + sig { override.returns(T::Array[::T.nilable(Node)]) } + def deconstruct; end + + sig { params(keys: ::T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, ::T.untyped]) } + def deconstruct_keys(keys); end + + # See `Node#type`. + sig { override.returns(Symbol) } + def type; end + + # See `Node.type`. + sig { override.returns(Symbol) } + def self.type; end + + sig { override.returns(String) } + def inspect; end + + # The expression used in the pinned expression + # + # foo in ^(bar) + # ^^^ + sig { returns(Node) } + def expression; end + + # The Location of the `^` operator + # + # foo in ^(bar) + # ^ + sig { returns(Location) } + def operator_loc; end + + # Save the operator_loc location using the given saved source so that + # it can be retrieved later. + sig { params(repository: ::T.untyped).returns(Relocation::Entry) } + def save_operator_loc(repository); end + + # The Location of the opening parenthesis. + # + # foo in ^(bar) + # ^ + sig { returns(Location) } + def lparen_loc; end + + # Save the lparen_loc location using the given saved source so that + # it can be retrieved later. + sig { params(repository: ::T.untyped).returns(Relocation::Entry) } + def save_lparen_loc(repository); end + + # The Location of the closing parenthesis. + # + # foo in ^(bar) + # ^ + sig { returns(Location) } + def rparen_loc; end + + # Save the rparen_loc location using the given saved source so that + # it can be retrieved later. + sig { params(repository: ::T.untyped).returns(Relocation::Entry) } + def save_rparen_loc(repository); end + + # Slice the location of operator_loc from the source. + sig { returns(String) } + def operator; end + + # Slice the location of lparen_loc from the source. + sig { returns(String) } + def lparen; end + + # Slice the location of rparen_loc from the source. + sig { returns(String) } + def rparen; end + + sig { params(other: ::T.untyped).returns(::T.nilable(T::Boolean)) } + def ===(other); 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. + sig { params(source: Source, node_id: Integer, location: Location, flags: Integer, variable: ::T.any(LocalVariableReadNode, InstanceVariableReadNode, ClassVariableReadNode, GlobalVariableReadNode, BackReferenceReadNode, NumberedReferenceReadNode, ItLocalVariableReadNode, MissingNode), operator_loc: Location).void } + def initialize(source, node_id, location, flags, variable, operator_loc); end + + # See Node.accept. + sig { override.params(visitor: Visitor).returns(::T.untyped) } + def accept(visitor); end + + # See Node.child_nodes. + sig { override.returns(T::Array[::T.nilable(Node)]) } + def child_nodes; end + + # See Node.each_child_node. + sig { override.returns(T::Enumerator[Node]) } + sig { override.params(blk: ::T.proc.params(arg0: Node).void).void } + def each_child_node(&blk); end + + # See Node.compact_child_nodes. + sig { override.returns(T::Array[Node]) } + def compact_child_nodes; end + + # See Node.comment_targets. + sig { override.returns(T::Array[::T.any(Node, Location)]) } + def comment_targets; end + + # Creates a copy of self with the given fields, using self as the template. + sig { params(node_id: Integer, location: Location, flags: Integer, variable: ::T.any(LocalVariableReadNode, InstanceVariableReadNode, ClassVariableReadNode, GlobalVariableReadNode, BackReferenceReadNode, NumberedReferenceReadNode, ItLocalVariableReadNode, MissingNode), operator_loc: Location).returns(PinnedVariableNode) } + def copy(node_id: T.unsafe(nil), location: T.unsafe(nil), flags: T.unsafe(nil), variable: T.unsafe(nil), operator_loc: T.unsafe(nil)); end + + sig { override.returns(T::Array[::T.nilable(Node)]) } + def deconstruct; end + + sig { params(keys: ::T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, ::T.untyped]) } + def deconstruct_keys(keys); end + + # See `Node#type`. + sig { override.returns(Symbol) } + def type; end + + # See `Node.type`. + sig { override.returns(Symbol) } + def self.type; end + + sig { override.returns(String) } + def inspect; end + + # The variable used in the pinned expression + # + # foo in ^bar + # ^^^ + sig { returns(::T.any(LocalVariableReadNode, InstanceVariableReadNode, ClassVariableReadNode, GlobalVariableReadNode, BackReferenceReadNode, NumberedReferenceReadNode, ItLocalVariableReadNode, MissingNode)) } + def variable; end + + # The Location of the `^` operator + # + # foo in ^bar + # ^ + sig { returns(Location) } + def operator_loc; end + + # Save the operator_loc location using the given saved source so that + # it can be retrieved later. + sig { params(repository: ::T.untyped).returns(Relocation::Entry) } + def save_operator_loc(repository); end + + # Slice the location of operator_loc from the source. + sig { returns(String) } + def operator; end + + sig { params(other: ::T.untyped).returns(::T.nilable(T::Boolean)) } + def ===(other); end + end + + # Represents the use of the `END` keyword. + # + # END { foo } + # ^^^^^^^^^^^ + class PostExecutionNode < Node + # Initialize a new PostExecutionNode node. + sig { params(source: Source, node_id: Integer, location: Location, flags: Integer, statements: ::T.nilable(StatementsNode), keyword_loc: Location, opening_loc: Location, closing_loc: Location).void } + def initialize(source, node_id, location, flags, statements, keyword_loc, opening_loc, closing_loc); end + + # See Node.accept. + sig { override.params(visitor: Visitor).returns(::T.untyped) } + def accept(visitor); end + + # See Node.child_nodes. + sig { override.returns(T::Array[::T.nilable(Node)]) } + def child_nodes; end + + # See Node.each_child_node. + sig { override.returns(T::Enumerator[Node]) } + sig { override.params(blk: ::T.proc.params(arg0: Node).void).void } + def each_child_node(&blk); end + + # See Node.compact_child_nodes. + sig { override.returns(T::Array[Node]) } + def compact_child_nodes; end + + # See Node.comment_targets. + sig { override.returns(T::Array[::T.any(Node, Location)]) } + def comment_targets; end + + # Creates a copy of self with the given fields, using self as the template. + sig { params(node_id: Integer, location: Location, flags: Integer, statements: ::T.nilable(StatementsNode), keyword_loc: Location, opening_loc: Location, closing_loc: Location).returns(PostExecutionNode) } + def copy(node_id: T.unsafe(nil), location: T.unsafe(nil), flags: T.unsafe(nil), statements: T.unsafe(nil), keyword_loc: T.unsafe(nil), opening_loc: T.unsafe(nil), closing_loc: T.unsafe(nil)); end + + sig { override.returns(T::Array[::T.nilable(Node)]) } + def deconstruct; end + + sig { params(keys: ::T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, ::T.untyped]) } + def deconstruct_keys(keys); end + + # See `Node#type`. + sig { override.returns(Symbol) } + def type; end + + # See `Node.type`. + sig { override.returns(Symbol) } + def self.type; end + + sig { override.returns(String) } + def inspect; end + + # Returns the `statements` attribute. + sig { returns(::T.nilable(StatementsNode)) } + def statements; end + + # Returns the Location represented by `keyword_loc`. + sig { returns(Location) } + def keyword_loc; end + + # Save the keyword_loc location using the given saved source so that + # it can be retrieved later. + sig { params(repository: ::T.untyped).returns(Relocation::Entry) } + def save_keyword_loc(repository); end + + # Returns the Location represented by `opening_loc`. + sig { returns(Location) } + def opening_loc; end + + # Save the opening_loc location using the given saved source so that + # it can be retrieved later. + sig { params(repository: ::T.untyped).returns(Relocation::Entry) } + def save_opening_loc(repository); end + + # Returns the Location represented by `closing_loc`. + sig { returns(Location) } + def closing_loc; end + + # Save the closing_loc location using the given saved source so that + # it can be retrieved later. + sig { params(repository: ::T.untyped).returns(Relocation::Entry) } + def save_closing_loc(repository); end + + # Slice the location of keyword_loc from the source. + sig { returns(String) } + def keyword; end + + # Slice the location of opening_loc from the source. + sig { returns(String) } + def opening; end + + # Slice the location of closing_loc from the source. + sig { returns(String) } + def closing; end + + sig { params(other: ::T.untyped).returns(::T.nilable(T::Boolean)) } + def ===(other); end + end + + # Represents the use of the `BEGIN` keyword. + # + # BEGIN { foo } + # ^^^^^^^^^^^^^ + class PreExecutionNode < Node + # Initialize a new PreExecutionNode node. + sig { params(source: Source, node_id: Integer, location: Location, flags: Integer, statements: ::T.nilable(StatementsNode), keyword_loc: Location, opening_loc: Location, closing_loc: Location).void } + def initialize(source, node_id, location, flags, statements, keyword_loc, opening_loc, closing_loc); end + + # See Node.accept. + sig { override.params(visitor: Visitor).returns(::T.untyped) } + def accept(visitor); end + + # See Node.child_nodes. + sig { override.returns(T::Array[::T.nilable(Node)]) } + def child_nodes; end + + # See Node.each_child_node. + sig { override.returns(T::Enumerator[Node]) } + sig { override.params(blk: ::T.proc.params(arg0: Node).void).void } + def each_child_node(&blk); end + + # See Node.compact_child_nodes. + sig { override.returns(T::Array[Node]) } + def compact_child_nodes; end + + # See Node.comment_targets. + sig { override.returns(T::Array[::T.any(Node, Location)]) } + def comment_targets; end + + # Creates a copy of self with the given fields, using self as the template. + sig { params(node_id: Integer, location: Location, flags: Integer, statements: ::T.nilable(StatementsNode), keyword_loc: Location, opening_loc: Location, closing_loc: Location).returns(PreExecutionNode) } + def copy(node_id: T.unsafe(nil), location: T.unsafe(nil), flags: T.unsafe(nil), statements: T.unsafe(nil), keyword_loc: T.unsafe(nil), opening_loc: T.unsafe(nil), closing_loc: T.unsafe(nil)); end + + sig { override.returns(T::Array[::T.nilable(Node)]) } + def deconstruct; end + + sig { params(keys: ::T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, ::T.untyped]) } + def deconstruct_keys(keys); end + + # See `Node#type`. + sig { override.returns(Symbol) } + def type; end + + # See `Node.type`. + sig { override.returns(Symbol) } + def self.type; end + + sig { override.returns(String) } + def inspect; end + + # Returns the `statements` attribute. + sig { returns(::T.nilable(StatementsNode)) } + def statements; end + + # Returns the Location represented by `keyword_loc`. + sig { returns(Location) } + def keyword_loc; end + + # Save the keyword_loc location using the given saved source so that + # it can be retrieved later. + sig { params(repository: ::T.untyped).returns(Relocation::Entry) } + def save_keyword_loc(repository); end + + # Returns the Location represented by `opening_loc`. + sig { returns(Location) } + def opening_loc; end + + # Save the opening_loc location using the given saved source so that + # it can be retrieved later. + sig { params(repository: ::T.untyped).returns(Relocation::Entry) } + def save_opening_loc(repository); end + + # Returns the Location represented by `closing_loc`. + sig { returns(Location) } + def closing_loc; end + + # Save the closing_loc location using the given saved source so that + # it can be retrieved later. + sig { params(repository: ::T.untyped).returns(Relocation::Entry) } + def save_closing_loc(repository); end + + # Slice the location of keyword_loc from the source. + sig { returns(String) } + def keyword; end + + # Slice the location of opening_loc from the source. + sig { returns(String) } + def opening; end + + # Slice the location of closing_loc from the source. + sig { returns(String) } + def closing; end + + sig { params(other: ::T.untyped).returns(::T.nilable(T::Boolean)) } + def ===(other); end + end + + # The top level node of any parse tree. + class ProgramNode < Node + # Initialize a new ProgramNode node. + sig { params(source: Source, node_id: Integer, location: Location, flags: Integer, locals: T::Array[Symbol], statements: StatementsNode).void } + def initialize(source, node_id, location, flags, locals, statements); end + + # See Node.accept. + sig { override.params(visitor: Visitor).returns(::T.untyped) } + def accept(visitor); end + + # See Node.child_nodes. + sig { override.returns(T::Array[::T.nilable(Node)]) } + def child_nodes; end + + # See Node.each_child_node. + sig { override.returns(T::Enumerator[Node]) } + sig { override.params(blk: ::T.proc.params(arg0: Node).void).void } + def each_child_node(&blk); end + + # See Node.compact_child_nodes. + sig { override.returns(T::Array[Node]) } + def compact_child_nodes; end + + # See Node.comment_targets. + sig { override.returns(T::Array[::T.any(Node, Location)]) } + def comment_targets; end + + # Creates a copy of self with the given fields, using self as the template. + sig { params(node_id: Integer, location: Location, flags: Integer, locals: T::Array[Symbol], statements: StatementsNode).returns(ProgramNode) } + def copy(node_id: T.unsafe(nil), location: T.unsafe(nil), flags: T.unsafe(nil), locals: T.unsafe(nil), statements: T.unsafe(nil)); end + + sig { override.returns(T::Array[::T.nilable(Node)]) } + def deconstruct; end + + sig { params(keys: ::T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, ::T.untyped]) } + def deconstruct_keys(keys); end + + # See `Node#type`. + sig { override.returns(Symbol) } + def type; end + + # See `Node.type`. + sig { override.returns(Symbol) } + def self.type; end + + sig { override.returns(String) } + def inspect; end + + # Returns the `locals` attribute. + sig { returns(T::Array[Symbol]) } + def locals; end + + # Returns the `statements` attribute. + sig { returns(StatementsNode) } + def statements; end + + sig { params(other: ::T.untyped).returns(::T.nilable(T::Boolean)) } + def ===(other); 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. + sig { params(source: Source, node_id: Integer, location: Location, flags: Integer, left: ::T.nilable(Node), right: ::T.nilable(Node), operator_loc: Location).void } + def initialize(source, node_id, location, flags, left, right, operator_loc); end + + # See Node.accept. + sig { override.params(visitor: Visitor).returns(::T.untyped) } + def accept(visitor); end + + # See Node.child_nodes. + sig { override.returns(T::Array[::T.nilable(Node)]) } + def child_nodes; end + + # See Node.each_child_node. + sig { override.returns(T::Enumerator[Node]) } + sig { override.params(blk: ::T.proc.params(arg0: Node).void).void } + def each_child_node(&blk); end + + # See Node.compact_child_nodes. + sig { override.returns(T::Array[Node]) } + def compact_child_nodes; end + + # See Node.comment_targets. + sig { override.returns(T::Array[::T.any(Node, Location)]) } + def comment_targets; end + + # Creates a copy of self with the given fields, using self as the template. + sig { params(node_id: Integer, location: Location, flags: Integer, left: ::T.nilable(Node), right: ::T.nilable(Node), operator_loc: Location).returns(RangeNode) } + def copy(node_id: T.unsafe(nil), location: T.unsafe(nil), flags: T.unsafe(nil), left: T.unsafe(nil), right: T.unsafe(nil), operator_loc: T.unsafe(nil)); end + + sig { override.returns(T::Array[::T.nilable(Node)]) } + def deconstruct; end + + sig { params(keys: ::T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, ::T.untyped]) } + def deconstruct_keys(keys); end + + # See `Node#type`. + sig { override.returns(Symbol) } + def type; end + + # See `Node.type`. + sig { override.returns(Symbol) } + def self.type; end + + sig { override.returns(String) } + def inspect; end + + # ... operator + sig { returns(T::Boolean) } + def 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 + # ^^^^^ + sig { returns(::T.nilable(Node)) } + def left; end + + # 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. + sig { returns(::T.nilable(Node)) } + def right; end + + # The Location of the `..` or `...` operator. + sig { returns(Location) } + def operator_loc; end + + # Save the operator_loc location using the given saved source so that + # it can be retrieved later. + sig { params(repository: ::T.untyped).returns(Relocation::Entry) } + def save_operator_loc(repository); end + + # Slice the location of operator_loc from the source. + sig { returns(String) } + def operator; end + + sig { params(other: ::T.untyped).returns(::T.nilable(T::Boolean)) } + def ===(other); end + end + + # Represents a rational number literal. + # + # 1.0r + # ^^^^ + class RationalNode < Node + # Initialize a new RationalNode node. + sig { params(source: Source, node_id: Integer, location: Location, flags: Integer, numerator: Integer, denominator: Integer).void } + def initialize(source, node_id, location, flags, numerator, denominator); end + + # See Node.accept. + sig { override.params(visitor: Visitor).returns(::T.untyped) } + def accept(visitor); end + + # See Node.child_nodes. + sig { override.returns(T::Array[::T.nilable(Node)]) } + def child_nodes; end + + # See Node.each_child_node. + sig { override.returns(T::Enumerator[Node]) } + sig { override.params(blk: ::T.proc.params(arg0: Node).void).void } + def each_child_node(&blk); end + + # See Node.compact_child_nodes. + sig { override.returns(T::Array[Node]) } + def compact_child_nodes; end + + # See Node.comment_targets. + sig { override.returns(T::Array[::T.any(Node, Location)]) } + def comment_targets; end + + # Creates a copy of self with the given fields, using self as the template. + sig { params(node_id: Integer, location: Location, flags: Integer, numerator: Integer, denominator: Integer).returns(RationalNode) } + def copy(node_id: T.unsafe(nil), location: T.unsafe(nil), flags: T.unsafe(nil), numerator: T.unsafe(nil), denominator: T.unsafe(nil)); end + + sig { override.returns(T::Array[::T.nilable(Node)]) } + def deconstruct; end + + sig { params(keys: ::T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, ::T.untyped]) } + def deconstruct_keys(keys); end + + # See `Node#type`. + sig { override.returns(Symbol) } + def type; end + + # See `Node.type`. + sig { override.returns(Symbol) } + def self.type; end + + sig { override.returns(String) } + def inspect; end + + # 0b prefix + sig { returns(T::Boolean) } + def binary?; end + + # 0d or no prefix + sig { returns(T::Boolean) } + def decimal?; end + + # 0o or 0 prefix + sig { returns(T::Boolean) } + def octal?; end + + # 0x prefix + sig { returns(T::Boolean) } + def hexadecimal?; end + + # The numerator of the rational number. + # + # 1.5r # numerator 3 + sig { returns(Integer) } + def numerator; end + + # The denominator of the rational number. + # + # 1.5r # denominator 2 + sig { returns(Integer) } + def denominator; end + + sig { params(other: ::T.untyped).returns(::T.nilable(T::Boolean)) } + def ===(other); end + end + + # Represents the use of the `redo` keyword. + # + # redo + # ^^^^ + class RedoNode < Node + # Initialize a new RedoNode node. + sig { params(source: Source, node_id: Integer, location: Location, flags: Integer).void } + def initialize(source, node_id, location, flags); end + + # See Node.accept. + sig { override.params(visitor: Visitor).returns(::T.untyped) } + def accept(visitor); end + + # See Node.child_nodes. + sig { override.returns(T::Array[::T.nilable(Node)]) } + def child_nodes; end + + # See Node.each_child_node. + sig { override.returns(T::Enumerator[Node]) } + sig { override.params(blk: ::T.proc.params(arg0: Node).void).void } + def each_child_node(&blk); end + + # See Node.compact_child_nodes. + sig { override.returns(T::Array[Node]) } + def compact_child_nodes; end + + # See Node.comment_targets. + sig { override.returns(T::Array[::T.any(Node, Location)]) } + def comment_targets; end + + # Creates a copy of self with the given fields, using self as the template. + sig { params(node_id: Integer, location: Location, flags: Integer).returns(RedoNode) } + def copy(node_id: T.unsafe(nil), location: T.unsafe(nil), flags: T.unsafe(nil)); end + + sig { override.returns(T::Array[::T.nilable(Node)]) } + def deconstruct; end + + sig { params(keys: ::T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, ::T.untyped]) } + def deconstruct_keys(keys); end + + # See `Node#type`. + sig { override.returns(Symbol) } + def type; end + + # See `Node.type`. + sig { override.returns(Symbol) } + def self.type; end + + sig { override.returns(String) } + def inspect; end + + sig { params(other: ::T.untyped).returns(::T.nilable(T::Boolean)) } + def ===(other); end + end + + # Represents a regular expression literal with no interpolation. + # + # /foo/i + # ^^^^^^ + class RegularExpressionNode < Node + # Initialize a new RegularExpressionNode node. + sig { params(source: Source, node_id: Integer, location: Location, flags: Integer, opening_loc: Location, content_loc: Location, closing_loc: Location, unescaped: String).void } + def initialize(source, node_id, location, flags, opening_loc, content_loc, closing_loc, unescaped); end + + # See Node.accept. + sig { override.params(visitor: Visitor).returns(::T.untyped) } + def accept(visitor); end + + # See Node.child_nodes. + sig { override.returns(T::Array[::T.nilable(Node)]) } + def child_nodes; end + + # See Node.each_child_node. + sig { override.returns(T::Enumerator[Node]) } + sig { override.params(blk: ::T.proc.params(arg0: Node).void).void } + def each_child_node(&blk); end + + # See Node.compact_child_nodes. + sig { override.returns(T::Array[Node]) } + def compact_child_nodes; end + + # See Node.comment_targets. + sig { override.returns(T::Array[::T.any(Node, Location)]) } + def comment_targets; end + + # Creates a copy of self with the given fields, using self as the template. + sig { params(node_id: Integer, location: Location, flags: Integer, opening_loc: Location, content_loc: Location, closing_loc: Location, unescaped: String).returns(RegularExpressionNode) } + def copy(node_id: T.unsafe(nil), location: T.unsafe(nil), flags: T.unsafe(nil), opening_loc: T.unsafe(nil), content_loc: T.unsafe(nil), closing_loc: T.unsafe(nil), unescaped: T.unsafe(nil)); end + + sig { override.returns(T::Array[::T.nilable(Node)]) } + def deconstruct; end + + sig { params(keys: ::T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, ::T.untyped]) } + def deconstruct_keys(keys); end + + # See `Node#type`. + sig { override.returns(Symbol) } + def type; end + + # See `Node.type`. + sig { override.returns(Symbol) } + def self.type; end + + sig { override.returns(String) } + def inspect; end + + # i - ignores the case of characters when matching + sig { returns(T::Boolean) } + def ignore_case?; end + + # x - ignores whitespace and allows comments in regular expressions + sig { returns(T::Boolean) } + def extended?; end + + # m - allows $ to match the end of lines within strings + sig { returns(T::Boolean) } + def multi_line?; end + + # o - only interpolates values into the regular expression once + sig { returns(T::Boolean) } + def once?; end + + # e - forces the EUC-JP encoding + sig { returns(T::Boolean) } + def euc_jp?; end + + # n - forces the ASCII-8BIT encoding + sig { returns(T::Boolean) } + def ascii_8bit?; end + + # s - forces the Windows-31J encoding + sig { returns(T::Boolean) } + def windows_31j?; end + + # u - forces the UTF-8 encoding + sig { returns(T::Boolean) } + def utf_8?; end + + # internal bytes forced the encoding to UTF-8 + sig { returns(T::Boolean) } + def forced_utf8_encoding?; end + + # internal bytes forced the encoding to binary + sig { returns(T::Boolean) } + def forced_binary_encoding?; end + + # internal bytes forced the encoding to US-ASCII + sig { returns(T::Boolean) } + def forced_us_ascii_encoding?; end + + # Returns the Location represented by `opening_loc`. + sig { returns(Location) } + def opening_loc; end + + # Save the opening_loc location using the given saved source so that + # it can be retrieved later. + sig { params(repository: ::T.untyped).returns(Relocation::Entry) } + def save_opening_loc(repository); end + + # Returns the Location represented by `content_loc`. + sig { returns(Location) } + def content_loc; end + + # Save the content_loc location using the given saved source so that + # it can be retrieved later. + sig { params(repository: ::T.untyped).returns(Relocation::Entry) } + def save_content_loc(repository); end + + # Returns the Location represented by `closing_loc`. + sig { returns(Location) } + def closing_loc; end + + # Save the closing_loc location using the given saved source so that + # it can be retrieved later. + sig { params(repository: ::T.untyped).returns(Relocation::Entry) } + def save_closing_loc(repository); end + + # Returns the `unescaped` attribute. + sig { returns(String) } + def unescaped; end + + # Slice the location of opening_loc from the source. + sig { returns(String) } + def opening; end + + # Slice the location of content_loc from the source. + sig { returns(String) } + def content; end + + # Slice the location of closing_loc from the source. + sig { returns(String) } + def closing; end + + sig { params(other: ::T.untyped).returns(::T.nilable(T::Boolean)) } + def ===(other); 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. + sig { params(source: Source, node_id: Integer, location: Location, flags: Integer, name: Symbol, name_loc: Location).void } + def initialize(source, node_id, location, flags, name, name_loc); end + + # See Node.accept. + sig { override.params(visitor: Visitor).returns(::T.untyped) } + def accept(visitor); end + + # See Node.child_nodes. + sig { override.returns(T::Array[::T.nilable(Node)]) } + def child_nodes; end + + # See Node.each_child_node. + sig { override.returns(T::Enumerator[Node]) } + sig { override.params(blk: ::T.proc.params(arg0: Node).void).void } + def each_child_node(&blk); end + + # See Node.compact_child_nodes. + sig { override.returns(T::Array[Node]) } + def compact_child_nodes; end + + # See Node.comment_targets. + sig { override.returns(T::Array[::T.any(Node, Location)]) } + def comment_targets; end + + # Creates a copy of self with the given fields, using self as the template. + sig { params(node_id: Integer, location: Location, flags: Integer, name: Symbol, name_loc: Location).returns(RequiredKeywordParameterNode) } + def copy(node_id: T.unsafe(nil), location: T.unsafe(nil), flags: T.unsafe(nil), name: T.unsafe(nil), name_loc: T.unsafe(nil)); end + + sig { override.returns(T::Array[::T.nilable(Node)]) } + def deconstruct; end + + sig { params(keys: ::T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, ::T.untyped]) } + def deconstruct_keys(keys); end + + # See `Node#type`. + sig { override.returns(Symbol) } + def type; end + + # See `Node.type`. + sig { override.returns(Symbol) } + def self.type; end + + sig { override.returns(String) } + def inspect; end + + # a parameter name that has been repeated in the method signature + sig { returns(T::Boolean) } + def repeated_parameter?; end + + # Returns the `name` attribute. + sig { returns(Symbol) } + def name; end + + # Returns the Location represented by `name_loc`. + sig { returns(Location) } + def name_loc; end + + # Save the name_loc location using the given saved source so that + # it can be retrieved later. + sig { params(repository: ::T.untyped).returns(Relocation::Entry) } + def save_name_loc(repository); end + + sig { params(other: ::T.untyped).returns(::T.nilable(T::Boolean)) } + def ===(other); 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. + sig { params(source: Source, node_id: Integer, location: Location, flags: Integer, name: Symbol).void } + def initialize(source, node_id, location, flags, name); end + + # See Node.accept. + sig { override.params(visitor: Visitor).returns(::T.untyped) } + def accept(visitor); end + + # See Node.child_nodes. + sig { override.returns(T::Array[::T.nilable(Node)]) } + def child_nodes; end + + # See Node.each_child_node. + sig { override.returns(T::Enumerator[Node]) } + sig { override.params(blk: ::T.proc.params(arg0: Node).void).void } + def each_child_node(&blk); end + + # See Node.compact_child_nodes. + sig { override.returns(T::Array[Node]) } + def compact_child_nodes; end + + # See Node.comment_targets. + sig { override.returns(T::Array[::T.any(Node, Location)]) } + def comment_targets; end + + # Creates a copy of self with the given fields, using self as the template. + sig { params(node_id: Integer, location: Location, flags: Integer, name: Symbol).returns(RequiredParameterNode) } + def copy(node_id: T.unsafe(nil), location: T.unsafe(nil), flags: T.unsafe(nil), name: T.unsafe(nil)); end + + sig { override.returns(T::Array[::T.nilable(Node)]) } + def deconstruct; end + + sig { params(keys: ::T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, ::T.untyped]) } + def deconstruct_keys(keys); end + + # See `Node#type`. + sig { override.returns(Symbol) } + def type; end + + # See `Node.type`. + sig { override.returns(Symbol) } + def self.type; end + + sig { override.returns(String) } + def inspect; end + + # a parameter name that has been repeated in the method signature + sig { returns(T::Boolean) } + def repeated_parameter?; end + + # Returns the `name` attribute. + sig { returns(Symbol) } + def name; end + + sig { params(other: ::T.untyped).returns(::T.nilable(T::Boolean)) } + def ===(other); end + end + + # Represents an expression modified with a rescue. + # + # foo rescue nil + # ^^^^^^^^^^^^^^ + class RescueModifierNode < Node + # Initialize a new RescueModifierNode node. + sig { params(source: Source, node_id: Integer, location: Location, flags: Integer, expression: Node, keyword_loc: Location, rescue_expression: Node).void } + def initialize(source, node_id, location, flags, expression, keyword_loc, rescue_expression); end + + # See Node.accept. + sig { override.params(visitor: Visitor).returns(::T.untyped) } + def accept(visitor); end + + # See Node.child_nodes. + sig { override.returns(T::Array[::T.nilable(Node)]) } + def child_nodes; end + + # See Node.each_child_node. + sig { override.returns(T::Enumerator[Node]) } + sig { override.params(blk: ::T.proc.params(arg0: Node).void).void } + def each_child_node(&blk); end + + # See Node.compact_child_nodes. + sig { override.returns(T::Array[Node]) } + def compact_child_nodes; end + + # See Node.comment_targets. + sig { override.returns(T::Array[::T.any(Node, Location)]) } + def comment_targets; end + + # Creates a copy of self with the given fields, using self as the template. + sig { params(node_id: Integer, location: Location, flags: Integer, expression: Node, keyword_loc: Location, rescue_expression: Node).returns(RescueModifierNode) } + def copy(node_id: T.unsafe(nil), location: T.unsafe(nil), flags: T.unsafe(nil), expression: T.unsafe(nil), keyword_loc: T.unsafe(nil), rescue_expression: T.unsafe(nil)); end + + sig { override.returns(T::Array[::T.nilable(Node)]) } + def deconstruct; end + + sig { params(keys: ::T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, ::T.untyped]) } + def deconstruct_keys(keys); end + + # See `Node#type`. + sig { override.returns(Symbol) } + def type; end + + # See `Node.type`. + sig { override.returns(Symbol) } + def self.type; end + + sig { override.returns(String) } + def inspect; end + + # Returns the `expression` attribute. + sig { returns(Node) } + def expression; end + + # Returns the Location represented by `keyword_loc`. + sig { returns(Location) } + def keyword_loc; end + + # Save the keyword_loc location using the given saved source so that + # it can be retrieved later. + sig { params(repository: ::T.untyped).returns(Relocation::Entry) } + def save_keyword_loc(repository); end + + # Returns the `rescue_expression` attribute. + sig { returns(Node) } + def rescue_expression; end + + # Slice the location of keyword_loc from the source. + sig { returns(String) } + def keyword; end + + sig { params(other: ::T.untyped).returns(::T.nilable(T::Boolean)) } + def ===(other); 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. + sig { params(source: Source, node_id: Integer, location: Location, flags: Integer, keyword_loc: Location, exceptions: T::Array[Node], operator_loc: ::T.nilable(Location), reference: ::T.nilable(::T.any(LocalVariableTargetNode, InstanceVariableTargetNode, ClassVariableTargetNode, GlobalVariableTargetNode, ConstantTargetNode, ConstantPathTargetNode, CallTargetNode, IndexTargetNode, BackReferenceReadNode, NumberedReferenceReadNode, MissingNode)), then_keyword_loc: ::T.nilable(Location), statements: ::T.nilable(StatementsNode), subsequent: ::T.nilable(RescueNode)).void } + def initialize(source, node_id, location, flags, keyword_loc, exceptions, operator_loc, reference, then_keyword_loc, statements, subsequent); end + + # See Node.accept. + sig { override.params(visitor: Visitor).returns(::T.untyped) } + def accept(visitor); end + + # See Node.child_nodes. + sig { override.returns(T::Array[::T.nilable(Node)]) } + def child_nodes; end + + # See Node.each_child_node. + sig { override.returns(T::Enumerator[Node]) } + sig { override.params(blk: ::T.proc.params(arg0: Node).void).void } + def each_child_node(&blk); end + + # See Node.compact_child_nodes. + sig { override.returns(T::Array[Node]) } + def compact_child_nodes; end + + # See Node.comment_targets. + sig { override.returns(T::Array[::T.any(Node, Location)]) } + def comment_targets; end + + # Creates a copy of self with the given fields, using self as the template. + sig { params(node_id: Integer, location: Location, flags: Integer, keyword_loc: Location, exceptions: T::Array[Node], operator_loc: ::T.nilable(Location), reference: ::T.nilable(::T.any(LocalVariableTargetNode, InstanceVariableTargetNode, ClassVariableTargetNode, GlobalVariableTargetNode, ConstantTargetNode, ConstantPathTargetNode, CallTargetNode, IndexTargetNode, BackReferenceReadNode, NumberedReferenceReadNode, MissingNode)), then_keyword_loc: ::T.nilable(Location), statements: ::T.nilable(StatementsNode), subsequent: ::T.nilable(RescueNode)).returns(RescueNode) } + def copy(node_id: T.unsafe(nil), location: T.unsafe(nil), flags: T.unsafe(nil), keyword_loc: T.unsafe(nil), exceptions: T.unsafe(nil), operator_loc: T.unsafe(nil), reference: T.unsafe(nil), then_keyword_loc: T.unsafe(nil), statements: T.unsafe(nil), subsequent: T.unsafe(nil)); end + + sig { override.returns(T::Array[::T.nilable(Node)]) } + def deconstruct; end + + sig { params(keys: ::T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, ::T.untyped]) } + def deconstruct_keys(keys); end + + # See `Node#type`. + sig { override.returns(Symbol) } + def type; end + + # See `Node.type`. + sig { override.returns(Symbol) } + def self.type; end + + sig { override.returns(String) } + def inspect; end + + # Returns the Location represented by `keyword_loc`. + sig { returns(Location) } + def keyword_loc; end + + # Save the keyword_loc location using the given saved source so that + # it can be retrieved later. + sig { params(repository: ::T.untyped).returns(Relocation::Entry) } + def save_keyword_loc(repository); end + + # Returns the `exceptions` attribute. + sig { returns(T::Array[Node]) } + def exceptions; end + + # Returns the Location represented by `operator_loc`. + sig { returns(::T.nilable(Location)) } + def operator_loc; end + + # Save the operator_loc location using the given saved source so that + # it can be retrieved later. + sig { params(repository: ::T.untyped).returns(::T.nilable(Relocation::Entry)) } + def save_operator_loc(repository); end + + # Returns the `reference` attribute. + sig { returns(::T.nilable(::T.any(LocalVariableTargetNode, InstanceVariableTargetNode, ClassVariableTargetNode, GlobalVariableTargetNode, ConstantTargetNode, ConstantPathTargetNode, CallTargetNode, IndexTargetNode, BackReferenceReadNode, NumberedReferenceReadNode, MissingNode))) } + def reference; end + + # Returns the Location represented by `then_keyword_loc`. + sig { returns(::T.nilable(Location)) } + def then_keyword_loc; end + + # Save the then_keyword_loc location using the given saved source so that + # it can be retrieved later. + sig { params(repository: ::T.untyped).returns(::T.nilable(Relocation::Entry)) } + def save_then_keyword_loc(repository); end + + # Returns the `statements` attribute. + sig { returns(::T.nilable(StatementsNode)) } + def statements; end + + # Returns the `subsequent` attribute. + sig { returns(::T.nilable(RescueNode)) } + def subsequent; end + + # Slice the location of keyword_loc from the source. + sig { returns(String) } + def keyword; end + + # Slice the location of operator_loc from the source. + sig { returns(::T.nilable(String)) } + def operator; end + + # Slice the location of then_keyword_loc from the source. + sig { returns(::T.nilable(String)) } + def then_keyword; end + + sig { params(other: ::T.untyped).returns(::T.nilable(T::Boolean)) } + def ===(other); 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. + sig { params(source: Source, node_id: Integer, location: Location, flags: Integer, name: ::T.nilable(Symbol), name_loc: ::T.nilable(Location), operator_loc: Location).void } + def initialize(source, node_id, location, flags, name, name_loc, operator_loc); end + + # See Node.accept. + sig { override.params(visitor: Visitor).returns(::T.untyped) } + def accept(visitor); end + + # See Node.child_nodes. + sig { override.returns(T::Array[::T.nilable(Node)]) } + def child_nodes; end + + # See Node.each_child_node. + sig { override.returns(T::Enumerator[Node]) } + sig { override.params(blk: ::T.proc.params(arg0: Node).void).void } + def each_child_node(&blk); end + + # See Node.compact_child_nodes. + sig { override.returns(T::Array[Node]) } + def compact_child_nodes; end + + # See Node.comment_targets. + sig { override.returns(T::Array[::T.any(Node, Location)]) } + def comment_targets; end + + # Creates a copy of self with the given fields, using self as the template. + sig { params(node_id: Integer, location: Location, flags: Integer, name: ::T.nilable(Symbol), name_loc: ::T.nilable(Location), operator_loc: Location).returns(RestParameterNode) } + def copy(node_id: T.unsafe(nil), location: T.unsafe(nil), flags: T.unsafe(nil), name: T.unsafe(nil), name_loc: T.unsafe(nil), operator_loc: T.unsafe(nil)); end + + sig { override.returns(T::Array[::T.nilable(Node)]) } + def deconstruct; end + + sig { params(keys: ::T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, ::T.untyped]) } + def deconstruct_keys(keys); end + + # See `Node#type`. + sig { override.returns(Symbol) } + def type; end + + # See `Node.type`. + sig { override.returns(Symbol) } + def self.type; end + + sig { override.returns(String) } + def inspect; end + + # a parameter name that has been repeated in the method signature + sig { returns(T::Boolean) } + def repeated_parameter?; end + + # Returns the `name` attribute. + sig { returns(::T.nilable(Symbol)) } + def name; end + + # Returns the Location represented by `name_loc`. + sig { returns(::T.nilable(Location)) } + def name_loc; end + + # Save the name_loc location using the given saved source so that + # it can be retrieved later. + sig { params(repository: ::T.untyped).returns(::T.nilable(Relocation::Entry)) } + def save_name_loc(repository); end + + # Returns the Location represented by `operator_loc`. + sig { returns(Location) } + def operator_loc; end + + # Save the operator_loc location using the given saved source so that + # it can be retrieved later. + sig { params(repository: ::T.untyped).returns(Relocation::Entry) } + def save_operator_loc(repository); end + + # Slice the location of operator_loc from the source. + sig { returns(String) } + def operator; end + + sig { params(other: ::T.untyped).returns(::T.nilable(T::Boolean)) } + def ===(other); end + end + + # Represents the use of the `retry` keyword. + # + # retry + # ^^^^^ + class RetryNode < Node + # Initialize a new RetryNode node. + sig { params(source: Source, node_id: Integer, location: Location, flags: Integer).void } + def initialize(source, node_id, location, flags); end + + # See Node.accept. + sig { override.params(visitor: Visitor).returns(::T.untyped) } + def accept(visitor); end + + # See Node.child_nodes. + sig { override.returns(T::Array[::T.nilable(Node)]) } + def child_nodes; end + + # See Node.each_child_node. + sig { override.returns(T::Enumerator[Node]) } + sig { override.params(blk: ::T.proc.params(arg0: Node).void).void } + def each_child_node(&blk); end + + # See Node.compact_child_nodes. + sig { override.returns(T::Array[Node]) } + def compact_child_nodes; end + + # See Node.comment_targets. + sig { override.returns(T::Array[::T.any(Node, Location)]) } + def comment_targets; end + + # Creates a copy of self with the given fields, using self as the template. + sig { params(node_id: Integer, location: Location, flags: Integer).returns(RetryNode) } + def copy(node_id: T.unsafe(nil), location: T.unsafe(nil), flags: T.unsafe(nil)); end + + sig { override.returns(T::Array[::T.nilable(Node)]) } + def deconstruct; end + + sig { params(keys: ::T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, ::T.untyped]) } + def deconstruct_keys(keys); end + + # See `Node#type`. + sig { override.returns(Symbol) } + def type; end + + # See `Node.type`. + sig { override.returns(Symbol) } + def self.type; end + + sig { override.returns(String) } + def inspect; end + + sig { params(other: ::T.untyped).returns(::T.nilable(T::Boolean)) } + def ===(other); end + end + + # Represents the use of the `return` keyword. + # + # return 1 + # ^^^^^^^^ + class ReturnNode < Node + # Initialize a new ReturnNode node. + sig { params(source: Source, node_id: Integer, location: Location, flags: Integer, keyword_loc: Location, arguments: ::T.nilable(ArgumentsNode)).void } + def initialize(source, node_id, location, flags, keyword_loc, arguments); end + + # See Node.accept. + sig { override.params(visitor: Visitor).returns(::T.untyped) } + def accept(visitor); end + + # See Node.child_nodes. + sig { override.returns(T::Array[::T.nilable(Node)]) } + def child_nodes; end + + # See Node.each_child_node. + sig { override.returns(T::Enumerator[Node]) } + sig { override.params(blk: ::T.proc.params(arg0: Node).void).void } + def each_child_node(&blk); end + + # See Node.compact_child_nodes. + sig { override.returns(T::Array[Node]) } + def compact_child_nodes; end + + # See Node.comment_targets. + sig { override.returns(T::Array[::T.any(Node, Location)]) } + def comment_targets; end + + # Creates a copy of self with the given fields, using self as the template. + sig { params(node_id: Integer, location: Location, flags: Integer, keyword_loc: Location, arguments: ::T.nilable(ArgumentsNode)).returns(ReturnNode) } + def copy(node_id: T.unsafe(nil), location: T.unsafe(nil), flags: T.unsafe(nil), keyword_loc: T.unsafe(nil), arguments: T.unsafe(nil)); end + + sig { override.returns(T::Array[::T.nilable(Node)]) } + def deconstruct; end + + sig { params(keys: ::T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, ::T.untyped]) } + def deconstruct_keys(keys); end + + # See `Node#type`. + sig { override.returns(Symbol) } + def type; end + + # See `Node.type`. + sig { override.returns(Symbol) } + def self.type; end + + sig { override.returns(String) } + def inspect; end + + # Returns the Location represented by `keyword_loc`. + sig { returns(Location) } + def keyword_loc; end + + # Save the keyword_loc location using the given saved source so that + # it can be retrieved later. + sig { params(repository: ::T.untyped).returns(Relocation::Entry) } + def save_keyword_loc(repository); end + + # Returns the `arguments` attribute. + sig { returns(::T.nilable(ArgumentsNode)) } + def arguments; end + + # Slice the location of keyword_loc from the source. + sig { returns(String) } + def keyword; end + + sig { params(other: ::T.untyped).returns(::T.nilable(T::Boolean)) } + def ===(other); end + end + + # Represents the `self` keyword. + # + # self + # ^^^^ + class SelfNode < Node + # Initialize a new SelfNode node. + sig { params(source: Source, node_id: Integer, location: Location, flags: Integer).void } + def initialize(source, node_id, location, flags); end + + # See Node.accept. + sig { override.params(visitor: Visitor).returns(::T.untyped) } + def accept(visitor); end + + # See Node.child_nodes. + sig { override.returns(T::Array[::T.nilable(Node)]) } + def child_nodes; end + + # See Node.each_child_node. + sig { override.returns(T::Enumerator[Node]) } + sig { override.params(blk: ::T.proc.params(arg0: Node).void).void } + def each_child_node(&blk); end + + # See Node.compact_child_nodes. + sig { override.returns(T::Array[Node]) } + def compact_child_nodes; end + + # See Node.comment_targets. + sig { override.returns(T::Array[::T.any(Node, Location)]) } + def comment_targets; end + + # Creates a copy of self with the given fields, using self as the template. + sig { params(node_id: Integer, location: Location, flags: Integer).returns(SelfNode) } + def copy(node_id: T.unsafe(nil), location: T.unsafe(nil), flags: T.unsafe(nil)); end + + sig { override.returns(T::Array[::T.nilable(Node)]) } + def deconstruct; end + + sig { params(keys: ::T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, ::T.untyped]) } + def deconstruct_keys(keys); end + + # See `Node#type`. + sig { override.returns(Symbol) } + def type; end + + # See `Node.type`. + sig { override.returns(Symbol) } + def self.type; end + + sig { override.returns(String) } + def inspect; end + + sig { params(other: ::T.untyped).returns(::T.nilable(T::Boolean)) } + def ===(other); 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. + sig { params(source: Source, node_id: Integer, location: Location, flags: Integer, write: ::T.any(ConstantWriteNode, ConstantAndWriteNode, ConstantOrWriteNode, ConstantOperatorWriteNode, ConstantPathWriteNode, ConstantPathAndWriteNode, ConstantPathOrWriteNode, ConstantPathOperatorWriteNode)).void } + def initialize(source, node_id, location, flags, write); end + + # See Node.accept. + sig { override.params(visitor: Visitor).returns(::T.untyped) } + def accept(visitor); end + + # See Node.child_nodes. + sig { override.returns(T::Array[::T.nilable(Node)]) } + def child_nodes; end + + # See Node.each_child_node. + sig { override.returns(T::Enumerator[Node]) } + sig { override.params(blk: ::T.proc.params(arg0: Node).void).void } + def each_child_node(&blk); end + + # See Node.compact_child_nodes. + sig { override.returns(T::Array[Node]) } + def compact_child_nodes; end + + # See Node.comment_targets. + sig { override.returns(T::Array[::T.any(Node, Location)]) } + def comment_targets; end + + # Creates a copy of self with the given fields, using self as the template. + sig { params(node_id: Integer, location: Location, flags: Integer, write: ::T.any(ConstantWriteNode, ConstantAndWriteNode, ConstantOrWriteNode, ConstantOperatorWriteNode, ConstantPathWriteNode, ConstantPathAndWriteNode, ConstantPathOrWriteNode, ConstantPathOperatorWriteNode)).returns(ShareableConstantNode) } + def copy(node_id: T.unsafe(nil), location: T.unsafe(nil), flags: T.unsafe(nil), write: T.unsafe(nil)); end + + sig { override.returns(T::Array[::T.nilable(Node)]) } + def deconstruct; end + + sig { params(keys: ::T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, ::T.untyped]) } + def deconstruct_keys(keys); end + + # See `Node#type`. + sig { override.returns(Symbol) } + def type; end + + # See `Node.type`. + sig { override.returns(Symbol) } + def self.type; end + + sig { override.returns(String) } + def inspect; end + + # constant writes that should be modified with shareable constant value literal + sig { returns(T::Boolean) } + def literal?; end + + # constant writes that should be modified with shareable constant value experimental everything + sig { returns(T::Boolean) } + def experimental_everything?; end + + # constant writes that should be modified with shareable constant value experimental copy + sig { returns(T::Boolean) } + def experimental_copy?; end + + # The constant write that should be modified with the shareability state. + sig { returns(::T.any(ConstantWriteNode, ConstantAndWriteNode, ConstantOrWriteNode, ConstantOperatorWriteNode, ConstantPathWriteNode, ConstantPathAndWriteNode, ConstantPathOrWriteNode, ConstantPathOperatorWriteNode)) } + def write; end + + sig { params(other: ::T.untyped).returns(::T.nilable(T::Boolean)) } + def ===(other); end + end + + # Represents a singleton class declaration involving the `class` keyword. + # + # class << self end + # ^^^^^^^^^^^^^^^^^ + class SingletonClassNode < Node + # Initialize a new SingletonClassNode node. + sig { params(source: Source, node_id: Integer, location: Location, flags: Integer, locals: T::Array[Symbol], class_keyword_loc: Location, operator_loc: Location, expression: Node, body: ::T.nilable(::T.any(StatementsNode, BeginNode)), end_keyword_loc: Location).void } + def initialize(source, node_id, location, flags, locals, class_keyword_loc, operator_loc, expression, body, end_keyword_loc); end + + # See Node.accept. + sig { override.params(visitor: Visitor).returns(::T.untyped) } + def accept(visitor); end + + # See Node.child_nodes. + sig { override.returns(T::Array[::T.nilable(Node)]) } + def child_nodes; end + + # See Node.each_child_node. + sig { override.returns(T::Enumerator[Node]) } + sig { override.params(blk: ::T.proc.params(arg0: Node).void).void } + def each_child_node(&blk); end + + # See Node.compact_child_nodes. + sig { override.returns(T::Array[Node]) } + def compact_child_nodes; end + + # See Node.comment_targets. + sig { override.returns(T::Array[::T.any(Node, Location)]) } + def comment_targets; end + + # Creates a copy of self with the given fields, using self as the template. + sig { params(node_id: Integer, location: Location, flags: Integer, locals: T::Array[Symbol], class_keyword_loc: Location, operator_loc: Location, expression: Node, body: ::T.nilable(::T.any(StatementsNode, BeginNode)), end_keyword_loc: Location).returns(SingletonClassNode) } + def copy(node_id: T.unsafe(nil), location: T.unsafe(nil), flags: T.unsafe(nil), locals: T.unsafe(nil), class_keyword_loc: T.unsafe(nil), operator_loc: T.unsafe(nil), expression: T.unsafe(nil), body: T.unsafe(nil), end_keyword_loc: T.unsafe(nil)); end + + sig { override.returns(T::Array[::T.nilable(Node)]) } + def deconstruct; end + + sig { params(keys: ::T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, ::T.untyped]) } + def deconstruct_keys(keys); end + + # See `Node#type`. + sig { override.returns(Symbol) } + def type; end + + # See `Node.type`. + sig { override.returns(Symbol) } + def self.type; end + + sig { override.returns(String) } + def inspect; end + + # Returns the `locals` attribute. + sig { returns(T::Array[Symbol]) } + def locals; end + + # Returns the Location represented by `class_keyword_loc`. + sig { returns(Location) } + def class_keyword_loc; end + + # Save the class_keyword_loc location using the given saved source so that + # it can be retrieved later. + sig { params(repository: ::T.untyped).returns(Relocation::Entry) } + def save_class_keyword_loc(repository); end + + # Returns the Location represented by `operator_loc`. + sig { returns(Location) } + def operator_loc; end + + # Save the operator_loc location using the given saved source so that + # it can be retrieved later. + sig { params(repository: ::T.untyped).returns(Relocation::Entry) } + def save_operator_loc(repository); end + + # Returns the `expression` attribute. + sig { returns(Node) } + def expression; end + + # Returns the `body` attribute. + sig { returns(::T.nilable(::T.any(StatementsNode, BeginNode))) } + def body; end + + # Returns the Location represented by `end_keyword_loc`. + sig { returns(Location) } + def end_keyword_loc; end + + # Save the end_keyword_loc location using the given saved source so that + # it can be retrieved later. + sig { params(repository: ::T.untyped).returns(Relocation::Entry) } + def save_end_keyword_loc(repository); end + + # Slice the location of class_keyword_loc from the source. + sig { returns(String) } + def class_keyword; end + + # Slice the location of operator_loc from the source. + sig { returns(String) } + def operator; end + + # Slice the location of end_keyword_loc from the source. + sig { returns(String) } + def end_keyword; end + + sig { params(other: ::T.untyped).returns(::T.nilable(T::Boolean)) } + def ===(other); end + end + + # Represents the use of the `__ENCODING__` keyword. + # + # __ENCODING__ + # ^^^^^^^^^^^^ + class SourceEncodingNode < Node + # Initialize a new SourceEncodingNode node. + sig { params(source: Source, node_id: Integer, location: Location, flags: Integer).void } + def initialize(source, node_id, location, flags); end + + # See Node.accept. + sig { override.params(visitor: Visitor).returns(::T.untyped) } + def accept(visitor); end + + # See Node.child_nodes. + sig { override.returns(T::Array[::T.nilable(Node)]) } + def child_nodes; end + + # See Node.each_child_node. + sig { override.returns(T::Enumerator[Node]) } + sig { override.params(blk: ::T.proc.params(arg0: Node).void).void } + def each_child_node(&blk); end + + # See Node.compact_child_nodes. + sig { override.returns(T::Array[Node]) } + def compact_child_nodes; end + + # See Node.comment_targets. + sig { override.returns(T::Array[::T.any(Node, Location)]) } + def comment_targets; end + + # Creates a copy of self with the given fields, using self as the template. + sig { params(node_id: Integer, location: Location, flags: Integer).returns(SourceEncodingNode) } + def copy(node_id: T.unsafe(nil), location: T.unsafe(nil), flags: T.unsafe(nil)); end + + sig { override.returns(T::Array[::T.nilable(Node)]) } + def deconstruct; end + + sig { params(keys: ::T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, ::T.untyped]) } + def deconstruct_keys(keys); end + + # See `Node#type`. + sig { override.returns(Symbol) } + def type; end + + # See `Node.type`. + sig { override.returns(Symbol) } + def self.type; end + + sig { override.returns(String) } + def inspect; end + + sig { params(other: ::T.untyped).returns(::T.nilable(T::Boolean)) } + def ===(other); end + end + + # Represents the use of the `__FILE__` keyword. + # + # __FILE__ + # ^^^^^^^^ + class SourceFileNode < Node + # Initialize a new SourceFileNode node. + sig { params(source: Source, node_id: Integer, location: Location, flags: Integer, filepath: String).void } + def initialize(source, node_id, location, flags, filepath); end + + # See Node.accept. + sig { override.params(visitor: Visitor).returns(::T.untyped) } + def accept(visitor); end + + # See Node.child_nodes. + sig { override.returns(T::Array[::T.nilable(Node)]) } + def child_nodes; end + + # See Node.each_child_node. + sig { override.returns(T::Enumerator[Node]) } + sig { override.params(blk: ::T.proc.params(arg0: Node).void).void } + def each_child_node(&blk); end + + # See Node.compact_child_nodes. + sig { override.returns(T::Array[Node]) } + def compact_child_nodes; end + + # See Node.comment_targets. + sig { override.returns(T::Array[::T.any(Node, Location)]) } + def comment_targets; end + + # Creates a copy of self with the given fields, using self as the template. + sig { params(node_id: Integer, location: Location, flags: Integer, filepath: String).returns(SourceFileNode) } + def copy(node_id: T.unsafe(nil), location: T.unsafe(nil), flags: T.unsafe(nil), filepath: T.unsafe(nil)); end + + sig { override.returns(T::Array[::T.nilable(Node)]) } + def deconstruct; end + + sig { params(keys: ::T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, ::T.untyped]) } + def deconstruct_keys(keys); end + + # See `Node#type`. + sig { override.returns(Symbol) } + def type; end + + # See `Node.type`. + sig { override.returns(Symbol) } + def self.type; end + + sig { override.returns(String) } + def inspect; end + + # internal bytes forced the encoding to UTF-8 + sig { returns(T::Boolean) } + def forced_utf8_encoding?; end + + # internal bytes forced the encoding to binary + sig { returns(T::Boolean) } + def forced_binary_encoding?; end + + # frozen by virtue of a `frozen_string_literal: true` comment or `--enable-frozen-string-literal` + sig { returns(T::Boolean) } + def frozen?; end + + # mutable by virtue of a `frozen_string_literal: false` comment or `--disable-frozen-string-literal` + sig { returns(T::Boolean) } + def mutable?; end + + # Represents the file path being parsed. This corresponds directly to the `filepath` option given to the various `Prism.parse*` APIs. + sig { returns(String) } + def filepath; end + + sig { params(other: ::T.untyped).returns(::T.nilable(T::Boolean)) } + def ===(other); end + end + + # Represents the use of the `__LINE__` keyword. + # + # __LINE__ + # ^^^^^^^^ + class SourceLineNode < Node + # Initialize a new SourceLineNode node. + sig { params(source: Source, node_id: Integer, location: Location, flags: Integer).void } + def initialize(source, node_id, location, flags); end + + # See Node.accept. + sig { override.params(visitor: Visitor).returns(::T.untyped) } + def accept(visitor); end + + # See Node.child_nodes. + sig { override.returns(T::Array[::T.nilable(Node)]) } + def child_nodes; end + + # See Node.each_child_node. + sig { override.returns(T::Enumerator[Node]) } + sig { override.params(blk: ::T.proc.params(arg0: Node).void).void } + def each_child_node(&blk); end + + # See Node.compact_child_nodes. + sig { override.returns(T::Array[Node]) } + def compact_child_nodes; end + + # See Node.comment_targets. + sig { override.returns(T::Array[::T.any(Node, Location)]) } + def comment_targets; end + + # Creates a copy of self with the given fields, using self as the template. + sig { params(node_id: Integer, location: Location, flags: Integer).returns(SourceLineNode) } + def copy(node_id: T.unsafe(nil), location: T.unsafe(nil), flags: T.unsafe(nil)); end + + sig { override.returns(T::Array[::T.nilable(Node)]) } + def deconstruct; end + + sig { params(keys: ::T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, ::T.untyped]) } + def deconstruct_keys(keys); end + + # See `Node#type`. + sig { override.returns(Symbol) } + def type; end + + # See `Node.type`. + sig { override.returns(Symbol) } + def self.type; end + + sig { override.returns(String) } + def inspect; end + + sig { params(other: ::T.untyped).returns(::T.nilable(T::Boolean)) } + def ===(other); end + end + + # Represents the use of the splat operator. + # + # [*a] + # ^^ + class SplatNode < Node + # Initialize a new SplatNode node. + sig { params(source: Source, node_id: Integer, location: Location, flags: Integer, operator_loc: Location, expression: ::T.nilable(Node)).void } + def initialize(source, node_id, location, flags, operator_loc, expression); end + + # See Node.accept. + sig { override.params(visitor: Visitor).returns(::T.untyped) } + def accept(visitor); end + + # See Node.child_nodes. + sig { override.returns(T::Array[::T.nilable(Node)]) } + def child_nodes; end + + # See Node.each_child_node. + sig { override.returns(T::Enumerator[Node]) } + sig { override.params(blk: ::T.proc.params(arg0: Node).void).void } + def each_child_node(&blk); end + + # See Node.compact_child_nodes. + sig { override.returns(T::Array[Node]) } + def compact_child_nodes; end + + # See Node.comment_targets. + sig { override.returns(T::Array[::T.any(Node, Location)]) } + def comment_targets; end + + # Creates a copy of self with the given fields, using self as the template. + sig { params(node_id: Integer, location: Location, flags: Integer, operator_loc: Location, expression: ::T.nilable(Node)).returns(SplatNode) } + def copy(node_id: T.unsafe(nil), location: T.unsafe(nil), flags: T.unsafe(nil), operator_loc: T.unsafe(nil), expression: T.unsafe(nil)); end + + sig { override.returns(T::Array[::T.nilable(Node)]) } + def deconstruct; end + + sig { params(keys: ::T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, ::T.untyped]) } + def deconstruct_keys(keys); end + + # See `Node#type`. + sig { override.returns(Symbol) } + def type; end + + # See `Node.type`. + sig { override.returns(Symbol) } + def self.type; end + + sig { override.returns(String) } + def inspect; end + + # Returns the Location represented by `operator_loc`. + sig { returns(Location) } + def operator_loc; end + + # Save the operator_loc location using the given saved source so that + # it can be retrieved later. + sig { params(repository: ::T.untyped).returns(Relocation::Entry) } + def save_operator_loc(repository); end + + # Returns the `expression` attribute. + sig { returns(::T.nilable(Node)) } + def expression; end + + # Slice the location of operator_loc from the source. + sig { returns(String) } + def operator; end + + sig { params(other: ::T.untyped).returns(::T.nilable(T::Boolean)) } + def ===(other); end + end + + # Represents a set of statements contained within some scope. + # + # foo; bar; baz + # ^^^^^^^^^^^^^ + class StatementsNode < Node + # Initialize a new StatementsNode node. + sig { params(source: Source, node_id: Integer, location: Location, flags: Integer, body: T::Array[Node]).void } + def initialize(source, node_id, location, flags, body); end + + # See Node.accept. + sig { override.params(visitor: Visitor).returns(::T.untyped) } + def accept(visitor); end + + # See Node.child_nodes. + sig { override.returns(T::Array[::T.nilable(Node)]) } + def child_nodes; end + + # See Node.each_child_node. + sig { override.returns(T::Enumerator[Node]) } + sig { override.params(blk: ::T.proc.params(arg0: Node).void).void } + def each_child_node(&blk); end + + # See Node.compact_child_nodes. + sig { override.returns(T::Array[Node]) } + def compact_child_nodes; end + + # See Node.comment_targets. + sig { override.returns(T::Array[::T.any(Node, Location)]) } + def comment_targets; end + + # Creates a copy of self with the given fields, using self as the template. + sig { params(node_id: Integer, location: Location, flags: Integer, body: T::Array[Node]).returns(StatementsNode) } + def copy(node_id: T.unsafe(nil), location: T.unsafe(nil), flags: T.unsafe(nil), body: T.unsafe(nil)); end + + sig { override.returns(T::Array[::T.nilable(Node)]) } + def deconstruct; end + + sig { params(keys: ::T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, ::T.untyped]) } + def deconstruct_keys(keys); end + + # See `Node#type`. + sig { override.returns(Symbol) } + def type; end + + # See `Node.type`. + sig { override.returns(Symbol) } + def self.type; end + + sig { override.returns(String) } + def inspect; end + + # Returns the `body` attribute. + sig { returns(T::Array[Node]) } + def body; end + + sig { params(other: ::T.untyped).returns(::T.nilable(T::Boolean)) } + def ===(other); 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. + sig { params(source: Source, node_id: Integer, location: Location, flags: Integer, opening_loc: ::T.nilable(Location), content_loc: Location, closing_loc: ::T.nilable(Location), unescaped: String).void } + def initialize(source, node_id, location, flags, opening_loc, content_loc, closing_loc, unescaped); end + + # See Node.accept. + sig { override.params(visitor: Visitor).returns(::T.untyped) } + def accept(visitor); end + + # See Node.child_nodes. + sig { override.returns(T::Array[::T.nilable(Node)]) } + def child_nodes; end + + # See Node.each_child_node. + sig { override.returns(T::Enumerator[Node]) } + sig { override.params(blk: ::T.proc.params(arg0: Node).void).void } + def each_child_node(&blk); end + + # See Node.compact_child_nodes. + sig { override.returns(T::Array[Node]) } + def compact_child_nodes; end + + # See Node.comment_targets. + sig { override.returns(T::Array[::T.any(Node, Location)]) } + def comment_targets; end + + # Creates a copy of self with the given fields, using self as the template. + sig { params(node_id: Integer, location: Location, flags: Integer, opening_loc: ::T.nilable(Location), content_loc: Location, closing_loc: ::T.nilable(Location), unescaped: String).returns(StringNode) } + def copy(node_id: T.unsafe(nil), location: T.unsafe(nil), flags: T.unsafe(nil), opening_loc: T.unsafe(nil), content_loc: T.unsafe(nil), closing_loc: T.unsafe(nil), unescaped: T.unsafe(nil)); end + + sig { override.returns(T::Array[::T.nilable(Node)]) } + def deconstruct; end + + sig { params(keys: ::T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, ::T.untyped]) } + def deconstruct_keys(keys); end + + # See `Node#type`. + sig { override.returns(Symbol) } + def type; end + + # See `Node.type`. + sig { override.returns(Symbol) } + def self.type; end + + sig { override.returns(String) } + def inspect; end + + # internal bytes forced the encoding to UTF-8 + sig { returns(T::Boolean) } + def forced_utf8_encoding?; end + + # internal bytes forced the encoding to binary + sig { returns(T::Boolean) } + def forced_binary_encoding?; end + + # frozen by virtue of a `frozen_string_literal: true` comment or `--enable-frozen-string-literal` + sig { returns(T::Boolean) } + def frozen?; end + + # mutable by virtue of a `frozen_string_literal: false` comment or `--disable-frozen-string-literal` + sig { returns(T::Boolean) } + def mutable?; end + + # Returns the Location represented by `opening_loc`. + sig { returns(::T.nilable(Location)) } + def opening_loc; end + + # Save the opening_loc location using the given saved source so that + # it can be retrieved later. + sig { params(repository: ::T.untyped).returns(::T.nilable(Relocation::Entry)) } + def save_opening_loc(repository); end + + # Returns the Location represented by `content_loc`. + sig { returns(Location) } + def content_loc; end + + # Save the content_loc location using the given saved source so that + # it can be retrieved later. + sig { params(repository: ::T.untyped).returns(Relocation::Entry) } + def save_content_loc(repository); end + + # Returns the Location represented by `closing_loc`. + sig { returns(::T.nilable(Location)) } + def closing_loc; end + + # Save the closing_loc location using the given saved source so that + # it can be retrieved later. + sig { params(repository: ::T.untyped).returns(::T.nilable(Relocation::Entry)) } + def save_closing_loc(repository); end + + # Returns the `unescaped` attribute. + sig { returns(String) } + def unescaped; end + + # Slice the location of opening_loc from the source. + sig { returns(::T.nilable(String)) } + def opening; end + + # Slice the location of content_loc from the source. + sig { returns(String) } + def content; end + + # Slice the location of closing_loc from the source. + sig { returns(::T.nilable(String)) } + def closing; end + + sig { params(other: ::T.untyped).returns(::T.nilable(T::Boolean)) } + def ===(other); 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. + sig { params(source: Source, node_id: Integer, location: Location, flags: Integer, keyword_loc: Location, lparen_loc: ::T.nilable(Location), arguments: ::T.nilable(ArgumentsNode), rparen_loc: ::T.nilable(Location), block: ::T.nilable(::T.any(BlockNode, BlockArgumentNode))).void } + def initialize(source, node_id, location, flags, keyword_loc, lparen_loc, arguments, rparen_loc, block); end + + # See Node.accept. + sig { override.params(visitor: Visitor).returns(::T.untyped) } + def accept(visitor); end + + # See Node.child_nodes. + sig { override.returns(T::Array[::T.nilable(Node)]) } + def child_nodes; end + + # See Node.each_child_node. + sig { override.returns(T::Enumerator[Node]) } + sig { override.params(blk: ::T.proc.params(arg0: Node).void).void } + def each_child_node(&blk); end + + # See Node.compact_child_nodes. + sig { override.returns(T::Array[Node]) } + def compact_child_nodes; end + + # See Node.comment_targets. + sig { override.returns(T::Array[::T.any(Node, Location)]) } + def comment_targets; end + + # Creates a copy of self with the given fields, using self as the template. + sig { params(node_id: Integer, location: Location, flags: Integer, keyword_loc: Location, lparen_loc: ::T.nilable(Location), arguments: ::T.nilable(ArgumentsNode), rparen_loc: ::T.nilable(Location), block: ::T.nilable(::T.any(BlockNode, BlockArgumentNode))).returns(SuperNode) } + def copy(node_id: T.unsafe(nil), location: T.unsafe(nil), flags: T.unsafe(nil), keyword_loc: T.unsafe(nil), lparen_loc: T.unsafe(nil), arguments: T.unsafe(nil), rparen_loc: T.unsafe(nil), block: T.unsafe(nil)); end + + sig { override.returns(T::Array[::T.nilable(Node)]) } + def deconstruct; end + + sig { params(keys: ::T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, ::T.untyped]) } + def deconstruct_keys(keys); end + + # See `Node#type`. + sig { override.returns(Symbol) } + def type; end + + # See `Node.type`. + sig { override.returns(Symbol) } + def self.type; end + + sig { override.returns(String) } + def inspect; end + + # Returns the Location represented by `keyword_loc`. + sig { returns(Location) } + def keyword_loc; end + + # Save the keyword_loc location using the given saved source so that + # it can be retrieved later. + sig { params(repository: ::T.untyped).returns(Relocation::Entry) } + def save_keyword_loc(repository); end + + # Returns the Location represented by `lparen_loc`. + sig { returns(::T.nilable(Location)) } + def lparen_loc; end + + # Save the lparen_loc location using the given saved source so that + # it can be retrieved later. + sig { params(repository: ::T.untyped).returns(::T.nilable(Relocation::Entry)) } + def save_lparen_loc(repository); end + + # Can be only `nil` when there are empty parentheses, like `super()`. + sig { returns(::T.nilable(ArgumentsNode)) } + def arguments; end + + # Returns the Location represented by `rparen_loc`. + sig { returns(::T.nilable(Location)) } + def rparen_loc; end + + # Save the rparen_loc location using the given saved source so that + # it can be retrieved later. + sig { params(repository: ::T.untyped).returns(::T.nilable(Relocation::Entry)) } + def save_rparen_loc(repository); end + + # Returns the `block` attribute. + sig { returns(::T.nilable(::T.any(BlockNode, BlockArgumentNode))) } + def block; end + + # Slice the location of keyword_loc from the source. + sig { returns(String) } + def keyword; end + + # Slice the location of lparen_loc from the source. + sig { returns(::T.nilable(String)) } + def lparen; end + + # Slice the location of rparen_loc from the source. + sig { returns(::T.nilable(String)) } + def rparen; end + + sig { params(other: ::T.untyped).returns(::T.nilable(T::Boolean)) } + def ===(other); 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. + sig { params(source: Source, node_id: Integer, location: Location, flags: Integer, opening_loc: ::T.nilable(Location), value_loc: ::T.nilable(Location), closing_loc: ::T.nilable(Location), unescaped: String).void } + def initialize(source, node_id, location, flags, opening_loc, value_loc, closing_loc, unescaped); end + + # See Node.accept. + sig { override.params(visitor: Visitor).returns(::T.untyped) } + def accept(visitor); end + + # See Node.child_nodes. + sig { override.returns(T::Array[::T.nilable(Node)]) } + def child_nodes; end + + # See Node.each_child_node. + sig { override.returns(T::Enumerator[Node]) } + sig { override.params(blk: ::T.proc.params(arg0: Node).void).void } + def each_child_node(&blk); end + + # See Node.compact_child_nodes. + sig { override.returns(T::Array[Node]) } + def compact_child_nodes; end + + # See Node.comment_targets. + sig { override.returns(T::Array[::T.any(Node, Location)]) } + def comment_targets; end + + # Creates a copy of self with the given fields, using self as the template. + sig { params(node_id: Integer, location: Location, flags: Integer, opening_loc: ::T.nilable(Location), value_loc: ::T.nilable(Location), closing_loc: ::T.nilable(Location), unescaped: String).returns(SymbolNode) } + def copy(node_id: T.unsafe(nil), location: T.unsafe(nil), flags: T.unsafe(nil), opening_loc: T.unsafe(nil), value_loc: T.unsafe(nil), closing_loc: T.unsafe(nil), unescaped: T.unsafe(nil)); end + + sig { override.returns(T::Array[::T.nilable(Node)]) } + def deconstruct; end + + sig { params(keys: ::T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, ::T.untyped]) } + def deconstruct_keys(keys); end + + # See `Node#type`. + sig { override.returns(Symbol) } + def type; end + + # See `Node.type`. + sig { override.returns(Symbol) } + def self.type; end + + sig { override.returns(String) } + def inspect; end + + # internal bytes forced the encoding to UTF-8 + sig { returns(T::Boolean) } + def forced_utf8_encoding?; end + + # internal bytes forced the encoding to binary + sig { returns(T::Boolean) } + def forced_binary_encoding?; end + + # internal bytes forced the encoding to US-ASCII + sig { returns(T::Boolean) } + def forced_us_ascii_encoding?; end + + # Returns the Location represented by `opening_loc`. + sig { returns(::T.nilable(Location)) } + def opening_loc; end + + # Save the opening_loc location using the given saved source so that + # it can be retrieved later. + sig { params(repository: ::T.untyped).returns(::T.nilable(Relocation::Entry)) } + def save_opening_loc(repository); end + + # Returns the Location represented by `value_loc`. + sig { returns(::T.nilable(Location)) } + def value_loc; end + + # Save the value_loc location using the given saved source so that + # it can be retrieved later. + sig { params(repository: ::T.untyped).returns(::T.nilable(Relocation::Entry)) } + def save_value_loc(repository); end + + # Returns the Location represented by `closing_loc`. + sig { returns(::T.nilable(Location)) } + def closing_loc; end + + # Save the closing_loc location using the given saved source so that + # it can be retrieved later. + sig { params(repository: ::T.untyped).returns(::T.nilable(Relocation::Entry)) } + def save_closing_loc(repository); end + + # Returns the `unescaped` attribute. + sig { returns(String) } + def unescaped; end + + # Slice the location of opening_loc from the source. + sig { returns(::T.nilable(String)) } + def opening; end + + # Slice the location of value_loc from the source. + sig { returns(::T.nilable(String)) } + def value; end + + # Slice the location of closing_loc from the source. + sig { returns(::T.nilable(String)) } + def closing; end + + sig { params(other: ::T.untyped).returns(::T.nilable(T::Boolean)) } + def ===(other); end + end + + # Represents the use of the literal `true` keyword. + # + # true + # ^^^^ + class TrueNode < Node + # Initialize a new TrueNode node. + sig { params(source: Source, node_id: Integer, location: Location, flags: Integer).void } + def initialize(source, node_id, location, flags); end + + # See Node.accept. + sig { override.params(visitor: Visitor).returns(::T.untyped) } + def accept(visitor); end + + # See Node.child_nodes. + sig { override.returns(T::Array[::T.nilable(Node)]) } + def child_nodes; end + + # See Node.each_child_node. + sig { override.returns(T::Enumerator[Node]) } + sig { override.params(blk: ::T.proc.params(arg0: Node).void).void } + def each_child_node(&blk); end + + # See Node.compact_child_nodes. + sig { override.returns(T::Array[Node]) } + def compact_child_nodes; end + + # See Node.comment_targets. + sig { override.returns(T::Array[::T.any(Node, Location)]) } + def comment_targets; end + + # Creates a copy of self with the given fields, using self as the template. + sig { params(node_id: Integer, location: Location, flags: Integer).returns(TrueNode) } + def copy(node_id: T.unsafe(nil), location: T.unsafe(nil), flags: T.unsafe(nil)); end + + sig { override.returns(T::Array[::T.nilable(Node)]) } + def deconstruct; end + + sig { params(keys: ::T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, ::T.untyped]) } + def deconstruct_keys(keys); end + + # See `Node#type`. + sig { override.returns(Symbol) } + def type; end + + # See `Node.type`. + sig { override.returns(Symbol) } + def self.type; end + + sig { override.returns(String) } + def inspect; end + + sig { params(other: ::T.untyped).returns(::T.nilable(T::Boolean)) } + def ===(other); end + end + + # Represents the use of the `undef` keyword. + # + # undef :foo, :bar, :baz + # ^^^^^^^^^^^^^^^^^^^^^^ + class UndefNode < Node + # Initialize a new UndefNode node. + sig { params(source: Source, node_id: Integer, location: Location, flags: Integer, names: T::Array[::T.any(SymbolNode, InterpolatedSymbolNode)], keyword_loc: Location).void } + def initialize(source, node_id, location, flags, names, keyword_loc); end + + # See Node.accept. + sig { override.params(visitor: Visitor).returns(::T.untyped) } + def accept(visitor); end + + # See Node.child_nodes. + sig { override.returns(T::Array[::T.nilable(Node)]) } + def child_nodes; end + + # See Node.each_child_node. + sig { override.returns(T::Enumerator[Node]) } + sig { override.params(blk: ::T.proc.params(arg0: Node).void).void } + def each_child_node(&blk); end + + # See Node.compact_child_nodes. + sig { override.returns(T::Array[Node]) } + def compact_child_nodes; end + + # See Node.comment_targets. + sig { override.returns(T::Array[::T.any(Node, Location)]) } + def comment_targets; end + + # Creates a copy of self with the given fields, using self as the template. + sig { params(node_id: Integer, location: Location, flags: Integer, names: T::Array[::T.any(SymbolNode, InterpolatedSymbolNode)], keyword_loc: Location).returns(UndefNode) } + def copy(node_id: T.unsafe(nil), location: T.unsafe(nil), flags: T.unsafe(nil), names: T.unsafe(nil), keyword_loc: T.unsafe(nil)); end + + sig { override.returns(T::Array[::T.nilable(Node)]) } + def deconstruct; end + + sig { params(keys: ::T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, ::T.untyped]) } + def deconstruct_keys(keys); end + + # See `Node#type`. + sig { override.returns(Symbol) } + def type; end + + # See `Node.type`. + sig { override.returns(Symbol) } + def self.type; end + + sig { override.returns(String) } + def inspect; end + + # Returns the `names` attribute. + sig { returns(T::Array[::T.any(SymbolNode, InterpolatedSymbolNode)]) } + def names; end + + # Returns the Location represented by `keyword_loc`. + sig { returns(Location) } + def keyword_loc; end + + # Save the keyword_loc location using the given saved source so that + # it can be retrieved later. + sig { params(repository: ::T.untyped).returns(Relocation::Entry) } + def save_keyword_loc(repository); end + + # Slice the location of keyword_loc from the source. + sig { returns(String) } + def keyword; end + + sig { params(other: ::T.untyped).returns(::T.nilable(T::Boolean)) } + def ===(other); 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. + sig { params(source: Source, node_id: Integer, location: Location, flags: Integer, keyword_loc: Location, predicate: Node, then_keyword_loc: ::T.nilable(Location), statements: ::T.nilable(StatementsNode), else_clause: ::T.nilable(ElseNode), end_keyword_loc: ::T.nilable(Location)).void } + def initialize(source, node_id, location, flags, keyword_loc, predicate, then_keyword_loc, statements, else_clause, end_keyword_loc); end + + # See Node.accept. + sig { override.params(visitor: Visitor).returns(::T.untyped) } + def accept(visitor); end + + # See Node.child_nodes. + sig { override.returns(T::Array[::T.nilable(Node)]) } + def child_nodes; end + + # See Node.each_child_node. + sig { override.returns(T::Enumerator[Node]) } + sig { override.params(blk: ::T.proc.params(arg0: Node).void).void } + def each_child_node(&blk); end + + # See Node.compact_child_nodes. + sig { override.returns(T::Array[Node]) } + def compact_child_nodes; end + + # See Node.comment_targets. + sig { override.returns(T::Array[::T.any(Node, Location)]) } + def comment_targets; end + + # Creates a copy of self with the given fields, using self as the template. + sig { params(node_id: Integer, location: Location, flags: Integer, keyword_loc: Location, predicate: Node, then_keyword_loc: ::T.nilable(Location), statements: ::T.nilable(StatementsNode), else_clause: ::T.nilable(ElseNode), end_keyword_loc: ::T.nilable(Location)).returns(UnlessNode) } + def copy(node_id: T.unsafe(nil), location: T.unsafe(nil), flags: T.unsafe(nil), keyword_loc: T.unsafe(nil), predicate: T.unsafe(nil), then_keyword_loc: T.unsafe(nil), statements: T.unsafe(nil), else_clause: T.unsafe(nil), end_keyword_loc: T.unsafe(nil)); end + + sig { override.returns(T::Array[::T.nilable(Node)]) } + def deconstruct; end + + sig { params(keys: ::T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, ::T.untyped]) } + def deconstruct_keys(keys); end + + # See `Node#type`. + sig { override.returns(Symbol) } + def type; end + + # See `Node.type`. + sig { override.returns(Symbol) } + def self.type; end + + sig { override.returns(String) } + def inspect; end + + # The Location of the `unless` keyword. + # + # unless cond then bar end + # ^^^^^^ + # + # bar unless cond + # ^^^^^^ + sig { returns(Location) } + def keyword_loc; end + + # Save the keyword_loc location using the given saved source so that + # it can be retrieved later. + sig { params(repository: ::T.untyped).returns(Relocation::Entry) } + def save_keyword_loc(repository); 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 + # ^^^^ + sig { returns(Node) } + def predicate; end + + # The Location of the `then` keyword, if present. + # + # unless cond then bar end + # ^^^^ + sig { returns(::T.nilable(Location)) } + def then_keyword_loc; end + + # Save the then_keyword_loc location using the given saved source so that + # it can be retrieved later. + sig { params(repository: ::T.untyped).returns(::T.nilable(Relocation::Entry)) } + def save_then_keyword_loc(repository); 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 + # ^^^ + sig { returns(::T.nilable(StatementsNode)) } + def statements; end + + # The else clause of the unless expression, if present. + # + # unless cond then bar else baz end + # ^^^^^^^^^^^^ + sig { returns(::T.nilable(ElseNode)) } + def else_clause; end + + # The Location of the `end` keyword, if present. + # + # unless cond then bar end + # ^^^ + sig { returns(::T.nilable(Location)) } + def end_keyword_loc; end + + # Save the end_keyword_loc location using the given saved source so that + # it can be retrieved later. + sig { params(repository: ::T.untyped).returns(::T.nilable(Relocation::Entry)) } + def save_end_keyword_loc(repository); end + + # Slice the location of keyword_loc from the source. + sig { returns(String) } + def keyword; end + + # Slice the location of then_keyword_loc from the source. + sig { returns(::T.nilable(String)) } + def then_keyword; end + + # Slice the location of end_keyword_loc from the source. + sig { returns(::T.nilable(String)) } + def end_keyword; end + + sig { params(other: ::T.untyped).returns(::T.nilable(T::Boolean)) } + def ===(other); 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. + sig { params(source: Source, node_id: Integer, location: Location, flags: Integer, keyword_loc: Location, do_keyword_loc: ::T.nilable(Location), closing_loc: ::T.nilable(Location), predicate: Node, statements: ::T.nilable(StatementsNode)).void } + def initialize(source, node_id, location, flags, keyword_loc, do_keyword_loc, closing_loc, predicate, statements); end + + # See Node.accept. + sig { override.params(visitor: Visitor).returns(::T.untyped) } + def accept(visitor); end + + # See Node.child_nodes. + sig { override.returns(T::Array[::T.nilable(Node)]) } + def child_nodes; end + + # See Node.each_child_node. + sig { override.returns(T::Enumerator[Node]) } + sig { override.params(blk: ::T.proc.params(arg0: Node).void).void } + def each_child_node(&blk); end + + # See Node.compact_child_nodes. + sig { override.returns(T::Array[Node]) } + def compact_child_nodes; end + + # See Node.comment_targets. + sig { override.returns(T::Array[::T.any(Node, Location)]) } + def comment_targets; end + + # Creates a copy of self with the given fields, using self as the template. + sig { params(node_id: Integer, location: Location, flags: Integer, keyword_loc: Location, do_keyword_loc: ::T.nilable(Location), closing_loc: ::T.nilable(Location), predicate: Node, statements: ::T.nilable(StatementsNode)).returns(UntilNode) } + def copy(node_id: T.unsafe(nil), location: T.unsafe(nil), flags: T.unsafe(nil), keyword_loc: T.unsafe(nil), do_keyword_loc: T.unsafe(nil), closing_loc: T.unsafe(nil), predicate: T.unsafe(nil), statements: T.unsafe(nil)); end + + sig { override.returns(T::Array[::T.nilable(Node)]) } + def deconstruct; end + + sig { params(keys: ::T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, ::T.untyped]) } + def deconstruct_keys(keys); end + + # See `Node#type`. + sig { override.returns(Symbol) } + def type; end + + # See `Node.type`. + sig { override.returns(Symbol) } + def self.type; end + + sig { override.returns(String) } + def inspect; end + + # a loop after a begin statement, so the body is executed first before the condition + sig { returns(T::Boolean) } + def begin_modifier?; end + + # Returns the Location represented by `keyword_loc`. + sig { returns(Location) } + def keyword_loc; end + + # Save the keyword_loc location using the given saved source so that + # it can be retrieved later. + sig { params(repository: ::T.untyped).returns(Relocation::Entry) } + def save_keyword_loc(repository); end + + # Returns the Location represented by `do_keyword_loc`. + sig { returns(::T.nilable(Location)) } + def do_keyword_loc; end + + # Save the do_keyword_loc location using the given saved source so that + # it can be retrieved later. + sig { params(repository: ::T.untyped).returns(::T.nilable(Relocation::Entry)) } + def save_do_keyword_loc(repository); end + + # Returns the Location represented by `closing_loc`. + sig { returns(::T.nilable(Location)) } + def closing_loc; end + + # Save the closing_loc location using the given saved source so that + # it can be retrieved later. + sig { params(repository: ::T.untyped).returns(::T.nilable(Relocation::Entry)) } + def save_closing_loc(repository); end + + # Returns the `predicate` attribute. + sig { returns(Node) } + def predicate; end + + # Returns the `statements` attribute. + sig { returns(::T.nilable(StatementsNode)) } + def statements; end + + # Slice the location of keyword_loc from the source. + sig { returns(String) } + def keyword; end + + # Slice the location of do_keyword_loc from the source. + sig { returns(::T.nilable(String)) } + def do_keyword; end + + # Slice the location of closing_loc from the source. + sig { returns(::T.nilable(String)) } + def closing; end + + sig { params(other: ::T.untyped).returns(::T.nilable(T::Boolean)) } + def ===(other); 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. + sig { params(source: Source, node_id: Integer, location: Location, flags: Integer, keyword_loc: Location, conditions: T::Array[Node], then_keyword_loc: ::T.nilable(Location), statements: ::T.nilable(StatementsNode)).void } + def initialize(source, node_id, location, flags, keyword_loc, conditions, then_keyword_loc, statements); end + + # See Node.accept. + sig { override.params(visitor: Visitor).returns(::T.untyped) } + def accept(visitor); end + + # See Node.child_nodes. + sig { override.returns(T::Array[::T.nilable(Node)]) } + def child_nodes; end + + # See Node.each_child_node. + sig { override.returns(T::Enumerator[Node]) } + sig { override.params(blk: ::T.proc.params(arg0: Node).void).void } + def each_child_node(&blk); end + + # See Node.compact_child_nodes. + sig { override.returns(T::Array[Node]) } + def compact_child_nodes; end + + # See Node.comment_targets. + sig { override.returns(T::Array[::T.any(Node, Location)]) } + def comment_targets; end + + # Creates a copy of self with the given fields, using self as the template. + sig { params(node_id: Integer, location: Location, flags: Integer, keyword_loc: Location, conditions: T::Array[Node], then_keyword_loc: ::T.nilable(Location), statements: ::T.nilable(StatementsNode)).returns(WhenNode) } + def copy(node_id: T.unsafe(nil), location: T.unsafe(nil), flags: T.unsafe(nil), keyword_loc: T.unsafe(nil), conditions: T.unsafe(nil), then_keyword_loc: T.unsafe(nil), statements: T.unsafe(nil)); end + + sig { override.returns(T::Array[::T.nilable(Node)]) } + def deconstruct; end + + sig { params(keys: ::T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, ::T.untyped]) } + def deconstruct_keys(keys); end + + # See `Node#type`. + sig { override.returns(Symbol) } + def type; end + + # See `Node.type`. + sig { override.returns(Symbol) } + def self.type; end + + sig { override.returns(String) } + def inspect; end + + # Returns the Location represented by `keyword_loc`. + sig { returns(Location) } + def keyword_loc; end + + # Save the keyword_loc location using the given saved source so that + # it can be retrieved later. + sig { params(repository: ::T.untyped).returns(Relocation::Entry) } + def save_keyword_loc(repository); end + + # Returns the `conditions` attribute. + sig { returns(T::Array[Node]) } + def conditions; end + + # Returns the Location represented by `then_keyword_loc`. + sig { returns(::T.nilable(Location)) } + def then_keyword_loc; end + + # Save the then_keyword_loc location using the given saved source so that + # it can be retrieved later. + sig { params(repository: ::T.untyped).returns(::T.nilable(Relocation::Entry)) } + def save_then_keyword_loc(repository); end + + # Returns the `statements` attribute. + sig { returns(::T.nilable(StatementsNode)) } + def statements; end + + # Slice the location of keyword_loc from the source. + sig { returns(String) } + def keyword; end + + # Slice the location of then_keyword_loc from the source. + sig { returns(::T.nilable(String)) } + def then_keyword; end + + sig { params(other: ::T.untyped).returns(::T.nilable(T::Boolean)) } + def ===(other); 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. + sig { params(source: Source, node_id: Integer, location: Location, flags: Integer, keyword_loc: Location, do_keyword_loc: ::T.nilable(Location), closing_loc: ::T.nilable(Location), predicate: Node, statements: ::T.nilable(StatementsNode)).void } + def initialize(source, node_id, location, flags, keyword_loc, do_keyword_loc, closing_loc, predicate, statements); end + + # See Node.accept. + sig { override.params(visitor: Visitor).returns(::T.untyped) } + def accept(visitor); end + + # See Node.child_nodes. + sig { override.returns(T::Array[::T.nilable(Node)]) } + def child_nodes; end + + # See Node.each_child_node. + sig { override.returns(T::Enumerator[Node]) } + sig { override.params(blk: ::T.proc.params(arg0: Node).void).void } + def each_child_node(&blk); end + + # See Node.compact_child_nodes. + sig { override.returns(T::Array[Node]) } + def compact_child_nodes; end + + # See Node.comment_targets. + sig { override.returns(T::Array[::T.any(Node, Location)]) } + def comment_targets; end + + # Creates a copy of self with the given fields, using self as the template. + sig { params(node_id: Integer, location: Location, flags: Integer, keyword_loc: Location, do_keyword_loc: ::T.nilable(Location), closing_loc: ::T.nilable(Location), predicate: Node, statements: ::T.nilable(StatementsNode)).returns(WhileNode) } + def copy(node_id: T.unsafe(nil), location: T.unsafe(nil), flags: T.unsafe(nil), keyword_loc: T.unsafe(nil), do_keyword_loc: T.unsafe(nil), closing_loc: T.unsafe(nil), predicate: T.unsafe(nil), statements: T.unsafe(nil)); end + + sig { override.returns(T::Array[::T.nilable(Node)]) } + def deconstruct; end + + sig { params(keys: ::T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, ::T.untyped]) } + def deconstruct_keys(keys); end + + # See `Node#type`. + sig { override.returns(Symbol) } + def type; end + + # See `Node.type`. + sig { override.returns(Symbol) } + def self.type; end + + sig { override.returns(String) } + def inspect; end + + # a loop after a begin statement, so the body is executed first before the condition + sig { returns(T::Boolean) } + def begin_modifier?; end + + # Returns the Location represented by `keyword_loc`. + sig { returns(Location) } + def keyword_loc; end + + # Save the keyword_loc location using the given saved source so that + # it can be retrieved later. + sig { params(repository: ::T.untyped).returns(Relocation::Entry) } + def save_keyword_loc(repository); end + + # Returns the Location represented by `do_keyword_loc`. + sig { returns(::T.nilable(Location)) } + def do_keyword_loc; end + + # Save the do_keyword_loc location using the given saved source so that + # it can be retrieved later. + sig { params(repository: ::T.untyped).returns(::T.nilable(Relocation::Entry)) } + def save_do_keyword_loc(repository); end + + # Returns the Location represented by `closing_loc`. + sig { returns(::T.nilable(Location)) } + def closing_loc; end + + # Save the closing_loc location using the given saved source so that + # it can be retrieved later. + sig { params(repository: ::T.untyped).returns(::T.nilable(Relocation::Entry)) } + def save_closing_loc(repository); end + + # Returns the `predicate` attribute. + sig { returns(Node) } + def predicate; end + + # Returns the `statements` attribute. + sig { returns(::T.nilable(StatementsNode)) } + def statements; end + + # Slice the location of keyword_loc from the source. + sig { returns(String) } + def keyword; end + + # Slice the location of do_keyword_loc from the source. + sig { returns(::T.nilable(String)) } + def do_keyword; end + + # Slice the location of closing_loc from the source. + sig { returns(::T.nilable(String)) } + def closing; end + + sig { params(other: ::T.untyped).returns(::T.nilable(T::Boolean)) } + def ===(other); end + end + + # Represents an xstring literal with no interpolation. + # + # `foo` + # ^^^^^ + class XStringNode < Node + # Initialize a new XStringNode node. + sig { params(source: Source, node_id: Integer, location: Location, flags: Integer, opening_loc: Location, content_loc: Location, closing_loc: Location, unescaped: String).void } + def initialize(source, node_id, location, flags, opening_loc, content_loc, closing_loc, unescaped); end + + # See Node.accept. + sig { override.params(visitor: Visitor).returns(::T.untyped) } + def accept(visitor); end + + # See Node.child_nodes. + sig { override.returns(T::Array[::T.nilable(Node)]) } + def child_nodes; end + + # See Node.each_child_node. + sig { override.returns(T::Enumerator[Node]) } + sig { override.params(blk: ::T.proc.params(arg0: Node).void).void } + def each_child_node(&blk); end + + # See Node.compact_child_nodes. + sig { override.returns(T::Array[Node]) } + def compact_child_nodes; end + + # See Node.comment_targets. + sig { override.returns(T::Array[::T.any(Node, Location)]) } + def comment_targets; end + + # Creates a copy of self with the given fields, using self as the template. + sig { params(node_id: Integer, location: Location, flags: Integer, opening_loc: Location, content_loc: Location, closing_loc: Location, unescaped: String).returns(XStringNode) } + def copy(node_id: T.unsafe(nil), location: T.unsafe(nil), flags: T.unsafe(nil), opening_loc: T.unsafe(nil), content_loc: T.unsafe(nil), closing_loc: T.unsafe(nil), unescaped: T.unsafe(nil)); end + + sig { override.returns(T::Array[::T.nilable(Node)]) } + def deconstruct; end + + sig { params(keys: ::T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, ::T.untyped]) } + def deconstruct_keys(keys); end + + # See `Node#type`. + sig { override.returns(Symbol) } + def type; end + + # See `Node.type`. + sig { override.returns(Symbol) } + def self.type; end + + sig { override.returns(String) } + def inspect; end + + # internal bytes forced the encoding to UTF-8 + sig { returns(T::Boolean) } + def forced_utf8_encoding?; end + + # internal bytes forced the encoding to binary + sig { returns(T::Boolean) } + def forced_binary_encoding?; end + + # Returns the Location represented by `opening_loc`. + sig { returns(Location) } + def opening_loc; end + + # Save the opening_loc location using the given saved source so that + # it can be retrieved later. + sig { params(repository: ::T.untyped).returns(Relocation::Entry) } + def save_opening_loc(repository); end + + # Returns the Location represented by `content_loc`. + sig { returns(Location) } + def content_loc; end + + # Save the content_loc location using the given saved source so that + # it can be retrieved later. + sig { params(repository: ::T.untyped).returns(Relocation::Entry) } + def save_content_loc(repository); end + + # Returns the Location represented by `closing_loc`. + sig { returns(Location) } + def closing_loc; end + + # Save the closing_loc location using the given saved source so that + # it can be retrieved later. + sig { params(repository: ::T.untyped).returns(Relocation::Entry) } + def save_closing_loc(repository); end + + # Returns the `unescaped` attribute. + sig { returns(String) } + def unescaped; end + + # Slice the location of opening_loc from the source. + sig { returns(String) } + def opening; end + + # Slice the location of content_loc from the source. + sig { returns(String) } + def content; end + + # Slice the location of closing_loc from the source. + sig { returns(String) } + def closing; end + + sig { params(other: ::T.untyped).returns(::T.nilable(T::Boolean)) } + def ===(other); end + end + + # Represents the use of the `yield` keyword. + # + # yield 1 + # ^^^^^^^ + class YieldNode < Node + # Initialize a new YieldNode node. + sig { params(source: Source, node_id: Integer, location: Location, flags: Integer, keyword_loc: Location, lparen_loc: ::T.nilable(Location), arguments: ::T.nilable(ArgumentsNode), rparen_loc: ::T.nilable(Location)).void } + def initialize(source, node_id, location, flags, keyword_loc, lparen_loc, arguments, rparen_loc); end + + # See Node.accept. + sig { override.params(visitor: Visitor).returns(::T.untyped) } + def accept(visitor); end + + # See Node.child_nodes. + sig { override.returns(T::Array[::T.nilable(Node)]) } + def child_nodes; end + + # See Node.each_child_node. + sig { override.returns(T::Enumerator[Node]) } + sig { override.params(blk: ::T.proc.params(arg0: Node).void).void } + def each_child_node(&blk); end + + # See Node.compact_child_nodes. + sig { override.returns(T::Array[Node]) } + def compact_child_nodes; end + + # See Node.comment_targets. + sig { override.returns(T::Array[::T.any(Node, Location)]) } + def comment_targets; end + + # Creates a copy of self with the given fields, using self as the template. + sig { params(node_id: Integer, location: Location, flags: Integer, keyword_loc: Location, lparen_loc: ::T.nilable(Location), arguments: ::T.nilable(ArgumentsNode), rparen_loc: ::T.nilable(Location)).returns(YieldNode) } + def copy(node_id: T.unsafe(nil), location: T.unsafe(nil), flags: T.unsafe(nil), keyword_loc: T.unsafe(nil), lparen_loc: T.unsafe(nil), arguments: T.unsafe(nil), rparen_loc: T.unsafe(nil)); end + + sig { override.returns(T::Array[::T.nilable(Node)]) } + def deconstruct; end + + sig { params(keys: ::T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, ::T.untyped]) } + def deconstruct_keys(keys); end + + # See `Node#type`. + sig { override.returns(Symbol) } + def type; end + + # See `Node.type`. + sig { override.returns(Symbol) } + def self.type; end + + sig { override.returns(String) } + def inspect; end + + # Returns the Location represented by `keyword_loc`. + sig { returns(Location) } + def keyword_loc; end + + # Save the keyword_loc location using the given saved source so that + # it can be retrieved later. + sig { params(repository: ::T.untyped).returns(Relocation::Entry) } + def save_keyword_loc(repository); end + + # Returns the Location represented by `lparen_loc`. + sig { returns(::T.nilable(Location)) } + def lparen_loc; end + + # Save the lparen_loc location using the given saved source so that + # it can be retrieved later. + sig { params(repository: ::T.untyped).returns(::T.nilable(Relocation::Entry)) } + def save_lparen_loc(repository); end + + # Returns the `arguments` attribute. + sig { returns(::T.nilable(ArgumentsNode)) } + def arguments; end + + # Returns the Location represented by `rparen_loc`. + sig { returns(::T.nilable(Location)) } + def rparen_loc; end + + # Save the rparen_loc location using the given saved source so that + # it can be retrieved later. + sig { params(repository: ::T.untyped).returns(::T.nilable(Relocation::Entry)) } + def save_rparen_loc(repository); end + + # Slice the location of keyword_loc from the source. + sig { returns(String) } + def keyword; end + + # Slice the location of lparen_loc from the source. + sig { returns(::T.nilable(String)) } + def lparen; end + + # Slice the location of rparen_loc from the source. + sig { returns(::T.nilable(String)) } + def rparen; end + + sig { params(other: ::T.untyped).returns(::T.nilable(T::Boolean)) } + def ===(other); end + end + + # Flags for arguments nodes. + module ArgumentsNodeFlags + # if the arguments contain forwarding + CONTAINS_FORWARDING = T.let(nil, ::T.untyped) + + # if the arguments contain keywords + CONTAINS_KEYWORDS = T.let(nil, ::T.untyped) + + # if the arguments contain a keyword splat + CONTAINS_KEYWORD_SPLAT = T.let(nil, ::T.untyped) + + # if the arguments contain a splat + CONTAINS_SPLAT = T.let(nil, ::T.untyped) + + # if the arguments contain multiple splats + CONTAINS_MULTIPLE_SPLATS = T.let(nil, ::T.untyped) + end + + # Flags for array nodes. + module ArrayNodeFlags + # if array contains splat nodes + CONTAINS_SPLAT = T.let(nil, ::T.untyped) + end + + # Flags for call nodes. + module CallNodeFlags + # &. operator + SAFE_NAVIGATION = T.let(nil, ::T.untyped) + + # a call that could have been a local variable + VARIABLE_CALL = T.let(nil, ::T.untyped) + + # a call that is an attribute write, so the value being written should be returned + ATTRIBUTE_WRITE = T.let(nil, ::T.untyped) + + # a call that ignores method visibility + IGNORE_VISIBILITY = T.let(nil, ::T.untyped) + end + + # Flags for nodes that have unescaped content. + module EncodingFlags + # internal bytes forced the encoding to UTF-8 + FORCED_UTF8_ENCODING = T.let(nil, ::T.untyped) + + # internal bytes forced the encoding to binary + FORCED_BINARY_ENCODING = T.let(nil, ::T.untyped) + end + + # Flags for integer nodes that correspond to the base of the integer. + module IntegerBaseFlags + # 0b prefix + BINARY = T.let(nil, ::T.untyped) + + # 0d or no prefix + DECIMAL = T.let(nil, ::T.untyped) + + # 0o or 0 prefix + OCTAL = T.let(nil, ::T.untyped) + + # 0x prefix + HEXADECIMAL = T.let(nil, ::T.untyped) + 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 = T.let(nil, ::T.untyped) + + # 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(nil, ::T.untyped) + 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 = T.let(nil, ::T.untyped) + 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 = T.let(nil, ::T.untyped) + end + + # Flags for parameter nodes. + module ParameterFlags + # a parameter name that has been repeated in the method signature + REPEATED_PARAMETER = T.let(nil, ::T.untyped) + end + + # Flags for parentheses nodes. + module ParenthesesNodeFlags + # parentheses that contain multiple potentially void statements + MULTIPLE_STATEMENTS = T.let(nil, ::T.untyped) + end + + # Flags for range and flip-flop nodes. + module RangeFlags + # ... operator + EXCLUDE_END = T.let(nil, ::T.untyped) + end + + # Flags for regular expression and match last line nodes. + module RegularExpressionFlags + # i - ignores the case of characters when matching + IGNORE_CASE = T.let(nil, ::T.untyped) + + # x - ignores whitespace and allows comments in regular expressions + EXTENDED = T.let(nil, ::T.untyped) + + # m - allows $ to match the end of lines within strings + MULTI_LINE = T.let(nil, ::T.untyped) + + # o - only interpolates values into the regular expression once + ONCE = T.let(nil, ::T.untyped) + + # e - forces the EUC-JP encoding + EUC_JP = T.let(nil, ::T.untyped) + + # n - forces the ASCII-8BIT encoding + ASCII_8BIT = T.let(nil, ::T.untyped) + + # s - forces the Windows-31J encoding + WINDOWS_31J = T.let(nil, ::T.untyped) + + # u - forces the UTF-8 encoding + UTF_8 = T.let(nil, ::T.untyped) + + # internal bytes forced the encoding to UTF-8 + FORCED_UTF8_ENCODING = T.let(nil, ::T.untyped) + + # internal bytes forced the encoding to binary + FORCED_BINARY_ENCODING = T.let(nil, ::T.untyped) + + # internal bytes forced the encoding to US-ASCII + FORCED_US_ASCII_ENCODING = T.let(nil, ::T.untyped) + end + + # Flags for shareable constant nodes. + module ShareableConstantNodeFlags + # constant writes that should be modified with shareable constant value literal + LITERAL = T.let(nil, ::T.untyped) + + # constant writes that should be modified with shareable constant value experimental everything + EXPERIMENTAL_EVERYTHING = T.let(nil, ::T.untyped) + + # constant writes that should be modified with shareable constant value experimental copy + EXPERIMENTAL_COPY = T.let(nil, ::T.untyped) + end + + # Flags for string nodes. + module StringFlags + # internal bytes forced the encoding to UTF-8 + FORCED_UTF8_ENCODING = T.let(nil, ::T.untyped) + + # internal bytes forced the encoding to binary + FORCED_BINARY_ENCODING = T.let(nil, ::T.untyped) + + # frozen by virtue of a `frozen_string_literal: true` comment or `--enable-frozen-string-literal` + FROZEN = T.let(nil, ::T.untyped) + + # mutable by virtue of a `frozen_string_literal: false` comment or `--disable-frozen-string-literal` + MUTABLE = T.let(nil, ::T.untyped) + end + + # Flags for symbol nodes. + module SymbolFlags + # internal bytes forced the encoding to UTF-8 + FORCED_UTF8_ENCODING = T.let(nil, ::T.untyped) + + # internal bytes forced the encoding to binary + FORCED_BINARY_ENCODING = T.let(nil, ::T.untyped) + + # internal bytes forced the encoding to US-ASCII + FORCED_US_ASCII_ENCODING = T.let(nil, ::T.untyped) + 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 = T.let(nil, 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(nil, Integer) + end +end diff --git a/rbi/generated/prism/node_ext.rbi b/rbi/generated/prism/node_ext.rbi new file mode 100644 index 0000000000..3177dca76f --- /dev/null +++ b/rbi/generated/prism/node_ext.rbi @@ -0,0 +1,185 @@ +# typed: true + +module Prism + class Node + abstract! + + sig { params(replacements: String).void } + def deprecated(*replacements); end + end + + module RegularExpressionOptions + # Returns a numeric value that represents the flags that were used to create + # the regular expression. + sig { params(flags: Integer).returns(Integer) } + def self.options(flags); end + end + + class InterpolatedMatchLastLineNode < Node + # Returns a numeric value that represents the flags that were used to create + # the regular expression. + sig { returns(Integer) } + def options; end + end + + class InterpolatedRegularExpressionNode < Node + # Returns a numeric value that represents the flags that were used to create + # the regular expression. + sig { returns(Integer) } + def options; end + end + + class MatchLastLineNode < Node + # Returns a numeric value that represents the flags that were used to create + # the regular expression. + sig { returns(Integer) } + def options; end + end + + class RegularExpressionNode < Node + # Returns a numeric value that represents the flags that were used to create + # the regular expression. + sig { returns(Integer) } + def options; end + end + + module HeredocQuery + # Returns true if this node was represented as a heredoc in the source code. + sig { params(opening: ::T.nilable(String)).returns(::T.nilable(T::Boolean)) } + def self.heredoc?(opening); end + end + + class InterpolatedStringNode < Node + # Returns true if this node was represented as a heredoc in the source code. + sig { returns(::T.nilable(T::Boolean)) } + def heredoc?; end + end + + class InterpolatedXStringNode < Node + # Returns true if this node was represented as a heredoc in the source code. + sig { returns(::T.nilable(T::Boolean)) } + def heredoc?; end + end + + class StringNode < Node + # Returns true if this node was represented as a heredoc in the source code. + sig { returns(::T.nilable(T::Boolean)) } + def heredoc?; end + + # Occasionally it's helpful to treat a string as if it were interpolated so + # that there's a consistent interface for working with strings. + sig { returns(InterpolatedStringNode) } + def to_interpolated; end + end + + class XStringNode < Node + # Returns true if this node was represented as a heredoc in the source code. + sig { returns(::T.nilable(T::Boolean)) } + def heredoc?; end + + # Occasionally it's helpful to treat a string as if it were interpolated so + # that there's a consistent interface for working with strings. + sig { returns(InterpolatedXStringNode) } + def to_interpolated; end + end + + class ImaginaryNode < Node + # Returns the value of the node as a Ruby Complex. + sig { returns(Complex) } + def value; end + end + + class RationalNode < Node + # Returns the value of the node as a Ruby Rational. + sig { returns(Rational) } + def value; end + end + + class ConstantReadNode < Node + # Returns the list of parts for the full name of this constant. + # For example: [:Foo] + sig { returns(T::Array[Symbol]) } + def full_name_parts; end + + # Returns the full name of this constant. For example: "Foo" + sig { returns(String) } + def full_name; end + end + + class ConstantWriteNode < Node + # Returns the list of parts for the full name of this constant. + # For example: [:Foo] + sig { returns(T::Array[Symbol]) } + def full_name_parts; end + + # Returns the full name of this constant. For example: "Foo" + sig { returns(String) } + def full_name; 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] + sig { returns(T::Array[Symbol]) } + def full_name_parts; end + + # Returns the full name of this constant path. For example: "Foo::Bar" + sig { returns(String) } + def full_name; end + end + + class ConstantPathTargetNode < Node + # Returns the list of parts for the full name of this constant path. + # For example: [:Foo, :Bar] + sig { returns(T::Array[Symbol]) } + def full_name_parts; end + + # Returns the full name of this constant path. For example: "Foo::Bar" + sig { returns(String) } + def full_name; end + end + + class ConstantTargetNode < Node + # Returns the list of parts for the full name of this constant. + # For example: [:Foo] + sig { returns(T::Array[Symbol]) } + def full_name_parts; end + + # Returns the full name of this constant. For example: "Foo" + sig { returns(String) } + def full_name; end + end + + class ParametersNode < Node + # Mirrors the Method#parameters method. + sig { returns(T::Array[::T.any([Symbol, Symbol], [Symbol])]) } + def signature; 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. + sig { returns(::T.nilable(Location)) } + def full_message_loc; end + end +end diff --git a/rbi/generated/prism/node_find.rbi b/rbi/generated/prism/node_find.rbi new file mode 100644 index 0000000000..17afe9cc40 --- /dev/null +++ b/rbi/generated/prism/node_find.rbi @@ -0,0 +1,71 @@ +# typed: true + +module Prism + # Finds the Prism AST node corresponding to a given Method, UnboundMethod, + # Proc, or Thread::Backtrace::Location. On CRuby, uses node_id from the + # instruction sequence for an exact match. On other implementations, falls + # back to best-effort matching by source location line number. + # + # This module is autoloaded so that programs that don't use Prism.find don't + # pay for its definition. + module NodeFind + # Find the node for the given callable or backtrace location. + sig { params(callable: ::T.any(Method, UnboundMethod, Proc, Thread::Backtrace::Location), rubyvm: T::Boolean).returns(::T.nilable(Node)) } + def self.find(callable, rubyvm); end + + # Base class that handles parsing a file. + class Find + # Parse the given file path, returning a ParseResult or nil. + sig { params(file: ::T.nilable(String)).returns(::T.nilable(ParseResult)) } + private def parse_file(file); end + end + + # Finds the AST node for a Method, UnboundMethod, or Proc using the node_id + # from the instruction sequence. + class RubyVMCallableFind < Find + # Find the node for the given callable using the ISeq node_id. + sig { params(callable: ::T.any(Method, UnboundMethod, Proc)).returns(::T.nilable(Node)) } + def find(callable); end + end + + # Finds the AST node for a Thread::Backtrace::Location using the node_id + # from the backtrace location. + class RubyVMBacktraceLocationFind < Find + # Find the node for the given backtrace location using node_id. + sig { params(location: Thread::Backtrace::Location).returns(::T.nilable(Node)) } + def find(location); end + end + + # Finds the AST node for a Method or UnboundMethod using best-effort line + # matching. Used on non-CRuby implementations. + class LineMethodFind < Find + # Find the node for the given method by matching on name and line. + sig { params(callable: ::T.any(Method, UnboundMethod)).returns(::T.nilable(Node)) } + def find(callable); end + end + + # Finds the AST node for a lambda using best-effort line matching. Used + # on non-CRuby implementations. + class LineLambdaFind < Find + # Find the node for the given lambda by matching on line. + sig { params(callable: Proc).returns(::T.nilable(Node)) } + def find(callable); end + end + + # Finds the AST node for a non-lambda Proc using best-effort line + # matching. Used on non-CRuby implementations. + class LineProcFind < Find + # Find the node for the given proc by matching on line. + sig { params(callable: Proc).returns(::T.nilable(Node)) } + def find(callable); end + end + + # Finds the AST node for a Thread::Backtrace::Location using best-effort + # line matching. Used on non-CRuby implementations. + class LineBacktraceLocationFind < Find + # Find the node for the given backtrace location by matching on line. + sig { params(location: Thread::Backtrace::Location).returns(::T.nilable(Node)) } + def find(location); end + end + end +end diff --git a/rbi/generated/prism/parse_result.rbi b/rbi/generated/prism/parse_result.rbi new file mode 100644 index 0000000000..4d065b5be1 --- /dev/null +++ b/rbi/generated/prism/parse_result.rbi @@ -0,0 +1,743 @@ +# typed: true + +module Prism + # This represents a source of Ruby code that has been parsed. It is used in + # conjunction with locations to allow them to resolve line numbers and source + # ranges. + class Source + # 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. + # + # Note that if you are calling this method manually, you will need to supply + # the start_line and offsets parameters. start_line is the line number that + # the source starts on, which is typically 1 but can be different if this + # source is a subset of a larger source or if this is an eval. offsets is an + # array of byte offsets for the start of each line in the source code, which + # can be calculated by iterating through the source code and recording the + # byte offset whenever a newline character is encountered. The first + # element is always 0 to mark the first line. + sig { params(source: String, start_line: Integer, offsets: T::Array[Integer]).returns(Source) } + def self.for(source, start_line, offsets); end + + # The source code that this source object represents. + sig { returns(String) } + attr_reader :source + + # The line number where this source starts. + sig { returns(Integer) } + attr_reader :start_line + + # The list of newline byte offsets in the source code. When initialized from + # the C extension, this may be a packed binary string of uint32_t values + # that is lazily unpacked on first access. + sig { returns(T::Array[Integer]) } + def offsets; end + + # Create a new source object with the given source code. The offsets + # parameter can be either an Array of Integer byte offsets or a packed + # binary string of uint32_t values (from the C extension). + sig { params(source: String, start_line: Integer, offsets: ::T.any(T::Array[Integer], String)).void } + def initialize(source, start_line, offsets); end + + # Replace the value of start_line with the given value. + sig { params(start_line: Integer).void } + def replace_start_line(start_line); end + + # Replace the value of offsets with the given value. + sig { params(offsets: T::Array[Integer]).void } + def replace_offsets(offsets); end + + # Returns the encoding of the source code, which is set by parameters to the + # parser or by the encoding magic comment. + sig { returns(Encoding) } + def encoding; end + + # Returns the lines of the source code as an array of strings. + sig { returns(T::Array[String]) } + def lines; end + + # Perform a byteslice on the source code using the given byte offset and + # byte length. + sig { params(byte_offset: Integer, length: Integer).returns(String) } + def slice(byte_offset, length); end + + # Converts the line number and column in bytes to a byte offset. + sig { params(line: Integer, column: Integer).returns(Integer) } + def byte_offset(line, column); end + + # Binary search through the offsets to find the line number for the given + # byte offset. + sig { params(byte_offset: Integer).returns(Integer) } + def line(byte_offset); end + + # Return the byte offset of the start of the line corresponding to the given + # byte offset. + sig { params(byte_offset: Integer).returns(Integer) } + def line_start(byte_offset); end + + # Returns the byte offset of the end of the line corresponding to the given + # byte offset. + sig { params(byte_offset: Integer).returns(Integer) } + def line_end(byte_offset); end + + # Return the column in bytes for the given byte offset. + sig { params(byte_offset: Integer).returns(Integer) } + def column(byte_offset); end + + # Return the character offset for the given byte offset. + sig { params(byte_offset: Integer).returns(Integer) } + def character_offset(byte_offset); end + + # Return the column in characters for the given byte offset. + sig { params(byte_offset: Integer).returns(Integer) } + def character_column(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. + sig { params(byte_offset: Integer, encoding: Encoding).returns(Integer) } + def code_units_offset(byte_offset, encoding); end + + # Generate a cache that targets a specific encoding for calculating code + # unit offsets. + sig { params(encoding: Encoding).returns(CodeUnitsCache) } + def code_units_cache(encoding); end + + # Returns the column in code units for the given encoding for the + # given byte offset. + sig { params(byte_offset: Integer, encoding: Encoding).returns(Integer) } + def code_units_column(byte_offset, encoding); end + + # Freeze this object and the objects it contains. + sig { void } + def deep_freeze; end + + # Binary search through the offsets to find the line number for the given + # byte offset. + sig { params(byte_offset: Integer).returns(Integer) } + private def find_line(byte_offset); 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 + sig { params(source: String, encoding: Encoding).void } + def initialize(source, encoding); end + + sig { params(byte_offset: Integer, byte_length: Integer).returns(Integer) } + def count(byte_offset, byte_length); end + end + + class LengthCounter + sig { params(source: String, encoding: Encoding).void } + def initialize(source, encoding); end + + sig { params(byte_offset: Integer, byte_length: Integer).returns(Integer) } + def count(byte_offset, byte_length); end + end + + # Initialize a new cache with the given source and encoding. + sig { params(source: String, encoding: Encoding).void } + def initialize(source, encoding); end + + # Retrieve the code units offset from the given byte offset. + sig { params(byte_offset: Integer).returns(Integer) } + def [](byte_offset); 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. + sig { params(byte_offset: Integer).returns(Integer) } + def character_offset(byte_offset); end + + # Return the column in characters for the given byte offset. + sig { params(byte_offset: Integer).returns(Integer) } + def character_column(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. + sig { params(byte_offset: Integer, encoding: Encoding).returns(Integer) } + def code_units_offset(byte_offset, encoding); 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. + sig { params(encoding: Encoding).returns(::T.untyped) } + def code_units_cache(encoding); 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`. + sig { params(byte_offset: Integer, encoding: Encoding).returns(Integer) } + def code_units_column(byte_offset, encoding); 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. + sig { returns(Source) } + attr_reader :source + + # The byte offset from the beginning of the source where this location + # starts. + sig { returns(Integer) } + attr_reader :start_offset + + # The length of this location in bytes. + sig { returns(Integer) } + attr_reader :length + + # Create a new location object with the given source, start byte offset, and + # byte length. + sig { params(source: Source, start_offset: Integer, length: Integer).void } + def initialize(source, start_offset, length); end + + # These are the comments that are associated with this location that exist + # before the start of this location. + sig { returns(T::Array[Comment]) } + def leading_comments; end + + # Attach a comment to the leading comments of this location. + sig { params(comment: Comment).void } + def leading_comment(comment); end + + # These are the comments that are associated with this location that exist + # after the end of this location. + sig { returns(T::Array[Comment]) } + def trailing_comments; end + + # Attach a comment to the trailing comments of this location. + sig { params(comment: Comment).void } + def trailing_comment(comment); end + + # Returns all comments that are associated with this location (both leading + # and trailing comments). + sig { returns(T::Array[Comment]) } + def comments; end + + # Create a new location object with the given options. + sig { params(source: Source, start_offset: Integer, length: Integer).returns(Location) } + def copy(source: T.unsafe(nil), start_offset: T.unsafe(nil), length: T.unsafe(nil)); end + + # Returns a new location that is the result of chopping off the last byte. + sig { returns(Location) } + def chop; end + + # Returns a string representation of this location. + sig { returns(String) } + def inspect; end + + # Returns all of the lines of the source code associated with this location. + sig { returns(T::Array[String]) } + def source_lines; end + + # The source code that this location represents. + sig { returns(String) } + def slice; 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. + sig { returns(String) } + def slice_lines; end + + # The character offset from the beginning of the source where this location + # starts. + sig { returns(Integer) } + def start_character_offset; end + + # The offset from the start of the file in code units of the given encoding. + sig { params(encoding: Encoding).returns(Integer) } + def start_code_units_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. + sig { params(cache: ::T.untyped).returns(Integer) } + def cached_start_code_units_offset(cache); end + + # The byte offset from the beginning of the source where this location ends. + sig { returns(Integer) } + def end_offset; end + + # The character offset from the beginning of the source where this location + # ends. + sig { returns(Integer) } + def end_character_offset; end + + # The offset from the start of the file in code units of the given encoding. + sig { params(encoding: Encoding).returns(Integer) } + def end_code_units_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. + sig { params(cache: ::T.untyped).returns(Integer) } + def cached_end_code_units_offset(cache); end + + # The line number where this location starts. + sig { returns(Integer) } + def start_line; end + + # The content of the line where this location starts before this location. + sig { returns(String) } + def start_line_slice; end + + # The line number where this location ends. + sig { returns(Integer) } + def end_line; end + + # The column in bytes where this location starts from the start of + # the line. + sig { returns(Integer) } + def start_column; end + + # The column in characters where this location ends from the start of + # the line. + sig { returns(Integer) } + def start_character_column; end + + # The column in code units of the given encoding where this location + # starts from the start of the line. + sig { params(encoding: Encoding).returns(Integer) } + def start_code_units_column(encoding = T.unsafe(nil)); end + + # The start column in code units using the given cache to fetch or calculate + # the value. + sig { params(cache: ::T.untyped).returns(Integer) } + def cached_start_code_units_column(cache); end + + # The column in bytes where this location ends from the start of the + # line. + sig { returns(Integer) } + def end_column; end + + # The column in characters where this location ends from the start of + # the line. + sig { returns(Integer) } + def end_character_column; end + + # The column in code units of the given encoding where this location + # ends from the start of the line. + sig { params(encoding: Encoding).returns(Integer) } + def end_code_units_column(encoding = T.unsafe(nil)); end + + # The end column in code units using the given cache to fetch or calculate + # the value. + sig { params(cache: ::T.untyped).returns(Integer) } + def cached_end_code_units_column(cache); end + + # Implement the hash pattern matching interface for Location. + sig { params(keys: ::T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, ::T.untyped]) } + def deconstruct_keys(keys); end + + # Implement the pretty print interface for Location. + sig { params(q: PP).void } + def pretty_print(q); end + + # Returns true if the given other location is equal to this location. + sig { params(other: ::T.untyped).returns(T::Boolean) } + def ==(other); 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. + sig { params(other: Location).returns(Location) } + def join(other); 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. + sig { params(string: String).returns(Location) } + def adjoin(string); 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. + sig { returns(Location) } + attr_reader :location + + # Create a new comment object with the given location. + sig { params(location: Location).void } + def initialize(location); end + + # Implement the hash pattern matching interface for Comment. + sig { params(keys: ::T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, ::T.untyped]) } + def deconstruct_keys(keys); end + + # Returns the content of the comment by slicing it from the source code. + sig { returns(String) } + def slice; end + + # Returns true if this comment happens on the same line as other code and + # false if the comment is by itself. This can only be true for inline + # comments and should be false for block comments. + sig { returns(T::Boolean) } + def trailing?; 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. + sig { returns(T::Boolean) } + def trailing?; end + + # Returns a string representation of this comment. + sig { returns(String) } + def inspect; end + end + + # EmbDocComment objects correspond to comments that are surrounded by =begin + # and =end. + class EmbDocComment < Comment + # Returns false. This can only be true for inline comments. + sig { returns(T::Boolean) } + def trailing?; end + + # Returns a string representation of this comment. + sig { returns(String) } + 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. + sig { returns(Location) } + attr_reader :key_loc + + # A Location object representing the location of the value in the source. + sig { returns(Location) } + attr_reader :value_loc + + # Create a new magic comment object with the given key and value locations. + sig { params(key_loc: Location, value_loc: Location).void } + def initialize(key_loc, value_loc); end + + # Returns the key of the magic comment by slicing it from the source code. + sig { returns(String) } + def key; end + + # Returns the value of the magic comment by slicing it from the source code. + sig { returns(String) } + def value; end + + # Implement the hash pattern matching interface for MagicComment. + sig { params(keys: ::T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, ::T.untyped]) } + def deconstruct_keys(keys); end + + # Returns a string representation of this magic comment. + sig { returns(String) } + 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. + sig { returns(Symbol) } + attr_reader :type + + # The message associated with this error. + sig { returns(String) } + attr_reader :message + + # A Location object representing the location of this error in the source. + sig { returns(Location) } + attr_reader :location + + # The level of this error. + sig { returns(Symbol) } + attr_reader :level + + # Create a new error object with the given message and location. + sig { params(type: Symbol, message: String, location: Location, level: Symbol).void } + def initialize(type, message, location, level); end + + # Implement the hash pattern matching interface for ParseError. + sig { params(keys: ::T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, ::T.untyped]) } + def deconstruct_keys(keys); end + + # Returns a string representation of this error. + sig { returns(String) } + 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. + sig { returns(Symbol) } + attr_reader :type + + # The message associated with this warning. + sig { returns(String) } + attr_reader :message + + # A Location object representing the location of this warning in the source. + sig { returns(Location) } + attr_reader :location + + # The level of this warning. + sig { returns(Symbol) } + attr_reader :level + + # Create a new warning object with the given message and location. + sig { params(type: Symbol, message: String, location: Location, level: Symbol).void } + def initialize(type, message, location, level); end + + # Implement the hash pattern matching interface for ParseWarning. + sig { params(keys: ::T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, ::T.untyped]) } + def deconstruct_keys(keys); end + + # Returns a string representation of this warning. + sig { returns(String) } + def inspect; end + end + + # This represents the result of a call to Prism.parse or Prism.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. + sig { returns(T::Array[Comment]) } + attr_reader :comments + + # The list of magic comments that were encountered during parsing. + sig { returns(T::Array[MagicComment]) } + 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. + sig { returns(::T.nilable(Location)) } + attr_reader :data_loc + + # The list of errors that were generated during parsing. + sig { returns(T::Array[ParseError]) } + attr_reader :errors + + # The list of warnings that were generated during parsing. + sig { returns(T::Array[ParseWarning]) } + attr_reader :warnings + + # A Source instance that represents the source code that was parsed. + sig { returns(Source) } + attr_reader :source + + # Create a new result object with the given values. + sig { params(comments: T::Array[Comment], magic_comments: T::Array[MagicComment], data_loc: ::T.nilable(Location), errors: T::Array[ParseError], warnings: T::Array[ParseWarning], continuable: T::Boolean, source: Source).void } + def initialize(comments, magic_comments, data_loc, errors, warnings, continuable, source); end + + # Implement the hash pattern matching interface for Result. + sig { params(keys: ::T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, ::T.untyped]) } + def deconstruct_keys(keys); end + + # Returns the encoding of the source code that was parsed. + sig { returns(Encoding) } + def encoding; end + + # Returns true if there were no errors during parsing and false if there + # were. + sig { returns(T::Boolean) } + def success?; end + + # Returns true if there were errors during parsing and false if there were + # not. + sig { returns(T::Boolean) } + def failure?; end + + # Returns true if the parsed source is an incomplete expression that could + # become valid with additional input. This is useful for REPL contexts (such + # as IRB) where the user may be entering a multi-line expression one line at + # a time and the implementation needs to determine whether to wait for more + # input or to evaluate what has been entered so far. + # + # Concretely, this returns true when every error present is caused by the + # parser reaching the end of the input before a construct was closed (e.g. + # an unclosed string, array, block, or keyword), and returns false when any + # error is caused by a token that makes the input structurally invalid + # regardless of what might follow (e.g. a stray `end`, `]`, or `)` with no + # matching opener). + # + # Examples: + # + # Prism.parse("1 + [").continuable? #=> true (unclosed array) + # Prism.parse("1 + ]").continuable? #=> false (stray ]) + # Prism.parse("tap do").continuable? #=> true (unclosed block) + # Prism.parse("end.tap do").continuable? #=> false (stray end) + # + sig { returns(T::Boolean) } + def continuable?; end + + # Create a code units cache for the given encoding. + sig { params(encoding: Encoding).returns(::T.untyped) } + def code_units_cache(encoding); end + end + + # This is a result specific to the `parse` and `parse_file` methods. + class ParseResult < Result + # The syntax tree that was parsed from the source code. + sig { returns(ProgramNode) } + attr_reader :value + + # Create a new parse result object with the given values. + sig { params(value: ProgramNode, comments: T::Array[Comment], magic_comments: T::Array[MagicComment], data_loc: ::T.nilable(Location), errors: T::Array[ParseError], warnings: T::Array[ParseWarning], continuable: T::Boolean, source: Source).void } + def initialize(value, comments, magic_comments, data_loc, errors, warnings, continuable, source); end + + # Implement the hash pattern matching interface for ParseResult. + sig { params(keys: ::T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, ::T.untyped]) } + def deconstruct_keys(keys); end + + # Attach the list of comments to their respective locations in the tree. + sig { void } + def attach_comments!; end + + # Walk the tree and mark nodes that are on a new line, loosely emulating + # the behavior of CRuby's `:line` tracepoint event. + sig { void } + def mark_newlines!; end + + # Returns a string representation of the syntax tree with the errors + # displayed inline. + sig { returns(String) } + def errors_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. + sig { returns(T::Array[[Token, Integer]]) } + attr_reader :value + + # Create a new lex result object with the given values. + sig { params(value: T::Array[[Token, Integer]], comments: T::Array[Comment], magic_comments: T::Array[MagicComment], data_loc: ::T.nilable(Location), errors: T::Array[ParseError], warnings: T::Array[ParseWarning], continuable: T::Boolean, source: Source).void } + def initialize(value, comments, magic_comments, data_loc, errors, warnings, continuable, source); end + + # Implement the hash pattern matching interface for LexResult. + sig { params(keys: ::T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, ::T.untyped]) } + def deconstruct_keys(keys); 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. + sig { returns([ProgramNode, T::Array[[Token, Integer]]]) } + attr_reader :value + + # Create a new parse lex result object with the given values. + sig { params(value: [ProgramNode, T::Array[[Token, Integer]]], comments: T::Array[Comment], magic_comments: T::Array[MagicComment], data_loc: ::T.nilable(Location), errors: T::Array[ParseError], warnings: T::Array[ParseWarning], continuable: T::Boolean, source: Source).void } + def initialize(value, comments, magic_comments, data_loc, errors, warnings, continuable, source); end + + # Implement the hash pattern matching interface for ParseLexResult. + sig { params(keys: ::T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, ::T.untyped]) } + def deconstruct_keys(keys); end + end + + # This represents a token from the Ruby source. + class Token + # The Source object that represents the source this token came from. + sig { returns(Source) } + attr_reader :source + + # The type of token that this token is. + sig { returns(Symbol) } + attr_reader :type + + # A byteslice of the source that this token represents. + sig { returns(String) } + attr_reader :value + + # Create a new token object with the given type, value, and location. + sig { params(source: Source, type: Symbol, value: String, location: ::T.any(Location, Integer)).void } + def initialize(source, type, value, location); end + + # Implement the hash pattern matching interface for Token. + sig { params(keys: ::T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, ::T.untyped]) } + def deconstruct_keys(keys); end + + # A Location object representing the location of this token in the source. + sig { returns(Location) } + def location; end + + # Implement the pretty print interface for Token. + sig { params(q: PP).void } + def pretty_print(q); end + + # Returns true if the given other token is equal to this token. + sig { params(other: ::T.untyped).returns(T::Boolean) } + def ==(other); end + + # Returns a string representation of this token. + sig { returns(String) } + def inspect; end + + # Freeze this object and the objects it contains. + sig { void } + def deep_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. + sig { returns(T::Array[Symbol]) } + 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 + sig { returns(T::Array[Symbol]) } + attr_reader :forwarding + + # Create a new scope object with the given locals and forwarding. + sig { params(locals: T::Array[Symbol], forwarding: T::Array[Symbol]).void } + def initialize(locals, 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. + sig { params(locals: T::Array[Symbol], forwarding: T::Array[Symbol]).returns(Scope) } + def self.scope(locals: T.unsafe(nil), forwarding: T.unsafe(nil)); end +end diff --git a/rbi/generated/prism/parse_result/comments.rbi b/rbi/generated/prism/parse_result/comments.rbi new file mode 100644 index 0000000000..f386d03f34 --- /dev/null +++ b/rbi/generated/prism/parse_result/comments.rbi @@ -0,0 +1,90 @@ +# typed: true + +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 + sig { returns(Node) } + attr_reader :node + + sig { params(node: Node).void } + def initialize(node); end + + sig { returns(Integer) } + def start_offset; end + + sig { returns(Integer) } + def end_offset; end + + sig { params(comment: Comment).returns(T::Boolean) } + def encloses?(comment); end + + sig { params(comment: Comment).void } + def leading_comment(comment); end + + sig { params(comment: Comment).void } + def 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 + sig { returns(Location) } + attr_reader :location + + sig { params(location: Location).void } + def initialize(location); end + + sig { returns(Integer) } + def start_offset; end + + sig { returns(Integer) } + def end_offset; end + + sig { params(comment: Comment).returns(T::Boolean) } + def encloses?(comment); end + + sig { params(comment: Comment).void } + def leading_comment(comment); end + + sig { params(comment: Comment).void } + def trailing_comment(comment); end + end + + # The parse result that we are attaching comments to. + sig { returns(ParseResult) } + attr_reader :parse_result + + # Create a new Comments object that will attach comments to the given + # parse result. + sig { params(parse_result: ParseResult).void } + def initialize(parse_result); end + + # Attach the comments to their respective locations in the tree by + # mutating the parse result. + sig { void } + def attach!; end + + # Responsible for finding the nearest targets to the given comment within + # the context of the given encapsulating node. + sig { params(node: Node, comment: Comment).returns([::T.untyped, ::T.untyped, ::T.untyped]) } + private def nearest_targets(node, comment); end + end + end +end diff --git a/rbi/generated/prism/parse_result/errors.rbi b/rbi/generated/prism/parse_result/errors.rbi new file mode 100644 index 0000000000..f864a3f8c2 --- /dev/null +++ b/rbi/generated/prism/parse_result/errors.rbi @@ -0,0 +1,21 @@ +# typed: true + +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. + sig { returns(ParseResult) } + attr_reader :parse_result + + # Initialize a new set of errors from the given parse result. + sig { params(parse_result: ParseResult).void } + def initialize(parse_result); end + + # Formats the errors in a human-readable way and return them as a string. + sig { returns(String) } + def format; end + end + end +end diff --git a/rbi/generated/prism/parse_result/newlines.rbi b/rbi/generated/prism/parse_result/newlines.rbi new file mode 100644 index 0000000000..f77dee3bbf --- /dev/null +++ b/rbi/generated/prism/parse_result/newlines.rbi @@ -0,0 +1,121 @@ +# typed: true + +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. + sig { params(lines: Integer).void } + def initialize(lines); end + + # Permit block nodes to mark newlines within themselves. + sig { params(node: BlockNode).void } + def visit_block_node(node); end + + # Permit lambda nodes to mark newlines within themselves. + sig { params(node: LambdaNode).void } + def visit_lambda_node(node); end + + # Mark if nodes as newlines. + sig { params(node: IfNode).void } + def visit_if_node(node); end + + # Mark unless nodes as newlines. + sig { params(node: UnlessNode).void } + def visit_unless_node(node); end + + # Permit statements lists to mark newlines within themselves. + sig { params(node: StatementsNode).void } + def visit_statements_node(node); end + end + end + + class Node + abstract! + + sig { returns(T::Boolean) } + def newline_flag?; end + + sig { params(lines: T::Array[T::Boolean]).void } + def newline_flag!(lines); end + end + + class BeginNode < Node + sig { params(lines: T::Array[T::Boolean]).void } + def newline_flag!(lines); end + end + + class ParenthesesNode < Node + sig { params(lines: T::Array[T::Boolean]).void } + def newline_flag!(lines); end + end + + class IfNode < Node + sig { params(lines: T::Array[T::Boolean]).void } + def newline_flag!(lines); end + end + + class UnlessNode < Node + sig { params(lines: T::Array[T::Boolean]).void } + def newline_flag!(lines); end + end + + class UntilNode < Node + sig { params(lines: T::Array[T::Boolean]).void } + def newline_flag!(lines); end + end + + class WhileNode < Node + sig { params(lines: T::Array[T::Boolean]).void } + def newline_flag!(lines); end + end + + class RescueModifierNode < Node + sig { params(lines: T::Array[T::Boolean]).void } + def newline_flag!(lines); end + end + + class InterpolatedMatchLastLineNode < Node + sig { params(lines: T::Array[T::Boolean]).void } + def newline_flag!(lines); end + end + + class InterpolatedRegularExpressionNode < Node + sig { params(lines: T::Array[T::Boolean]).void } + def newline_flag!(lines); end + end + + class InterpolatedStringNode < Node + sig { params(lines: T::Array[T::Boolean]).void } + def newline_flag!(lines); end + end + + class InterpolatedSymbolNode < Node + sig { params(lines: T::Array[T::Boolean]).void } + def newline_flag!(lines); end + end + + class InterpolatedXStringNode < Node + sig { params(lines: T::Array[T::Boolean]).void } + def newline_flag!(lines); end + end +end diff --git a/rbi/generated/prism/pattern.rbi b/rbi/generated/prism/pattern.rbi new file mode 100644 index 0000000000..375518b8f7 --- /dev/null +++ b/rbi/generated/prism/pattern.rbi @@ -0,0 +1,134 @@ +# typed: true + +module Prism + # A pattern is an object that wraps a Ruby pattern matching expression. The + # expression would normally be passed to an `in` clause within a `case` + # expression or a rightward assignment expression. For example, in the + # following snippet: + # + # case node + # in ConstantPathNode[ConstantReadNode[name: :Prism], ConstantReadNode[name: :Pattern]] + # end + # + # the pattern is the 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. + sig { params(repr: String).void } + def initialize(repr); end + end + + # The query that this pattern was initialized with. + sig { returns(String) } + attr_reader :query + + # Create a new pattern with the given query. The query should be a string + # containing a Ruby pattern matching expression. + sig { params(query: String).void } + def initialize(query); end + + # Compile the query into a callable object that can be used to match against + # nodes. + sig { returns(Proc) } + def compile; 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. + sig { params(root: Node).returns(T::Enumerator[Node]) } + sig { params(root: Node, blk: ::T.proc.params(arg0: Node).void).void } + def scan(root, &blk); end + + # Shortcut for combining two procs into one that returns true if both return + # true. + sig { params(left: Proc, right: Proc).returns(Proc) } + private def combine_and(left, right); end + + # Shortcut for combining two procs into one that returns true if either + # returns true. + sig { params(left: Proc, right: Proc).returns(Proc) } + private def combine_or(left, right); end + + # Raise an error because the given node is not supported. Note purposefully + # not typing this method since it is a no return method that Steep does not + # understand. + sig { params(node: Node).returns(::T.noreturn) } + private def compile_error(node); end + + # in [foo, bar, baz] + sig { params(node: ArrayPatternNode).returns(Proc) } + private def compile_array_pattern_node(node); end + + # in foo | bar + sig { params(node: AlternationPatternNode).returns(Proc) } + private def compile_alternation_pattern_node(node); end + + # in Prism::ConstantReadNode + sig { params(node: ConstantPathNode).returns(Proc) } + private def compile_constant_path_node(node); end + + # in ConstantReadNode + # in String + sig { params(node: ConstantReadNode).returns(Proc) } + private def compile_constant_read_node(node); end + + # Compile a name associated with a constant. + sig { params(node: ::T.any(ConstantPathNode, ConstantReadNode), name: Symbol).returns(Proc) } + private def compile_constant_name(node, name); end + + # in InstanceVariableReadNode[name: Symbol] + # in { name: Symbol } + sig { params(node: HashPatternNode).returns(Proc) } + private def compile_hash_pattern_node(node); end + + # in nil + sig { params(node: NilNode).returns(Proc) } + private def compile_nil_node(node); end + + # in /foo/ + sig { params(node: RegularExpressionNode).returns(Proc) } + private def compile_regular_expression_node(node); end + + # in "" + # in "foo" + sig { params(node: StringNode).returns(Proc) } + private def compile_string_node(node); end + + # in :+ + # in :foo + sig { params(node: SymbolNode).returns(Proc) } + private def compile_symbol_node(node); end + + # Compile any kind of node. Dispatch out to the individual compilation + # methods based on the type of node. + sig { params(node: Node).returns(Proc) } + private def compile_node(node); end + end +end diff --git a/rbi/generated/prism/reflection.rbi b/rbi/generated/prism/reflection.rbi new file mode 100644 index 0000000000..1094c461f4 --- /dev/null +++ b/rbi/generated/prism/reflection.rbi @@ -0,0 +1,89 @@ +# typed: true + +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. + sig { returns(Symbol) } + attr_reader :name + + # Initializes the field with the given name. + sig { params(name: Symbol).void } + def initialize(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. + sig { returns(T::Array[Symbol]) } + attr_reader :flags + + # Initializes the flags field with the given name and flags. + sig { params(name: Symbol, flags: T::Array[Symbol]).void } + def initialize(name, flags); end + end + + # Returns the fields for the given node. + sig { params(node: ::T.class_of(Node)).returns(T::Array[Field]) } + def self.fields_for(node); end + end +end diff --git a/rbi/generated/prism/relocation.rbi b/rbi/generated/prism/relocation.rbi new file mode 100644 index 0000000000..7868d065b0 --- /dev/null +++ b/rbi/generated/prism/relocation.rbi @@ -0,0 +1,389 @@ +# typed: true + +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. + sig { params(repository: Repository).void } + def initialize(repository); end + + # Fetch the filepath of the value. + sig { returns(String) } + def filepath; end + + # Fetch the start line of the value. + sig { returns(Integer) } + def start_line; end + + # Fetch the end line of the value. + sig { returns(Integer) } + def end_line; end + + # Fetch the start byte offset of the value. + sig { returns(Integer) } + def start_offset; end + + # Fetch the end byte offset of the value. + sig { returns(Integer) } + def end_offset; end + + # Fetch the start character offset of the value. + sig { returns(Integer) } + def start_character_offset; end + + # Fetch the end character offset of the value. + sig { returns(Integer) } + def end_character_offset; end + + # Fetch the start code units offset of the value, for the encoding that + # was configured on the repository. + sig { returns(Integer) } + def start_code_units_offset; end + + # Fetch the end code units offset of the value, for the encoding that was + # configured on the repository. + sig { returns(Integer) } + def end_code_units_offset; end + + # Fetch the start byte column of the value. + sig { returns(Integer) } + def start_column; end + + # Fetch the end byte column of the value. + sig { returns(Integer) } + def end_column; end + + # Fetch the start character column of the value. + sig { returns(Integer) } + def start_character_column; end + + # Fetch the end character column of the value. + sig { returns(Integer) } + def end_character_column; end + + # Fetch the start code units column of the value, for the encoding that + # was configured on the repository. + sig { returns(Integer) } + def start_code_units_column; end + + # Fetch the end code units column of the value, for the encoding that was + # configured on the repository. + sig { returns(Integer) } + def end_code_units_column; end + + # Fetch the leading comments of the value. + sig { returns(T::Array[CommentsField::Comment]) } + def leading_comments; end + + # Fetch the trailing comments of the value. + sig { returns(T::Array[CommentsField::Comment]) } + def trailing_comments; end + + # Fetch the leading and trailing comments of the value. + sig { returns(T::Array[CommentsField::Comment]) } + def 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. + sig { params(values: T::Hash[Symbol, ::T.untyped]).void } + def reify!(values); end + + # Fetch a value from the entry, raising an error if it is missing. + sig { params(name: Symbol).returns(::T.untyped) } + private def fetch_value(name); end + + # Return the values from the repository, reifying them if necessary. + sig { returns(T::Hash[Symbol, ::T.untyped]) } + private def values; end + end + + # Represents the source of a repository that will be reparsed. + class Source + # The value that will need to be reparsed. + sig { returns(::T.untyped) } + attr_reader :value + + # Initialize the source with the given value. + sig { params(value: ::T.untyped).void } + def initialize(value); end + + # Reparse the value and return the parse result. + sig { returns(ParseResult) } + def result; end + + # Create a code units cache for the given encoding. + sig { params(encoding: Encoding).returns(::T.untyped) } + def 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. + sig { returns(ParseResult) } + def result; end + end + + # A source that is represented by a string. + class SourceString < Source + # Reparse the string and return the parse result. + sig { returns(ParseResult) } + def result; end + end + + # A field that represents the file path. + class FilepathField + # The file path that this field represents. + sig { returns(String) } + attr_reader :value + + # Initialize a new field with the given file path. + sig { params(value: String).void } + def initialize(value); end + + # Fetch the file path. + sig { params(_value: ::T.untyped).returns(T::Hash[Symbol, ::T.untyped]) } + def fields(_value); end + end + + # A field representing the start and end lines. + class LinesField + # Fetches the start and end line of a value. + sig { params(value: ::T.untyped).returns(T::Hash[Symbol, ::T.untyped]) } + def fields(value); end + end + + # A field representing the start and end byte offsets. + class OffsetsField + # Fetches the start and end byte offset of a value. + sig { params(value: ::T.untyped).returns(T::Hash[Symbol, ::T.untyped]) } + def fields(value); end + end + + # A field representing the start and end character offsets. + class CharacterOffsetsField + # Fetches the start and end character offset of a value. + sig { params(value: ::T.untyped).returns(T::Hash[Symbol, ::T.untyped]) } + def fields(value); 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. + sig { returns(Repository) } + attr_reader :repository + + # The associated encoding for the code units. + sig { returns(Encoding) } + attr_reader :encoding + + # Initialize a new field with the associated repository and encoding. + sig { params(repository: Repository, encoding: Encoding).void } + def initialize(repository, encoding); end + + # Fetches the start and end code units offset of a value for a particular + # encoding. + sig { params(value: ::T.untyped).returns(T::Hash[Symbol, ::T.untyped]) } + def fields(value); end + + # Lazily create a code units cache for the associated encoding. + sig { returns(::T.untyped) } + private def cache; end + end + + # A field representing the start and end byte columns. + class ColumnsField + # Fetches the start and end byte column of a value. + sig { params(value: ::T.untyped).returns(T::Hash[Symbol, ::T.untyped]) } + def fields(value); end + end + + # A field representing the start and end character columns. + class CharacterColumnsField + # Fetches the start and end character column of a value. + sig { params(value: ::T.untyped).returns(T::Hash[Symbol, ::T.untyped]) } + def fields(value); 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. + sig { returns(Repository) } + attr_reader :repository + + # The associated encoding for the code units. + sig { returns(Encoding) } + attr_reader :encoding + + # Initialize a new field with the associated repository and encoding. + sig { params(repository: Repository, encoding: Encoding).void } + def initialize(repository, encoding); end + + # Fetches the start and end code units column of a value for a particular + # encoding. + sig { params(value: ::T.untyped).returns(T::Hash[Symbol, ::T.untyped]) } + def fields(value); end + + # Lazily create a code units cache for the associated encoding. + sig { returns(::T.untyped) } + private def cache; 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. + sig { returns(String) } + attr_reader :slice + + # Initialize a new comment with the given slice. + # + sig { params(slice: String).void } + def initialize(slice); end + end + + # Create comment objects from the given values. + sig { params(values: ::T.untyped).returns(T::Array[Comment]) } + private def comments(values); end + end + + # A field representing the leading comments. + class LeadingCommentsField < CommentsField + # Fetches the leading comments of a value. + sig { params(value: ::T.untyped).returns(T::Hash[Symbol, ::T.untyped]) } + def fields(value); end + end + + # A field representing the trailing comments. + class TrailingCommentsField < CommentsField + # Fetches the trailing comments of a value. + sig { params(value: ::T.untyped).returns(T::Hash[Symbol, ::T.untyped]) } + def fields(value); 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. + sig { returns(Source) } + attr_reader :source + + # The fields that have been configured on this repository. + sig { returns(T::Hash[Symbol, ::T.untyped]) } + attr_reader :fields + + # The entries that have been saved on this repository. + sig { returns(T::Hash[Integer, T::Hash[Symbol, Entry]]) } + attr_reader :entries + + # Initialize a new repository with the given source. + sig { params(source: Source).void } + def initialize(source); end + + # Create a code units cache for the given encoding from the source. + sig { params(encoding: Encoding).returns(::T.untyped) } + def code_units_cache(encoding); end + + # Configure the filepath field for this repository and return self. + sig { returns(::T.self_type) } + def filepath; end + + # Configure the lines field for this repository and return self. + sig { returns(::T.self_type) } + def lines; end + + # Configure the offsets field for this repository and return self. + sig { returns(::T.self_type) } + def offsets; end + + # Configure the character offsets field for this repository and return + # self. + sig { returns(::T.self_type) } + def character_offsets; end + + # Configure the code unit offsets field for this repository for a specific + # encoding and return self. + sig { params(encoding: Encoding).returns(::T.self_type) } + def code_unit_offsets(encoding); end + + # Configure the columns field for this repository and return self. + sig { returns(::T.self_type) } + def columns; end + + # Configure the character columns field for this repository and return + # self. + sig { returns(::T.self_type) } + def character_columns; end + + # Configure the code unit columns field for this repository for a specific + # encoding and return self. + sig { params(encoding: Encoding).returns(::T.self_type) } + def code_unit_columns(encoding); end + + # Configure the leading comments field for this repository and return + # self. + sig { returns(::T.self_type) } + def leading_comments; end + + # Configure the trailing comments field for this repository and return + # self. + sig { returns(::T.self_type) } + def trailing_comments; end + + # Configure both the leading and trailing comment fields for this + # repository and return self. + sig { returns(::T.self_type) } + def 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. + sig { params(node_id: Integer, field_name: Symbol).returns(Entry) } + def enter(node_id, field_name); 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. + sig { void } + def reify!; end + + # Append the given field to the repository and return the repository so + # that these calls can be chained. + sig { params(name: Symbol, arg0: ::T.untyped).returns(::T.self_type) } + private def field(name, arg0); end + end + + # Create a new repository for the given filepath. + sig { params(value: String).returns(Repository) } + def self.filepath(value); end + + # Create a new repository for the given string. + sig { params(value: String).returns(Repository) } + def self.string(value); end + end +end diff --git a/rbi/generated/prism/serialize.rbi b/rbi/generated/prism/serialize.rbi new file mode 100644 index 0000000000..57b4c61acb --- /dev/null +++ b/rbi/generated/prism/serialize.rbi @@ -0,0 +1,172 @@ +# typed: true + +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 = T.let(nil, Integer) + + # The minor version of prism that we are expecting to find in the serialized + # strings. + MINOR_VERSION = T.let(nil, Integer) + + # The patch version of prism that we are expecting to find in the serialized + # strings. + PATCH_VERSION = T.let(nil, Integer) + + # 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. + sig { params(input: String, serialized: String, freeze: T::Boolean).returns(ParseResult) } + def self.load_parse(input, serialized, freeze); 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. + sig { params(input: String, serialized: String, freeze: T::Boolean).returns(LexResult) } + def self.load_lex(input, serialized, freeze); 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. + sig { params(input: String, serialized: String, freeze: T::Boolean).returns(T::Array[Comment]) } + def self.load_parse_comments(input, serialized, freeze); 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. + sig { params(input: String, serialized: String, freeze: T::Boolean).returns(ParseLexResult) } + def self.load_parse_lex(input, serialized, freeze); end + + class ConstantPool + sig { returns(Integer) } + attr_reader :size + + sig { params(serialized: String, base: Integer, size: Integer).void } + def initialize(serialized, base, size); end + + sig { params(index: Integer, encoding: Encoding).returns(Symbol) } + def get(index, encoding); end + end + + FastStringIO = T.let(nil, ::T.untyped) + + class Loader + sig { returns(String) } + attr_reader :input + + sig { returns(StringIO) } + attr_reader :io + + sig { returns(Source) } + attr_reader :source + + sig { params(source: Source, serialized: String).void } + def initialize(source, serialized); end + + sig { returns(T::Boolean) } + def eof?; end + + sig { params(constant_pool: ConstantPool).void } + def load_constant_pool(constant_pool); end + + sig { void } + def load_header; end + + sig { returns(Encoding) } + def load_encoding; end + + sig { params(freeze: T::Boolean).returns(T::Array[Integer]) } + def load_line_offsets(freeze); end + + sig { params(freeze: T::Boolean).returns(T::Array[Comment]) } + def load_comments(freeze); end + + sig { params(freeze: T::Boolean).returns(T::Array[MagicComment]) } + def load_magic_comments(freeze); end + + DIAGNOSTIC_TYPES = T.let(nil, T::Array[Symbol]) + + sig { returns(Symbol) } + def load_error_level; end + + sig { params(encoding: Encoding, freeze: T::Boolean).returns(T::Array[ParseError]) } + def load_errors(encoding, freeze); end + + sig { returns(Symbol) } + def load_warning_level; end + + sig { params(encoding: Encoding, freeze: T::Boolean).returns(T::Array[ParseWarning]) } + def load_warnings(encoding, freeze); end + + sig { returns(T::Array[[Token, Integer]]) } + def load_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 + sig { returns(Integer) } + def load_varuint; end + + sig { returns(Integer) } + def load_varsint; end + + sig { returns(Integer) } + def load_integer; end + + sig { returns(Float) } + def load_double; end + + sig { returns(T::Boolean) } + def load_bool; end + + sig { returns(Integer) } + def load_uint32; end + + sig { params(constant_pool: ConstantPool, encoding: Encoding, freeze: T::Boolean).returns(::T.nilable(Node)) } + def load_optional_node(constant_pool, encoding, freeze); end + + sig { params(encoding: Encoding).returns(String) } + def load_string(encoding); end + + sig { params(freeze: T::Boolean).returns(Location) } + def load_location_object(freeze); end + + # Load a location object from the serialized data. Note that we are lying + # about the signature a bit here, because we sometimes load it as a packed + # integer instead of an object. + sig { params(freeze: T::Boolean).returns(Location) } + def load_location(freeze); end + + # Load an optional location object from the serialized data if it is + # present. Note that we are lying about the signature a bit here, because + # we sometimes load it as a packed integer instead of an object. + sig { params(freeze: T::Boolean).returns(::T.nilable(Location)) } + def load_optional_location(freeze); end + + sig { params(freeze: T::Boolean).returns(::T.nilable(Location)) } + def load_optional_location_object(freeze); end + + sig { params(constant_pool: ConstantPool, encoding: Encoding).returns(Symbol) } + def load_constant(constant_pool, encoding); end + + sig { params(constant_pool: ConstantPool, encoding: Encoding).returns(::T.nilable(Symbol)) } + def load_optional_constant(constant_pool, encoding); end + + sig { params(constant_pool: ConstantPool, encoding: Encoding, freeze: T::Boolean).returns(Node) } + def load_node(constant_pool, encoding, freeze); end + + sig { void } + def define_load_node_lambdas; end + end + + # The token types that can be indexed by their enum values. + TOKEN_TYPES = T.let(nil, T::Array[::T.nilable(Symbol)]) + end +end diff --git a/rbi/generated/prism/string_query.rbi b/rbi/generated/prism/string_query.rbi new file mode 100644 index 0000000000..688ea4f08b --- /dev/null +++ b/rbi/generated/prism/string_query.rbi @@ -0,0 +1,36 @@ +# typed: true + +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 + 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 + + # The string that this query is wrapping. + sig { returns(String) } + attr_reader :string + + # Initialize a new query with the given string. + sig { params(string: String).void } + def initialize(string); end + + # Whether or not this string is a valid local variable name. + sig { returns(T::Boolean) } + def local?; end + + # Whether or not this string is a valid constant name. + sig { returns(T::Boolean) } + def constant?; end + + # Whether or not this string is a valid method name. + sig { returns(T::Boolean) } + def method_name?; end + end +end diff --git a/rbi/generated/prism/translation.rbi b/rbi/generated/prism/translation.rbi new file mode 100644 index 0000000000..007625b541 --- /dev/null +++ b/rbi/generated/prism/translation.rbi @@ -0,0 +1,7 @@ +# typed: true + +module Prism + # This module is responsible for converting the prism syntax tree into other + # syntax trees. + module Translation; end +end diff --git a/rbi/generated/prism/visitor.rbi b/rbi/generated/prism/visitor.rbi new file mode 100644 index 0000000000..53534f1dc6 --- /dev/null +++ b/rbi/generated/prism/visitor.rbi @@ -0,0 +1,650 @@ +# typed: true + +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. + sig { params(node: ::T.nilable(Node)).void } + def visit(node); end + + # Visits each node in `nodes` by calling `accept` on each one. + sig { params(nodes: T::Array[::T.nilable(Node)]).void } + def visit_all(nodes); end + + # Visits the child nodes of `node` by calling `accept` on each one. + sig { params(node: Node).void } + def visit_child_nodes(node); 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 + sig { params(node: AliasGlobalVariableNode).void } + def visit_alias_global_variable_node(node); end + + # Visit a AliasMethodNode node + sig { params(node: AliasMethodNode).void } + def visit_alias_method_node(node); end + + # Visit a AlternationPatternNode node + sig { params(node: AlternationPatternNode).void } + def visit_alternation_pattern_node(node); end + + # Visit a AndNode node + sig { params(node: AndNode).void } + def visit_and_node(node); end + + # Visit a ArgumentsNode node + sig { params(node: ArgumentsNode).void } + def visit_arguments_node(node); end + + # Visit a ArrayNode node + sig { params(node: ArrayNode).void } + def visit_array_node(node); end + + # Visit a ArrayPatternNode node + sig { params(node: ArrayPatternNode).void } + def visit_array_pattern_node(node); end + + # Visit a AssocNode node + sig { params(node: AssocNode).void } + def visit_assoc_node(node); end + + # Visit a AssocSplatNode node + sig { params(node: AssocSplatNode).void } + def visit_assoc_splat_node(node); end + + # Visit a BackReferenceReadNode node + sig { params(node: BackReferenceReadNode).void } + def visit_back_reference_read_node(node); end + + # Visit a BeginNode node + sig { params(node: BeginNode).void } + def visit_begin_node(node); end + + # Visit a BlockArgumentNode node + sig { params(node: BlockArgumentNode).void } + def visit_block_argument_node(node); end + + # Visit a BlockLocalVariableNode node + sig { params(node: BlockLocalVariableNode).void } + def visit_block_local_variable_node(node); end + + # Visit a BlockNode node + sig { params(node: BlockNode).void } + def visit_block_node(node); end + + # Visit a BlockParameterNode node + sig { params(node: BlockParameterNode).void } + def visit_block_parameter_node(node); end + + # Visit a BlockParametersNode node + sig { params(node: BlockParametersNode).void } + def visit_block_parameters_node(node); end + + # Visit a BreakNode node + sig { params(node: BreakNode).void } + def visit_break_node(node); end + + # Visit a CallAndWriteNode node + sig { params(node: CallAndWriteNode).void } + def visit_call_and_write_node(node); end + + # Visit a CallNode node + sig { params(node: CallNode).void } + def visit_call_node(node); end + + # Visit a CallOperatorWriteNode node + sig { params(node: CallOperatorWriteNode).void } + def visit_call_operator_write_node(node); end + + # Visit a CallOrWriteNode node + sig { params(node: CallOrWriteNode).void } + def visit_call_or_write_node(node); end + + # Visit a CallTargetNode node + sig { params(node: CallTargetNode).void } + def visit_call_target_node(node); end + + # Visit a CapturePatternNode node + sig { params(node: CapturePatternNode).void } + def visit_capture_pattern_node(node); end + + # Visit a CaseMatchNode node + sig { params(node: CaseMatchNode).void } + def visit_case_match_node(node); end + + # Visit a CaseNode node + sig { params(node: CaseNode).void } + def visit_case_node(node); end + + # Visit a ClassNode node + sig { params(node: ClassNode).void } + def visit_class_node(node); end + + # Visit a ClassVariableAndWriteNode node + sig { params(node: ClassVariableAndWriteNode).void } + def visit_class_variable_and_write_node(node); end + + # Visit a ClassVariableOperatorWriteNode node + sig { params(node: ClassVariableOperatorWriteNode).void } + def visit_class_variable_operator_write_node(node); end + + # Visit a ClassVariableOrWriteNode node + sig { params(node: ClassVariableOrWriteNode).void } + def visit_class_variable_or_write_node(node); end + + # Visit a ClassVariableReadNode node + sig { params(node: ClassVariableReadNode).void } + def visit_class_variable_read_node(node); end + + # Visit a ClassVariableTargetNode node + sig { params(node: ClassVariableTargetNode).void } + def visit_class_variable_target_node(node); end + + # Visit a ClassVariableWriteNode node + sig { params(node: ClassVariableWriteNode).void } + def visit_class_variable_write_node(node); end + + # Visit a ConstantAndWriteNode node + sig { params(node: ConstantAndWriteNode).void } + def visit_constant_and_write_node(node); end + + # Visit a ConstantOperatorWriteNode node + sig { params(node: ConstantOperatorWriteNode).void } + def visit_constant_operator_write_node(node); end + + # Visit a ConstantOrWriteNode node + sig { params(node: ConstantOrWriteNode).void } + def visit_constant_or_write_node(node); end + + # Visit a ConstantPathAndWriteNode node + sig { params(node: ConstantPathAndWriteNode).void } + def visit_constant_path_and_write_node(node); end + + # Visit a ConstantPathNode node + sig { params(node: ConstantPathNode).void } + def visit_constant_path_node(node); end + + # Visit a ConstantPathOperatorWriteNode node + sig { params(node: ConstantPathOperatorWriteNode).void } + def visit_constant_path_operator_write_node(node); end + + # Visit a ConstantPathOrWriteNode node + sig { params(node: ConstantPathOrWriteNode).void } + def visit_constant_path_or_write_node(node); end + + # Visit a ConstantPathTargetNode node + sig { params(node: ConstantPathTargetNode).void } + def visit_constant_path_target_node(node); end + + # Visit a ConstantPathWriteNode node + sig { params(node: ConstantPathWriteNode).void } + def visit_constant_path_write_node(node); end + + # Visit a ConstantReadNode node + sig { params(node: ConstantReadNode).void } + def visit_constant_read_node(node); end + + # Visit a ConstantTargetNode node + sig { params(node: ConstantTargetNode).void } + def visit_constant_target_node(node); end + + # Visit a ConstantWriteNode node + sig { params(node: ConstantWriteNode).void } + def visit_constant_write_node(node); end + + # Visit a DefNode node + sig { params(node: DefNode).void } + def visit_def_node(node); end + + # Visit a DefinedNode node + sig { params(node: DefinedNode).void } + def visit_defined_node(node); end + + # Visit a ElseNode node + sig { params(node: ElseNode).void } + def visit_else_node(node); end + + # Visit a EmbeddedStatementsNode node + sig { params(node: EmbeddedStatementsNode).void } + def visit_embedded_statements_node(node); end + + # Visit a EmbeddedVariableNode node + sig { params(node: EmbeddedVariableNode).void } + def visit_embedded_variable_node(node); end + + # Visit a EnsureNode node + sig { params(node: EnsureNode).void } + def visit_ensure_node(node); end + + # Visit a FalseNode node + sig { params(node: FalseNode).void } + def visit_false_node(node); end + + # Visit a FindPatternNode node + sig { params(node: FindPatternNode).void } + def visit_find_pattern_node(node); end + + # Visit a FlipFlopNode node + sig { params(node: FlipFlopNode).void } + def visit_flip_flop_node(node); end + + # Visit a FloatNode node + sig { params(node: FloatNode).void } + def visit_float_node(node); end + + # Visit a ForNode node + sig { params(node: ForNode).void } + def visit_for_node(node); end + + # Visit a ForwardingArgumentsNode node + sig { params(node: ForwardingArgumentsNode).void } + def visit_forwarding_arguments_node(node); end + + # Visit a ForwardingParameterNode node + sig { params(node: ForwardingParameterNode).void } + def visit_forwarding_parameter_node(node); end + + # Visit a ForwardingSuperNode node + sig { params(node: ForwardingSuperNode).void } + def visit_forwarding_super_node(node); end + + # Visit a GlobalVariableAndWriteNode node + sig { params(node: GlobalVariableAndWriteNode).void } + def visit_global_variable_and_write_node(node); end + + # Visit a GlobalVariableOperatorWriteNode node + sig { params(node: GlobalVariableOperatorWriteNode).void } + def visit_global_variable_operator_write_node(node); end + + # Visit a GlobalVariableOrWriteNode node + sig { params(node: GlobalVariableOrWriteNode).void } + def visit_global_variable_or_write_node(node); end + + # Visit a GlobalVariableReadNode node + sig { params(node: GlobalVariableReadNode).void } + def visit_global_variable_read_node(node); end + + # Visit a GlobalVariableTargetNode node + sig { params(node: GlobalVariableTargetNode).void } + def visit_global_variable_target_node(node); end + + # Visit a GlobalVariableWriteNode node + sig { params(node: GlobalVariableWriteNode).void } + def visit_global_variable_write_node(node); end + + # Visit a HashNode node + sig { params(node: HashNode).void } + def visit_hash_node(node); end + + # Visit a HashPatternNode node + sig { params(node: HashPatternNode).void } + def visit_hash_pattern_node(node); end + + # Visit a IfNode node + sig { params(node: IfNode).void } + def visit_if_node(node); end + + # Visit a ImaginaryNode node + sig { params(node: ImaginaryNode).void } + def visit_imaginary_node(node); end + + # Visit a ImplicitNode node + sig { params(node: ImplicitNode).void } + def visit_implicit_node(node); end + + # Visit a ImplicitRestNode node + sig { params(node: ImplicitRestNode).void } + def visit_implicit_rest_node(node); end + + # Visit a InNode node + sig { params(node: InNode).void } + def visit_in_node(node); end + + # Visit a IndexAndWriteNode node + sig { params(node: IndexAndWriteNode).void } + def visit_index_and_write_node(node); end + + # Visit a IndexOperatorWriteNode node + sig { params(node: IndexOperatorWriteNode).void } + def visit_index_operator_write_node(node); end + + # Visit a IndexOrWriteNode node + sig { params(node: IndexOrWriteNode).void } + def visit_index_or_write_node(node); end + + # Visit a IndexTargetNode node + sig { params(node: IndexTargetNode).void } + def visit_index_target_node(node); end + + # Visit a InstanceVariableAndWriteNode node + sig { params(node: InstanceVariableAndWriteNode).void } + def visit_instance_variable_and_write_node(node); end + + # Visit a InstanceVariableOperatorWriteNode node + sig { params(node: InstanceVariableOperatorWriteNode).void } + def visit_instance_variable_operator_write_node(node); end + + # Visit a InstanceVariableOrWriteNode node + sig { params(node: InstanceVariableOrWriteNode).void } + def visit_instance_variable_or_write_node(node); end + + # Visit a InstanceVariableReadNode node + sig { params(node: InstanceVariableReadNode).void } + def visit_instance_variable_read_node(node); end + + # Visit a InstanceVariableTargetNode node + sig { params(node: InstanceVariableTargetNode).void } + def visit_instance_variable_target_node(node); end + + # Visit a InstanceVariableWriteNode node + sig { params(node: InstanceVariableWriteNode).void } + def visit_instance_variable_write_node(node); end + + # Visit a IntegerNode node + sig { params(node: IntegerNode).void } + def visit_integer_node(node); end + + # Visit a InterpolatedMatchLastLineNode node + sig { params(node: InterpolatedMatchLastLineNode).void } + def visit_interpolated_match_last_line_node(node); end + + # Visit a InterpolatedRegularExpressionNode node + sig { params(node: InterpolatedRegularExpressionNode).void } + def visit_interpolated_regular_expression_node(node); end + + # Visit a InterpolatedStringNode node + sig { params(node: InterpolatedStringNode).void } + def visit_interpolated_string_node(node); end + + # Visit a InterpolatedSymbolNode node + sig { params(node: InterpolatedSymbolNode).void } + def visit_interpolated_symbol_node(node); end + + # Visit a InterpolatedXStringNode node + sig { params(node: InterpolatedXStringNode).void } + def visit_interpolated_x_string_node(node); end + + # Visit a ItLocalVariableReadNode node + sig { params(node: ItLocalVariableReadNode).void } + def visit_it_local_variable_read_node(node); end + + # Visit a ItParametersNode node + sig { params(node: ItParametersNode).void } + def visit_it_parameters_node(node); end + + # Visit a KeywordHashNode node + sig { params(node: KeywordHashNode).void } + def visit_keyword_hash_node(node); end + + # Visit a KeywordRestParameterNode node + sig { params(node: KeywordRestParameterNode).void } + def visit_keyword_rest_parameter_node(node); end + + # Visit a LambdaNode node + sig { params(node: LambdaNode).void } + def visit_lambda_node(node); end + + # Visit a LocalVariableAndWriteNode node + sig { params(node: LocalVariableAndWriteNode).void } + def visit_local_variable_and_write_node(node); end + + # Visit a LocalVariableOperatorWriteNode node + sig { params(node: LocalVariableOperatorWriteNode).void } + def visit_local_variable_operator_write_node(node); end + + # Visit a LocalVariableOrWriteNode node + sig { params(node: LocalVariableOrWriteNode).void } + def visit_local_variable_or_write_node(node); end + + # Visit a LocalVariableReadNode node + sig { params(node: LocalVariableReadNode).void } + def visit_local_variable_read_node(node); end + + # Visit a LocalVariableTargetNode node + sig { params(node: LocalVariableTargetNode).void } + def visit_local_variable_target_node(node); end + + # Visit a LocalVariableWriteNode node + sig { params(node: LocalVariableWriteNode).void } + def visit_local_variable_write_node(node); end + + # Visit a MatchLastLineNode node + sig { params(node: MatchLastLineNode).void } + def visit_match_last_line_node(node); end + + # Visit a MatchPredicateNode node + sig { params(node: MatchPredicateNode).void } + def visit_match_predicate_node(node); end + + # Visit a MatchRequiredNode node + sig { params(node: MatchRequiredNode).void } + def visit_match_required_node(node); end + + # Visit a MatchWriteNode node + sig { params(node: MatchWriteNode).void } + def visit_match_write_node(node); end + + # Visit a MissingNode node + sig { params(node: MissingNode).void } + def visit_missing_node(node); end + + # Visit a ModuleNode node + sig { params(node: ModuleNode).void } + def visit_module_node(node); end + + # Visit a MultiTargetNode node + sig { params(node: MultiTargetNode).void } + def visit_multi_target_node(node); end + + # Visit a MultiWriteNode node + sig { params(node: MultiWriteNode).void } + def visit_multi_write_node(node); end + + # Visit a NextNode node + sig { params(node: NextNode).void } + def visit_next_node(node); end + + # Visit a NilNode node + sig { params(node: NilNode).void } + def visit_nil_node(node); end + + # Visit a NoBlockParameterNode node + sig { params(node: NoBlockParameterNode).void } + def visit_no_block_parameter_node(node); end + + # Visit a NoKeywordsParameterNode node + sig { params(node: NoKeywordsParameterNode).void } + def visit_no_keywords_parameter_node(node); end + + # Visit a NumberedParametersNode node + sig { params(node: NumberedParametersNode).void } + def visit_numbered_parameters_node(node); end + + # Visit a NumberedReferenceReadNode node + sig { params(node: NumberedReferenceReadNode).void } + def visit_numbered_reference_read_node(node); end + + # Visit a OptionalKeywordParameterNode node + sig { params(node: OptionalKeywordParameterNode).void } + def visit_optional_keyword_parameter_node(node); end + + # Visit a OptionalParameterNode node + sig { params(node: OptionalParameterNode).void } + def visit_optional_parameter_node(node); end + + # Visit a OrNode node + sig { params(node: OrNode).void } + def visit_or_node(node); end + + # Visit a ParametersNode node + sig { params(node: ParametersNode).void } + def visit_parameters_node(node); end + + # Visit a ParenthesesNode node + sig { params(node: ParenthesesNode).void } + def visit_parentheses_node(node); end + + # Visit a PinnedExpressionNode node + sig { params(node: PinnedExpressionNode).void } + def visit_pinned_expression_node(node); end + + # Visit a PinnedVariableNode node + sig { params(node: PinnedVariableNode).void } + def visit_pinned_variable_node(node); end + + # Visit a PostExecutionNode node + sig { params(node: PostExecutionNode).void } + def visit_post_execution_node(node); end + + # Visit a PreExecutionNode node + sig { params(node: PreExecutionNode).void } + def visit_pre_execution_node(node); end + + # Visit a ProgramNode node + sig { params(node: ProgramNode).void } + def visit_program_node(node); end + + # Visit a RangeNode node + sig { params(node: RangeNode).void } + def visit_range_node(node); end + + # Visit a RationalNode node + sig { params(node: RationalNode).void } + def visit_rational_node(node); end + + # Visit a RedoNode node + sig { params(node: RedoNode).void } + def visit_redo_node(node); end + + # Visit a RegularExpressionNode node + sig { params(node: RegularExpressionNode).void } + def visit_regular_expression_node(node); end + + # Visit a RequiredKeywordParameterNode node + sig { params(node: RequiredKeywordParameterNode).void } + def visit_required_keyword_parameter_node(node); end + + # Visit a RequiredParameterNode node + sig { params(node: RequiredParameterNode).void } + def visit_required_parameter_node(node); end + + # Visit a RescueModifierNode node + sig { params(node: RescueModifierNode).void } + def visit_rescue_modifier_node(node); end + + # Visit a RescueNode node + sig { params(node: RescueNode).void } + def visit_rescue_node(node); end + + # Visit a RestParameterNode node + sig { params(node: RestParameterNode).void } + def visit_rest_parameter_node(node); end + + # Visit a RetryNode node + sig { params(node: RetryNode).void } + def visit_retry_node(node); end + + # Visit a ReturnNode node + sig { params(node: ReturnNode).void } + def visit_return_node(node); end + + # Visit a SelfNode node + sig { params(node: SelfNode).void } + def visit_self_node(node); end + + # Visit a ShareableConstantNode node + sig { params(node: ShareableConstantNode).void } + def visit_shareable_constant_node(node); end + + # Visit a SingletonClassNode node + sig { params(node: SingletonClassNode).void } + def visit_singleton_class_node(node); end + + # Visit a SourceEncodingNode node + sig { params(node: SourceEncodingNode).void } + def visit_source_encoding_node(node); end + + # Visit a SourceFileNode node + sig { params(node: SourceFileNode).void } + def visit_source_file_node(node); end + + # Visit a SourceLineNode node + sig { params(node: SourceLineNode).void } + def visit_source_line_node(node); end + + # Visit a SplatNode node + sig { params(node: SplatNode).void } + def visit_splat_node(node); end + + # Visit a StatementsNode node + sig { params(node: StatementsNode).void } + def visit_statements_node(node); end + + # Visit a StringNode node + sig { params(node: StringNode).void } + def visit_string_node(node); end + + # Visit a SuperNode node + sig { params(node: SuperNode).void } + def visit_super_node(node); end + + # Visit a SymbolNode node + sig { params(node: SymbolNode).void } + def visit_symbol_node(node); end + + # Visit a TrueNode node + sig { params(node: TrueNode).void } + def visit_true_node(node); end + + # Visit a UndefNode node + sig { params(node: UndefNode).void } + def visit_undef_node(node); end + + # Visit a UnlessNode node + sig { params(node: UnlessNode).void } + def visit_unless_node(node); end + + # Visit a UntilNode node + sig { params(node: UntilNode).void } + def visit_until_node(node); end + + # Visit a WhenNode node + sig { params(node: WhenNode).void } + def visit_when_node(node); end + + # Visit a WhileNode node + sig { params(node: WhileNode).void } + def visit_while_node(node); end + + # Visit a XStringNode node + sig { params(node: XStringNode).void } + def visit_x_string_node(node); end + + # Visit a YieldNode node + sig { params(node: YieldNode).void } + def visit_yield_node(node); end + end +end diff --git a/rbi/prism.rbi b/rbi/prism.rbi deleted file mode 100644 index b31adc64a1..0000000000 --- a/rbi/prism.rbi +++ /dev/null @@ -1,63 +0,0 @@ -# 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/rbi/prism/compiler.rbi b/rbi/prism/compiler.rbi deleted file mode 100644 index c738236dea..0000000000 --- a/rbi/prism/compiler.rbi +++ /dev/null @@ -1,12 +0,0 @@ -# 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/rbi/prism/inspect_visitor.rbi b/rbi/prism/inspect_visitor.rbi deleted file mode 100644 index bb0c22409e..0000000000 --- a/rbi/prism/inspect_visitor.rbi +++ /dev/null @@ -1,12 +0,0 @@ -# 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/rbi/prism/node_ext.rbi b/rbi/prism/node_ext.rbi deleted file mode 100644 index e5cf7376cc..0000000000 --- a/rbi/prism/node_ext.rbi +++ /dev/null @@ -1,107 +0,0 @@ -# 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/rbi/prism/parse_result.rbi b/rbi/prism/parse_result.rbi deleted file mode 100644 index 6f2bbb6146..0000000000 --- a/rbi/prism/parse_result.rbi +++ /dev/null @@ -1,404 +0,0 @@ -# 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/rbi/prism/reflection.rbi b/rbi/prism/reflection.rbi deleted file mode 100644 index b21e9b5def..0000000000 --- a/rbi/prism/reflection.rbi +++ /dev/null @@ -1,58 +0,0 @@ -# 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/rbi/prism/string_query.rbi b/rbi/prism/string_query.rbi deleted file mode 100644 index 3e5bae1572..0000000000 --- a/rbi/prism/string_query.rbi +++ /dev/null @@ -1,12 +0,0 @@ -# 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/rbi/prism/translation/parser_versions.rbi b/rbi/prism/translation/parser_versions.rbi index 8a7297be3e..2aad1be440 100644 --- a/rbi/prism/translation/parser_versions.rbi +++ b/rbi/prism/translation/parser_versions.rbi @@ -21,3 +21,8 @@ class Prism::Translation::Parser40 < Prism::Translation::Parser sig { override.returns(Integer) } def version; end end + +class Prism::Translation::Parser41 < Prism::Translation::Parser + sig { override.returns(Integer) } + def version; end +end diff --git a/rbi/rubyvm/node_find.rbi b/rbi/rubyvm/node_find.rbi new file mode 100644 index 0000000000..92bae0af52 --- /dev/null +++ b/rbi/rubyvm/node_find.rbi @@ -0,0 +1,14 @@ +# typed: true + +class RubyVM::InstructionSequence + sig { params(callable: T.any(Method, UnboundMethod, Proc)).returns(T.nilable(RubyVM::InstructionSequence)) } + def self.of(callable); end + + sig { returns(T::Array[T.untyped]) } + def to_a; end +end + +module RubyVM::AbstractSyntaxTree + sig { params(location: Thread::Backtrace::Location).returns(Integer) } + def self.node_id_for_backtrace_location(location); end +end diff --git a/rust/ruby-prism-sys/Cargo.toml b/rust/ruby-prism-sys/Cargo.toml index edda0ed1fd..4578488af3 100644 --- a/rust/ruby-prism-sys/Cargo.toml +++ b/rust/ruby-prism-sys/Cargo.toml @@ -3,7 +3,6 @@ name = "ruby-prism-sys" version = "1.9.0" edition = "2021" license = "MIT" -license-file = "../../LICENSE.md" repository = "https://github.com/ruby/prism" description = "Rust bindings to Ruby's prism parsing library" links = "prism" diff --git a/rust/ruby-prism-sys/build/main.rs b/rust/ruby-prism-sys/build/main.rs index 798d06d8ff..594c01ad9c 100644 --- a/rust/ruby-prism-sys/build/main.rs +++ b/rust/ruby-prism-sys/build/main.rs @@ -9,6 +9,7 @@ fn main() { let ruby_build_path = prism_lib_path(); let ruby_include_path = prism_include_path(); + emit_rerun_hints(&ruby_include_path); // Tell cargo/rustc that we want to link against `libprism.a`. println!("cargo:rustc-link-lib=static=prism"); @@ -23,6 +24,19 @@ fn main() { write_bindings(&bindings); } +fn emit_rerun_hints(ruby_include_path: &Path) { + println!("cargo:rerun-if-env-changed=PRISM_INCLUDE_DIR"); + println!("cargo:rerun-if-env-changed=PRISM_LIB_DIR"); + println!("cargo:rerun-if-changed={}", ruby_include_path.display()); + + if let Some(project_root) = ruby_include_path.parent() { + let src_path = project_root.join("src"); + if src_path.exists() { + println!("cargo:rerun-if-changed={}", src_path.display()); + } + } +} + /// Gets the path to project files (`libprism*`) at `[root]/build/`. /// fn prism_lib_path() -> PathBuf { @@ -101,7 +115,6 @@ fn generate_bindings(ruby_include_path: &Path) -> bindgen::Bindings { .derive_default(true) .generate_block(true) .generate_comments(true) - .header(ruby_include_path.join("prism/defines.h").to_str().unwrap()) .header(ruby_include_path.join("prism.h").to_str().unwrap()) .clang_arg(format!("-I{}", ruby_include_path.to_str().unwrap())) .clang_arg("-fparse-all-comments") @@ -115,43 +128,86 @@ fn generate_bindings(ruby_include_path: &Path) -> bindgen::Bindings { .sort_semantically(true) // Structs .allowlist_type("pm_comment_t") + .allowlist_type("pm_constant_t") .allowlist_type("pm_diagnostic_t") - .allowlist_type("pm_list_t") + .allowlist_type("pm_error_level_t") + .allowlist_type("pm_line_column_t") + .allowlist_type("pm_line_offset_list_t") + .allowlist_type("pm_location_t") .allowlist_type("pm_magic_comment_t") .allowlist_type("pm_node_t") .allowlist_type("pm_node_type") - .allowlist_type("pm_pack_size") - .allowlist_type("pm_parser_t") + .allowlist_type("pm_options_t") + .allowlist_type("pm_options_scope_t") .allowlist_type("pm_string_t") + .allowlist_type("pm_warning_level_t") .allowlist_type(r"^pm_\w+_node_t") .allowlist_type(r"^pm_\w+_flags") // Enums .rustified_non_exhaustive_enum("pm_comment_type_t") + .rustified_non_exhaustive_enum("pm_error_level_t") .rustified_non_exhaustive_enum(r"pm_\w+_flags") .rustified_non_exhaustive_enum("pm_node_type") - .rustified_non_exhaustive_enum("pm_pack_encoding") - .rustified_non_exhaustive_enum("pm_pack_endian") - .rustified_non_exhaustive_enum("pm_pack_length_type") - .rustified_non_exhaustive_enum("pm_pack_result") - .rustified_non_exhaustive_enum("pm_pack_signed") - .rustified_non_exhaustive_enum("pm_pack_size") - .rustified_non_exhaustive_enum("pm_pack_type") - .rustified_non_exhaustive_enum("pm_pack_variant") + .rustified_non_exhaustive_enum("pm_warning_level_t") // Functions - .allowlist_function("pm_list_empty_p") - .allowlist_function("pm_list_free") - .allowlist_function("pm_node_destroy") - .allowlist_function("pm_pack_parse") + .allowlist_function("pm_arena_free") + .allowlist_function("pm_arena_new") + .allowlist_function("pm_comment_location") + .allowlist_function("pm_comment_type") + .allowlist_function("pm_constant_length") + .allowlist_function("pm_constant_start") + .allowlist_function("pm_diagnostic_error_level") + .allowlist_function("pm_diagnostic_location") + .allowlist_function("pm_diagnostic_message") + .allowlist_function("pm_diagnostic_type") + .allowlist_function("pm_diagnostic_warning_level") + .allowlist_function("pm_line_offset_list_line_column") + .allowlist_function("pm_magic_comment_key") + .allowlist_function("pm_magic_comment_value") + .allowlist_function("pm_options_command_line_set") + .allowlist_function("pm_options_encoding_locked_set") + .allowlist_function("pm_options_encoding_set") + .allowlist_function("pm_options_filepath_set") + .allowlist_function("pm_options_free") + .allowlist_function("pm_options_frozen_string_literal_set") + .allowlist_function("pm_options_line_set") + .allowlist_function("pm_options_main_script_set") + .allowlist_function("pm_options_new") + .allowlist_function("pm_options_partial_script_set") + .allowlist_function("pm_options_scope_forwarding_set") + .allowlist_function("pm_options_scope_init") + .allowlist_function("pm_options_scope_local_mut") + .allowlist_function("pm_options_scope_mut") + .allowlist_function("pm_options_scopes_init") + .allowlist_function("pm_options_version_set") .allowlist_function("pm_parse") + .allowlist_function("pm_parser_comments_each") + .allowlist_function("pm_parser_comments_size") + .allowlist_function("pm_parser_constant") + .allowlist_function("pm_parser_constants_each") + .allowlist_function("pm_parser_constants_size") + .allowlist_function("pm_parser_data_loc") + .allowlist_function("pm_parser_errors_each") + .allowlist_function("pm_parser_errors_size") .allowlist_function("pm_parser_free") - .allowlist_function("pm_parser_init") + .allowlist_function("pm_parser_frozen_string_literal") + .allowlist_function("pm_parser_line_offsets") + .allowlist_function("pm_parser_magic_comments_each") + .allowlist_function("pm_parser_magic_comments_size") + .allowlist_function("pm_parser_new") + .allowlist_function("pm_parser_start") + .allowlist_function("pm_parser_start_line") + .allowlist_function("pm_parser_warnings_each") + .allowlist_function("pm_parser_warnings_size") .allowlist_function("pm_size_to_native") - .allowlist_function("pm_string_free") + .allowlist_function("pm_string_constant_init") .allowlist_function("pm_string_length") .allowlist_function("pm_string_source") .allowlist_function("pm_version") // Vars .allowlist_var(r"^pm_encoding\S+") + .allowlist_var(r"^PM_OPTIONS_COMMAND_LINE_\w+") + .allowlist_var(r"^PM_OPTIONS_SCOPE_FORWARDING_\w+") .generate() .expect("Unable to generate prism bindings") } diff --git a/rust/ruby-prism-sys/src/lib.rs b/rust/ruby-prism-sys/src/lib.rs index 8d4c979f5d..7d200124fa 100644 --- a/rust/ruby-prism-sys/src/lib.rs +++ b/rust/ruby-prism-sys/src/lib.rs @@ -27,6 +27,7 @@ #[allow(unused_qualifications)] #[allow(clippy::missing_const_for_fn)] #[allow(clippy::use_self)] +#[allow(trivial_casts)] mod bindings { // In `build.rs`, we use `bindgen` to generate bindings based on C headers // and `libprism`. Here is where we pull in those bindings and make diff --git a/rust/ruby-prism-sys/tests/node_tests.rs b/rust/ruby-prism-sys/tests/node_tests.rs index 87b6c2a337..31040668b7 100644 --- a/rust/ruby-prism-sys/tests/node_tests.rs +++ b/rust/ruby-prism-sys/tests/node_tests.rs @@ -1,27 +1,25 @@ -use std::{ffi::CString, mem::MaybeUninit}; +use std::ffi::CString; -use ruby_prism_sys::{pm_node_destroy, pm_node_type}; -use ruby_prism_sys::{pm_parse, pm_parser_free, pm_parser_init, pm_parser_t}; +use ruby_prism_sys::{pm_arena_free, pm_arena_new, pm_node_type}; +use ruby_prism_sys::{pm_parse, pm_parser_free, pm_parser_new}; #[test] fn node_test() { - let mut parser = MaybeUninit::::uninit(); let code = CString::new("class Foo; end").unwrap(); unsafe { - pm_parser_init( - parser.as_mut_ptr(), + let arena = pm_arena_new(); + let parser = pm_parser_new( + arena, code.as_ptr().cast::(), code.as_bytes().len(), std::ptr::null(), ); + let node = pm_parse(parser); - let parser = parser.assume_init_mut(); - let parsed_node = pm_parse(parser); + assert_eq!((*node).type_, pm_node_type::PM_PROGRAM_NODE as u16); - assert_eq!((*parsed_node).type_, pm_node_type::PM_PROGRAM_NODE as u16); - - pm_node_destroy(parser, parsed_node); pm_parser_free(parser); + pm_arena_free(arena); } } diff --git a/rust/ruby-prism-sys/tests/pack_tests.rs b/rust/ruby-prism-sys/tests/pack_tests.rs deleted file mode 100644 index 63bc1cb1ea..0000000000 --- a/rust/ruby-prism-sys/tests/pack_tests.rs +++ /dev/null @@ -1,57 +0,0 @@ -use std::{ffi::CString, mem::MaybeUninit}; - -use ruby_prism_sys::{ - pm_pack_encoding, pm_pack_endian, pm_pack_length_type, pm_pack_parse, pm_pack_result, pm_pack_signed, pm_pack_size, - pm_pack_type, pm_pack_variant, pm_size_to_native, -}; - -#[test] -fn pack_parse_test() { - let variant_arg = pm_pack_variant::PM_PACK_VARIANT_PACK; - let first_format = CString::new("C").unwrap(); - let end_format = CString::new("").unwrap(); - let mut format = vec![first_format.as_ptr(), end_format.as_ptr()]; - - let mut type_out = MaybeUninit::::uninit(); - let mut signed_type_out = MaybeUninit::::uninit(); - let mut endian_out = MaybeUninit::::uninit(); - let mut size_out = MaybeUninit::::uninit(); - let mut length_type_out = MaybeUninit::::uninit(); - let mut length_out = 0_u64; - let mut encoding_out = MaybeUninit::::uninit(); - - unsafe { - let result = pm_pack_parse( - variant_arg, - format.as_mut_ptr(), - end_format.as_ptr(), - type_out.as_mut_ptr(), - signed_type_out.as_mut_ptr(), - endian_out.as_mut_ptr(), - size_out.as_mut_ptr(), - length_type_out.as_mut_ptr(), - &raw mut length_out, - encoding_out.as_mut_ptr(), - ); - - assert_eq!(result, pm_pack_result::PM_PACK_OK); - - let type_out = type_out.assume_init(); - let signed_type_out = signed_type_out.assume_init(); - let endian_out = endian_out.assume_init(); - let size_out = size_out.assume_init(); - let length_type_out = length_type_out.assume_init(); - let encoding_out = encoding_out.assume_init(); - - assert_eq!(type_out, pm_pack_type::PM_PACK_INTEGER); - assert_eq!(signed_type_out, pm_pack_signed::PM_PACK_UNSIGNED); - assert_eq!(endian_out, pm_pack_endian::PM_PACK_AGNOSTIC_ENDIAN); - assert_eq!(size_out, pm_pack_size::PM_PACK_SIZE_8); - assert_eq!(length_type_out, pm_pack_length_type::PM_PACK_LENGTH_FIXED); - assert_eq!(length_out, 1); - assert_eq!(encoding_out, pm_pack_encoding::PM_PACK_ENCODING_ASCII_8BIT); - - let native_size = pm_size_to_native(size_out); - assert_eq!(native_size, 1); - } -} diff --git a/rust/ruby-prism-sys/tests/parser_tests.rs b/rust/ruby-prism-sys/tests/parser_tests.rs index 6ce3a3b980..0cfc234de2 100644 --- a/rust/ruby-prism-sys/tests/parser_tests.rs +++ b/rust/ruby-prism-sys/tests/parser_tests.rs @@ -1,14 +1,24 @@ -use std::{ - ffi::{CStr, CString}, - mem::MaybeUninit, - path::Path, -}; +use std::ffi::{CStr, CString}; +use std::path::Path; use ruby_prism_sys::{ - pm_comment_t, pm_comment_type_t, pm_diagnostic_t, pm_node_destroy, pm_parse, pm_parser_free, pm_parser_init, - pm_parser_t, + pm_arena_free, pm_arena_new, pm_comment_location, pm_comment_type, pm_comment_type_t, pm_diagnostic_location, + pm_diagnostic_message, pm_parse, pm_parser_comments_each, pm_parser_errors_each, pm_parser_free, pm_parser_new, }; +unsafe extern "C" fn collect_comment(comment: *const ruby_prism_sys::pm_comment_t, data: *mut std::ffi::c_void) { + let vec = &mut *(data.cast::>()); + vec.push(comment); +} + +unsafe extern "C" fn collect_diagnostic( + diagnostic: *const ruby_prism_sys::pm_diagnostic_t, + data: *mut std::ffi::c_void, +) { + let vec = &mut *(data.cast::>()); + vec.push(diagnostic); +} + fn ruby_file_contents() -> (CString, usize) { let rust_path = Path::new(env!("CARGO_MANIFEST_DIR")); let ruby_file_path = rust_path.join("../../lib/prism.rb").canonicalize().unwrap(); @@ -22,80 +32,75 @@ fn ruby_file_contents() -> (CString, usize) { fn init_test() { let (ruby_file_contents, len) = ruby_file_contents(); let source = ruby_file_contents.as_ptr().cast::(); - let mut parser = MaybeUninit::::uninit(); unsafe { - pm_parser_init(parser.as_mut_ptr(), source, len, std::ptr::null()); - let parser = parser.assume_init_mut(); + let arena = pm_arena_new(); + let parser = pm_parser_new(arena, source, len, std::ptr::null()); pm_parser_free(parser); + pm_arena_free(arena); } } #[test] fn comments_test() { let source = CString::new("# Meow!").unwrap(); - let mut parser = MaybeUninit::::uninit(); unsafe { - pm_parser_init( - parser.as_mut_ptr(), + let arena = pm_arena_new(); + let parser = pm_parser_new( + arena, source.as_ptr().cast::(), source.as_bytes().len(), std::ptr::null(), ); - let parser = parser.assume_init_mut(); - let node = pm_parse(parser); + let _node = pm_parse(parser); + + let mut comments: Vec<*const ruby_prism_sys::pm_comment_t> = Vec::new(); + pm_parser_comments_each(parser, Some(collect_comment), (&raw mut comments).cast()); - let comment_list = &parser.comment_list; - let comment = comment_list.head as *const pm_comment_t; - assert_eq!((*comment).type_, pm_comment_type_t::PM_COMMENT_INLINE); + assert_eq!(comments.len(), 1); + let comment = comments[0]; + assert_eq!(pm_comment_type(comment), pm_comment_type_t::PM_COMMENT_INLINE); - let location = { - let start = (*comment).location.start; - let end = (*comment).location.start + (*comment).location.length; - start..end - }; - assert_eq!(location, 0..7); + let location = pm_comment_location(comment); + assert_eq!(location.start..location.start + location.length, 0..7); - pm_node_destroy(parser, node); pm_parser_free(parser); + pm_arena_free(arena); } } #[test] fn diagnostics_test() { let source = CString::new("class Foo;").unwrap(); - let mut parser = MaybeUninit::::uninit(); unsafe { - pm_parser_init( - parser.as_mut_ptr(), + let arena = pm_arena_new(); + let parser = pm_parser_new( + arena, source.as_ptr().cast::(), source.as_bytes().len(), std::ptr::null(), ); - let parser = parser.assume_init_mut(); - let node = pm_parse(parser); + let _node = pm_parse(parser); + + let mut errors: Vec<*const ruby_prism_sys::pm_diagnostic_t> = Vec::new(); + pm_parser_errors_each(parser, Some(collect_diagnostic), (&raw mut errors).cast()); - let error_list = &parser.error_list; - assert!(!error_list.head.is_null()); + assert!(!errors.is_empty()); + let error = errors[0]; - let error = error_list.head as *const pm_diagnostic_t; - let message = CStr::from_ptr((*error).message); + let message = CStr::from_ptr(pm_diagnostic_message(error)); assert_eq!( message.to_string_lossy(), "unexpected end-of-input, assuming it is closing the parent top level context" ); - let location = { - let start = (*error).location.start; - let end = (*error).location.start + (*error).location.length; - start..end - }; - assert_eq!(location, 10..10); + let location = pm_diagnostic_location(error); + assert_eq!(location.start..location.start + location.length, 10..10); - pm_node_destroy(parser, node); pm_parser_free(parser); + pm_arena_free(arena); } } diff --git a/rust/ruby-prism-sys/tests/utils_tests.rs b/rust/ruby-prism-sys/tests/utils_tests.rs index 13de5e8761..c619765c63 100644 --- a/rust/ruby-prism-sys/tests/utils_tests.rs +++ b/rust/ruby-prism-sys/tests/utils_tests.rs @@ -1,7 +1,4 @@ -use std::{ - ffi::{CStr, CString}, - mem::MaybeUninit, -}; +use std::ffi::CStr; #[test] fn version_test() { @@ -14,108 +11,3 @@ fn version_test() { assert_eq!(&cstring.to_string_lossy(), "1.9.0"); } - -#[test] -fn list_test() { - use ruby_prism_sys::{pm_list_empty_p, pm_list_free, pm_list_t}; - - let mut list = MaybeUninit::::zeroed(); - - unsafe { - let list = list.assume_init_mut(); - - assert!(pm_list_empty_p(list)); - - pm_list_free(list); - } -} - -mod string { - use ruby_prism_sys::{ - pm_string_free, pm_string_length, pm_string_source, pm_string_t, pm_string_t__bindgen_ty_1, PM_STRING_CONSTANT, - PM_STRING_MAPPED, PM_STRING_OWNED, PM_STRING_SHARED, - }; - - use super::*; - - struct S { - c_string: CString, - pm_string: pm_string_t, - } - - impl S { - fn start_ptr(&self) -> *const u8 { - self.c_string.as_ptr().cast::() - } - } - - fn make_string(string_type: pm_string_t__bindgen_ty_1) -> S { - let c_string = CString::new("0123456789012345").unwrap(); - - let pm_string = pm_string_t { - type_: string_type, - source: c_string.as_ptr().cast::(), - length: c_string.as_bytes().len(), - }; - - S { c_string, pm_string } - } - - #[test] - fn shared_string_test() { - let mut s = make_string(PM_STRING_SHARED); - - unsafe { - let len = pm_string_length(&raw const s.pm_string); - assert_eq!(len, 16); - - let result_start = pm_string_source(&raw const s.pm_string); - assert_eq!(s.start_ptr(), result_start); - - pm_string_free(&raw mut s.pm_string); - } - } - - #[test] - fn owned_string_test() { - let s = make_string(PM_STRING_OWNED); - - unsafe { - let result_len = pm_string_length(&raw const s.pm_string); - assert_eq!(result_len, 16); - - let result_start = pm_string_source(&raw const s.pm_string); - assert_eq!(s.pm_string.source, result_start); - - // Don't drop the pm_string--we don't own it anymore! - } - } - - #[test] - fn constant_string_test() { - let mut s = make_string(PM_STRING_CONSTANT); - - unsafe { - let result_len = pm_string_length(&raw const s.pm_string); - assert_eq!(result_len, 16); - - let result_start = pm_string_source(&raw const s.pm_string); - assert_eq!(s.pm_string.source, result_start); - - pm_string_free(&raw mut s.pm_string); - } - } - - #[test] - fn mapped_string_test() { - let s = make_string(PM_STRING_MAPPED); - - unsafe { - let result_len = pm_string_length(&raw const s.pm_string); - assert_eq!(result_len, 16); - - let result_start = pm_string_source(&raw const s.pm_string); - assert_eq!(s.pm_string.source, result_start); - } - } -} diff --git a/rust/ruby-prism/Cargo.toml b/rust/ruby-prism/Cargo.toml index 426ae159dc..302780399c 100644 --- a/rust/ruby-prism/Cargo.toml +++ b/rust/ruby-prism/Cargo.toml @@ -3,7 +3,6 @@ name = "ruby-prism" version = "1.9.0" edition = "2021" license = "MIT" -license-file = "../../LICENSE.md" repository = "https://github.com/ruby/prism" description = "Rustified version of Ruby's prism parsing library" authors = [ diff --git a/rust/ruby-prism/build.rs b/rust/ruby-prism/build.rs index 8faad957ab..1a3bb45a17 100644 --- a/rust/ruby-prism/build.rs +++ b/rust/ruby-prism/build.rs @@ -235,7 +235,7 @@ fn write_node(file: &mut File, flags: &[Flags], node: &Node) -> Result<(), Box {{", node.name)?; writeln!(file, " /// The pointer to the parser this node came from.")?; - writeln!(file, " parser: NonNull,")?; + writeln!(file, " parser: *const pm_parser_t,")?; writeln!(file)?; writeln!(file, " /// The raw pointer to the node allocated by prism.")?; writeln!(file, " pointer: *mut pm{}_t,", struct_name(&node.name))?; @@ -554,7 +554,6 @@ fn write_bindings(config: &Config) -> Result<(), Box> { file, r" use std::marker::PhantomData; -use std::ptr::NonNull; #[allow(clippy::wildcard_imports)] use ruby_prism_sys::*; @@ -581,7 +580,7 @@ use crate::{{ConstantId, ConstantList, Integer, Location, NodeList}}; writeln!(file, " /// The `{}` node", node.name)?; writeln!(file, " {} {{", node.name)?; writeln!(file, " /// The pointer to the associated parser this node came from.")?; - writeln!(file, " parser: NonNull,")?; + writeln!(file, " parser: *const pm_parser_t,")?; writeln!(file)?; writeln!(file, " /// The raw pointer to the node allocated by prism.")?; writeln!(file, " pointer: *mut pm{}_t,", struct_name(&node.name))?; @@ -606,7 +605,7 @@ impl<'pr> Node<'pr> {{ /// #[allow(clippy::not_unsafe_ptr_arg_deref)] #[allow(clippy::cast_ptr_alignment)] - pub(crate) fn new(parser: NonNull, node: *mut pm_node_t) -> Self {{ + pub(crate) fn new(parser: *const pm_parser_t, node: *mut pm_node_t) -> Self {{ match unsafe {{ (*node).type_ }} {{" )?; diff --git a/rust/ruby-prism/src/lib.rs b/rust/ruby-prism/src/lib.rs index 6824768193..6337957d20 100644 --- a/rust/ruby-prism/src/lib.rs +++ b/rust/ruby-prism/src/lib.rs @@ -8,22 +8,349 @@ // that doesn't follow the clippy rules. We don't want to see those warnings. #[allow(clippy::too_many_lines, clippy::use_self)] mod bindings { + use std::ptr::NonNull; + // In `build.rs`, we generate bindings based on the config.yml file. Here is // where we pull in those bindings and make them part of our library. include!(concat!(env!("OUT_DIR"), "/bindings.rs")); } mod node; +mod node_ext; mod parse_result; -use std::mem::MaybeUninit; +use std::ffi::CString; use std::ptr::NonNull; pub use self::bindings::*; pub use self::node::{ConstantId, ConstantList, ConstantListIter, Integer, NodeList, NodeListIter}; +pub use self::node_ext::{ConstantPathError, FullName}; pub use self::parse_result::{Comment, CommentType, Comments, Diagnostic, Diagnostics, Location, MagicComment, MagicComments, ParseResult}; -use ruby_prism_sys::{pm_parse, pm_parser_init, pm_parser_t}; +use ruby_prism_sys::{ + pm_arena_new, pm_options_command_line_set, pm_options_encoding_locked_set, pm_options_encoding_set, pm_options_filepath_set, pm_options_free, pm_options_frozen_string_literal_set, pm_options_line_set, pm_options_main_script_set, pm_options_new, pm_options_partial_script_set, + pm_options_scope_forwarding_set, pm_options_scope_init, pm_options_scope_local_mut, pm_options_scope_mut, pm_options_scopes_init, pm_options_t, pm_options_version_set, pm_parse, pm_parser_new, pm_string_constant_init, +}; + +/// The version of Ruby syntax to parse with. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum Version { + /// Use the latest version of prism. + Latest, + /// The vendored version of prism in `CRuby` 3.3.x. + CRuby3_3, + /// The vendored version of prism in `CRuby` 3.4.x. + CRuby3_4, + /// The vendored version of prism in `CRuby` 3.5.x / 4.0.x. + CRuby3_5, + /// The vendored version of prism in `CRuby` 4.1.x. + CRuby4_1, +} + +impl Version { + /// Calls `pm_options_version_set` with the appropriate version string. + /// `Latest` passes `NULL` to get the default behavior. + unsafe fn set_on(self, opts: *mut pm_options_t) { + match self { + Self::Latest => { + pm_options_version_set(opts, std::ptr::null(), 0); + }, + Self::CRuby3_3 => { + pm_options_version_set(opts, c"3.3".as_ptr(), 3); + }, + Self::CRuby3_4 => { + pm_options_version_set(opts, c"3.4".as_ptr(), 3); + }, + Self::CRuby3_5 => { + pm_options_version_set(opts, c"3.5".as_ptr(), 3); + }, + Self::CRuby4_1 => { + pm_options_version_set(opts, c"4.1".as_ptr(), 3); + }, + } + } +} + +/// A command line option that affects parsing behavior. +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub enum CommandLineFlag { + /// `-a`: splits the input line `$_` into `$F`. + A, + /// `-e`: specifies a script to be executed. + E, + /// `-l`: chomps the input line by default. + L, + /// `-n`: wraps the script in a `while gets` loop. + N, + /// `-p`: prints the value of `$_` at the end of each loop. + P, + /// `-x`: searches the input file for a shebang. + X, +} + +impl From for u8 { + fn from(flag: CommandLineFlag) -> Self { + match flag { + CommandLineFlag::A => ruby_prism_sys::PM_OPTIONS_COMMAND_LINE_A, + CommandLineFlag::E => ruby_prism_sys::PM_OPTIONS_COMMAND_LINE_E, + CommandLineFlag::L => ruby_prism_sys::PM_OPTIONS_COMMAND_LINE_L, + CommandLineFlag::N => ruby_prism_sys::PM_OPTIONS_COMMAND_LINE_N, + CommandLineFlag::P => ruby_prism_sys::PM_OPTIONS_COMMAND_LINE_P, + CommandLineFlag::X => ruby_prism_sys::PM_OPTIONS_COMMAND_LINE_X, + } + } +} + +/// A forwarding parameter for a scope. +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub enum ScopeForwardingFlag { + /// Forwarding with the `*` parameter. + Positionals, + /// Forwarding with the `**` parameter. + Keywords, + /// Forwarding with the `&` parameter. + Block, + /// Forwarding with the `...` parameter. + All, +} + +impl From for u8 { + fn from(flag: ScopeForwardingFlag) -> Self { + match flag { + ScopeForwardingFlag::Positionals => ruby_prism_sys::PM_OPTIONS_SCOPE_FORWARDING_POSITIONALS, + ScopeForwardingFlag::Keywords => ruby_prism_sys::PM_OPTIONS_SCOPE_FORWARDING_KEYWORDS, + ScopeForwardingFlag::Block => ruby_prism_sys::PM_OPTIONS_SCOPE_FORWARDING_BLOCK, + ScopeForwardingFlag::All => ruby_prism_sys::PM_OPTIONS_SCOPE_FORWARDING_ALL, + } + } +} + +/// A scope of locals surrounding the code that is being parsed. +#[derive(Debug, Clone, Default)] +pub struct Scope { + locals: Vec>, + forwarding: Vec, +} + +impl Scope { + /// Sets the local variable names in this scope. + #[must_use] + pub fn locals(mut self, locals: Vec>) -> Self { + self.locals = locals; + self + } + + /// Sets the forwarding parameters in this scope. + #[must_use] + pub fn forwarding(mut self, forwarding: Vec) -> Self { + self.forwarding = forwarding; + self + } +} + +/// Options that can be passed to the parser. +#[derive(Debug, Clone, Default)] +pub struct Options { + filepath: Option, + line: Option, + encoding: Option, + encoding_locked: bool, + frozen_string_literal: Option, + command_line: Vec, + version: Option, + main_script: bool, + partial_script: bool, + scopes: Vec, +} + +impl Options { + /// Sets the filepath option. + #[must_use] + pub fn filepath(mut self, filepath: &str) -> Self { + self.filepath = Some(filepath.to_string()); + self + } + + /// Sets the line option. + #[must_use] + pub const fn line(mut self, line: i32) -> Self { + self.line = Some(line); + self + } + + /// Sets the encoding option. + #[must_use] + pub fn encoding(mut self, encoding: &str) -> Self { + self.encoding = Some(encoding.to_string()); + self + } + + /// Sets the encoding locked option. + #[must_use] + pub const fn encoding_locked(mut self, locked: bool) -> Self { + self.encoding_locked = locked; + self + } + + /// Sets the frozen string literal option. `Some(true)` freezes string + /// literals, `Some(false)` keeps them mutable, and `None` leaves the + /// option unset. + #[must_use] + pub const fn frozen_string_literal(mut self, frozen: Option) -> Self { + self.frozen_string_literal = frozen; + self + } + + /// Sets the command line flags. + #[must_use] + pub fn command_line(mut self, command_line: Vec) -> Self { + self.command_line = command_line; + self + } + + /// Sets the version option. + #[must_use] + pub const fn version(mut self, version: Version) -> Self { + self.version = Some(version); + self + } + + /// Sets the main script option. + #[must_use] + pub const fn main_script(mut self, main_script: bool) -> Self { + self.main_script = main_script; + self + } + + /// Sets the partial script option. + #[must_use] + pub const fn partial_script(mut self, partial_script: bool) -> Self { + self.partial_script = partial_script; + self + } + + /// Adds a scope to the options. + #[must_use] + pub fn scope(mut self, scope: Scope) -> Self { + self.scopes.push(scope); + self + } + + /// Sets the scopes, replacing any previously added scopes. + #[must_use] + pub fn scopes(mut self, scopes: Vec) -> Self { + self.scopes = scopes; + self + } + + /// Builds the C-level parse options from these options. The returned + /// `ParseOptions` must outlive any `ParseResult` created from it. + /// + /// # Panics + /// + /// Panics if `filepath` or `encoding` contain interior null bytes. + #[must_use] + pub fn build(self) -> ParseOptions { + let opts = unsafe { pm_options_new() }; + + let c_filepath = self.filepath.map(|filepath| { + let cstring = CString::new(filepath).unwrap(); + unsafe { pm_options_filepath_set(opts, cstring.as_ptr()) }; + cstring + }); + + if let Some(line) = self.line { + unsafe { pm_options_line_set(opts, line) }; + } + + let c_encoding = self.encoding.map(|encoding| { + let cstring = CString::new(encoding).unwrap(); + unsafe { pm_options_encoding_set(opts, cstring.as_ptr()) }; + cstring + }); + + if self.encoding_locked { + unsafe { pm_options_encoding_locked_set(opts, true) }; + } + + if let Some(frozen) = self.frozen_string_literal { + unsafe { pm_options_frozen_string_literal_set(opts, frozen) }; + } + + let command_line = self.command_line.iter().fold(0u8, |acc, &flag| acc | u8::from(flag)); + if command_line != 0 { + unsafe { pm_options_command_line_set(opts, command_line) }; + } + + if let Some(version) = self.version { + unsafe { version.set_on(opts) }; + } + + if self.main_script { + unsafe { pm_options_main_script_set(opts, true) }; + } + + if self.partial_script { + unsafe { pm_options_partial_script_set(opts, true) }; + } + + if !self.scopes.is_empty() { + unsafe { pm_options_scopes_init(opts, self.scopes.len()) }; + + for (scope_index, scope) in self.scopes.iter().enumerate() { + let pm_scope = unsafe { pm_options_scope_mut(opts, scope_index) }; + unsafe { pm_options_scope_init(pm_scope, scope.locals.len()) }; + + for (local_index, local) in scope.locals.iter().enumerate() { + let pm_local = unsafe { pm_options_scope_local_mut(pm_scope, local_index) }; + unsafe { pm_string_constant_init(pm_local, local.as_ptr().cast::(), local.len()) }; + } + + let forwarding = scope.forwarding.iter().fold(0u8, |acc, &flag| acc | u8::from(flag)); + if forwarding != 0 { + unsafe { pm_options_scope_forwarding_set(pm_scope, forwarding) }; + } + } + } + + ParseOptions { + options: opts, + _filepath: c_filepath, + _encoding: c_encoding, + _scopes: self.scopes, + } + } +} + +/// The C-level parse options. Created from [`Options::build`]. Must outlive +/// any [`ParseResult`] created with [`parse_with_options`]. +pub struct ParseOptions { + options: *mut pm_options_t, + // These CStrings back the constant pm_string_t values inside `options`. + // They must not be dropped before `options` is freed. + _filepath: Option, + _encoding: Option, + // The scopes data that pm_string_t values point into. + _scopes: Vec, +} + +impl Drop for ParseOptions { + fn drop(&mut self) { + unsafe { pm_options_free(self.options) }; + } +} + +/// Initializes a parser, parses the source, and returns the result. +/// +/// # Safety +/// +/// `options` must be a valid pointer to a `pm_options_t` or null. +unsafe fn parse_impl(source: &[u8], options: *const pm_options_t) -> ParseResult<'_> { + let arena = pm_arena_new(); + let parser = pm_parser_new(arena, source.as_ptr(), source.len(), options); + let node = NonNull::new_unchecked(pm_parse(parser)); + ParseResult::new(source, arena, parser, node) +} /// Parses the given source string and returns a parse result. /// @@ -33,20 +360,19 @@ use ruby_prism_sys::{pm_parse, pm_parser_init, pm_parser_t}; /// #[must_use] pub fn parse(source: &[u8]) -> ParseResult<'_> { - unsafe { - let uninit = Box::new(MaybeUninit::::uninit()); - let uninit = Box::into_raw(uninit); - - pm_parser_init((*uninit).as_mut_ptr(), source.as_ptr(), source.len(), std::ptr::null()); - - let parser = (*uninit).assume_init_mut(); - let parser = NonNull::new_unchecked(parser); - - let node = pm_parse(parser.as_ptr()); - let node = NonNull::new_unchecked(node); + unsafe { parse_impl(source, std::ptr::null()) } +} - ParseResult::new(source, parser, node) - } +/// Parses the given source string with the given options and returns a parse +/// result. The `options` must outlive the returned `ParseResult`. +/// +/// # Panics +/// +/// Panics if the parser fails to initialize. +/// +#[must_use] +pub fn parse_with_options<'a>(source: &'a [u8], options: &'a ParseOptions) -> ParseResult<'a> { + unsafe { parse_impl(source, options.options) } } #[cfg(test)] @@ -65,6 +391,27 @@ mod tests { } } + #[test] + fn line_offsets_test() { + let source = ""; + let result = parse(source.as_ref()); + + let expected: [u32; 1] = [0]; + assert_eq!(expected, result.line_offsets()); + + let source = "1 + 1"; + let result = parse(source.as_ref()); + + let expected: [u32; 1] = [0]; + assert_eq!(expected, result.line_offsets()); + + let source = "begin\n1 + 1\n2 + 2\nend"; + let result = parse(source.as_ref()); + + let expected: [u32; 4] = [0, 6, 12, 18]; + assert_eq!(expected, result.line_offsets()); + } + #[test] fn magic_comments_test() { use crate::MagicComment; @@ -106,7 +453,7 @@ mod tests { } #[test] - fn location_test() { + fn location_slice_test() { let source = "111 + 222 + 333"; let result = parse(source.as_ref()); @@ -121,6 +468,7 @@ mod tests { assert_eq!(slice, "222"); assert_eq!(6, location.start); + assert_eq!(location.start, location.start()); assert_eq!(9, location.end()); let recv_loc = plus.receiver().unwrap().location(); @@ -160,6 +508,48 @@ mod tests { assert_eq!(slice, "222"); } + #[test] + fn location_line_column_test() { + let source = "first\nsecond\nthird"; + let result = parse(source.as_ref()); + + let node = result.node(); + let program = node.as_program_node().unwrap(); + let statements = program.statements().body(); + let mut iter = statements.iter(); + + let _first = iter.next().unwrap(); + let second = iter.next().unwrap(); + let third = iter.next().unwrap(); + + let second_loc = second.location(); + assert_eq!(second_loc.start_line(), 2); + assert_eq!(second_loc.end_line(), 2); + assert_eq!(second_loc.start_column(), 0); + assert_eq!(second_loc.end_column(), 6); + + let third_loc = third.location(); + assert_eq!(third_loc.start_line(), 3); + assert_eq!(third_loc.end_line(), 3); + assert_eq!(third_loc.start_column(), 0); + assert_eq!(third_loc.end_column(), 5); + } + + #[test] + fn location_chop_test() { + let result = parse(b"foo"); + let mut location = result.node().as_program_node().unwrap().location(); + + assert_eq!(location.chop().as_slice(), b"fo"); + assert_eq!(location.chop().chop().chop().as_slice(), b""); + + // Check that we don't go negative. + for _ in 0..10 { + location = location.chop(); + } + assert_eq!(location.as_slice(), b""); + } + #[test] fn visitor_test() { use super::{visit_interpolated_regular_expression_node, visit_regular_expression_node, InterpolatedRegularExpressionNode, RegularExpressionNode, Visit}; @@ -721,6 +1111,55 @@ end assert_eq!(3, extractor.scopes.len()); } + #[test] + fn parse_with_options_frozen_string_literal_test() { + use super::{parse_with_options, Options}; + + let source = b"\"foo\""; + let options = Options::default().frozen_string_literal(Some(true)).build(); + let result = parse_with_options(source, &options); + + let node = result.node(); + let string = node.as_program_node().unwrap().statements().body().iter().next().unwrap(); + let string = string.as_string_node().unwrap(); + assert!(string.is_frozen()); + } + + #[test] + fn parse_with_options_filepath_test() { + use super::{parse_with_options, Options}; + + let source = b"__FILE__"; + let options = Options::default().filepath("test.rb").build(); + let result = parse_with_options(source, &options); + assert!(result.errors().next().is_none()); + } + + #[test] + fn parse_with_options_line_test() { + use super::{parse_with_options, Options}; + + let source = b"1 + 2"; + let options = Options::default().line(10).build(); + let result = parse_with_options(source, &options); + assert!(result.errors().next().is_none()); + } + + #[test] + fn parse_with_options_scopes_test() { + use super::{parse_with_options, Options, Scope}; + + let source = b"x"; + let scope = Scope::default().locals(vec![b"x".to_vec()]); + let options = Options::default().scope(scope).build(); + let result = parse_with_options(source, &options); + assert!(result.errors().next().is_none()); + + let node = result.node(); + let stmt = node.as_program_node().unwrap().statements().body().iter().next().unwrap(); + assert!(stmt.as_local_variable_read_node().is_some()); + } + #[test] fn malformed_shebang() { let source = "#!\x00"; diff --git a/rust/ruby-prism/src/node.rs b/rust/ruby-prism/src/node.rs index cf44a119dc..e797c94b2e 100644 --- a/rust/ruby-prism/src/node.rs +++ b/rust/ruby-prism/src/node.rs @@ -18,7 +18,7 @@ use crate::Node; /// An iterator over the nodes in a list. pub struct NodeListIter<'pr> { - pub(crate) parser: NonNull, + pub(crate) parser: *const pm_parser_t, pub(crate) pointer: NonNull, pub(crate) index: usize, pub(crate) marker: PhantomData<&'pr mut pm_node_list>, @@ -40,7 +40,7 @@ impl<'pr> Iterator for NodeListIter<'pr> { /// A list of nodes. pub struct NodeList<'pr> { - pub(crate) parser: NonNull, + pub(crate) parser: *const pm_parser_t, pub(crate) pointer: NonNull, pub(crate) marker: PhantomData<&'pr mut pm_node_list>, } @@ -115,13 +115,13 @@ impl std::fmt::Debug for NodeList<'_> { /// A handle for a constant ID. pub struct ConstantId<'pr> { - pub(crate) parser: NonNull, + pub(crate) parser: *const pm_parser_t, pub(crate) id: pm_constant_id_t, pub(crate) marker: PhantomData<&'pr mut pm_constant_id_t>, } impl<'pr> ConstantId<'pr> { - pub(crate) const fn new(parser: NonNull, id: pm_constant_id_t) -> Self { + pub(crate) const fn new(parser: *const pm_parser_t, id: pm_constant_id_t) -> Self { ConstantId { parser, id, marker: PhantomData } } @@ -133,9 +133,10 @@ impl<'pr> ConstantId<'pr> { #[must_use] pub fn as_slice(&self) -> &'pr [u8] { unsafe { - let pool = &(*self.parser.as_ptr()).constant_pool; - let constant = &(*pool.constants.add((self.id - 1).try_into().unwrap())); - std::slice::from_raw_parts(constant.start, constant.length) + let constant = ruby_prism_sys::pm_parser_constant(self.parser, self.id); + let start = ruby_prism_sys::pm_constant_start(constant); + let length = ruby_prism_sys::pm_constant_length(constant); + std::slice::from_raw_parts(start, length) } } } @@ -148,7 +149,7 @@ impl std::fmt::Debug for ConstantId<'_> { /// An iterator over the constants in a list. pub struct ConstantListIter<'pr> { - pub(crate) parser: NonNull, + pub(crate) parser: *const pm_parser_t, pub(crate) pointer: NonNull, pub(crate) index: usize, pub(crate) marker: PhantomData<&'pr mut pm_constant_id_list_t>, @@ -171,7 +172,7 @@ impl<'pr> Iterator for ConstantListIter<'pr> { /// A list of constants. pub struct ConstantList<'pr> { /// The raw pointer to the parser where this list came from. - pub(crate) parser: NonNull, + pub(crate) parser: *const pm_parser_t, /// The raw pointer to the list allocated by prism. pub(crate) pointer: NonNull, diff --git a/rust/ruby-prism/src/node_ext.rs b/rust/ruby-prism/src/node_ext.rs new file mode 100644 index 0000000000..8e61532315 --- /dev/null +++ b/rust/ruby-prism/src/node_ext.rs @@ -0,0 +1,250 @@ +//! Node extension methods for the prism parser. +//! +//! This module provides convenience methods on AST nodes that aren't generated +//! from the config, mirroring Ruby's `node_ext.rb`. + +use std::fmt; + +use crate::{ConstantPathNode, ConstantPathTargetNode, ConstantReadNode, ConstantTargetNode, ConstantWriteNode, Node}; + +/// Errors for constant path name computation. +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum ConstantPathError { + /// The constant path contains dynamic parts (e.g., `var::Bar::Baz`). + DynamicParts, + /// The constant path contains missing nodes (e.g., `Foo::`). + MissingNodes, +} + +impl fmt::Display for ConstantPathError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::DynamicParts => { + write!(f, "Constant path contains dynamic parts. Cannot compute full name") + }, + Self::MissingNodes => { + write!(f, "Constant path contains missing nodes. Cannot compute full name") + }, + } + } +} + +impl std::error::Error for ConstantPathError {} + +/// Trait for nodes that can compute their full constant name. +/// +/// Implemented by constant-related nodes (`ConstantReadNode`, +/// `ConstantWriteNode`, `ConstantTargetNode`, `ConstantPathNode`, and +/// `ConstantPathTargetNode`). +pub trait FullName<'pr> { + /// Returns the list of parts for the full name of this constant. + /// + /// # Errors + /// + /// Returns [`ConstantPathError`] if the path contains dynamic parts or + /// missing nodes. + fn full_name_parts(&self) -> Result, ConstantPathError>; + + /// Returns the full name of this constant. + /// + /// # Errors + /// + /// Returns [`ConstantPathError`] if the path contains dynamic parts or + /// missing nodes. + fn full_name(&self) -> Result, ConstantPathError> { + let parts = self.full_name_parts()?; + let mut result = Vec::new(); + for (index, part) in parts.iter().enumerate() { + if index > 0 { + result.extend_from_slice(b"::"); + } + result.extend_from_slice(part); + } + Ok(result) + } +} + +/// Computes `full_name_parts` for a `Node` by dispatching to the appropriate +/// `FullName` implementation. +#[allow(clippy::option_if_let_else)] +fn full_name_parts_for_node<'pr>(node: &Node<'pr>) -> Result, ConstantPathError> { + if let Some(path_node) = node.as_constant_path_node() { + path_node.full_name_parts() + } else if let Some(read_node) = node.as_constant_read_node() { + read_node.full_name_parts() + } else { + Err(ConstantPathError::DynamicParts) + } +} + +/// Computes `full_name_parts` for a constant path node given its name and +/// parent. +fn constant_path_full_name_parts<'pr>(name: Option>, parent: Option>) -> Result, ConstantPathError> { + let name = name.ok_or(ConstantPathError::MissingNodes)?; + + let mut parts = match parent { + Some(parent) => full_name_parts_for_node(&parent)?, + None => vec![b"".as_slice()], + }; + + parts.push(name.as_slice()); + Ok(parts) +} + +/// Implements `FullName` for simple constant nodes that have a `name()` method +/// returning a single constant identifier. +macro_rules! impl_simple_full_name { + ($node_type:ident) => { + impl<'pr> FullName<'pr> for $node_type<'pr> { + fn full_name_parts(&self) -> Result, ConstantPathError> { + Ok(vec![self.name().as_slice()]) + } + } + }; +} + +impl_simple_full_name!(ConstantReadNode); +impl_simple_full_name!(ConstantWriteNode); +impl_simple_full_name!(ConstantTargetNode); + +impl<'pr> FullName<'pr> for ConstantPathNode<'pr> { + fn full_name_parts(&self) -> Result, ConstantPathError> { + constant_path_full_name_parts(self.name(), self.parent()) + } +} + +impl<'pr> FullName<'pr> for ConstantPathTargetNode<'pr> { + fn full_name_parts(&self) -> Result, ConstantPathError> { + constant_path_full_name_parts(self.name(), self.parent()) + } +} + +#[cfg(test)] +mod tests { + use super::{ConstantPathError, FullName}; + use crate::parse; + + #[test] + fn test_full_name_for_constant_read_node() { + let result = parse(b"Foo"); + let node = result.node().as_program_node().unwrap().statements().body().iter().next().unwrap(); + let constant = node.as_constant_read_node().unwrap(); + + assert_eq!(constant.full_name_parts().unwrap(), vec![b"Foo".as_slice()]); + assert_eq!(constant.full_name().unwrap(), b"Foo"); + } + + #[test] + fn test_full_name_for_constant_write_node() { + let result = parse(b"Foo = 1"); + let node = result.node().as_program_node().unwrap().statements().body().iter().next().unwrap(); + let constant = node.as_constant_write_node().unwrap(); + + assert_eq!(constant.full_name_parts().unwrap(), vec![b"Foo".as_slice()]); + assert_eq!(constant.full_name().unwrap(), b"Foo"); + } + + #[test] + fn test_full_name_for_constant_target_node() { + let result = parse(b"Foo, Bar = [1, 2]"); + let node = result.node().as_program_node().unwrap().statements().body().iter().next().unwrap(); + let multi_write = node.as_multi_write_node().unwrap(); + let target = multi_write.lefts().iter().next().unwrap(); + let constant = target.as_constant_target_node().unwrap(); + + assert_eq!(constant.full_name_parts().unwrap(), vec![b"Foo".as_slice()]); + assert_eq!(constant.full_name().unwrap(), b"Foo"); + } + + #[test] + fn test_full_name_for_constant_path() { + let result = parse(b"Foo::Bar"); + let node = result.node().as_program_node().unwrap().statements().body().iter().next().unwrap(); + let constant_path = node.as_constant_path_node().unwrap(); + + assert_eq!(constant_path.full_name_parts().unwrap(), vec![b"Foo".as_slice(), b"Bar".as_slice()]); + assert_eq!(constant_path.full_name().unwrap(), b"Foo::Bar"); + } + + #[test] + fn test_full_name_for_constant_path_with_stovetop() { + let result = parse(b"::Foo::Bar"); + let node = result.node().as_program_node().unwrap().statements().body().iter().next().unwrap(); + let constant_path = node.as_constant_path_node().unwrap(); + + assert_eq!(constant_path.full_name_parts().unwrap(), vec![b"".as_slice(), b"Foo".as_slice(), b"Bar".as_slice()]); + assert_eq!(constant_path.full_name().unwrap(), b"::Foo::Bar"); + } + + #[test] + fn test_full_name_for_constant_path_with_self() { + let source = r" +self:: + Bar::Baz:: + Qux +"; + let result = parse(source.as_bytes()); + let node = result.node().as_program_node().unwrap().statements().body().iter().next().unwrap(); + let constant_path = node.as_constant_path_node().unwrap(); + + assert_eq!(constant_path.full_name().unwrap_err(), ConstantPathError::DynamicParts); + } + + #[test] + fn test_full_name_for_constant_path_with_variable() { + let source = r" +foo:: + Bar::Baz:: + Qux +"; + let result = parse(source.as_bytes()); + let node = result.node().as_program_node().unwrap().statements().body().iter().next().unwrap(); + let constant_path = node.as_constant_path_node().unwrap(); + + assert_eq!(constant_path.full_name().unwrap_err(), ConstantPathError::DynamicParts); + } + + #[test] + fn test_full_name_for_constant_path_with_missing_name() { + let result = parse(b"Foo::"); + let node = result.node().as_program_node().unwrap().statements().body().iter().next().unwrap(); + let constant_path = node.as_constant_path_node().unwrap(); + + assert_eq!(constant_path.full_name().unwrap_err(), ConstantPathError::MissingNodes); + } + + #[test] + fn test_full_name_for_constant_path_target() { + let result = parse(b"Foo::Bar, Baz = [1, 2]"); + let node = result.node().as_program_node().unwrap().statements().body().iter().next().unwrap(); + let multi_write = node.as_multi_write_node().unwrap(); + let target = multi_write.lefts().iter().next().unwrap(); + let constant_path = target.as_constant_path_target_node().unwrap(); + + assert_eq!(constant_path.full_name_parts().unwrap(), vec![b"Foo".as_slice(), b"Bar".as_slice()]); + assert_eq!(constant_path.full_name().unwrap(), b"Foo::Bar"); + } + + #[test] + fn test_full_name_for_constant_path_target_with_stovetop() { + let result = parse(b"::Foo, Bar = [1, 2]"); + let node = result.node().as_program_node().unwrap().statements().body().iter().next().unwrap(); + let multi_write = node.as_multi_write_node().unwrap(); + let target = multi_write.lefts().iter().next().unwrap(); + let constant_path = target.as_constant_path_target_node().unwrap(); + + assert_eq!(constant_path.full_name_parts().unwrap(), vec![b"".as_slice(), b"Foo".as_slice()]); + assert_eq!(constant_path.full_name().unwrap(), b"::Foo"); + } + + #[test] + fn test_full_name_for_constant_path_target_with_self() { + let result = parse(b"self::Foo, Bar = [1, 2]"); + let node = result.node().as_program_node().unwrap().statements().body().iter().next().unwrap(); + let multi_write = node.as_multi_write_node().unwrap(); + let target = multi_write.lefts().iter().next().unwrap(); + let constant_path = target.as_constant_path_target_node().unwrap(); + + assert_eq!(constant_path.full_name().unwrap_err(), ConstantPathError::DynamicParts); + } +} diff --git a/rust/ruby-prism/src/parse_result/comments.rs b/rust/ruby-prism/src/parse_result/comments.rs index 767de6330a..9f6c80f83a 100644 --- a/rust/ruby-prism/src/parse_result/comments.rs +++ b/rust/ruby-prism/src/parse_result/comments.rs @@ -1,9 +1,8 @@ //! Comment handling for the prism parser. use std::marker::PhantomData; -use std::ptr::NonNull; -use ruby_prism_sys::{pm_comment_t, pm_comment_type_t, pm_magic_comment_t, pm_parser_t}; +use ruby_prism_sys::{pm_comment_location, pm_comment_t, pm_comment_type, pm_comment_type_t, pm_magic_comment_key, pm_magic_comment_t, pm_magic_comment_value, pm_parser_start, pm_parser_t}; use super::Location; @@ -19,16 +18,13 @@ pub enum CommentType { /// A comment that was found during parsing. #[derive(Debug)] pub struct Comment<'pr> { - content: NonNull, - parser: NonNull, + raw: *const pm_comment_t, + parser: *const pm_parser_t, marker: PhantomData<&'pr pm_comment_t>, } impl<'pr> Comment<'pr> { /// Returns the text of the comment. - /// - /// # Panics - /// Panics if the end offset is not greater than the start offset. #[must_use] pub fn text(&self) -> &[u8] { self.location().as_slice() @@ -37,7 +33,7 @@ impl<'pr> Comment<'pr> { /// Returns the type of the comment. #[must_use] pub fn type_(&self) -> CommentType { - let type_ = unsafe { self.content.as_ref().type_ }; + let type_ = unsafe { pm_comment_type(self.raw) }; if type_ == pm_comment_type_t::PM_COMMENT_EMBDOC { CommentType::EmbDocComment } else { @@ -47,22 +43,28 @@ impl<'pr> Comment<'pr> { /// The location of the comment in the source. #[must_use] - pub const fn location(&self) -> Location<'pr> { - Location::new(self.parser, unsafe { &self.content.as_ref().location }) + pub fn location(&self) -> Location<'pr> { + let loc = unsafe { pm_comment_location(self.raw) }; + Location { + parser: self.parser, + start: loc.start, + length: loc.length, + marker: PhantomData, + } } } -/// A struct created by the `comments` method on `ParseResult`. It can be used -/// to iterate over the comments in the parse result. +/// An iterator over comments collected from the parse result. pub struct Comments<'pr> { - comment: *mut pm_comment_t, - parser: NonNull, + ptrs: Vec<*const pm_comment_t>, + index: usize, + parser: *const pm_parser_t, marker: PhantomData<&'pr pm_comment_t>, } impl Comments<'_> { - pub(crate) const fn new(comment: *mut pm_comment_t, parser: NonNull) -> Self { - Comments { comment, parser, marker: PhantomData } + pub(crate) const fn new(ptrs: Vec<*const pm_comment_t>, parser: *const pm_parser_t) -> Self { + Comments { ptrs, index: 0, parser, marker: PhantomData } } } @@ -70,14 +72,10 @@ impl<'pr> Iterator for Comments<'pr> { type Item = Comment<'pr>; fn next(&mut self) -> Option { - if let Some(comment) = NonNull::new(self.comment) { - let current = Comment { - content: comment, - parser: self.parser, - marker: PhantomData, - }; - self.comment = unsafe { comment.as_ref().node.next.cast::() }; - Some(current) + if self.index < self.ptrs.len() { + let comment = self.ptrs[self.index]; + self.index += 1; + Some(Comment { raw: comment, parser: self.parser, marker: PhantomData }) } else { None } @@ -87,44 +85,44 @@ impl<'pr> Iterator for Comments<'pr> { /// A magic comment that was found during parsing. #[derive(Debug)] pub struct MagicComment<'pr> { - parser: NonNull, - comment: NonNull, + parser: *const pm_parser_t, + raw: *const pm_magic_comment_t, marker: PhantomData<&'pr pm_magic_comment_t>, } impl MagicComment<'_> { /// Returns the text of the comment's key. #[must_use] - pub const fn key(&self) -> &[u8] { + pub fn key(&self) -> &[u8] { unsafe { - let start = self.parser.as_ref().start.add(self.comment.as_ref().key.start as usize); - let len = self.comment.as_ref().key.length as usize; - std::slice::from_raw_parts(start, len) + let loc = pm_magic_comment_key(self.raw); + let start = pm_parser_start(self.parser).add(loc.start as usize); + std::slice::from_raw_parts(start, loc.length as usize) } } /// Returns the text of the comment's value. #[must_use] - pub const fn value(&self) -> &[u8] { + pub fn value(&self) -> &[u8] { unsafe { - let start = self.parser.as_ref().start.add(self.comment.as_ref().value.start as usize); - let len = self.comment.as_ref().value.length as usize; - std::slice::from_raw_parts(start, len) + let loc = pm_magic_comment_value(self.raw); + let start = pm_parser_start(self.parser).add(loc.start as usize); + std::slice::from_raw_parts(start, loc.length as usize) } } } -/// A struct created by the `magic_comments` method on `ParseResult`. It can be used -/// to iterate over the magic comments in the parse result. +/// An iterator over magic comments collected from the parse result. pub struct MagicComments<'pr> { - parser: NonNull, - comment: *mut pm_magic_comment_t, + ptrs: Vec<*const pm_magic_comment_t>, + index: usize, + parser: *const pm_parser_t, marker: PhantomData<&'pr pm_magic_comment_t>, } impl MagicComments<'_> { - pub(crate) const fn new(parser: NonNull, comment: *mut pm_magic_comment_t) -> Self { - MagicComments { parser, comment, marker: PhantomData } + pub(crate) const fn new(ptrs: Vec<*const pm_magic_comment_t>, parser: *const pm_parser_t) -> Self { + MagicComments { ptrs, index: 0, parser, marker: PhantomData } } } @@ -132,10 +130,10 @@ impl<'pr> Iterator for MagicComments<'pr> { type Item = MagicComment<'pr>; fn next(&mut self) -> Option { - if let Some(comment) = NonNull::new(self.comment) { - let current = MagicComment { parser: self.parser, comment, marker: PhantomData }; - self.comment = unsafe { comment.as_ref().node.next.cast::() }; - Some(current) + if self.index < self.ptrs.len() { + let comment = self.ptrs[self.index]; + self.index += 1; + Some(MagicComment { parser: self.parser, raw: comment, marker: PhantomData }) } else { None } diff --git a/rust/ruby-prism/src/parse_result/diagnostics.rs b/rust/ruby-prism/src/parse_result/diagnostics.rs index 00fc9ffe33..ba231fabb2 100644 --- a/rust/ruby-prism/src/parse_result/diagnostics.rs +++ b/rust/ruby-prism/src/parse_result/diagnostics.rs @@ -1,18 +1,17 @@ //! Diagnostic handling for parse errors and warnings. -use std::ffi::{c_char, CStr}; +use std::ffi::CStr; use std::marker::PhantomData; -use std::ptr::NonNull; -use ruby_prism_sys::{pm_diagnostic_t, pm_parser_t}; +use ruby_prism_sys::{pm_diagnostic_location, pm_diagnostic_message, pm_diagnostic_t, pm_parser_t}; use super::Location; /// A diagnostic message that came back from the parser. #[derive(Debug)] pub struct Diagnostic<'pr> { - diag: NonNull, - parser: NonNull, + raw: *const pm_diagnostic_t, + parser: *const pm_parser_t, marker: PhantomData<&'pr pm_diagnostic_t>, } @@ -21,34 +20,39 @@ impl<'pr> Diagnostic<'pr> { /// /// # Panics /// - /// Panics if the message is not able to be converted into a `CStr`. - /// + /// Panics if the message is not valid UTF-8. #[must_use] pub fn message(&self) -> &str { unsafe { - let message: *mut c_char = self.diag.as_ref().message.cast_mut(); + let message = pm_diagnostic_message(self.raw); CStr::from_ptr(message).to_str().expect("prism allows only UTF-8 for diagnostics.") } } /// The location of the diagnostic in the source. #[must_use] - pub const fn location(&self) -> Location<'pr> { - Location::new(self.parser, unsafe { &self.diag.as_ref().location }) + pub fn location(&self) -> Location<'pr> { + let loc = unsafe { pm_diagnostic_location(self.raw) }; + Location { + parser: self.parser, + start: loc.start, + length: loc.length, + marker: PhantomData, + } } } -/// A struct created by the `errors` or `warnings` methods on `ParseResult`. It -/// can be used to iterate over the diagnostics in the parse result. +/// An iterator over diagnostics collected from the parse result. pub struct Diagnostics<'pr> { - diagnostic: *mut pm_diagnostic_t, - parser: NonNull, + ptrs: Vec<*const pm_diagnostic_t>, + index: usize, + parser: *const pm_parser_t, marker: PhantomData<&'pr pm_diagnostic_t>, } impl Diagnostics<'_> { - pub(crate) const fn new(diagnostic: *mut pm_diagnostic_t, parser: NonNull) -> Self { - Diagnostics { diagnostic, parser, marker: PhantomData } + pub(crate) const fn new(ptrs: Vec<*const pm_diagnostic_t>, parser: *const pm_parser_t) -> Self { + Diagnostics { ptrs, index: 0, parser, marker: PhantomData } } } @@ -56,14 +60,14 @@ impl<'pr> Iterator for Diagnostics<'pr> { type Item = Diagnostic<'pr>; fn next(&mut self) -> Option { - if let Some(diagnostic) = NonNull::new(self.diagnostic) { - let current = Diagnostic { - diag: diagnostic, + if self.index < self.ptrs.len() { + let diagnostic = self.ptrs[self.index]; + self.index += 1; + Some(Diagnostic { + raw: diagnostic, parser: self.parser, marker: PhantomData, - }; - self.diagnostic = unsafe { diagnostic.as_ref().node.next.cast::() }; - Some(current) + }) } else { None } diff --git a/rust/ruby-prism/src/parse_result/mod.rs b/rust/ruby-prism/src/parse_result/mod.rs index 33eb1ac9a0..82cae4b731 100644 --- a/rust/ruby-prism/src/parse_result/mod.rs +++ b/rust/ruby-prism/src/parse_result/mod.rs @@ -1,14 +1,14 @@ //! Parse result types for the prism parser. -//! -//! This module contains types related to the result of parsing, including -//! the main `ParseResult` struct, location tracking, comments, and diagnostics. mod comments; mod diagnostics; use std::ptr::NonNull; -use ruby_prism_sys::{pm_comment_t, pm_diagnostic_t, pm_location_t, pm_magic_comment_t, pm_node_destroy, pm_node_t, pm_parser_free, pm_parser_t}; +use ruby_prism_sys::{ + pm_arena_free, pm_arena_t, pm_comment_t, pm_diagnostic_t, pm_line_offset_list_line_column, pm_location_t, pm_magic_comment_t, pm_node_t, pm_parser_comments_each, pm_parser_comments_size, pm_parser_data_loc, pm_parser_errors_each, pm_parser_errors_size, pm_parser_free, + pm_parser_frozen_string_literal, pm_parser_line_offsets, pm_parser_magic_comments_each, pm_parser_magic_comments_size, pm_parser_start, pm_parser_start_line, pm_parser_t, pm_parser_warnings_each, pm_parser_warnings_size, +}; pub use self::comments::{Comment, CommentType, Comments, MagicComment, MagicComments}; pub use self::diagnostics::{Diagnostic, Diagnostics}; @@ -17,7 +17,7 @@ use crate::Node; /// A range in the source file, represented as a start offset and length. pub struct Location<'pr> { - pub(crate) parser: NonNull, + pub(crate) parser: *const pm_parser_t, pub(crate) start: u32, pub(crate) length: u32, marker: std::marker::PhantomData<&'pr [u8]>, @@ -28,14 +28,14 @@ impl<'pr> Location<'pr> { #[must_use] pub fn as_slice(&self) -> &'pr [u8] { unsafe { - let parser_start = (*self.parser.as_ptr()).start; + let parser_start = pm_parser_start(self.parser); std::slice::from_raw_parts(parser_start.add(self.start as usize), self.length as usize) } } /// Return a Location from the given `pm_location_t`. #[must_use] - pub(crate) const fn new(parser: NonNull, location: &'pr pm_location_t) -> Self { + pub(crate) const fn new(parser: *const pm_parser_t, location: &'pr pm_location_t) -> Self { Location { parser, start: location.start, @@ -44,6 +44,12 @@ impl<'pr> Location<'pr> { } } + /// Returns the start offset from the beginning of the parsed source. + #[must_use] + pub const fn start(&self) -> u32 { + self.start + } + /// Returns the end offset from the beginning of the parsed source. #[must_use] pub const fn end(&self) -> u32 { @@ -66,6 +72,55 @@ impl<'pr> Location<'pr> { }) } } + + /// Returns a new location that is the result of chopping off the last byte. + #[must_use] + pub const fn chop(&self) -> Self { + Location { + parser: self.parser, + start: self.start, + length: if self.length == 0 { 0 } else { self.length - 1 }, + marker: std::marker::PhantomData, + } + } +} + +impl Location<'_> { + /// Returns the line number where this location starts. + #[must_use] + pub fn start_line(&self) -> i32 { + self.line_column(self.start).0 + } + + /// Returns the column number in bytes where this location starts from the + /// start of the line. + #[must_use] + pub fn start_column(&self) -> u32 { + self.line_column(self.start).1 + } + + /// Returns the line number where this location ends. + #[must_use] + pub fn end_line(&self) -> i32 { + self.line_column(self.end()).0 + } + + /// Returns the column number in bytes where this location ends from the + /// start of the line. + #[must_use] + pub fn end_column(&self) -> u32 { + self.line_column(self.end()).1 + } + + /// Returns the line and column number for the given byte offset. + fn line_column(&self, cursor: u32) -> (i32, u32) { + unsafe { + let line_offsets = pm_parser_line_offsets(self.parser); + let start_line = pm_parser_start_line(self.parser); + let result = pm_line_offset_list_line_column(line_offsets, cursor, start_line); + (result.line, result.column) + } + } } impl std::fmt::Debug for Location<'_> { @@ -85,17 +140,36 @@ impl std::fmt::Debug for Location<'_> { } } +// C callback that collects comment pointers into a Vec +unsafe extern "C" fn collect_comment(comment: *const pm_comment_t, data: *mut std::ffi::c_void) { + let vec = &mut *(data.cast::>()); + vec.push(comment); +} + +// C callback that collects magic comment pointers into a Vec +unsafe extern "C" fn collect_magic_comment(comment: *const pm_magic_comment_t, data: *mut std::ffi::c_void) { + let vec = &mut *(data.cast::>()); + vec.push(comment); +} + +// C callback that collects diagnostic pointers into a Vec +unsafe extern "C" fn collect_diagnostic(diagnostic: *const pm_diagnostic_t, data: *mut std::ffi::c_void) { + let vec = &mut *(data.cast::>()); + vec.push(diagnostic); +} + /// The result of parsing a source string. #[derive(Debug)] pub struct ParseResult<'pr> { source: &'pr [u8], - parser: NonNull, + arena: *mut pm_arena_t, + parser: *mut pm_parser_t, node: NonNull, } impl<'pr> ParseResult<'pr> { - pub(crate) const unsafe fn new(source: &'pr [u8], parser: NonNull, node: NonNull) -> Self { - ParseResult { source, parser, node } + pub(crate) const unsafe fn new(source: &'pr [u8], arena: *mut pm_arena_t, parser: *mut pm_parser_t, node: NonNull) -> Self { + ParseResult { source, arena, parser, node } } /// Returns the source string that was parsed. @@ -107,7 +181,7 @@ impl<'pr> ParseResult<'pr> { /// Returns whether we found a `frozen_string_literal` magic comment with a true value. #[must_use] pub fn frozen_string_literals(&self) -> bool { - unsafe { (*self.parser.as_ptr()).frozen_string_literal == 1 } + unsafe { pm_parser_frozen_string_literal(self.parser) == 1 } } /// Returns a slice of the source string that was parsed using the given @@ -119,50 +193,68 @@ impl<'pr> ParseResult<'pr> { &self.source[start..end] } + /// Returns a slice containing the offsets of the start of each line in the source string + /// that was parsed. + #[must_use] + pub fn line_offsets(&self) -> &'pr [u32] { + unsafe { + let list = &*pm_parser_line_offsets(self.parser); + std::slice::from_raw_parts(list.offsets, list.size) + } + } + /// Returns an iterator that can be used to iterate over the errors in the /// parse result. #[must_use] pub fn errors(&self) -> Diagnostics<'_> { + let size = unsafe { pm_parser_errors_size(self.parser) }; + let mut ptrs: Vec<*const pm_diagnostic_t> = Vec::with_capacity(size); unsafe { - let list = &mut (*self.parser.as_ptr()).error_list; - Diagnostics::new(list.head.cast::(), self.parser) + pm_parser_errors_each(self.parser, Some(collect_diagnostic), (&raw mut ptrs).cast()); } + Diagnostics::new(ptrs, self.parser) } /// Returns an iterator that can be used to iterate over the warnings in the /// parse result. #[must_use] pub fn warnings(&self) -> Diagnostics<'_> { + let size = unsafe { pm_parser_warnings_size(self.parser) }; + let mut ptrs: Vec<*const pm_diagnostic_t> = Vec::with_capacity(size); unsafe { - let list = &mut (*self.parser.as_ptr()).warning_list; - Diagnostics::new(list.head.cast::(), self.parser) + pm_parser_warnings_each(self.parser, Some(collect_diagnostic), (&raw mut ptrs).cast()); } + Diagnostics::new(ptrs, self.parser) } /// Returns an iterator that can be used to iterate over the comments in the /// parse result. #[must_use] pub fn comments(&self) -> Comments<'_> { + let size = unsafe { pm_parser_comments_size(self.parser) }; + let mut ptrs: Vec<*const pm_comment_t> = Vec::with_capacity(size); unsafe { - let list = &mut (*self.parser.as_ptr()).comment_list; - Comments::new(list.head.cast::(), self.parser) + pm_parser_comments_each(self.parser, Some(collect_comment), (&raw mut ptrs).cast()); } + Comments::new(ptrs, self.parser) } /// Returns an iterator that can be used to iterate over the magic comments in the /// parse result. #[must_use] pub fn magic_comments(&self) -> MagicComments<'_> { + let size = unsafe { pm_parser_magic_comments_size(self.parser) }; + let mut ptrs: Vec<*const pm_magic_comment_t> = Vec::with_capacity(size); unsafe { - let list = &mut (*self.parser.as_ptr()).magic_comment_list; - MagicComments::new(self.parser, list.head.cast::()) + pm_parser_magic_comments_each(self.parser, Some(collect_magic_comment), (&raw mut ptrs).cast()); } + MagicComments::new(ptrs, self.parser) } /// Returns an optional location of the __END__ marker and the rest of the content of the file. #[must_use] pub fn data_loc(&self) -> Option> { - let location = unsafe { &(*self.parser.as_ptr()).data_loc }; + let location = unsafe { &*pm_parser_data_loc(self.parser) }; if location.length == 0 { None } else { @@ -175,14 +267,46 @@ impl<'pr> ParseResult<'pr> { pub fn node(&self) -> Node<'_> { Node::new(self.parser, self.node.as_ptr()) } + + /// Returns true if there were no errors during parsing and false if there + /// were. + #[must_use] + pub fn is_success(&self) -> bool { + self.errors().next().is_none() + } + + /// Returns true if there were errors during parsing and false if there were + /// not. + #[must_use] + pub fn is_failure(&self) -> bool { + !self.is_success() + } } impl Drop for ParseResult<'_> { fn drop(&mut self) { unsafe { - pm_node_destroy(self.parser.as_ptr(), self.node.as_ptr()); - pm_parser_free(self.parser.as_ptr()); - drop(Box::from_raw(self.parser.as_ptr())); + pm_parser_free(self.parser); + pm_arena_free(self.arena); } } } + +#[cfg(test)] +mod tests { + use crate::parse; + + #[test] + fn test_is_success() { + let result = parse(b"1 + 1"); + assert!(result.is_success()); + assert!(!result.is_failure()); + } + + #[test] + fn test_is_failure() { + let result = parse(b"<>"); + assert!(result.is_failure()); + assert!(!result.is_success()); + } +} diff --git a/sig/_shims/rubyvm.rbs b/sig/_shims/rubyvm.rbs new file mode 100644 index 0000000000..bf4e5ab18f --- /dev/null +++ b/sig/_shims/rubyvm.rbs @@ -0,0 +1,6 @@ +class RubyVM + class InstructionSequence + def self.of: (Method | UnboundMethod | Proc) -> RubyVM::InstructionSequence? + def to_a: () -> Array[untyped] + end +end diff --git a/sig/generated/prism.rbs b/sig/generated/prism.rbs new file mode 100644 index 0000000000..93fbba28eb --- /dev/null +++ b/sig/generated/prism.rbs @@ -0,0 +1,84 @@ +# Generated from lib/prism.rb with RBS::Inline + +# The Prism Ruby parser. +# +# "Parsing Ruby is suddenly manageable!" +# - You, hopefully +module Prism + # 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. + # -- + # : (String version) -> void + def initialize: (String version) -> void + end + + # :call-seq: + # 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. + # -- + # : (String source, **untyped options) -> LexCompat::Result + def self.lex_compat: (String source, **untyped options) -> LexCompat::Result + + # :call-seq: + # load(source, serialized, freeze) -> ParseResult + # + # Load the serialized AST using the source as a reference into a tree. + # -- + # : (String source, String serialized, ?bool freeze) -> ParseResult + def self.load: (String source, String serialized, ?bool freeze) -> ParseResult + + # Given a Method, UnboundMethod, Proc, or Thread::Backtrace::Location, + # returns the Prism node representing it. On CRuby, this uses node_id for + # an exact match. On other implementations, it falls back to best-effort + # matching by source location line number. + # -- + # : (Method | UnboundMethod | Proc | Thread::Backtrace::Location callable, ?rubyvm: bool) -> Node? + def self.find: (Method | UnboundMethod | Proc | Thread::Backtrace::Location callable, ?rubyvm: bool) -> Node? + + VERSION: String + + BACKEND: :CEXT | :FFI + + interface _Stream + def gets: (?Integer integer) -> (String | nil) + end + + def self.parse: (String source, ?filepath: String, ?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: (String source, ?filepath: String, ?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) -> void + + def self.lex: (String source, ?filepath: String, ?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: (String source, ?filepath: String, ?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: (String source, ?filepath: String, ?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_comments: (String source, ?filepath: String, ?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_success?: (String source, ?filepath: String, ?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_failure?: (String source, ?filepath: String, ?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_stream: (_Stream stream, ?filepath: String, ?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.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) -> void + + 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 +end diff --git a/sig/generated/prism/compiler.rbs b/sig/generated/prism/compiler.rbs new file mode 100644 index 0000000000..c2e4db0ace --- /dev/null +++ b/sig/generated/prism/compiler.rbs @@ -0,0 +1,492 @@ +# Generated from lib/prism/compiler.rb with RBS::Inline + +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. + # -- + # : (node?) -> untyped + def visit: (node?) -> untyped + + # Visit a list of nodes. + # -- + # : (Array[node?]) -> untyped + def visit_all: (Array[node?]) -> untyped + + # Visit the child nodes of the given node. + # -- + # : (node) -> Array[untyped] + def visit_child_nodes: (node) -> Array[untyped] + + # : (AliasGlobalVariableNode) -> Array[untyped] + def visit_alias_global_variable_node: (AliasGlobalVariableNode) -> Array[untyped] + + # : (AliasMethodNode) -> Array[untyped] + def visit_alias_method_node: (AliasMethodNode) -> Array[untyped] + + # : (AlternationPatternNode) -> Array[untyped] + def visit_alternation_pattern_node: (AlternationPatternNode) -> Array[untyped] + + # : (AndNode) -> Array[untyped] + def visit_and_node: (AndNode) -> Array[untyped] + + # : (ArgumentsNode) -> Array[untyped] + def visit_arguments_node: (ArgumentsNode) -> Array[untyped] + + # : (ArrayNode) -> Array[untyped] + def visit_array_node: (ArrayNode) -> Array[untyped] + + # : (ArrayPatternNode) -> Array[untyped] + def visit_array_pattern_node: (ArrayPatternNode) -> Array[untyped] + + # : (AssocNode) -> Array[untyped] + def visit_assoc_node: (AssocNode) -> Array[untyped] + + # : (AssocSplatNode) -> Array[untyped] + def visit_assoc_splat_node: (AssocSplatNode) -> Array[untyped] + + # : (BackReferenceReadNode) -> Array[untyped] + def visit_back_reference_read_node: (BackReferenceReadNode) -> Array[untyped] + + # : (BeginNode) -> Array[untyped] + def visit_begin_node: (BeginNode) -> Array[untyped] + + # : (BlockArgumentNode) -> Array[untyped] + def visit_block_argument_node: (BlockArgumentNode) -> Array[untyped] + + # : (BlockLocalVariableNode) -> Array[untyped] + def visit_block_local_variable_node: (BlockLocalVariableNode) -> Array[untyped] + + # : (BlockNode) -> Array[untyped] + def visit_block_node: (BlockNode) -> Array[untyped] + + # : (BlockParameterNode) -> Array[untyped] + def visit_block_parameter_node: (BlockParameterNode) -> Array[untyped] + + # : (BlockParametersNode) -> Array[untyped] + def visit_block_parameters_node: (BlockParametersNode) -> Array[untyped] + + # : (BreakNode) -> Array[untyped] + def visit_break_node: (BreakNode) -> Array[untyped] + + # : (CallAndWriteNode) -> Array[untyped] + def visit_call_and_write_node: (CallAndWriteNode) -> Array[untyped] + + # : (CallNode) -> Array[untyped] + def visit_call_node: (CallNode) -> Array[untyped] + + # : (CallOperatorWriteNode) -> Array[untyped] + def visit_call_operator_write_node: (CallOperatorWriteNode) -> Array[untyped] + + # : (CallOrWriteNode) -> Array[untyped] + def visit_call_or_write_node: (CallOrWriteNode) -> Array[untyped] + + # : (CallTargetNode) -> Array[untyped] + def visit_call_target_node: (CallTargetNode) -> Array[untyped] + + # : (CapturePatternNode) -> Array[untyped] + def visit_capture_pattern_node: (CapturePatternNode) -> Array[untyped] + + # : (CaseMatchNode) -> Array[untyped] + def visit_case_match_node: (CaseMatchNode) -> Array[untyped] + + # : (CaseNode) -> Array[untyped] + def visit_case_node: (CaseNode) -> Array[untyped] + + # : (ClassNode) -> Array[untyped] + def visit_class_node: (ClassNode) -> Array[untyped] + + # : (ClassVariableAndWriteNode) -> Array[untyped] + def visit_class_variable_and_write_node: (ClassVariableAndWriteNode) -> Array[untyped] + + # : (ClassVariableOperatorWriteNode) -> Array[untyped] + def visit_class_variable_operator_write_node: (ClassVariableOperatorWriteNode) -> Array[untyped] + + # : (ClassVariableOrWriteNode) -> Array[untyped] + def visit_class_variable_or_write_node: (ClassVariableOrWriteNode) -> Array[untyped] + + # : (ClassVariableReadNode) -> Array[untyped] + def visit_class_variable_read_node: (ClassVariableReadNode) -> Array[untyped] + + # : (ClassVariableTargetNode) -> Array[untyped] + def visit_class_variable_target_node: (ClassVariableTargetNode) -> Array[untyped] + + # : (ClassVariableWriteNode) -> Array[untyped] + def visit_class_variable_write_node: (ClassVariableWriteNode) -> Array[untyped] + + # : (ConstantAndWriteNode) -> Array[untyped] + def visit_constant_and_write_node: (ConstantAndWriteNode) -> Array[untyped] + + # : (ConstantOperatorWriteNode) -> Array[untyped] + def visit_constant_operator_write_node: (ConstantOperatorWriteNode) -> Array[untyped] + + # : (ConstantOrWriteNode) -> Array[untyped] + def visit_constant_or_write_node: (ConstantOrWriteNode) -> Array[untyped] + + # : (ConstantPathAndWriteNode) -> Array[untyped] + def visit_constant_path_and_write_node: (ConstantPathAndWriteNode) -> Array[untyped] + + # : (ConstantPathNode) -> Array[untyped] + def visit_constant_path_node: (ConstantPathNode) -> Array[untyped] + + # : (ConstantPathOperatorWriteNode) -> Array[untyped] + def visit_constant_path_operator_write_node: (ConstantPathOperatorWriteNode) -> Array[untyped] + + # : (ConstantPathOrWriteNode) -> Array[untyped] + def visit_constant_path_or_write_node: (ConstantPathOrWriteNode) -> Array[untyped] + + # : (ConstantPathTargetNode) -> Array[untyped] + def visit_constant_path_target_node: (ConstantPathTargetNode) -> Array[untyped] + + # : (ConstantPathWriteNode) -> Array[untyped] + def visit_constant_path_write_node: (ConstantPathWriteNode) -> Array[untyped] + + # : (ConstantReadNode) -> Array[untyped] + def visit_constant_read_node: (ConstantReadNode) -> Array[untyped] + + # : (ConstantTargetNode) -> Array[untyped] + def visit_constant_target_node: (ConstantTargetNode) -> Array[untyped] + + # : (ConstantWriteNode) -> Array[untyped] + def visit_constant_write_node: (ConstantWriteNode) -> Array[untyped] + + # : (DefNode) -> Array[untyped] + def visit_def_node: (DefNode) -> Array[untyped] + + # : (DefinedNode) -> Array[untyped] + def visit_defined_node: (DefinedNode) -> Array[untyped] + + # : (ElseNode) -> Array[untyped] + def visit_else_node: (ElseNode) -> Array[untyped] + + # : (EmbeddedStatementsNode) -> Array[untyped] + def visit_embedded_statements_node: (EmbeddedStatementsNode) -> Array[untyped] + + # : (EmbeddedVariableNode) -> Array[untyped] + def visit_embedded_variable_node: (EmbeddedVariableNode) -> Array[untyped] + + # : (EnsureNode) -> Array[untyped] + def visit_ensure_node: (EnsureNode) -> Array[untyped] + + # : (FalseNode) -> Array[untyped] + def visit_false_node: (FalseNode) -> Array[untyped] + + # : (FindPatternNode) -> Array[untyped] + def visit_find_pattern_node: (FindPatternNode) -> Array[untyped] + + # : (FlipFlopNode) -> Array[untyped] + def visit_flip_flop_node: (FlipFlopNode) -> Array[untyped] + + # : (FloatNode) -> Array[untyped] + def visit_float_node: (FloatNode) -> Array[untyped] + + # : (ForNode) -> Array[untyped] + def visit_for_node: (ForNode) -> Array[untyped] + + # : (ForwardingArgumentsNode) -> Array[untyped] + def visit_forwarding_arguments_node: (ForwardingArgumentsNode) -> Array[untyped] + + # : (ForwardingParameterNode) -> Array[untyped] + def visit_forwarding_parameter_node: (ForwardingParameterNode) -> Array[untyped] + + # : (ForwardingSuperNode) -> Array[untyped] + def visit_forwarding_super_node: (ForwardingSuperNode) -> Array[untyped] + + # : (GlobalVariableAndWriteNode) -> Array[untyped] + def visit_global_variable_and_write_node: (GlobalVariableAndWriteNode) -> Array[untyped] + + # : (GlobalVariableOperatorWriteNode) -> Array[untyped] + def visit_global_variable_operator_write_node: (GlobalVariableOperatorWriteNode) -> Array[untyped] + + # : (GlobalVariableOrWriteNode) -> Array[untyped] + def visit_global_variable_or_write_node: (GlobalVariableOrWriteNode) -> Array[untyped] + + # : (GlobalVariableReadNode) -> Array[untyped] + def visit_global_variable_read_node: (GlobalVariableReadNode) -> Array[untyped] + + # : (GlobalVariableTargetNode) -> Array[untyped] + def visit_global_variable_target_node: (GlobalVariableTargetNode) -> Array[untyped] + + # : (GlobalVariableWriteNode) -> Array[untyped] + def visit_global_variable_write_node: (GlobalVariableWriteNode) -> Array[untyped] + + # : (HashNode) -> Array[untyped] + def visit_hash_node: (HashNode) -> Array[untyped] + + # : (HashPatternNode) -> Array[untyped] + def visit_hash_pattern_node: (HashPatternNode) -> Array[untyped] + + # : (IfNode) -> Array[untyped] + def visit_if_node: (IfNode) -> Array[untyped] + + # : (ImaginaryNode) -> Array[untyped] + def visit_imaginary_node: (ImaginaryNode) -> Array[untyped] + + # : (ImplicitNode) -> Array[untyped] + def visit_implicit_node: (ImplicitNode) -> Array[untyped] + + # : (ImplicitRestNode) -> Array[untyped] + def visit_implicit_rest_node: (ImplicitRestNode) -> Array[untyped] + + # : (InNode) -> Array[untyped] + def visit_in_node: (InNode) -> Array[untyped] + + # : (IndexAndWriteNode) -> Array[untyped] + def visit_index_and_write_node: (IndexAndWriteNode) -> Array[untyped] + + # : (IndexOperatorWriteNode) -> Array[untyped] + def visit_index_operator_write_node: (IndexOperatorWriteNode) -> Array[untyped] + + # : (IndexOrWriteNode) -> Array[untyped] + def visit_index_or_write_node: (IndexOrWriteNode) -> Array[untyped] + + # : (IndexTargetNode) -> Array[untyped] + def visit_index_target_node: (IndexTargetNode) -> Array[untyped] + + # : (InstanceVariableAndWriteNode) -> Array[untyped] + def visit_instance_variable_and_write_node: (InstanceVariableAndWriteNode) -> Array[untyped] + + # : (InstanceVariableOperatorWriteNode) -> Array[untyped] + def visit_instance_variable_operator_write_node: (InstanceVariableOperatorWriteNode) -> Array[untyped] + + # : (InstanceVariableOrWriteNode) -> Array[untyped] + def visit_instance_variable_or_write_node: (InstanceVariableOrWriteNode) -> Array[untyped] + + # : (InstanceVariableReadNode) -> Array[untyped] + def visit_instance_variable_read_node: (InstanceVariableReadNode) -> Array[untyped] + + # : (InstanceVariableTargetNode) -> Array[untyped] + def visit_instance_variable_target_node: (InstanceVariableTargetNode) -> Array[untyped] + + # : (InstanceVariableWriteNode) -> Array[untyped] + def visit_instance_variable_write_node: (InstanceVariableWriteNode) -> Array[untyped] + + # : (IntegerNode) -> Array[untyped] + def visit_integer_node: (IntegerNode) -> Array[untyped] + + # : (InterpolatedMatchLastLineNode) -> Array[untyped] + def visit_interpolated_match_last_line_node: (InterpolatedMatchLastLineNode) -> Array[untyped] + + # : (InterpolatedRegularExpressionNode) -> Array[untyped] + def visit_interpolated_regular_expression_node: (InterpolatedRegularExpressionNode) -> Array[untyped] + + # : (InterpolatedStringNode) -> Array[untyped] + def visit_interpolated_string_node: (InterpolatedStringNode) -> Array[untyped] + + # : (InterpolatedSymbolNode) -> Array[untyped] + def visit_interpolated_symbol_node: (InterpolatedSymbolNode) -> Array[untyped] + + # : (InterpolatedXStringNode) -> Array[untyped] + def visit_interpolated_x_string_node: (InterpolatedXStringNode) -> Array[untyped] + + # : (ItLocalVariableReadNode) -> Array[untyped] + def visit_it_local_variable_read_node: (ItLocalVariableReadNode) -> Array[untyped] + + # : (ItParametersNode) -> Array[untyped] + def visit_it_parameters_node: (ItParametersNode) -> Array[untyped] + + # : (KeywordHashNode) -> Array[untyped] + def visit_keyword_hash_node: (KeywordHashNode) -> Array[untyped] + + # : (KeywordRestParameterNode) -> Array[untyped] + def visit_keyword_rest_parameter_node: (KeywordRestParameterNode) -> Array[untyped] + + # : (LambdaNode) -> Array[untyped] + def visit_lambda_node: (LambdaNode) -> Array[untyped] + + # : (LocalVariableAndWriteNode) -> Array[untyped] + def visit_local_variable_and_write_node: (LocalVariableAndWriteNode) -> Array[untyped] + + # : (LocalVariableOperatorWriteNode) -> Array[untyped] + def visit_local_variable_operator_write_node: (LocalVariableOperatorWriteNode) -> Array[untyped] + + # : (LocalVariableOrWriteNode) -> Array[untyped] + def visit_local_variable_or_write_node: (LocalVariableOrWriteNode) -> Array[untyped] + + # : (LocalVariableReadNode) -> Array[untyped] + def visit_local_variable_read_node: (LocalVariableReadNode) -> Array[untyped] + + # : (LocalVariableTargetNode) -> Array[untyped] + def visit_local_variable_target_node: (LocalVariableTargetNode) -> Array[untyped] + + # : (LocalVariableWriteNode) -> Array[untyped] + def visit_local_variable_write_node: (LocalVariableWriteNode) -> Array[untyped] + + # : (MatchLastLineNode) -> Array[untyped] + def visit_match_last_line_node: (MatchLastLineNode) -> Array[untyped] + + # : (MatchPredicateNode) -> Array[untyped] + def visit_match_predicate_node: (MatchPredicateNode) -> Array[untyped] + + # : (MatchRequiredNode) -> Array[untyped] + def visit_match_required_node: (MatchRequiredNode) -> Array[untyped] + + # : (MatchWriteNode) -> Array[untyped] + def visit_match_write_node: (MatchWriteNode) -> Array[untyped] + + # : (MissingNode) -> Array[untyped] + def visit_missing_node: (MissingNode) -> Array[untyped] + + # : (ModuleNode) -> Array[untyped] + def visit_module_node: (ModuleNode) -> Array[untyped] + + # : (MultiTargetNode) -> Array[untyped] + def visit_multi_target_node: (MultiTargetNode) -> Array[untyped] + + # : (MultiWriteNode) -> Array[untyped] + def visit_multi_write_node: (MultiWriteNode) -> Array[untyped] + + # : (NextNode) -> Array[untyped] + def visit_next_node: (NextNode) -> Array[untyped] + + # : (NilNode) -> Array[untyped] + def visit_nil_node: (NilNode) -> Array[untyped] + + # : (NoBlockParameterNode) -> Array[untyped] + def visit_no_block_parameter_node: (NoBlockParameterNode) -> Array[untyped] + + # : (NoKeywordsParameterNode) -> Array[untyped] + def visit_no_keywords_parameter_node: (NoKeywordsParameterNode) -> Array[untyped] + + # : (NumberedParametersNode) -> Array[untyped] + def visit_numbered_parameters_node: (NumberedParametersNode) -> Array[untyped] + + # : (NumberedReferenceReadNode) -> Array[untyped] + def visit_numbered_reference_read_node: (NumberedReferenceReadNode) -> Array[untyped] + + # : (OptionalKeywordParameterNode) -> Array[untyped] + def visit_optional_keyword_parameter_node: (OptionalKeywordParameterNode) -> Array[untyped] + + # : (OptionalParameterNode) -> Array[untyped] + def visit_optional_parameter_node: (OptionalParameterNode) -> Array[untyped] + + # : (OrNode) -> Array[untyped] + def visit_or_node: (OrNode) -> Array[untyped] + + # : (ParametersNode) -> Array[untyped] + def visit_parameters_node: (ParametersNode) -> Array[untyped] + + # : (ParenthesesNode) -> Array[untyped] + def visit_parentheses_node: (ParenthesesNode) -> Array[untyped] + + # : (PinnedExpressionNode) -> Array[untyped] + def visit_pinned_expression_node: (PinnedExpressionNode) -> Array[untyped] + + # : (PinnedVariableNode) -> Array[untyped] + def visit_pinned_variable_node: (PinnedVariableNode) -> Array[untyped] + + # : (PostExecutionNode) -> Array[untyped] + def visit_post_execution_node: (PostExecutionNode) -> Array[untyped] + + # : (PreExecutionNode) -> Array[untyped] + def visit_pre_execution_node: (PreExecutionNode) -> Array[untyped] + + # : (ProgramNode) -> Array[untyped] + def visit_program_node: (ProgramNode) -> Array[untyped] + + # : (RangeNode) -> Array[untyped] + def visit_range_node: (RangeNode) -> Array[untyped] + + # : (RationalNode) -> Array[untyped] + def visit_rational_node: (RationalNode) -> Array[untyped] + + # : (RedoNode) -> Array[untyped] + def visit_redo_node: (RedoNode) -> Array[untyped] + + # : (RegularExpressionNode) -> Array[untyped] + def visit_regular_expression_node: (RegularExpressionNode) -> Array[untyped] + + # : (RequiredKeywordParameterNode) -> Array[untyped] + def visit_required_keyword_parameter_node: (RequiredKeywordParameterNode) -> Array[untyped] + + # : (RequiredParameterNode) -> Array[untyped] + def visit_required_parameter_node: (RequiredParameterNode) -> Array[untyped] + + # : (RescueModifierNode) -> Array[untyped] + def visit_rescue_modifier_node: (RescueModifierNode) -> Array[untyped] + + # : (RescueNode) -> Array[untyped] + def visit_rescue_node: (RescueNode) -> Array[untyped] + + # : (RestParameterNode) -> Array[untyped] + def visit_rest_parameter_node: (RestParameterNode) -> Array[untyped] + + # : (RetryNode) -> Array[untyped] + def visit_retry_node: (RetryNode) -> Array[untyped] + + # : (ReturnNode) -> Array[untyped] + def visit_return_node: (ReturnNode) -> Array[untyped] + + # : (SelfNode) -> Array[untyped] + def visit_self_node: (SelfNode) -> Array[untyped] + + # : (ShareableConstantNode) -> Array[untyped] + def visit_shareable_constant_node: (ShareableConstantNode) -> Array[untyped] + + # : (SingletonClassNode) -> Array[untyped] + def visit_singleton_class_node: (SingletonClassNode) -> Array[untyped] + + # : (SourceEncodingNode) -> Array[untyped] + def visit_source_encoding_node: (SourceEncodingNode) -> Array[untyped] + + # : (SourceFileNode) -> Array[untyped] + def visit_source_file_node: (SourceFileNode) -> Array[untyped] + + # : (SourceLineNode) -> Array[untyped] + def visit_source_line_node: (SourceLineNode) -> Array[untyped] + + # : (SplatNode) -> Array[untyped] + def visit_splat_node: (SplatNode) -> Array[untyped] + + # : (StatementsNode) -> Array[untyped] + def visit_statements_node: (StatementsNode) -> Array[untyped] + + # : (StringNode) -> Array[untyped] + def visit_string_node: (StringNode) -> Array[untyped] + + # : (SuperNode) -> Array[untyped] + def visit_super_node: (SuperNode) -> Array[untyped] + + # : (SymbolNode) -> Array[untyped] + def visit_symbol_node: (SymbolNode) -> Array[untyped] + + # : (TrueNode) -> Array[untyped] + def visit_true_node: (TrueNode) -> Array[untyped] + + # : (UndefNode) -> Array[untyped] + def visit_undef_node: (UndefNode) -> Array[untyped] + + # : (UnlessNode) -> Array[untyped] + def visit_unless_node: (UnlessNode) -> Array[untyped] + + # : (UntilNode) -> Array[untyped] + def visit_until_node: (UntilNode) -> Array[untyped] + + # : (WhenNode) -> Array[untyped] + def visit_when_node: (WhenNode) -> Array[untyped] + + # : (WhileNode) -> Array[untyped] + def visit_while_node: (WhileNode) -> Array[untyped] + + # : (XStringNode) -> Array[untyped] + def visit_x_string_node: (XStringNode) -> Array[untyped] + + # : (YieldNode) -> Array[untyped] + def visit_yield_node: (YieldNode) -> Array[untyped] + end +end diff --git a/sig/generated/prism/desugar_compiler.rbs b/sig/generated/prism/desugar_compiler.rbs new file mode 100644 index 0000000000..d5bb07edf1 --- /dev/null +++ b/sig/generated/prism/desugar_compiler.rbs @@ -0,0 +1,305 @@ +# Generated from lib/prism/desugar_compiler.rb with RBS::Inline + +module Prism + class DesugarAndWriteNode + include DSL + + attr_reader node: ClassVariableAndWriteNode | ConstantAndWriteNode | GlobalVariableAndWriteNode | InstanceVariableAndWriteNode | LocalVariableAndWriteNode + + attr_reader default_source: Source + + attr_reader read_class: Symbol + + attr_reader write_class: Symbol + + attr_reader arguments: Hash[Symbol, untyped] + + # : ((ClassVariableAndWriteNode | ConstantAndWriteNode | GlobalVariableAndWriteNode | InstanceVariableAndWriteNode | LocalVariableAndWriteNode) node, Source default_source, Symbol read_class, Symbol write_class, **untyped arguments) -> void + def initialize: (ClassVariableAndWriteNode | ConstantAndWriteNode | GlobalVariableAndWriteNode | InstanceVariableAndWriteNode | LocalVariableAndWriteNode node, Source default_source, Symbol read_class, Symbol write_class, **untyped arguments) -> void + + # Desugar `x &&= y` to `x && x = y` + # -- + # : () -> node + def compile: () -> node + end + + class DesugarOrWriteDefinedNode + include DSL + + attr_reader node: ClassVariableOrWriteNode | ConstantOrWriteNode | GlobalVariableOrWriteNode + + attr_reader default_source: Source + + attr_reader read_class: Symbol + + attr_reader write_class: Symbol + + attr_reader arguments: Hash[Symbol, untyped] + + # : ((ClassVariableOrWriteNode | ConstantOrWriteNode | GlobalVariableOrWriteNode) node, Source default_source, Symbol read_class, Symbol write_class, **untyped arguments) -> void + def initialize: (ClassVariableOrWriteNode | ConstantOrWriteNode | GlobalVariableOrWriteNode node, Source default_source, Symbol read_class, Symbol write_class, **untyped arguments) -> void + + # Desugar `x ||= y` to `defined?(x) ? x : x = y` + # -- + # : () -> node + def compile: () -> node + end + + class DesugarOperatorWriteNode + include DSL + + attr_reader node: ClassVariableOperatorWriteNode | ConstantOperatorWriteNode | GlobalVariableOperatorWriteNode | InstanceVariableOperatorWriteNode | LocalVariableOperatorWriteNode + + attr_reader default_source: Source + + attr_reader read_class: Symbol + + attr_reader write_class: Symbol + + attr_reader arguments: Hash[Symbol, untyped] + + # : ((ClassVariableOperatorWriteNode | ConstantOperatorWriteNode | GlobalVariableOperatorWriteNode | InstanceVariableOperatorWriteNode | LocalVariableOperatorWriteNode) node, Source default_source, Symbol read_class, Symbol write_class, **untyped arguments) -> void + def initialize: (ClassVariableOperatorWriteNode | ConstantOperatorWriteNode | GlobalVariableOperatorWriteNode | InstanceVariableOperatorWriteNode | LocalVariableOperatorWriteNode node, Source default_source, Symbol read_class, Symbol write_class, **untyped arguments) -> void + + # Desugar `x += y` to `x = x + y` + # -- + # : () -> node + def compile: () -> node + end + + class DesugarOrWriteNode + include DSL + + attr_reader node: InstanceVariableOrWriteNode | LocalVariableOrWriteNode + + attr_reader default_source: Source + + attr_reader read_class: Symbol + + attr_reader write_class: Symbol + + attr_reader arguments: Hash[Symbol, untyped] + + # : ((InstanceVariableOrWriteNode | LocalVariableOrWriteNode) node, Source default_source, Symbol read_class, Symbol write_class, **untyped arguments) -> void + def initialize: (InstanceVariableOrWriteNode | LocalVariableOrWriteNode node, Source default_source, Symbol read_class, Symbol write_class, **untyped arguments) -> void + + # Desugar `x ||= y` to `x || x = y` + # -- + # : () -> node + def compile: () -> node + end + + class ClassVariableAndWriteNode + # : () -> node + def desugar: () -> node + end + + class ClassVariableOrWriteNode + # : () -> node + def desugar: () -> node + end + + class ClassVariableOperatorWriteNode + # : () -> node + def desugar: () -> node + end + + class ConstantAndWriteNode + # : () -> node + def desugar: () -> node + end + + class ConstantOrWriteNode + # : () -> node + def desugar: () -> node + end + + class ConstantOperatorWriteNode + # : () -> node + def desugar: () -> node + end + + class GlobalVariableAndWriteNode + # : () -> node + def desugar: () -> node + end + + class GlobalVariableOrWriteNode + # : () -> node + def desugar: () -> node + end + + class GlobalVariableOperatorWriteNode + # : () -> node + def desugar: () -> node + end + + class InstanceVariableAndWriteNode + # : () -> node + def desugar: () -> node + end + + class InstanceVariableOrWriteNode + # : () -> node + def desugar: () -> node + end + + class InstanceVariableOperatorWriteNode + # : () -> node + def desugar: () -> node + end + + class LocalVariableAndWriteNode + # : () -> node + def desugar: () -> node + end + + class LocalVariableOrWriteNode + # : () -> node + def desugar: () -> node + end + + class LocalVariableOperatorWriteNode + # : () -> node + def desugar: () -> node + 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` + # -- + # : (ClassVariableAndWriteNode node) -> node + def visit_class_variable_and_write_node: (ClassVariableAndWriteNode node) -> node + + # `@@foo ||= bar` + # + # becomes + # + # `defined?(@@foo) ? @@foo : @@foo = bar` + # -- + # : (ClassVariableOrWriteNode node) -> node + def visit_class_variable_or_write_node: (ClassVariableOrWriteNode node) -> node + + # `@@foo += bar` + # + # becomes + # + # `@@foo = @@foo + bar` + # -- + # : (ClassVariableOperatorWriteNode node) -> node + def visit_class_variable_operator_write_node: (ClassVariableOperatorWriteNode node) -> node + + # `Foo &&= bar` + # + # becomes + # + # `Foo && Foo = bar` + # -- + # : (ConstantAndWriteNode node) -> node + def visit_constant_and_write_node: (ConstantAndWriteNode node) -> node + + # `Foo ||= bar` + # + # becomes + # + # `defined?(Foo) ? Foo : Foo = bar` + # -- + # : (ConstantOrWriteNode node) -> node + def visit_constant_or_write_node: (ConstantOrWriteNode node) -> node + + # `Foo += bar` + # + # becomes + # + # `Foo = Foo + bar` + # -- + # : (ConstantOperatorWriteNode node) -> node + def visit_constant_operator_write_node: (ConstantOperatorWriteNode node) -> node + + # `$foo &&= bar` + # + # becomes + # + # `$foo && $foo = bar` + # -- + # : (GlobalVariableAndWriteNode node) -> node + def visit_global_variable_and_write_node: (GlobalVariableAndWriteNode node) -> node + + # `$foo ||= bar` + # + # becomes + # + # `defined?($foo) ? $foo : $foo = bar` + # -- + # : (GlobalVariableOrWriteNode node) -> node + def visit_global_variable_or_write_node: (GlobalVariableOrWriteNode node) -> node + + # `$foo += bar` + # + # becomes + # + # `$foo = $foo + bar` + # -- + # : (GlobalVariableOperatorWriteNode node) -> node + def visit_global_variable_operator_write_node: (GlobalVariableOperatorWriteNode node) -> node + + # `@foo &&= bar` + # + # becomes + # + # `@foo && @foo = bar` + # -- + # : (InstanceVariableAndWriteNode node) -> node + def visit_instance_variable_and_write_node: (InstanceVariableAndWriteNode node) -> node + + # `@foo ||= bar` + # + # becomes + # + # `@foo || @foo = bar` + # -- + # : (InstanceVariableOrWriteNode node) -> node + def visit_instance_variable_or_write_node: (InstanceVariableOrWriteNode node) -> node + + # `@foo += bar` + # + # becomes + # + # `@foo = @foo + bar` + # -- + # : (InstanceVariableOperatorWriteNode node) -> node + def visit_instance_variable_operator_write_node: (InstanceVariableOperatorWriteNode node) -> node + + # `foo &&= bar` + # + # becomes + # + # `foo && foo = bar` + # -- + # : (LocalVariableAndWriteNode node) -> node + def visit_local_variable_and_write_node: (LocalVariableAndWriteNode node) -> node + + # `foo ||= bar` + # + # becomes + # + # `foo || foo = bar` + # -- + # : (LocalVariableOrWriteNode node) -> node + def visit_local_variable_or_write_node: (LocalVariableOrWriteNode node) -> node + + # `foo += bar` + # + # becomes + # + # `foo = foo + bar` + # -- + # : (LocalVariableOperatorWriteNode node) -> node + def visit_local_variable_operator_write_node: (LocalVariableOperatorWriteNode node) -> node + end +end diff --git a/sig/generated/prism/dispatcher.rbs b/sig/generated/prism/dispatcher.rbs new file mode 100644 index 0000000000..0463d2b0ba --- /dev/null +++ b/sig/generated/prism/dispatcher.rbs @@ -0,0 +1,1292 @@ +# Generated from lib/prism/dispatcher.rb with RBS::Inline + +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 + # A hash mapping event names to arrays of listeners that should be notified + # when that event is fired. + attr_reader listeners: Hash[Symbol, Array[untyped]] + + # Initialize a new dispatcher. + # -- + # : () -> void + def initialize: () -> void + + # Register a listener for one or more events. + # -- + # : (untyped, *Symbol) -> void + def register: (untyped, *Symbol) -> void + + # Register all public methods of a listener that match the pattern + # `on__(enter|leave)`. + # -- + # : (untyped) -> void + def register_public_methods: (untyped) -> void + + # Register a listener for the given events. + # -- + # : (untyped, Array[Symbol]) -> void + private def register_events: (untyped, Array[Symbol]) -> void + + # Walks `root` dispatching events to all registered listeners. + alias dispatch visit + + # Dispatches a single event for `node` to all registered listeners. + # -- + # : (node node) -> void + def dispatch_once: (node node) -> void + + # : (AliasGlobalVariableNode node) -> void + def visit_alias_global_variable_node: (AliasGlobalVariableNode node) -> void + + # : (AliasMethodNode node) -> void + def visit_alias_method_node: (AliasMethodNode node) -> void + + # : (AlternationPatternNode node) -> void + def visit_alternation_pattern_node: (AlternationPatternNode node) -> void + + # : (AndNode node) -> void + def visit_and_node: (AndNode node) -> void + + # : (ArgumentsNode node) -> void + def visit_arguments_node: (ArgumentsNode node) -> void + + # : (ArrayNode node) -> void + def visit_array_node: (ArrayNode node) -> void + + # : (ArrayPatternNode node) -> void + def visit_array_pattern_node: (ArrayPatternNode node) -> void + + # : (AssocNode node) -> void + def visit_assoc_node: (AssocNode node) -> void + + # : (AssocSplatNode node) -> void + def visit_assoc_splat_node: (AssocSplatNode node) -> void + + # : (BackReferenceReadNode node) -> void + def visit_back_reference_read_node: (BackReferenceReadNode node) -> void + + # : (BeginNode node) -> void + def visit_begin_node: (BeginNode node) -> void + + # : (BlockArgumentNode node) -> void + def visit_block_argument_node: (BlockArgumentNode node) -> void + + # : (BlockLocalVariableNode node) -> void + def visit_block_local_variable_node: (BlockLocalVariableNode node) -> void + + # : (BlockNode node) -> void + def visit_block_node: (BlockNode node) -> void + + # : (BlockParameterNode node) -> void + def visit_block_parameter_node: (BlockParameterNode node) -> void + + # : (BlockParametersNode node) -> void + def visit_block_parameters_node: (BlockParametersNode node) -> void + + # : (BreakNode node) -> void + def visit_break_node: (BreakNode node) -> void + + # : (CallAndWriteNode node) -> void + def visit_call_and_write_node: (CallAndWriteNode node) -> void + + # : (CallNode node) -> void + def visit_call_node: (CallNode node) -> void + + # : (CallOperatorWriteNode node) -> void + def visit_call_operator_write_node: (CallOperatorWriteNode node) -> void + + # : (CallOrWriteNode node) -> void + def visit_call_or_write_node: (CallOrWriteNode node) -> void + + # : (CallTargetNode node) -> void + def visit_call_target_node: (CallTargetNode node) -> void + + # : (CapturePatternNode node) -> void + def visit_capture_pattern_node: (CapturePatternNode node) -> void + + # : (CaseMatchNode node) -> void + def visit_case_match_node: (CaseMatchNode node) -> void + + # : (CaseNode node) -> void + def visit_case_node: (CaseNode node) -> void + + # : (ClassNode node) -> void + def visit_class_node: (ClassNode node) -> void + + # : (ClassVariableAndWriteNode node) -> void + def visit_class_variable_and_write_node: (ClassVariableAndWriteNode node) -> void + + # : (ClassVariableOperatorWriteNode node) -> void + def visit_class_variable_operator_write_node: (ClassVariableOperatorWriteNode node) -> void + + # : (ClassVariableOrWriteNode node) -> void + def visit_class_variable_or_write_node: (ClassVariableOrWriteNode node) -> void + + # : (ClassVariableReadNode node) -> void + def visit_class_variable_read_node: (ClassVariableReadNode node) -> void + + # : (ClassVariableTargetNode node) -> void + def visit_class_variable_target_node: (ClassVariableTargetNode node) -> void + + # : (ClassVariableWriteNode node) -> void + def visit_class_variable_write_node: (ClassVariableWriteNode node) -> void + + # : (ConstantAndWriteNode node) -> void + def visit_constant_and_write_node: (ConstantAndWriteNode node) -> void + + # : (ConstantOperatorWriteNode node) -> void + def visit_constant_operator_write_node: (ConstantOperatorWriteNode node) -> void + + # : (ConstantOrWriteNode node) -> void + def visit_constant_or_write_node: (ConstantOrWriteNode node) -> void + + # : (ConstantPathAndWriteNode node) -> void + def visit_constant_path_and_write_node: (ConstantPathAndWriteNode node) -> void + + # : (ConstantPathNode node) -> void + def visit_constant_path_node: (ConstantPathNode node) -> void + + # : (ConstantPathOperatorWriteNode node) -> void + def visit_constant_path_operator_write_node: (ConstantPathOperatorWriteNode node) -> void + + # : (ConstantPathOrWriteNode node) -> void + def visit_constant_path_or_write_node: (ConstantPathOrWriteNode node) -> void + + # : (ConstantPathTargetNode node) -> void + def visit_constant_path_target_node: (ConstantPathTargetNode node) -> void + + # : (ConstantPathWriteNode node) -> void + def visit_constant_path_write_node: (ConstantPathWriteNode node) -> void + + # : (ConstantReadNode node) -> void + def visit_constant_read_node: (ConstantReadNode node) -> void + + # : (ConstantTargetNode node) -> void + def visit_constant_target_node: (ConstantTargetNode node) -> void + + # : (ConstantWriteNode node) -> void + def visit_constant_write_node: (ConstantWriteNode node) -> void + + # : (DefNode node) -> void + def visit_def_node: (DefNode node) -> void + + # : (DefinedNode node) -> void + def visit_defined_node: (DefinedNode node) -> void + + # : (ElseNode node) -> void + def visit_else_node: (ElseNode node) -> void + + # : (EmbeddedStatementsNode node) -> void + def visit_embedded_statements_node: (EmbeddedStatementsNode node) -> void + + # : (EmbeddedVariableNode node) -> void + def visit_embedded_variable_node: (EmbeddedVariableNode node) -> void + + # : (EnsureNode node) -> void + def visit_ensure_node: (EnsureNode node) -> void + + # : (FalseNode node) -> void + def visit_false_node: (FalseNode node) -> void + + # : (FindPatternNode node) -> void + def visit_find_pattern_node: (FindPatternNode node) -> void + + # : (FlipFlopNode node) -> void + def visit_flip_flop_node: (FlipFlopNode node) -> void + + # : (FloatNode node) -> void + def visit_float_node: (FloatNode node) -> void + + # : (ForNode node) -> void + def visit_for_node: (ForNode node) -> void + + # : (ForwardingArgumentsNode node) -> void + def visit_forwarding_arguments_node: (ForwardingArgumentsNode node) -> void + + # : (ForwardingParameterNode node) -> void + def visit_forwarding_parameter_node: (ForwardingParameterNode node) -> void + + # : (ForwardingSuperNode node) -> void + def visit_forwarding_super_node: (ForwardingSuperNode node) -> void + + # : (GlobalVariableAndWriteNode node) -> void + def visit_global_variable_and_write_node: (GlobalVariableAndWriteNode node) -> void + + # : (GlobalVariableOperatorWriteNode node) -> void + def visit_global_variable_operator_write_node: (GlobalVariableOperatorWriteNode node) -> void + + # : (GlobalVariableOrWriteNode node) -> void + def visit_global_variable_or_write_node: (GlobalVariableOrWriteNode node) -> void + + # : (GlobalVariableReadNode node) -> void + def visit_global_variable_read_node: (GlobalVariableReadNode node) -> void + + # : (GlobalVariableTargetNode node) -> void + def visit_global_variable_target_node: (GlobalVariableTargetNode node) -> void + + # : (GlobalVariableWriteNode node) -> void + def visit_global_variable_write_node: (GlobalVariableWriteNode node) -> void + + # : (HashNode node) -> void + def visit_hash_node: (HashNode node) -> void + + # : (HashPatternNode node) -> void + def visit_hash_pattern_node: (HashPatternNode node) -> void + + # : (IfNode node) -> void + def visit_if_node: (IfNode node) -> void + + # : (ImaginaryNode node) -> void + def visit_imaginary_node: (ImaginaryNode node) -> void + + # : (ImplicitNode node) -> void + def visit_implicit_node: (ImplicitNode node) -> void + + # : (ImplicitRestNode node) -> void + def visit_implicit_rest_node: (ImplicitRestNode node) -> void + + # : (InNode node) -> void + def visit_in_node: (InNode node) -> void + + # : (IndexAndWriteNode node) -> void + def visit_index_and_write_node: (IndexAndWriteNode node) -> void + + # : (IndexOperatorWriteNode node) -> void + def visit_index_operator_write_node: (IndexOperatorWriteNode node) -> void + + # : (IndexOrWriteNode node) -> void + def visit_index_or_write_node: (IndexOrWriteNode node) -> void + + # : (IndexTargetNode node) -> void + def visit_index_target_node: (IndexTargetNode node) -> void + + # : (InstanceVariableAndWriteNode node) -> void + def visit_instance_variable_and_write_node: (InstanceVariableAndWriteNode node) -> void + + # : (InstanceVariableOperatorWriteNode node) -> void + def visit_instance_variable_operator_write_node: (InstanceVariableOperatorWriteNode node) -> void + + # : (InstanceVariableOrWriteNode node) -> void + def visit_instance_variable_or_write_node: (InstanceVariableOrWriteNode node) -> void + + # : (InstanceVariableReadNode node) -> void + def visit_instance_variable_read_node: (InstanceVariableReadNode node) -> void + + # : (InstanceVariableTargetNode node) -> void + def visit_instance_variable_target_node: (InstanceVariableTargetNode node) -> void + + # : (InstanceVariableWriteNode node) -> void + def visit_instance_variable_write_node: (InstanceVariableWriteNode node) -> void + + # : (IntegerNode node) -> void + def visit_integer_node: (IntegerNode node) -> void + + # : (InterpolatedMatchLastLineNode node) -> void + def visit_interpolated_match_last_line_node: (InterpolatedMatchLastLineNode node) -> void + + # : (InterpolatedRegularExpressionNode node) -> void + def visit_interpolated_regular_expression_node: (InterpolatedRegularExpressionNode node) -> void + + # : (InterpolatedStringNode node) -> void + def visit_interpolated_string_node: (InterpolatedStringNode node) -> void + + # : (InterpolatedSymbolNode node) -> void + def visit_interpolated_symbol_node: (InterpolatedSymbolNode node) -> void + + # : (InterpolatedXStringNode node) -> void + def visit_interpolated_x_string_node: (InterpolatedXStringNode node) -> void + + # : (ItLocalVariableReadNode node) -> void + def visit_it_local_variable_read_node: (ItLocalVariableReadNode node) -> void + + # : (ItParametersNode node) -> void + def visit_it_parameters_node: (ItParametersNode node) -> void + + # : (KeywordHashNode node) -> void + def visit_keyword_hash_node: (KeywordHashNode node) -> void + + # : (KeywordRestParameterNode node) -> void + def visit_keyword_rest_parameter_node: (KeywordRestParameterNode node) -> void + + # : (LambdaNode node) -> void + def visit_lambda_node: (LambdaNode node) -> void + + # : (LocalVariableAndWriteNode node) -> void + def visit_local_variable_and_write_node: (LocalVariableAndWriteNode node) -> void + + # : (LocalVariableOperatorWriteNode node) -> void + def visit_local_variable_operator_write_node: (LocalVariableOperatorWriteNode node) -> void + + # : (LocalVariableOrWriteNode node) -> void + def visit_local_variable_or_write_node: (LocalVariableOrWriteNode node) -> void + + # : (LocalVariableReadNode node) -> void + def visit_local_variable_read_node: (LocalVariableReadNode node) -> void + + # : (LocalVariableTargetNode node) -> void + def visit_local_variable_target_node: (LocalVariableTargetNode node) -> void + + # : (LocalVariableWriteNode node) -> void + def visit_local_variable_write_node: (LocalVariableWriteNode node) -> void + + # : (MatchLastLineNode node) -> void + def visit_match_last_line_node: (MatchLastLineNode node) -> void + + # : (MatchPredicateNode node) -> void + def visit_match_predicate_node: (MatchPredicateNode node) -> void + + # : (MatchRequiredNode node) -> void + def visit_match_required_node: (MatchRequiredNode node) -> void + + # : (MatchWriteNode node) -> void + def visit_match_write_node: (MatchWriteNode node) -> void + + # : (MissingNode node) -> void + def visit_missing_node: (MissingNode node) -> void + + # : (ModuleNode node) -> void + def visit_module_node: (ModuleNode node) -> void + + # : (MultiTargetNode node) -> void + def visit_multi_target_node: (MultiTargetNode node) -> void + + # : (MultiWriteNode node) -> void + def visit_multi_write_node: (MultiWriteNode node) -> void + + # : (NextNode node) -> void + def visit_next_node: (NextNode node) -> void + + # : (NilNode node) -> void + def visit_nil_node: (NilNode node) -> void + + # : (NoBlockParameterNode node) -> void + def visit_no_block_parameter_node: (NoBlockParameterNode node) -> void + + # : (NoKeywordsParameterNode node) -> void + def visit_no_keywords_parameter_node: (NoKeywordsParameterNode node) -> void + + # : (NumberedParametersNode node) -> void + def visit_numbered_parameters_node: (NumberedParametersNode node) -> void + + # : (NumberedReferenceReadNode node) -> void + def visit_numbered_reference_read_node: (NumberedReferenceReadNode node) -> void + + # : (OptionalKeywordParameterNode node) -> void + def visit_optional_keyword_parameter_node: (OptionalKeywordParameterNode node) -> void + + # : (OptionalParameterNode node) -> void + def visit_optional_parameter_node: (OptionalParameterNode node) -> void + + # : (OrNode node) -> void + def visit_or_node: (OrNode node) -> void + + # : (ParametersNode node) -> void + def visit_parameters_node: (ParametersNode node) -> void + + # : (ParenthesesNode node) -> void + def visit_parentheses_node: (ParenthesesNode node) -> void + + # : (PinnedExpressionNode node) -> void + def visit_pinned_expression_node: (PinnedExpressionNode node) -> void + + # : (PinnedVariableNode node) -> void + def visit_pinned_variable_node: (PinnedVariableNode node) -> void + + # : (PostExecutionNode node) -> void + def visit_post_execution_node: (PostExecutionNode node) -> void + + # : (PreExecutionNode node) -> void + def visit_pre_execution_node: (PreExecutionNode node) -> void + + # : (ProgramNode node) -> void + def visit_program_node: (ProgramNode node) -> void + + # : (RangeNode node) -> void + def visit_range_node: (RangeNode node) -> void + + # : (RationalNode node) -> void + def visit_rational_node: (RationalNode node) -> void + + # : (RedoNode node) -> void + def visit_redo_node: (RedoNode node) -> void + + # : (RegularExpressionNode node) -> void + def visit_regular_expression_node: (RegularExpressionNode node) -> void + + # : (RequiredKeywordParameterNode node) -> void + def visit_required_keyword_parameter_node: (RequiredKeywordParameterNode node) -> void + + # : (RequiredParameterNode node) -> void + def visit_required_parameter_node: (RequiredParameterNode node) -> void + + # : (RescueModifierNode node) -> void + def visit_rescue_modifier_node: (RescueModifierNode node) -> void + + # : (RescueNode node) -> void + def visit_rescue_node: (RescueNode node) -> void + + # : (RestParameterNode node) -> void + def visit_rest_parameter_node: (RestParameterNode node) -> void + + # : (RetryNode node) -> void + def visit_retry_node: (RetryNode node) -> void + + # : (ReturnNode node) -> void + def visit_return_node: (ReturnNode node) -> void + + # : (SelfNode node) -> void + def visit_self_node: (SelfNode node) -> void + + # : (ShareableConstantNode node) -> void + def visit_shareable_constant_node: (ShareableConstantNode node) -> void + + # : (SingletonClassNode node) -> void + def visit_singleton_class_node: (SingletonClassNode node) -> void + + # : (SourceEncodingNode node) -> void + def visit_source_encoding_node: (SourceEncodingNode node) -> void + + # : (SourceFileNode node) -> void + def visit_source_file_node: (SourceFileNode node) -> void + + # : (SourceLineNode node) -> void + def visit_source_line_node: (SourceLineNode node) -> void + + # : (SplatNode node) -> void + def visit_splat_node: (SplatNode node) -> void + + # : (StatementsNode node) -> void + def visit_statements_node: (StatementsNode node) -> void + + # : (StringNode node) -> void + def visit_string_node: (StringNode node) -> void + + # : (SuperNode node) -> void + def visit_super_node: (SuperNode node) -> void + + # : (SymbolNode node) -> void + def visit_symbol_node: (SymbolNode node) -> void + + # : (TrueNode node) -> void + def visit_true_node: (TrueNode node) -> void + + # : (UndefNode node) -> void + def visit_undef_node: (UndefNode node) -> void + + # : (UnlessNode node) -> void + def visit_unless_node: (UnlessNode node) -> void + + # : (UntilNode node) -> void + def visit_until_node: (UntilNode node) -> void + + # : (WhenNode node) -> void + def visit_when_node: (WhenNode node) -> void + + # : (WhileNode node) -> void + def visit_while_node: (WhileNode node) -> void + + # : (XStringNode node) -> void + def visit_x_string_node: (XStringNode node) -> void + + # : (YieldNode node) -> void + def visit_yield_node: (YieldNode node) -> void + + class DispatchOnce < Visitor + attr_reader listeners: Hash[Symbol, Array[untyped]] + + # : (Hash[Symbol, Array[untyped]] listeners) -> void + def initialize: (Hash[Symbol, Array[untyped]] listeners) -> void + + # Dispatch enter and leave events for AliasGlobalVariableNode nodes. + # -- + # : (AliasGlobalVariableNode node) -> void + def visit_alias_global_variable_node: (AliasGlobalVariableNode node) -> void + + # Dispatch enter and leave events for AliasMethodNode nodes. + # -- + # : (AliasMethodNode node) -> void + def visit_alias_method_node: (AliasMethodNode node) -> void + + # Dispatch enter and leave events for AlternationPatternNode nodes. + # -- + # : (AlternationPatternNode node) -> void + def visit_alternation_pattern_node: (AlternationPatternNode node) -> void + + # Dispatch enter and leave events for AndNode nodes. + # -- + # : (AndNode node) -> void + def visit_and_node: (AndNode node) -> void + + # Dispatch enter and leave events for ArgumentsNode nodes. + # -- + # : (ArgumentsNode node) -> void + def visit_arguments_node: (ArgumentsNode node) -> void + + # Dispatch enter and leave events for ArrayNode nodes. + # -- + # : (ArrayNode node) -> void + def visit_array_node: (ArrayNode node) -> void + + # Dispatch enter and leave events for ArrayPatternNode nodes. + # -- + # : (ArrayPatternNode node) -> void + def visit_array_pattern_node: (ArrayPatternNode node) -> void + + # Dispatch enter and leave events for AssocNode nodes. + # -- + # : (AssocNode node) -> void + def visit_assoc_node: (AssocNode node) -> void + + # Dispatch enter and leave events for AssocSplatNode nodes. + # -- + # : (AssocSplatNode node) -> void + def visit_assoc_splat_node: (AssocSplatNode node) -> void + + # Dispatch enter and leave events for BackReferenceReadNode nodes. + # -- + # : (BackReferenceReadNode node) -> void + def visit_back_reference_read_node: (BackReferenceReadNode node) -> void + + # Dispatch enter and leave events for BeginNode nodes. + # -- + # : (BeginNode node) -> void + def visit_begin_node: (BeginNode node) -> void + + # Dispatch enter and leave events for BlockArgumentNode nodes. + # -- + # : (BlockArgumentNode node) -> void + def visit_block_argument_node: (BlockArgumentNode node) -> void + + # Dispatch enter and leave events for BlockLocalVariableNode nodes. + # -- + # : (BlockLocalVariableNode node) -> void + def visit_block_local_variable_node: (BlockLocalVariableNode node) -> void + + # Dispatch enter and leave events for BlockNode nodes. + # -- + # : (BlockNode node) -> void + def visit_block_node: (BlockNode node) -> void + + # Dispatch enter and leave events for BlockParameterNode nodes. + # -- + # : (BlockParameterNode node) -> void + def visit_block_parameter_node: (BlockParameterNode node) -> void + + # Dispatch enter and leave events for BlockParametersNode nodes. + # -- + # : (BlockParametersNode node) -> void + def visit_block_parameters_node: (BlockParametersNode node) -> void + + # Dispatch enter and leave events for BreakNode nodes. + # -- + # : (BreakNode node) -> void + def visit_break_node: (BreakNode node) -> void + + # Dispatch enter and leave events for CallAndWriteNode nodes. + # -- + # : (CallAndWriteNode node) -> void + def visit_call_and_write_node: (CallAndWriteNode node) -> void + + # Dispatch enter and leave events for CallNode nodes. + # -- + # : (CallNode node) -> void + def visit_call_node: (CallNode node) -> void + + # Dispatch enter and leave events for CallOperatorWriteNode nodes. + # -- + # : (CallOperatorWriteNode node) -> void + def visit_call_operator_write_node: (CallOperatorWriteNode node) -> void + + # Dispatch enter and leave events for CallOrWriteNode nodes. + # -- + # : (CallOrWriteNode node) -> void + def visit_call_or_write_node: (CallOrWriteNode node) -> void + + # Dispatch enter and leave events for CallTargetNode nodes. + # -- + # : (CallTargetNode node) -> void + def visit_call_target_node: (CallTargetNode node) -> void + + # Dispatch enter and leave events for CapturePatternNode nodes. + # -- + # : (CapturePatternNode node) -> void + def visit_capture_pattern_node: (CapturePatternNode node) -> void + + # Dispatch enter and leave events for CaseMatchNode nodes. + # -- + # : (CaseMatchNode node) -> void + def visit_case_match_node: (CaseMatchNode node) -> void + + # Dispatch enter and leave events for CaseNode nodes. + # -- + # : (CaseNode node) -> void + def visit_case_node: (CaseNode node) -> void + + # Dispatch enter and leave events for ClassNode nodes. + # -- + # : (ClassNode node) -> void + def visit_class_node: (ClassNode node) -> void + + # Dispatch enter and leave events for ClassVariableAndWriteNode nodes. + # -- + # : (ClassVariableAndWriteNode node) -> void + def visit_class_variable_and_write_node: (ClassVariableAndWriteNode node) -> void + + # Dispatch enter and leave events for ClassVariableOperatorWriteNode nodes. + # -- + # : (ClassVariableOperatorWriteNode node) -> void + def visit_class_variable_operator_write_node: (ClassVariableOperatorWriteNode node) -> void + + # Dispatch enter and leave events for ClassVariableOrWriteNode nodes. + # -- + # : (ClassVariableOrWriteNode node) -> void + def visit_class_variable_or_write_node: (ClassVariableOrWriteNode node) -> void + + # Dispatch enter and leave events for ClassVariableReadNode nodes. + # -- + # : (ClassVariableReadNode node) -> void + def visit_class_variable_read_node: (ClassVariableReadNode node) -> void + + # Dispatch enter and leave events for ClassVariableTargetNode nodes. + # -- + # : (ClassVariableTargetNode node) -> void + def visit_class_variable_target_node: (ClassVariableTargetNode node) -> void + + # Dispatch enter and leave events for ClassVariableWriteNode nodes. + # -- + # : (ClassVariableWriteNode node) -> void + def visit_class_variable_write_node: (ClassVariableWriteNode node) -> void + + # Dispatch enter and leave events for ConstantAndWriteNode nodes. + # -- + # : (ConstantAndWriteNode node) -> void + def visit_constant_and_write_node: (ConstantAndWriteNode node) -> void + + # Dispatch enter and leave events for ConstantOperatorWriteNode nodes. + # -- + # : (ConstantOperatorWriteNode node) -> void + def visit_constant_operator_write_node: (ConstantOperatorWriteNode node) -> void + + # Dispatch enter and leave events for ConstantOrWriteNode nodes. + # -- + # : (ConstantOrWriteNode node) -> void + def visit_constant_or_write_node: (ConstantOrWriteNode node) -> void + + # Dispatch enter and leave events for ConstantPathAndWriteNode nodes. + # -- + # : (ConstantPathAndWriteNode node) -> void + def visit_constant_path_and_write_node: (ConstantPathAndWriteNode node) -> void + + # Dispatch enter and leave events for ConstantPathNode nodes. + # -- + # : (ConstantPathNode node) -> void + def visit_constant_path_node: (ConstantPathNode node) -> void + + # Dispatch enter and leave events for ConstantPathOperatorWriteNode nodes. + # -- + # : (ConstantPathOperatorWriteNode node) -> void + def visit_constant_path_operator_write_node: (ConstantPathOperatorWriteNode node) -> void + + # Dispatch enter and leave events for ConstantPathOrWriteNode nodes. + # -- + # : (ConstantPathOrWriteNode node) -> void + def visit_constant_path_or_write_node: (ConstantPathOrWriteNode node) -> void + + # Dispatch enter and leave events for ConstantPathTargetNode nodes. + # -- + # : (ConstantPathTargetNode node) -> void + def visit_constant_path_target_node: (ConstantPathTargetNode node) -> void + + # Dispatch enter and leave events for ConstantPathWriteNode nodes. + # -- + # : (ConstantPathWriteNode node) -> void + def visit_constant_path_write_node: (ConstantPathWriteNode node) -> void + + # Dispatch enter and leave events for ConstantReadNode nodes. + # -- + # : (ConstantReadNode node) -> void + def visit_constant_read_node: (ConstantReadNode node) -> void + + # Dispatch enter and leave events for ConstantTargetNode nodes. + # -- + # : (ConstantTargetNode node) -> void + def visit_constant_target_node: (ConstantTargetNode node) -> void + + # Dispatch enter and leave events for ConstantWriteNode nodes. + # -- + # : (ConstantWriteNode node) -> void + def visit_constant_write_node: (ConstantWriteNode node) -> void + + # Dispatch enter and leave events for DefNode nodes. + # -- + # : (DefNode node) -> void + def visit_def_node: (DefNode node) -> void + + # Dispatch enter and leave events for DefinedNode nodes. + # -- + # : (DefinedNode node) -> void + def visit_defined_node: (DefinedNode node) -> void + + # Dispatch enter and leave events for ElseNode nodes. + # -- + # : (ElseNode node) -> void + def visit_else_node: (ElseNode node) -> void + + # Dispatch enter and leave events for EmbeddedStatementsNode nodes. + # -- + # : (EmbeddedStatementsNode node) -> void + def visit_embedded_statements_node: (EmbeddedStatementsNode node) -> void + + # Dispatch enter and leave events for EmbeddedVariableNode nodes. + # -- + # : (EmbeddedVariableNode node) -> void + def visit_embedded_variable_node: (EmbeddedVariableNode node) -> void + + # Dispatch enter and leave events for EnsureNode nodes. + # -- + # : (EnsureNode node) -> void + def visit_ensure_node: (EnsureNode node) -> void + + # Dispatch enter and leave events for FalseNode nodes. + # -- + # : (FalseNode node) -> void + def visit_false_node: (FalseNode node) -> void + + # Dispatch enter and leave events for FindPatternNode nodes. + # -- + # : (FindPatternNode node) -> void + def visit_find_pattern_node: (FindPatternNode node) -> void + + # Dispatch enter and leave events for FlipFlopNode nodes. + # -- + # : (FlipFlopNode node) -> void + def visit_flip_flop_node: (FlipFlopNode node) -> void + + # Dispatch enter and leave events for FloatNode nodes. + # -- + # : (FloatNode node) -> void + def visit_float_node: (FloatNode node) -> void + + # Dispatch enter and leave events for ForNode nodes. + # -- + # : (ForNode node) -> void + def visit_for_node: (ForNode node) -> void + + # Dispatch enter and leave events for ForwardingArgumentsNode nodes. + # -- + # : (ForwardingArgumentsNode node) -> void + def visit_forwarding_arguments_node: (ForwardingArgumentsNode node) -> void + + # Dispatch enter and leave events for ForwardingParameterNode nodes. + # -- + # : (ForwardingParameterNode node) -> void + def visit_forwarding_parameter_node: (ForwardingParameterNode node) -> void + + # Dispatch enter and leave events for ForwardingSuperNode nodes. + # -- + # : (ForwardingSuperNode node) -> void + def visit_forwarding_super_node: (ForwardingSuperNode node) -> void + + # Dispatch enter and leave events for GlobalVariableAndWriteNode nodes. + # -- + # : (GlobalVariableAndWriteNode node) -> void + def visit_global_variable_and_write_node: (GlobalVariableAndWriteNode node) -> void + + # Dispatch enter and leave events for GlobalVariableOperatorWriteNode nodes. + # -- + # : (GlobalVariableOperatorWriteNode node) -> void + def visit_global_variable_operator_write_node: (GlobalVariableOperatorWriteNode node) -> void + + # Dispatch enter and leave events for GlobalVariableOrWriteNode nodes. + # -- + # : (GlobalVariableOrWriteNode node) -> void + def visit_global_variable_or_write_node: (GlobalVariableOrWriteNode node) -> void + + # Dispatch enter and leave events for GlobalVariableReadNode nodes. + # -- + # : (GlobalVariableReadNode node) -> void + def visit_global_variable_read_node: (GlobalVariableReadNode node) -> void + + # Dispatch enter and leave events for GlobalVariableTargetNode nodes. + # -- + # : (GlobalVariableTargetNode node) -> void + def visit_global_variable_target_node: (GlobalVariableTargetNode node) -> void + + # Dispatch enter and leave events for GlobalVariableWriteNode nodes. + # -- + # : (GlobalVariableWriteNode node) -> void + def visit_global_variable_write_node: (GlobalVariableWriteNode node) -> void + + # Dispatch enter and leave events for HashNode nodes. + # -- + # : (HashNode node) -> void + def visit_hash_node: (HashNode node) -> void + + # Dispatch enter and leave events for HashPatternNode nodes. + # -- + # : (HashPatternNode node) -> void + def visit_hash_pattern_node: (HashPatternNode node) -> void + + # Dispatch enter and leave events for IfNode nodes. + # -- + # : (IfNode node) -> void + def visit_if_node: (IfNode node) -> void + + # Dispatch enter and leave events for ImaginaryNode nodes. + # -- + # : (ImaginaryNode node) -> void + def visit_imaginary_node: (ImaginaryNode node) -> void + + # Dispatch enter and leave events for ImplicitNode nodes. + # -- + # : (ImplicitNode node) -> void + def visit_implicit_node: (ImplicitNode node) -> void + + # Dispatch enter and leave events for ImplicitRestNode nodes. + # -- + # : (ImplicitRestNode node) -> void + def visit_implicit_rest_node: (ImplicitRestNode node) -> void + + # Dispatch enter and leave events for InNode nodes. + # -- + # : (InNode node) -> void + def visit_in_node: (InNode node) -> void + + # Dispatch enter and leave events for IndexAndWriteNode nodes. + # -- + # : (IndexAndWriteNode node) -> void + def visit_index_and_write_node: (IndexAndWriteNode node) -> void + + # Dispatch enter and leave events for IndexOperatorWriteNode nodes. + # -- + # : (IndexOperatorWriteNode node) -> void + def visit_index_operator_write_node: (IndexOperatorWriteNode node) -> void + + # Dispatch enter and leave events for IndexOrWriteNode nodes. + # -- + # : (IndexOrWriteNode node) -> void + def visit_index_or_write_node: (IndexOrWriteNode node) -> void + + # Dispatch enter and leave events for IndexTargetNode nodes. + # -- + # : (IndexTargetNode node) -> void + def visit_index_target_node: (IndexTargetNode node) -> void + + # Dispatch enter and leave events for InstanceVariableAndWriteNode nodes. + # -- + # : (InstanceVariableAndWriteNode node) -> void + def visit_instance_variable_and_write_node: (InstanceVariableAndWriteNode node) -> void + + # Dispatch enter and leave events for InstanceVariableOperatorWriteNode nodes. + # -- + # : (InstanceVariableOperatorWriteNode node) -> void + def visit_instance_variable_operator_write_node: (InstanceVariableOperatorWriteNode node) -> void + + # Dispatch enter and leave events for InstanceVariableOrWriteNode nodes. + # -- + # : (InstanceVariableOrWriteNode node) -> void + def visit_instance_variable_or_write_node: (InstanceVariableOrWriteNode node) -> void + + # Dispatch enter and leave events for InstanceVariableReadNode nodes. + # -- + # : (InstanceVariableReadNode node) -> void + def visit_instance_variable_read_node: (InstanceVariableReadNode node) -> void + + # Dispatch enter and leave events for InstanceVariableTargetNode nodes. + # -- + # : (InstanceVariableTargetNode node) -> void + def visit_instance_variable_target_node: (InstanceVariableTargetNode node) -> void + + # Dispatch enter and leave events for InstanceVariableWriteNode nodes. + # -- + # : (InstanceVariableWriteNode node) -> void + def visit_instance_variable_write_node: (InstanceVariableWriteNode node) -> void + + # Dispatch enter and leave events for IntegerNode nodes. + # -- + # : (IntegerNode node) -> void + def visit_integer_node: (IntegerNode node) -> void + + # Dispatch enter and leave events for InterpolatedMatchLastLineNode nodes. + # -- + # : (InterpolatedMatchLastLineNode node) -> void + def visit_interpolated_match_last_line_node: (InterpolatedMatchLastLineNode node) -> void + + # Dispatch enter and leave events for InterpolatedRegularExpressionNode nodes. + # -- + # : (InterpolatedRegularExpressionNode node) -> void + def visit_interpolated_regular_expression_node: (InterpolatedRegularExpressionNode node) -> void + + # Dispatch enter and leave events for InterpolatedStringNode nodes. + # -- + # : (InterpolatedStringNode node) -> void + def visit_interpolated_string_node: (InterpolatedStringNode node) -> void + + # Dispatch enter and leave events for InterpolatedSymbolNode nodes. + # -- + # : (InterpolatedSymbolNode node) -> void + def visit_interpolated_symbol_node: (InterpolatedSymbolNode node) -> void + + # Dispatch enter and leave events for InterpolatedXStringNode nodes. + # -- + # : (InterpolatedXStringNode node) -> void + def visit_interpolated_x_string_node: (InterpolatedXStringNode node) -> void + + # Dispatch enter and leave events for ItLocalVariableReadNode nodes. + # -- + # : (ItLocalVariableReadNode node) -> void + def visit_it_local_variable_read_node: (ItLocalVariableReadNode node) -> void + + # Dispatch enter and leave events for ItParametersNode nodes. + # -- + # : (ItParametersNode node) -> void + def visit_it_parameters_node: (ItParametersNode node) -> void + + # Dispatch enter and leave events for KeywordHashNode nodes. + # -- + # : (KeywordHashNode node) -> void + def visit_keyword_hash_node: (KeywordHashNode node) -> void + + # Dispatch enter and leave events for KeywordRestParameterNode nodes. + # -- + # : (KeywordRestParameterNode node) -> void + def visit_keyword_rest_parameter_node: (KeywordRestParameterNode node) -> void + + # Dispatch enter and leave events for LambdaNode nodes. + # -- + # : (LambdaNode node) -> void + def visit_lambda_node: (LambdaNode node) -> void + + # Dispatch enter and leave events for LocalVariableAndWriteNode nodes. + # -- + # : (LocalVariableAndWriteNode node) -> void + def visit_local_variable_and_write_node: (LocalVariableAndWriteNode node) -> void + + # Dispatch enter and leave events for LocalVariableOperatorWriteNode nodes. + # -- + # : (LocalVariableOperatorWriteNode node) -> void + def visit_local_variable_operator_write_node: (LocalVariableOperatorWriteNode node) -> void + + # Dispatch enter and leave events for LocalVariableOrWriteNode nodes. + # -- + # : (LocalVariableOrWriteNode node) -> void + def visit_local_variable_or_write_node: (LocalVariableOrWriteNode node) -> void + + # Dispatch enter and leave events for LocalVariableReadNode nodes. + # -- + # : (LocalVariableReadNode node) -> void + def visit_local_variable_read_node: (LocalVariableReadNode node) -> void + + # Dispatch enter and leave events for LocalVariableTargetNode nodes. + # -- + # : (LocalVariableTargetNode node) -> void + def visit_local_variable_target_node: (LocalVariableTargetNode node) -> void + + # Dispatch enter and leave events for LocalVariableWriteNode nodes. + # -- + # : (LocalVariableWriteNode node) -> void + def visit_local_variable_write_node: (LocalVariableWriteNode node) -> void + + # Dispatch enter and leave events for MatchLastLineNode nodes. + # -- + # : (MatchLastLineNode node) -> void + def visit_match_last_line_node: (MatchLastLineNode node) -> void + + # Dispatch enter and leave events for MatchPredicateNode nodes. + # -- + # : (MatchPredicateNode node) -> void + def visit_match_predicate_node: (MatchPredicateNode node) -> void + + # Dispatch enter and leave events for MatchRequiredNode nodes. + # -- + # : (MatchRequiredNode node) -> void + def visit_match_required_node: (MatchRequiredNode node) -> void + + # Dispatch enter and leave events for MatchWriteNode nodes. + # -- + # : (MatchWriteNode node) -> void + def visit_match_write_node: (MatchWriteNode node) -> void + + # Dispatch enter and leave events for MissingNode nodes. + # -- + # : (MissingNode node) -> void + def visit_missing_node: (MissingNode node) -> void + + # Dispatch enter and leave events for ModuleNode nodes. + # -- + # : (ModuleNode node) -> void + def visit_module_node: (ModuleNode node) -> void + + # Dispatch enter and leave events for MultiTargetNode nodes. + # -- + # : (MultiTargetNode node) -> void + def visit_multi_target_node: (MultiTargetNode node) -> void + + # Dispatch enter and leave events for MultiWriteNode nodes. + # -- + # : (MultiWriteNode node) -> void + def visit_multi_write_node: (MultiWriteNode node) -> void + + # Dispatch enter and leave events for NextNode nodes. + # -- + # : (NextNode node) -> void + def visit_next_node: (NextNode node) -> void + + # Dispatch enter and leave events for NilNode nodes. + # -- + # : (NilNode node) -> void + def visit_nil_node: (NilNode node) -> void + + # Dispatch enter and leave events for NoBlockParameterNode nodes. + # -- + # : (NoBlockParameterNode node) -> void + def visit_no_block_parameter_node: (NoBlockParameterNode node) -> void + + # Dispatch enter and leave events for NoKeywordsParameterNode nodes. + # -- + # : (NoKeywordsParameterNode node) -> void + def visit_no_keywords_parameter_node: (NoKeywordsParameterNode node) -> void + + # Dispatch enter and leave events for NumberedParametersNode nodes. + # -- + # : (NumberedParametersNode node) -> void + def visit_numbered_parameters_node: (NumberedParametersNode node) -> void + + # Dispatch enter and leave events for NumberedReferenceReadNode nodes. + # -- + # : (NumberedReferenceReadNode node) -> void + def visit_numbered_reference_read_node: (NumberedReferenceReadNode node) -> void + + # Dispatch enter and leave events for OptionalKeywordParameterNode nodes. + # -- + # : (OptionalKeywordParameterNode node) -> void + def visit_optional_keyword_parameter_node: (OptionalKeywordParameterNode node) -> void + + # Dispatch enter and leave events for OptionalParameterNode nodes. + # -- + # : (OptionalParameterNode node) -> void + def visit_optional_parameter_node: (OptionalParameterNode node) -> void + + # Dispatch enter and leave events for OrNode nodes. + # -- + # : (OrNode node) -> void + def visit_or_node: (OrNode node) -> void + + # Dispatch enter and leave events for ParametersNode nodes. + # -- + # : (ParametersNode node) -> void + def visit_parameters_node: (ParametersNode node) -> void + + # Dispatch enter and leave events for ParenthesesNode nodes. + # -- + # : (ParenthesesNode node) -> void + def visit_parentheses_node: (ParenthesesNode node) -> void + + # Dispatch enter and leave events for PinnedExpressionNode nodes. + # -- + # : (PinnedExpressionNode node) -> void + def visit_pinned_expression_node: (PinnedExpressionNode node) -> void + + # Dispatch enter and leave events for PinnedVariableNode nodes. + # -- + # : (PinnedVariableNode node) -> void + def visit_pinned_variable_node: (PinnedVariableNode node) -> void + + # Dispatch enter and leave events for PostExecutionNode nodes. + # -- + # : (PostExecutionNode node) -> void + def visit_post_execution_node: (PostExecutionNode node) -> void + + # Dispatch enter and leave events for PreExecutionNode nodes. + # -- + # : (PreExecutionNode node) -> void + def visit_pre_execution_node: (PreExecutionNode node) -> void + + # Dispatch enter and leave events for ProgramNode nodes. + # -- + # : (ProgramNode node) -> void + def visit_program_node: (ProgramNode node) -> void + + # Dispatch enter and leave events for RangeNode nodes. + # -- + # : (RangeNode node) -> void + def visit_range_node: (RangeNode node) -> void + + # Dispatch enter and leave events for RationalNode nodes. + # -- + # : (RationalNode node) -> void + def visit_rational_node: (RationalNode node) -> void + + # Dispatch enter and leave events for RedoNode nodes. + # -- + # : (RedoNode node) -> void + def visit_redo_node: (RedoNode node) -> void + + # Dispatch enter and leave events for RegularExpressionNode nodes. + # -- + # : (RegularExpressionNode node) -> void + def visit_regular_expression_node: (RegularExpressionNode node) -> void + + # Dispatch enter and leave events for RequiredKeywordParameterNode nodes. + # -- + # : (RequiredKeywordParameterNode node) -> void + def visit_required_keyword_parameter_node: (RequiredKeywordParameterNode node) -> void + + # Dispatch enter and leave events for RequiredParameterNode nodes. + # -- + # : (RequiredParameterNode node) -> void + def visit_required_parameter_node: (RequiredParameterNode node) -> void + + # Dispatch enter and leave events for RescueModifierNode nodes. + # -- + # : (RescueModifierNode node) -> void + def visit_rescue_modifier_node: (RescueModifierNode node) -> void + + # Dispatch enter and leave events for RescueNode nodes. + # -- + # : (RescueNode node) -> void + def visit_rescue_node: (RescueNode node) -> void + + # Dispatch enter and leave events for RestParameterNode nodes. + # -- + # : (RestParameterNode node) -> void + def visit_rest_parameter_node: (RestParameterNode node) -> void + + # Dispatch enter and leave events for RetryNode nodes. + # -- + # : (RetryNode node) -> void + def visit_retry_node: (RetryNode node) -> void + + # Dispatch enter and leave events for ReturnNode nodes. + # -- + # : (ReturnNode node) -> void + def visit_return_node: (ReturnNode node) -> void + + # Dispatch enter and leave events for SelfNode nodes. + # -- + # : (SelfNode node) -> void + def visit_self_node: (SelfNode node) -> void + + # Dispatch enter and leave events for ShareableConstantNode nodes. + # -- + # : (ShareableConstantNode node) -> void + def visit_shareable_constant_node: (ShareableConstantNode node) -> void + + # Dispatch enter and leave events for SingletonClassNode nodes. + # -- + # : (SingletonClassNode node) -> void + def visit_singleton_class_node: (SingletonClassNode node) -> void + + # Dispatch enter and leave events for SourceEncodingNode nodes. + # -- + # : (SourceEncodingNode node) -> void + def visit_source_encoding_node: (SourceEncodingNode node) -> void + + # Dispatch enter and leave events for SourceFileNode nodes. + # -- + # : (SourceFileNode node) -> void + def visit_source_file_node: (SourceFileNode node) -> void + + # Dispatch enter and leave events for SourceLineNode nodes. + # -- + # : (SourceLineNode node) -> void + def visit_source_line_node: (SourceLineNode node) -> void + + # Dispatch enter and leave events for SplatNode nodes. + # -- + # : (SplatNode node) -> void + def visit_splat_node: (SplatNode node) -> void + + # Dispatch enter and leave events for StatementsNode nodes. + # -- + # : (StatementsNode node) -> void + def visit_statements_node: (StatementsNode node) -> void + + # Dispatch enter and leave events for StringNode nodes. + # -- + # : (StringNode node) -> void + def visit_string_node: (StringNode node) -> void + + # Dispatch enter and leave events for SuperNode nodes. + # -- + # : (SuperNode node) -> void + def visit_super_node: (SuperNode node) -> void + + # Dispatch enter and leave events for SymbolNode nodes. + # -- + # : (SymbolNode node) -> void + def visit_symbol_node: (SymbolNode node) -> void + + # Dispatch enter and leave events for TrueNode nodes. + # -- + # : (TrueNode node) -> void + def visit_true_node: (TrueNode node) -> void + + # Dispatch enter and leave events for UndefNode nodes. + # -- + # : (UndefNode node) -> void + def visit_undef_node: (UndefNode node) -> void + + # Dispatch enter and leave events for UnlessNode nodes. + # -- + # : (UnlessNode node) -> void + def visit_unless_node: (UnlessNode node) -> void + + # Dispatch enter and leave events for UntilNode nodes. + # -- + # : (UntilNode node) -> void + def visit_until_node: (UntilNode node) -> void + + # Dispatch enter and leave events for WhenNode nodes. + # -- + # : (WhenNode node) -> void + def visit_when_node: (WhenNode node) -> void + + # Dispatch enter and leave events for WhileNode nodes. + # -- + # : (WhileNode node) -> void + def visit_while_node: (WhileNode node) -> void + + # Dispatch enter and leave events for XStringNode nodes. + # -- + # : (XStringNode node) -> void + def visit_x_string_node: (XStringNode node) -> void + + # Dispatch enter and leave events for YieldNode nodes. + # -- + # : (YieldNode node) -> void + def visit_yield_node: (YieldNode node) -> void + end + end +end diff --git a/sig/generated/prism/dot_visitor.rbs b/sig/generated/prism/dot_visitor.rbs new file mode 100644 index 0000000000..2cbc0ee31b --- /dev/null +++ b/sig/generated/prism/dot_visitor.rbs @@ -0,0 +1,635 @@ +# Generated from lib/prism/dot_visitor.rb with RBS::Inline + +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: String + + attr_reader value: String? + + attr_reader port: bool + + # : (String name, String? value, bool port) -> void + def initialize: (String name, String? value, bool port) -> void + + # : () -> String + def to_dot: () -> String + end + + class Table + # :nodoc: + attr_reader name: String + + attr_reader fields: Array[Field] + + # : (String name) -> void + def initialize: (String name) -> void + + # : (String name, ?String? value, ?port: bool) -> void + def field: (String name, ?String? value, ?port: bool) -> void + + # : () -> String + def to_dot: () -> String + end + + class Digraph + # :nodoc: + attr_reader nodes: Array[String] + + # :nodoc: + attr_reader waypoints: Array[String] + + # :nodoc: + attr_reader edges: Array[String] + + # : () -> void + def initialize: () -> void + + # : (String value) -> void + def node: (String value) -> void + + # : (String value) -> void + def waypoint: (String value) -> void + + # : (String value) -> void + def edge: (String value) -> void + + # : () -> String + def to_dot: () -> String + end + + # The digraph that is being built. + attr_reader digraph: Digraph + + # Initialize a new dot visitor. + # -- + # : () -> void + def initialize: () -> void + + # Convert this visitor into a graphviz dot graph string. + # -- + # : () -> String + def to_dot: () -> String + + # : (AliasGlobalVariableNode) -> void + def visit_alias_global_variable_node: (AliasGlobalVariableNode) -> void + + # : (AliasMethodNode) -> void + def visit_alias_method_node: (AliasMethodNode) -> void + + # : (AlternationPatternNode) -> void + def visit_alternation_pattern_node: (AlternationPatternNode) -> void + + # : (AndNode) -> void + def visit_and_node: (AndNode) -> void + + # : (ArgumentsNode) -> void + def visit_arguments_node: (ArgumentsNode) -> void + + # : (ArrayNode) -> void + def visit_array_node: (ArrayNode) -> void + + # : (ArrayPatternNode) -> void + def visit_array_pattern_node: (ArrayPatternNode) -> void + + # : (AssocNode) -> void + def visit_assoc_node: (AssocNode) -> void + + # : (AssocSplatNode) -> void + def visit_assoc_splat_node: (AssocSplatNode) -> void + + # : (BackReferenceReadNode) -> void + def visit_back_reference_read_node: (BackReferenceReadNode) -> void + + # : (BeginNode) -> void + def visit_begin_node: (BeginNode) -> void + + # : (BlockArgumentNode) -> void + def visit_block_argument_node: (BlockArgumentNode) -> void + + # : (BlockLocalVariableNode) -> void + def visit_block_local_variable_node: (BlockLocalVariableNode) -> void + + # : (BlockNode) -> void + def visit_block_node: (BlockNode) -> void + + # : (BlockParameterNode) -> void + def visit_block_parameter_node: (BlockParameterNode) -> void + + # : (BlockParametersNode) -> void + def visit_block_parameters_node: (BlockParametersNode) -> void + + # : (BreakNode) -> void + def visit_break_node: (BreakNode) -> void + + # : (CallAndWriteNode) -> void + def visit_call_and_write_node: (CallAndWriteNode) -> void + + # : (CallNode) -> void + def visit_call_node: (CallNode) -> void + + # : (CallOperatorWriteNode) -> void + def visit_call_operator_write_node: (CallOperatorWriteNode) -> void + + # : (CallOrWriteNode) -> void + def visit_call_or_write_node: (CallOrWriteNode) -> void + + # : (CallTargetNode) -> void + def visit_call_target_node: (CallTargetNode) -> void + + # : (CapturePatternNode) -> void + def visit_capture_pattern_node: (CapturePatternNode) -> void + + # : (CaseMatchNode) -> void + def visit_case_match_node: (CaseMatchNode) -> void + + # : (CaseNode) -> void + def visit_case_node: (CaseNode) -> void + + # : (ClassNode) -> void + def visit_class_node: (ClassNode) -> void + + # : (ClassVariableAndWriteNode) -> void + def visit_class_variable_and_write_node: (ClassVariableAndWriteNode) -> void + + # : (ClassVariableOperatorWriteNode) -> void + def visit_class_variable_operator_write_node: (ClassVariableOperatorWriteNode) -> void + + # : (ClassVariableOrWriteNode) -> void + def visit_class_variable_or_write_node: (ClassVariableOrWriteNode) -> void + + # : (ClassVariableReadNode) -> void + def visit_class_variable_read_node: (ClassVariableReadNode) -> void + + # : (ClassVariableTargetNode) -> void + def visit_class_variable_target_node: (ClassVariableTargetNode) -> void + + # : (ClassVariableWriteNode) -> void + def visit_class_variable_write_node: (ClassVariableWriteNode) -> void + + # : (ConstantAndWriteNode) -> void + def visit_constant_and_write_node: (ConstantAndWriteNode) -> void + + # : (ConstantOperatorWriteNode) -> void + def visit_constant_operator_write_node: (ConstantOperatorWriteNode) -> void + + # : (ConstantOrWriteNode) -> void + def visit_constant_or_write_node: (ConstantOrWriteNode) -> void + + # : (ConstantPathAndWriteNode) -> void + def visit_constant_path_and_write_node: (ConstantPathAndWriteNode) -> void + + # : (ConstantPathNode) -> void + def visit_constant_path_node: (ConstantPathNode) -> void + + # : (ConstantPathOperatorWriteNode) -> void + def visit_constant_path_operator_write_node: (ConstantPathOperatorWriteNode) -> void + + # : (ConstantPathOrWriteNode) -> void + def visit_constant_path_or_write_node: (ConstantPathOrWriteNode) -> void + + # : (ConstantPathTargetNode) -> void + def visit_constant_path_target_node: (ConstantPathTargetNode) -> void + + # : (ConstantPathWriteNode) -> void + def visit_constant_path_write_node: (ConstantPathWriteNode) -> void + + # : (ConstantReadNode) -> void + def visit_constant_read_node: (ConstantReadNode) -> void + + # : (ConstantTargetNode) -> void + def visit_constant_target_node: (ConstantTargetNode) -> void + + # : (ConstantWriteNode) -> void + def visit_constant_write_node: (ConstantWriteNode) -> void + + # : (DefNode) -> void + def visit_def_node: (DefNode) -> void + + # : (DefinedNode) -> void + def visit_defined_node: (DefinedNode) -> void + + # : (ElseNode) -> void + def visit_else_node: (ElseNode) -> void + + # : (EmbeddedStatementsNode) -> void + def visit_embedded_statements_node: (EmbeddedStatementsNode) -> void + + # : (EmbeddedVariableNode) -> void + def visit_embedded_variable_node: (EmbeddedVariableNode) -> void + + # : (EnsureNode) -> void + def visit_ensure_node: (EnsureNode) -> void + + # : (FalseNode) -> void + def visit_false_node: (FalseNode) -> void + + # : (FindPatternNode) -> void + def visit_find_pattern_node: (FindPatternNode) -> void + + # : (FlipFlopNode) -> void + def visit_flip_flop_node: (FlipFlopNode) -> void + + # : (FloatNode) -> void + def visit_float_node: (FloatNode) -> void + + # : (ForNode) -> void + def visit_for_node: (ForNode) -> void + + # : (ForwardingArgumentsNode) -> void + def visit_forwarding_arguments_node: (ForwardingArgumentsNode) -> void + + # : (ForwardingParameterNode) -> void + def visit_forwarding_parameter_node: (ForwardingParameterNode) -> void + + # : (ForwardingSuperNode) -> void + def visit_forwarding_super_node: (ForwardingSuperNode) -> void + + # : (GlobalVariableAndWriteNode) -> void + def visit_global_variable_and_write_node: (GlobalVariableAndWriteNode) -> void + + # : (GlobalVariableOperatorWriteNode) -> void + def visit_global_variable_operator_write_node: (GlobalVariableOperatorWriteNode) -> void + + # : (GlobalVariableOrWriteNode) -> void + def visit_global_variable_or_write_node: (GlobalVariableOrWriteNode) -> void + + # : (GlobalVariableReadNode) -> void + def visit_global_variable_read_node: (GlobalVariableReadNode) -> void + + # : (GlobalVariableTargetNode) -> void + def visit_global_variable_target_node: (GlobalVariableTargetNode) -> void + + # : (GlobalVariableWriteNode) -> void + def visit_global_variable_write_node: (GlobalVariableWriteNode) -> void + + # : (HashNode) -> void + def visit_hash_node: (HashNode) -> void + + # : (HashPatternNode) -> void + def visit_hash_pattern_node: (HashPatternNode) -> void + + # : (IfNode) -> void + def visit_if_node: (IfNode) -> void + + # : (ImaginaryNode) -> void + def visit_imaginary_node: (ImaginaryNode) -> void + + # : (ImplicitNode) -> void + def visit_implicit_node: (ImplicitNode) -> void + + # : (ImplicitRestNode) -> void + def visit_implicit_rest_node: (ImplicitRestNode) -> void + + # : (InNode) -> void + def visit_in_node: (InNode) -> void + + # : (IndexAndWriteNode) -> void + def visit_index_and_write_node: (IndexAndWriteNode) -> void + + # : (IndexOperatorWriteNode) -> void + def visit_index_operator_write_node: (IndexOperatorWriteNode) -> void + + # : (IndexOrWriteNode) -> void + def visit_index_or_write_node: (IndexOrWriteNode) -> void + + # : (IndexTargetNode) -> void + def visit_index_target_node: (IndexTargetNode) -> void + + # : (InstanceVariableAndWriteNode) -> void + def visit_instance_variable_and_write_node: (InstanceVariableAndWriteNode) -> void + + # : (InstanceVariableOperatorWriteNode) -> void + def visit_instance_variable_operator_write_node: (InstanceVariableOperatorWriteNode) -> void + + # : (InstanceVariableOrWriteNode) -> void + def visit_instance_variable_or_write_node: (InstanceVariableOrWriteNode) -> void + + # : (InstanceVariableReadNode) -> void + def visit_instance_variable_read_node: (InstanceVariableReadNode) -> void + + # : (InstanceVariableTargetNode) -> void + def visit_instance_variable_target_node: (InstanceVariableTargetNode) -> void + + # : (InstanceVariableWriteNode) -> void + def visit_instance_variable_write_node: (InstanceVariableWriteNode) -> void + + # : (IntegerNode) -> void + def visit_integer_node: (IntegerNode) -> void + + # : (InterpolatedMatchLastLineNode) -> void + def visit_interpolated_match_last_line_node: (InterpolatedMatchLastLineNode) -> void + + # : (InterpolatedRegularExpressionNode) -> void + def visit_interpolated_regular_expression_node: (InterpolatedRegularExpressionNode) -> void + + # : (InterpolatedStringNode) -> void + def visit_interpolated_string_node: (InterpolatedStringNode) -> void + + # : (InterpolatedSymbolNode) -> void + def visit_interpolated_symbol_node: (InterpolatedSymbolNode) -> void + + # : (InterpolatedXStringNode) -> void + def visit_interpolated_x_string_node: (InterpolatedXStringNode) -> void + + # : (ItLocalVariableReadNode) -> void + def visit_it_local_variable_read_node: (ItLocalVariableReadNode) -> void + + # : (ItParametersNode) -> void + def visit_it_parameters_node: (ItParametersNode) -> void + + # : (KeywordHashNode) -> void + def visit_keyword_hash_node: (KeywordHashNode) -> void + + # : (KeywordRestParameterNode) -> void + def visit_keyword_rest_parameter_node: (KeywordRestParameterNode) -> void + + # : (LambdaNode) -> void + def visit_lambda_node: (LambdaNode) -> void + + # : (LocalVariableAndWriteNode) -> void + def visit_local_variable_and_write_node: (LocalVariableAndWriteNode) -> void + + # : (LocalVariableOperatorWriteNode) -> void + def visit_local_variable_operator_write_node: (LocalVariableOperatorWriteNode) -> void + + # : (LocalVariableOrWriteNode) -> void + def visit_local_variable_or_write_node: (LocalVariableOrWriteNode) -> void + + # : (LocalVariableReadNode) -> void + def visit_local_variable_read_node: (LocalVariableReadNode) -> void + + # : (LocalVariableTargetNode) -> void + def visit_local_variable_target_node: (LocalVariableTargetNode) -> void + + # : (LocalVariableWriteNode) -> void + def visit_local_variable_write_node: (LocalVariableWriteNode) -> void + + # : (MatchLastLineNode) -> void + def visit_match_last_line_node: (MatchLastLineNode) -> void + + # : (MatchPredicateNode) -> void + def visit_match_predicate_node: (MatchPredicateNode) -> void + + # : (MatchRequiredNode) -> void + def visit_match_required_node: (MatchRequiredNode) -> void + + # : (MatchWriteNode) -> void + def visit_match_write_node: (MatchWriteNode) -> void + + # : (MissingNode) -> void + def visit_missing_node: (MissingNode) -> void + + # : (ModuleNode) -> void + def visit_module_node: (ModuleNode) -> void + + # : (MultiTargetNode) -> void + def visit_multi_target_node: (MultiTargetNode) -> void + + # : (MultiWriteNode) -> void + def visit_multi_write_node: (MultiWriteNode) -> void + + # : (NextNode) -> void + def visit_next_node: (NextNode) -> void + + # : (NilNode) -> void + def visit_nil_node: (NilNode) -> void + + # : (NoBlockParameterNode) -> void + def visit_no_block_parameter_node: (NoBlockParameterNode) -> void + + # : (NoKeywordsParameterNode) -> void + def visit_no_keywords_parameter_node: (NoKeywordsParameterNode) -> void + + # : (NumberedParametersNode) -> void + def visit_numbered_parameters_node: (NumberedParametersNode) -> void + + # : (NumberedReferenceReadNode) -> void + def visit_numbered_reference_read_node: (NumberedReferenceReadNode) -> void + + # : (OptionalKeywordParameterNode) -> void + def visit_optional_keyword_parameter_node: (OptionalKeywordParameterNode) -> void + + # : (OptionalParameterNode) -> void + def visit_optional_parameter_node: (OptionalParameterNode) -> void + + # : (OrNode) -> void + def visit_or_node: (OrNode) -> void + + # : (ParametersNode) -> void + def visit_parameters_node: (ParametersNode) -> void + + # : (ParenthesesNode) -> void + def visit_parentheses_node: (ParenthesesNode) -> void + + # : (PinnedExpressionNode) -> void + def visit_pinned_expression_node: (PinnedExpressionNode) -> void + + # : (PinnedVariableNode) -> void + def visit_pinned_variable_node: (PinnedVariableNode) -> void + + # : (PostExecutionNode) -> void + def visit_post_execution_node: (PostExecutionNode) -> void + + # : (PreExecutionNode) -> void + def visit_pre_execution_node: (PreExecutionNode) -> void + + # : (ProgramNode) -> void + def visit_program_node: (ProgramNode) -> void + + # : (RangeNode) -> void + def visit_range_node: (RangeNode) -> void + + # : (RationalNode) -> void + def visit_rational_node: (RationalNode) -> void + + # : (RedoNode) -> void + def visit_redo_node: (RedoNode) -> void + + # : (RegularExpressionNode) -> void + def visit_regular_expression_node: (RegularExpressionNode) -> void + + # : (RequiredKeywordParameterNode) -> void + def visit_required_keyword_parameter_node: (RequiredKeywordParameterNode) -> void + + # : (RequiredParameterNode) -> void + def visit_required_parameter_node: (RequiredParameterNode) -> void + + # : (RescueModifierNode) -> void + def visit_rescue_modifier_node: (RescueModifierNode) -> void + + # : (RescueNode) -> void + def visit_rescue_node: (RescueNode) -> void + + # : (RestParameterNode) -> void + def visit_rest_parameter_node: (RestParameterNode) -> void + + # : (RetryNode) -> void + def visit_retry_node: (RetryNode) -> void + + # : (ReturnNode) -> void + def visit_return_node: (ReturnNode) -> void + + # : (SelfNode) -> void + def visit_self_node: (SelfNode) -> void + + # : (ShareableConstantNode) -> void + def visit_shareable_constant_node: (ShareableConstantNode) -> void + + # : (SingletonClassNode) -> void + def visit_singleton_class_node: (SingletonClassNode) -> void + + # : (SourceEncodingNode) -> void + def visit_source_encoding_node: (SourceEncodingNode) -> void + + # : (SourceFileNode) -> void + def visit_source_file_node: (SourceFileNode) -> void + + # : (SourceLineNode) -> void + def visit_source_line_node: (SourceLineNode) -> void + + # : (SplatNode) -> void + def visit_splat_node: (SplatNode) -> void + + # : (StatementsNode) -> void + def visit_statements_node: (StatementsNode) -> void + + # : (StringNode) -> void + def visit_string_node: (StringNode) -> void + + # : (SuperNode) -> void + def visit_super_node: (SuperNode) -> void + + # : (SymbolNode) -> void + def visit_symbol_node: (SymbolNode) -> void + + # : (TrueNode) -> void + def visit_true_node: (TrueNode) -> void + + # : (UndefNode) -> void + def visit_undef_node: (UndefNode) -> void + + # : (UnlessNode) -> void + def visit_unless_node: (UnlessNode) -> void + + # : (UntilNode) -> void + def visit_until_node: (UntilNode) -> void + + # : (WhenNode) -> void + def visit_when_node: (WhenNode) -> void + + # : (WhileNode) -> void + def visit_while_node: (WhileNode) -> void + + # : (XStringNode) -> void + def visit_x_string_node: (XStringNode) -> void + + # : (YieldNode) -> void + def visit_yield_node: (YieldNode) -> void + + private + + # Generate a unique node ID for a node throughout the digraph. + # -- + # : (node) -> String + def node_id: (node) -> String + + # Inspect a location to display the start and end line and columns in bytes. + # -- + # : (Location) -> String + def location_inspect: (Location) -> String + + # Inspect a node that has arguments_node_flags flags to display the flags as a + # comma-separated list. + # -- + # : (ArgumentsNode node) -> String + def arguments_node_flags_inspect: (ArgumentsNode node) -> String + + # Inspect a node that has array_node_flags flags to display the flags as a + # comma-separated list. + # -- + # : (ArrayNode node) -> String + def array_node_flags_inspect: (ArrayNode node) -> String + + # Inspect a node that has call_node_flags flags to display the flags as a + # comma-separated list. + # -- + # : (CallAndWriteNode | CallNode | CallOperatorWriteNode | CallOrWriteNode | CallTargetNode | IndexAndWriteNode | IndexOperatorWriteNode | IndexOrWriteNode | IndexTargetNode node) -> String + def call_node_flags_inspect: (CallAndWriteNode | CallNode | CallOperatorWriteNode | CallOrWriteNode | CallTargetNode | IndexAndWriteNode | IndexOperatorWriteNode | IndexOrWriteNode | IndexTargetNode node) -> String + + # Inspect a node that has encoding_flags flags to display the flags as a + # comma-separated list. + # -- + # : (XStringNode node) -> String + def encoding_flags_inspect: (XStringNode node) -> String + + # Inspect a node that has integer_base_flags flags to display the flags as a + # comma-separated list. + # -- + # : (IntegerNode | RationalNode node) -> String + def integer_base_flags_inspect: (IntegerNode | RationalNode node) -> String + + # Inspect a node that has interpolated_string_node_flags flags to display the flags as a + # comma-separated list. + # -- + # : (InterpolatedStringNode node) -> String + def interpolated_string_node_flags_inspect: (InterpolatedStringNode node) -> String + + # Inspect a node that has keyword_hash_node_flags flags to display the flags as a + # comma-separated list. + # -- + # : (KeywordHashNode node) -> String + def keyword_hash_node_flags_inspect: (KeywordHashNode node) -> String + + # Inspect a node that has loop_flags flags to display the flags as a + # comma-separated list. + # -- + # : (UntilNode | WhileNode node) -> String + def loop_flags_inspect: (UntilNode | WhileNode node) -> String + + # Inspect a node that has parameter_flags flags to display the flags as a + # comma-separated list. + # -- + # : (BlockLocalVariableNode | BlockParameterNode | KeywordRestParameterNode | OptionalKeywordParameterNode | OptionalParameterNode | RequiredKeywordParameterNode | RequiredParameterNode | RestParameterNode node) -> String + def parameter_flags_inspect: (BlockLocalVariableNode | BlockParameterNode | KeywordRestParameterNode | OptionalKeywordParameterNode | OptionalParameterNode | RequiredKeywordParameterNode | RequiredParameterNode | RestParameterNode node) -> String + + # Inspect a node that has parentheses_node_flags flags to display the flags as a + # comma-separated list. + # -- + # : (ParenthesesNode node) -> String + def parentheses_node_flags_inspect: (ParenthesesNode node) -> String + + # Inspect a node that has range_flags flags to display the flags as a + # comma-separated list. + # -- + # : (FlipFlopNode | RangeNode node) -> String + def range_flags_inspect: (FlipFlopNode | RangeNode node) -> String + + # Inspect a node that has regular_expression_flags flags to display the flags as a + # comma-separated list. + # -- + # : (InterpolatedMatchLastLineNode | InterpolatedRegularExpressionNode | MatchLastLineNode | RegularExpressionNode node) -> String + def regular_expression_flags_inspect: (InterpolatedMatchLastLineNode | InterpolatedRegularExpressionNode | MatchLastLineNode | RegularExpressionNode node) -> String + + # Inspect a node that has shareable_constant_node_flags flags to display the flags as a + # comma-separated list. + # -- + # : (ShareableConstantNode node) -> String + def shareable_constant_node_flags_inspect: (ShareableConstantNode node) -> String + + # Inspect a node that has string_flags flags to display the flags as a + # comma-separated list. + # -- + # : (SourceFileNode | StringNode node) -> String + def string_flags_inspect: (SourceFileNode | StringNode node) -> String + + # Inspect a node that has symbol_flags flags to display the flags as a + # comma-separated list. + # -- + # : (SymbolNode node) -> String + def symbol_flags_inspect: (SymbolNode node) -> String + end +end diff --git a/sig/generated/prism/dsl.rbs b/sig/generated/prism/dsl.rbs new file mode 100644 index 0000000000..fc0c198705 --- /dev/null +++ b/sig/generated/prism/dsl.rbs @@ -0,0 +1,928 @@ +# Generated from lib/prism/dsl.rb with RBS::Inline + +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]", 1, [0]) + # + # 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 + # Create a new Source object. + # -- + # : (String string) -> Source + def source: (String string) -> Source + + # Create a new Location object. + # -- + # : (?source: Source, ?start_offset: Integer, ?length: Integer) -> Location + def location: (?source: Source, ?start_offset: Integer, ?length: Integer) -> Location + + # Create a new AliasGlobalVariableNode 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_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 + + # Create a new AliasMethodNode 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 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 + + # Create a new AlternationPatternNode node. + # -- + # : (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer, ?left: Prism::node, ?right: Prism::node, ?operator_loc: Location) -> AlternationPatternNode + def alternation_pattern_node: (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer, ?left: Prism::node, ?right: Prism::node, ?operator_loc: Location) -> AlternationPatternNode + + # Create a new AndNode node. + # -- + # : (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer, ?left: Prism::node, ?right: Prism::node, ?operator_loc: Location) -> AndNode + def and_node: (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer, ?left: Prism::node, ?right: Prism::node, ?operator_loc: Location) -> AndNode + + # Create a new ArgumentsNode node. + # -- + # : (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer, ?arguments: Array[Prism::node]) -> ArgumentsNode + def arguments_node: (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer, ?arguments: Array[Prism::node]) -> ArgumentsNode + + # Create a new ArrayNode node. + # -- + # : (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer, ?elements: Array[Prism::node], ?opening_loc: Location?, ?closing_loc: Location?) -> ArrayNode + def array_node: (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer, ?elements: Array[Prism::node], ?opening_loc: Location?, ?closing_loc: Location?) -> ArrayNode + + # Create a new ArrayPatternNode node. + # -- + # : (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer, ?constant: (ConstantPathNode | ConstantReadNode)?, ?requireds: Array[Prism::node], ?rest: Prism::node?, ?posts: Array[Prism::node], ?opening_loc: Location?, ?closing_loc: Location?) -> ArrayPatternNode + def array_pattern_node: (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer, ?constant: (ConstantPathNode | ConstantReadNode)?, ?requireds: Array[Prism::node], ?rest: Prism::node?, ?posts: Array[Prism::node], ?opening_loc: Location?, ?closing_loc: Location?) -> ArrayPatternNode + + # Create a new AssocNode node. + # -- + # : (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer, ?key: Prism::node, ?value: Prism::node, ?operator_loc: Location?) -> AssocNode + def assoc_node: (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer, ?key: Prism::node, ?value: Prism::node, ?operator_loc: Location?) -> AssocNode + + # Create a new AssocSplatNode node. + # -- + # : (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer, ?value: Prism::node?, ?operator_loc: Location) -> AssocSplatNode + def assoc_splat_node: (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer, ?value: Prism::node?, ?operator_loc: Location) -> AssocSplatNode + + # Create a new BackReferenceReadNode node. + # -- + # : (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer, ?name: Symbol) -> BackReferenceReadNode + def back_reference_read_node: (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer, ?name: Symbol) -> BackReferenceReadNode + + # Create a new BeginNode 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 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 + + # Create a new BlockArgumentNode node. + # -- + # : (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer, ?expression: Prism::node?, ?operator_loc: Location) -> BlockArgumentNode + def block_argument_node: (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer, ?expression: Prism::node?, ?operator_loc: Location) -> BlockArgumentNode + + # Create a new BlockLocalVariableNode node. + # -- + # : (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer, ?name: Symbol) -> BlockLocalVariableNode + def block_local_variable_node: (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer, ?name: Symbol) -> BlockLocalVariableNode + + # Create a new BlockNode node. + # -- + # : (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer, ?locals: Array[Symbol], ?parameters: (BlockParametersNode | NumberedParametersNode | ItParametersNode)?, ?body: (StatementsNode | BeginNode)?, ?opening_loc: Location, ?closing_loc: Location) -> BlockNode + def block_node: (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer, ?locals: Array[Symbol], ?parameters: (BlockParametersNode | NumberedParametersNode | ItParametersNode)?, ?body: (StatementsNode | BeginNode)?, ?opening_loc: Location, ?closing_loc: Location) -> BlockNode + + # Create a new BlockParameterNode node. + # -- + # : (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer, ?name: Symbol?, ?name_loc: Location?, ?operator_loc: Location) -> BlockParameterNode + def block_parameter_node: (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer, ?name: Symbol?, ?name_loc: Location?, ?operator_loc: Location) -> BlockParameterNode + + # Create a new BlockParametersNode node. + # -- + # : (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer, ?parameters: ParametersNode?, ?locals: Array[BlockLocalVariableNode], ?opening_loc: Location?, ?closing_loc: Location?) -> BlockParametersNode + 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 + + # Create a new BreakNode node. + # -- + # : (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer, ?arguments: ArgumentsNode?, ?keyword_loc: Location) -> BreakNode + def break_node: (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer, ?arguments: ArgumentsNode?, ?keyword_loc: Location) -> BreakNode + + # Create a new CallAndWriteNode 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_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 + + # Create a new CallNode 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)?) -> CallNode + 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)?) -> CallNode + + # Create a new CallOperatorWriteNode 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_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 + + # Create a new CallOrWriteNode 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_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 + + # Create a new CallTargetNode node. + # -- + # : (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer, ?receiver: Prism::node, ?call_operator_loc: Location, ?name: Symbol, ?message_loc: Location) -> CallTargetNode + 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 + + # Create a new CapturePatternNode node. + # -- + # : (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer, ?value: Prism::node, ?target: LocalVariableTargetNode, ?operator_loc: Location) -> CapturePatternNode + def capture_pattern_node: (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer, ?value: Prism::node, ?target: LocalVariableTargetNode, ?operator_loc: Location) -> CapturePatternNode + + # Create a new CaseMatchNode 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_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 + + # Create a new CaseNode 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 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 + + # Create a new ClassNode 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)?, ?end_keyword_loc: Location, ?name: Symbol) -> ClassNode + 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)?, ?end_keyword_loc: Location, ?name: Symbol) -> ClassNode + + # Create a new ClassVariableAndWriteNode 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_and_write_node: (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer, ?name: Symbol, ?name_loc: Location, ?operator_loc: Location, ?value: Prism::node) -> ClassVariableAndWriteNode + + # Create a new ClassVariableOperatorWriteNode 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_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 + + # Create a new ClassVariableOrWriteNode 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_or_write_node: (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer, ?name: Symbol, ?name_loc: Location, ?operator_loc: Location, ?value: Prism::node) -> ClassVariableOrWriteNode + + # Create a new ClassVariableReadNode node. + # -- + # : (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer, ?name: Symbol) -> ClassVariableReadNode + def class_variable_read_node: (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer, ?name: Symbol) -> ClassVariableReadNode + + # Create a new ClassVariableTargetNode node. + # -- + # : (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer, ?name: Symbol) -> ClassVariableTargetNode + def class_variable_target_node: (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer, ?name: Symbol) -> ClassVariableTargetNode + + # Create a new ClassVariableWriteNode node. + # -- + # : (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer, ?name: Symbol, ?name_loc: Location, ?value: Prism::node, ?operator_loc: Location) -> ClassVariableWriteNode + 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 + + # Create a new ConstantAndWriteNode node. + # -- + # : (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer, ?name: Symbol, ?name_loc: Location, ?operator_loc: Location, ?value: Prism::node) -> ConstantAndWriteNode + 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 + + # Create a new ConstantOperatorWriteNode 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_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 + + # Create a new ConstantOrWriteNode node. + # -- + # : (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer, ?name: Symbol, ?name_loc: Location, ?operator_loc: Location, ?value: Prism::node) -> ConstantOrWriteNode + 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 + + # Create a new ConstantPathAndWriteNode node. + # -- + # : (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer, ?target: ConstantPathNode, ?operator_loc: Location, ?value: Prism::node) -> ConstantPathAndWriteNode + def constant_path_and_write_node: (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer, ?target: ConstantPathNode, ?operator_loc: Location, ?value: Prism::node) -> ConstantPathAndWriteNode + + # Create a new ConstantPathNode 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_node: (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer, ?parent: Prism::node?, ?name: Symbol?, ?delimiter_loc: Location, ?name_loc: Location) -> ConstantPathNode + + # Create a new ConstantPathOperatorWriteNode 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_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 + + # Create a new ConstantPathOrWriteNode node. + # -- + # : (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer, ?target: ConstantPathNode, ?operator_loc: Location, ?value: Prism::node) -> ConstantPathOrWriteNode + def constant_path_or_write_node: (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer, ?target: ConstantPathNode, ?operator_loc: Location, ?value: Prism::node) -> ConstantPathOrWriteNode + + # Create a new ConstantPathTargetNode 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_target_node: (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer, ?parent: Prism::node?, ?name: Symbol?, ?delimiter_loc: Location, ?name_loc: Location) -> ConstantPathTargetNode + + # Create a new ConstantPathWriteNode node. + # -- + # : (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer, ?target: ConstantPathNode, ?operator_loc: Location, ?value: Prism::node) -> ConstantPathWriteNode + def constant_path_write_node: (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer, ?target: ConstantPathNode, ?operator_loc: Location, ?value: Prism::node) -> ConstantPathWriteNode + + # Create a new ConstantReadNode node. + # -- + # : (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer, ?name: Symbol) -> ConstantReadNode + def constant_read_node: (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer, ?name: Symbol) -> ConstantReadNode + + # Create a new ConstantTargetNode node. + # -- + # : (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer, ?name: Symbol) -> ConstantTargetNode + def constant_target_node: (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer, ?name: Symbol) -> ConstantTargetNode + + # Create a new ConstantWriteNode node. + # -- + # : (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer, ?name: Symbol, ?name_loc: Location, ?value: Prism::node, ?operator_loc: Location) -> ConstantWriteNode + 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 + + # Create a new DefNode node. + # -- + # : (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer, ?name: Symbol, ?name_loc: Location, ?receiver: Prism::node?, ?parameters: ParametersNode?, ?body: (StatementsNode | BeginNode)?, ?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 def_node: (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer, ?name: Symbol, ?name_loc: Location, ?receiver: Prism::node?, ?parameters: ParametersNode?, ?body: (StatementsNode | BeginNode)?, ?locals: Array[Symbol], ?def_keyword_loc: Location, ?operator_loc: Location?, ?lparen_loc: Location?, ?rparen_loc: Location?, ?equal_loc: Location?, ?end_keyword_loc: Location?) -> DefNode + + # Create a new DefinedNode node. + # -- + # : (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer, ?lparen_loc: Location?, ?value: Prism::node, ?rparen_loc: Location?, ?keyword_loc: Location) -> DefinedNode + 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 + + # Create a new ElseNode node. + # -- + # : (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer, ?else_keyword_loc: Location, ?statements: StatementsNode?, ?end_keyword_loc: Location?) -> ElseNode + def else_node: (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer, ?else_keyword_loc: Location, ?statements: StatementsNode?, ?end_keyword_loc: Location?) -> ElseNode + + # Create a new EmbeddedStatementsNode node. + # -- + # : (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer, ?opening_loc: Location, ?statements: StatementsNode?, ?closing_loc: Location) -> EmbeddedStatementsNode + def embedded_statements_node: (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer, ?opening_loc: Location, ?statements: StatementsNode?, ?closing_loc: Location) -> EmbeddedStatementsNode + + # Create a new EmbeddedVariableNode node. + # -- + # : (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer, ?operator_loc: Location, ?variable: (InstanceVariableReadNode | ClassVariableReadNode | GlobalVariableReadNode | BackReferenceReadNode | NumberedReferenceReadNode)) -> EmbeddedVariableNode + def embedded_variable_node: (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer, ?operator_loc: Location, ?variable: InstanceVariableReadNode | ClassVariableReadNode | GlobalVariableReadNode | BackReferenceReadNode | NumberedReferenceReadNode) -> EmbeddedVariableNode + + # Create a new EnsureNode node. + # -- + # : (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer, ?ensure_keyword_loc: Location, ?statements: StatementsNode?, ?end_keyword_loc: Location) -> EnsureNode + def ensure_node: (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer, ?ensure_keyword_loc: Location, ?statements: StatementsNode?, ?end_keyword_loc: Location) -> EnsureNode + + # Create a new FalseNode node. + # -- + # : (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer) -> FalseNode + def false_node: (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer) -> FalseNode + + # Create a new FindPatternNode node. + # -- + # : (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer, ?constant: (ConstantPathNode | ConstantReadNode)?, ?left: SplatNode, ?requireds: Array[Prism::node], ?right: (SplatNode | MissingNode), ?opening_loc: Location?, ?closing_loc: Location?) -> FindPatternNode + def find_pattern_node: (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer, ?constant: (ConstantPathNode | ConstantReadNode)?, ?left: SplatNode, ?requireds: Array[Prism::node], ?right: SplatNode | MissingNode, ?opening_loc: Location?, ?closing_loc: Location?) -> FindPatternNode + + # Create a new FlipFlopNode node. + # -- + # : (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer, ?left: Prism::node?, ?right: Prism::node?, ?operator_loc: Location) -> FlipFlopNode + def flip_flop_node: (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer, ?left: Prism::node?, ?right: Prism::node?, ?operator_loc: Location) -> FlipFlopNode + + # Create a new FloatNode node. + # -- + # : (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer, ?value: Float) -> FloatNode + def float_node: (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer, ?value: Float) -> FloatNode + + # Create a new ForNode 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 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 + + # Create a new ForwardingArgumentsNode node. + # -- + # : (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer) -> ForwardingArgumentsNode + def forwarding_arguments_node: (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer) -> ForwardingArgumentsNode + + # Create a new ForwardingParameterNode node. + # -- + # : (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer) -> ForwardingParameterNode + def forwarding_parameter_node: (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer) -> ForwardingParameterNode + + # Create a new ForwardingSuperNode node. + # -- + # : (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer, ?block: BlockNode?) -> ForwardingSuperNode + def forwarding_super_node: (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer, ?block: BlockNode?) -> ForwardingSuperNode + + # Create a new GlobalVariableAndWriteNode 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_and_write_node: (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer, ?name: Symbol, ?name_loc: Location, ?operator_loc: Location, ?value: Prism::node) -> GlobalVariableAndWriteNode + + # Create a new GlobalVariableOperatorWriteNode 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_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 + + # Create a new GlobalVariableOrWriteNode 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_or_write_node: (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer, ?name: Symbol, ?name_loc: Location, ?operator_loc: Location, ?value: Prism::node) -> GlobalVariableOrWriteNode + + # Create a new GlobalVariableReadNode node. + # -- + # : (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer, ?name: Symbol) -> GlobalVariableReadNode + def global_variable_read_node: (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer, ?name: Symbol) -> GlobalVariableReadNode + + # Create a new GlobalVariableTargetNode node. + # -- + # : (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer, ?name: Symbol) -> GlobalVariableTargetNode + def global_variable_target_node: (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer, ?name: Symbol) -> GlobalVariableTargetNode + + # Create a new GlobalVariableWriteNode node. + # -- + # : (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer, ?name: Symbol, ?name_loc: Location, ?value: Prism::node, ?operator_loc: Location) -> GlobalVariableWriteNode + 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 + + # Create a new HashNode node. + # -- + # : (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer, ?opening_loc: Location, ?elements: Array[AssocNode | AssocSplatNode], ?closing_loc: Location) -> HashNode + def hash_node: (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer, ?opening_loc: Location, ?elements: Array[AssocNode | AssocSplatNode], ?closing_loc: Location) -> HashNode + + # Create a new HashPatternNode node. + # -- + # : (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer, ?constant: (ConstantPathNode | ConstantReadNode)?, ?elements: Array[AssocNode], ?rest: (AssocSplatNode | NoKeywordsParameterNode)?, ?opening_loc: Location?, ?closing_loc: Location?) -> HashPatternNode + def hash_pattern_node: (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer, ?constant: (ConstantPathNode | ConstantReadNode)?, ?elements: Array[AssocNode], ?rest: (AssocSplatNode | NoKeywordsParameterNode)?, ?opening_loc: Location?, ?closing_loc: Location?) -> HashPatternNode + + # Create a new IfNode 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)?, ?end_keyword_loc: Location?) -> IfNode + 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)?, ?end_keyword_loc: Location?) -> IfNode + + # Create a new ImaginaryNode node. + # -- + # : (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer, ?numeric: (FloatNode | IntegerNode | RationalNode)) -> ImaginaryNode + def imaginary_node: (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer, ?numeric: FloatNode | IntegerNode | RationalNode) -> ImaginaryNode + + # Create a new ImplicitNode node. + # -- + # : (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer, ?value: (LocalVariableReadNode | CallNode | ConstantReadNode | LocalVariableTargetNode)) -> ImplicitNode + def implicit_node: (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer, ?value: LocalVariableReadNode | CallNode | ConstantReadNode | LocalVariableTargetNode) -> ImplicitNode + + # Create a new ImplicitRestNode node. + # -- + # : (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer) -> ImplicitRestNode + def implicit_rest_node: (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer) -> ImplicitRestNode + + # Create a new InNode node. + # -- + # : (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer, ?pattern: Prism::node, ?statements: StatementsNode?, ?in_loc: Location, ?then_loc: Location?) -> InNode + def in_node: (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer, ?pattern: Prism::node, ?statements: StatementsNode?, ?in_loc: Location, ?then_loc: Location?) -> InNode + + # Create a new IndexAndWriteNode 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_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 + + # Create a new IndexOperatorWriteNode 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_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 + + # Create a new IndexOrWriteNode 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_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 + + # Create a new IndexTargetNode 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 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 + + # Create a new InstanceVariableAndWriteNode 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_and_write_node: (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer, ?name: Symbol, ?name_loc: Location, ?operator_loc: Location, ?value: Prism::node) -> InstanceVariableAndWriteNode + + # Create a new InstanceVariableOperatorWriteNode 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_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 + + # Create a new InstanceVariableOrWriteNode 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_or_write_node: (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer, ?name: Symbol, ?name_loc: Location, ?operator_loc: Location, ?value: Prism::node) -> InstanceVariableOrWriteNode + + # Create a new InstanceVariableReadNode node. + # -- + # : (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer, ?name: Symbol) -> InstanceVariableReadNode + def instance_variable_read_node: (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer, ?name: Symbol) -> InstanceVariableReadNode + + # Create a new InstanceVariableTargetNode node. + # -- + # : (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer, ?name: Symbol) -> InstanceVariableTargetNode + def instance_variable_target_node: (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer, ?name: Symbol) -> InstanceVariableTargetNode + + # Create a new InstanceVariableWriteNode node. + # -- + # : (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer, ?name: Symbol, ?name_loc: Location, ?value: Prism::node, ?operator_loc: Location) -> InstanceVariableWriteNode + 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 + + # Create a new IntegerNode node. + # -- + # : (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer, ?value: Integer) -> IntegerNode + def integer_node: (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer, ?value: Integer) -> IntegerNode + + # Create a new InterpolatedMatchLastLineNode node. + # -- + # : (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer, ?opening_loc: Location, ?parts: Array[StringNode | EmbeddedStatementsNode | EmbeddedVariableNode], ?closing_loc: Location) -> InterpolatedMatchLastLineNode + 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 + + # Create a new InterpolatedRegularExpressionNode node. + # -- + # : (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer, ?opening_loc: Location, ?parts: Array[StringNode | EmbeddedStatementsNode | EmbeddedVariableNode], ?closing_loc: Location) -> InterpolatedRegularExpressionNode + 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 + + # Create a new InterpolatedStringNode 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_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 + + # Create a new InterpolatedSymbolNode node. + # -- + # : (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer, ?opening_loc: Location?, ?parts: Array[StringNode | EmbeddedStatementsNode | EmbeddedVariableNode], ?closing_loc: Location?) -> InterpolatedSymbolNode + 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 + + # Create a new InterpolatedXStringNode node. + # -- + # : (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer, ?opening_loc: Location, ?parts: Array[StringNode | EmbeddedStatementsNode | EmbeddedVariableNode], ?closing_loc: Location) -> InterpolatedXStringNode + 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 + + # Create a new ItLocalVariableReadNode node. + # -- + # : (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer) -> ItLocalVariableReadNode + def it_local_variable_read_node: (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer) -> ItLocalVariableReadNode + + # Create a new ItParametersNode node. + # -- + # : (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer) -> ItParametersNode + def it_parameters_node: (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer) -> ItParametersNode + + # Create a new KeywordHashNode node. + # -- + # : (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer, ?elements: Array[AssocNode | AssocSplatNode]) -> KeywordHashNode + def keyword_hash_node: (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer, ?elements: Array[AssocNode | AssocSplatNode]) -> KeywordHashNode + + # Create a new KeywordRestParameterNode node. + # -- + # : (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer, ?name: Symbol?, ?name_loc: Location?, ?operator_loc: Location) -> KeywordRestParameterNode + def keyword_rest_parameter_node: (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer, ?name: Symbol?, ?name_loc: Location?, ?operator_loc: Location) -> KeywordRestParameterNode + + # Create a new LambdaNode 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)?, ?body: (StatementsNode | BeginNode)?) -> LambdaNode + 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)?, ?body: (StatementsNode | BeginNode)?) -> LambdaNode + + # Create a new LocalVariableAndWriteNode 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_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 + + # Create a new LocalVariableOperatorWriteNode 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_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 + + # Create a new LocalVariableOrWriteNode 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_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 + + # Create a new LocalVariableReadNode node. + # -- + # : (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer, ?name: Symbol, ?depth: Integer) -> LocalVariableReadNode + def local_variable_read_node: (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer, ?name: Symbol, ?depth: Integer) -> LocalVariableReadNode + + # Create a new LocalVariableTargetNode node. + # -- + # : (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer, ?name: Symbol, ?depth: Integer) -> LocalVariableTargetNode + def local_variable_target_node: (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer, ?name: Symbol, ?depth: Integer) -> LocalVariableTargetNode + + # Create a new LocalVariableWriteNode 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 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 + + # Create a new MatchLastLineNode node. + # -- + # : (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer, ?opening_loc: Location, ?content_loc: Location, ?closing_loc: Location, ?unescaped: String) -> MatchLastLineNode + 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 + + # Create a new MatchPredicateNode node. + # -- + # : (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer, ?value: Prism::node, ?pattern: Prism::node, ?operator_loc: Location) -> MatchPredicateNode + def match_predicate_node: (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer, ?value: Prism::node, ?pattern: Prism::node, ?operator_loc: Location) -> MatchPredicateNode + + # Create a new MatchRequiredNode node. + # -- + # : (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer, ?value: Prism::node, ?pattern: Prism::node, ?operator_loc: Location) -> MatchRequiredNode + def match_required_node: (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer, ?value: Prism::node, ?pattern: Prism::node, ?operator_loc: Location) -> MatchRequiredNode + + # Create a new MatchWriteNode node. + # -- + # : (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer, ?call: CallNode, ?targets: Array[LocalVariableTargetNode]) -> MatchWriteNode + def match_write_node: (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer, ?call: CallNode, ?targets: Array[LocalVariableTargetNode]) -> MatchWriteNode + + # Create a new MissingNode node. + # -- + # : (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer) -> MissingNode + def missing_node: (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer) -> MissingNode + + # Create a new ModuleNode 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)?, ?end_keyword_loc: Location, ?name: Symbol) -> ModuleNode + 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)?, ?end_keyword_loc: Location, ?name: Symbol) -> ModuleNode + + # Create a new MultiTargetNode 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)?, ?rights: Array[LocalVariableTargetNode | InstanceVariableTargetNode | ClassVariableTargetNode | GlobalVariableTargetNode | ConstantTargetNode | ConstantPathTargetNode | CallTargetNode | IndexTargetNode | MultiTargetNode | RequiredParameterNode | BackReferenceReadNode | NumberedReferenceReadNode], ?lparen_loc: Location?, ?rparen_loc: Location?) -> MultiTargetNode + 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)?, ?rights: Array[LocalVariableTargetNode | InstanceVariableTargetNode | ClassVariableTargetNode | GlobalVariableTargetNode | ConstantTargetNode | ConstantPathTargetNode | CallTargetNode | IndexTargetNode | MultiTargetNode | RequiredParameterNode | BackReferenceReadNode | NumberedReferenceReadNode], ?lparen_loc: Location?, ?rparen_loc: Location?) -> MultiTargetNode + + # Create a new MultiWriteNode 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)?, ?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 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)?, ?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 + + # Create a new NextNode node. + # -- + # : (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer, ?arguments: ArgumentsNode?, ?keyword_loc: Location) -> NextNode + def next_node: (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer, ?arguments: ArgumentsNode?, ?keyword_loc: Location) -> NextNode + + # Create a new NilNode node. + # -- + # : (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer) -> NilNode + def nil_node: (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer) -> NilNode + + # Create a new NoBlockParameterNode node. + # -- + # : (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer, ?operator_loc: Location, ?keyword_loc: Location) -> NoBlockParameterNode + def no_block_parameter_node: (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer, ?operator_loc: Location, ?keyword_loc: Location) -> NoBlockParameterNode + + # Create a new NoKeywordsParameterNode node. + # -- + # : (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer, ?operator_loc: Location, ?keyword_loc: Location) -> NoKeywordsParameterNode + def no_keywords_parameter_node: (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer, ?operator_loc: Location, ?keyword_loc: Location) -> NoKeywordsParameterNode + + # Create a new NumberedParametersNode node. + # -- + # : (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer, ?maximum: Integer) -> NumberedParametersNode + def numbered_parameters_node: (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer, ?maximum: Integer) -> NumberedParametersNode + + # Create a new NumberedReferenceReadNode node. + # -- + # : (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer, ?number: Integer) -> NumberedReferenceReadNode + def numbered_reference_read_node: (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer, ?number: Integer) -> NumberedReferenceReadNode + + # Create a new OptionalKeywordParameterNode node. + # -- + # : (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer, ?name: Symbol, ?name_loc: Location, ?value: Prism::node) -> OptionalKeywordParameterNode + def optional_keyword_parameter_node: (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer, ?name: Symbol, ?name_loc: Location, ?value: Prism::node) -> OptionalKeywordParameterNode + + # Create a new OptionalParameterNode node. + # -- + # : (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer, ?name: Symbol, ?name_loc: Location, ?operator_loc: Location, ?value: Prism::node) -> OptionalParameterNode + 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 + + # Create a new OrNode node. + # -- + # : (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer, ?left: Prism::node, ?right: Prism::node, ?operator_loc: Location) -> OrNode + def or_node: (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer, ?left: Prism::node, ?right: Prism::node, ?operator_loc: Location) -> OrNode + + # Create a new ParametersNode node. + # -- + # : (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer, ?requireds: Array[RequiredParameterNode | MultiTargetNode], ?optionals: Array[OptionalParameterNode], ?rest: (RestParameterNode | ImplicitRestNode)?, ?posts: Array[RequiredParameterNode | MultiTargetNode | KeywordRestParameterNode | NoKeywordsParameterNode | ForwardingParameterNode | BlockParameterNode | NoBlockParameterNode], ?keywords: Array[RequiredKeywordParameterNode | OptionalKeywordParameterNode], ?keyword_rest: (KeywordRestParameterNode | ForwardingParameterNode | NoKeywordsParameterNode)?, ?block: (BlockParameterNode | NoBlockParameterNode)?) -> ParametersNode + def parameters_node: (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer, ?requireds: Array[RequiredParameterNode | MultiTargetNode], ?optionals: Array[OptionalParameterNode], ?rest: (RestParameterNode | ImplicitRestNode)?, ?posts: Array[RequiredParameterNode | MultiTargetNode | KeywordRestParameterNode | NoKeywordsParameterNode | ForwardingParameterNode | BlockParameterNode | NoBlockParameterNode], ?keywords: Array[RequiredKeywordParameterNode | OptionalKeywordParameterNode], ?keyword_rest: (KeywordRestParameterNode | ForwardingParameterNode | NoKeywordsParameterNode)?, ?block: (BlockParameterNode | NoBlockParameterNode)?) -> ParametersNode + + # Create a new ParenthesesNode node. + # -- + # : (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer, ?body: Prism::node?, ?opening_loc: Location, ?closing_loc: Location) -> ParenthesesNode + def parentheses_node: (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer, ?body: Prism::node?, ?opening_loc: Location, ?closing_loc: Location) -> ParenthesesNode + + # Create a new PinnedExpressionNode 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_expression_node: (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer, ?expression: Prism::node, ?operator_loc: Location, ?lparen_loc: Location, ?rparen_loc: Location) -> PinnedExpressionNode + + # Create a new PinnedVariableNode node. + # -- + # : (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer, ?variable: (LocalVariableReadNode | InstanceVariableReadNode | ClassVariableReadNode | GlobalVariableReadNode | BackReferenceReadNode | NumberedReferenceReadNode | ItLocalVariableReadNode | MissingNode), ?operator_loc: Location) -> PinnedVariableNode + 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 + + # Create a new PostExecutionNode node. + # -- + # : (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer, ?statements: StatementsNode?, ?keyword_loc: Location, ?opening_loc: Location, ?closing_loc: Location) -> PostExecutionNode + 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 + + # Create a new PreExecutionNode node. + # -- + # : (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer, ?statements: StatementsNode?, ?keyword_loc: Location, ?opening_loc: Location, ?closing_loc: Location) -> PreExecutionNode + 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 + + # Create a new ProgramNode node. + # -- + # : (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer, ?locals: Array[Symbol], ?statements: StatementsNode) -> ProgramNode + def program_node: (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer, ?locals: Array[Symbol], ?statements: StatementsNode) -> ProgramNode + + # Create a new RangeNode node. + # -- + # : (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer, ?left: Prism::node?, ?right: Prism::node?, ?operator_loc: Location) -> RangeNode + def range_node: (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer, ?left: Prism::node?, ?right: Prism::node?, ?operator_loc: Location) -> RangeNode + + # Create a new RationalNode node. + # -- + # : (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer, ?numerator: Integer, ?denominator: Integer) -> RationalNode + def rational_node: (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer, ?numerator: Integer, ?denominator: Integer) -> RationalNode + + # Create a new RedoNode node. + # -- + # : (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer) -> RedoNode + def redo_node: (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer) -> RedoNode + + # Create a new RegularExpressionNode node. + # -- + # : (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer, ?opening_loc: Location, ?content_loc: Location, ?closing_loc: Location, ?unescaped: String) -> RegularExpressionNode + 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 + + # Create a new RequiredKeywordParameterNode node. + # -- + # : (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer, ?name: Symbol, ?name_loc: Location) -> RequiredKeywordParameterNode + def required_keyword_parameter_node: (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer, ?name: Symbol, ?name_loc: Location) -> RequiredKeywordParameterNode + + # Create a new RequiredParameterNode node. + # -- + # : (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer, ?name: Symbol) -> RequiredParameterNode + def required_parameter_node: (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer, ?name: Symbol) -> RequiredParameterNode + + # Create a new RescueModifierNode node. + # -- + # : (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer, ?expression: Prism::node, ?keyword_loc: Location, ?rescue_expression: Prism::node) -> RescueModifierNode + def rescue_modifier_node: (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer, ?expression: Prism::node, ?keyword_loc: Location, ?rescue_expression: Prism::node) -> RescueModifierNode + + # Create a new RescueNode 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)?, ?then_keyword_loc: Location?, ?statements: StatementsNode?, ?subsequent: RescueNode?) -> RescueNode + 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)?, ?then_keyword_loc: Location?, ?statements: StatementsNode?, ?subsequent: RescueNode?) -> RescueNode + + # Create a new RestParameterNode node. + # -- + # : (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer, ?name: Symbol?, ?name_loc: Location?, ?operator_loc: Location) -> RestParameterNode + def rest_parameter_node: (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer, ?name: Symbol?, ?name_loc: Location?, ?operator_loc: Location) -> RestParameterNode + + # Create a new RetryNode node. + # -- + # : (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer) -> RetryNode + def retry_node: (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer) -> RetryNode + + # Create a new ReturnNode node. + # -- + # : (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer, ?keyword_loc: Location, ?arguments: ArgumentsNode?) -> ReturnNode + def return_node: (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer, ?keyword_loc: Location, ?arguments: ArgumentsNode?) -> ReturnNode + + # Create a new SelfNode node. + # -- + # : (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer) -> SelfNode + def self_node: (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer) -> SelfNode + + # Create a new ShareableConstantNode node. + # -- + # : (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer, ?write: (ConstantWriteNode | ConstantAndWriteNode | ConstantOrWriteNode | ConstantOperatorWriteNode | ConstantPathWriteNode | ConstantPathAndWriteNode | ConstantPathOrWriteNode | ConstantPathOperatorWriteNode)) -> ShareableConstantNode + def shareable_constant_node: (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer, ?write: ConstantWriteNode | ConstantAndWriteNode | ConstantOrWriteNode | ConstantOperatorWriteNode | ConstantPathWriteNode | ConstantPathAndWriteNode | ConstantPathOrWriteNode | ConstantPathOperatorWriteNode) -> ShareableConstantNode + + # Create a new SingletonClassNode 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)?, ?end_keyword_loc: Location) -> SingletonClassNode + 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)?, ?end_keyword_loc: Location) -> SingletonClassNode + + # Create a new SourceEncodingNode node. + # -- + # : (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer) -> SourceEncodingNode + def source_encoding_node: (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer) -> SourceEncodingNode + + # Create a new SourceFileNode node. + # -- + # : (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer, ?filepath: String) -> SourceFileNode + def source_file_node: (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer, ?filepath: String) -> SourceFileNode + + # Create a new SourceLineNode node. + # -- + # : (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer) -> SourceLineNode + def source_line_node: (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer) -> SourceLineNode + + # Create a new SplatNode node. + # -- + # : (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer, ?operator_loc: Location, ?expression: Prism::node?) -> SplatNode + def splat_node: (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer, ?operator_loc: Location, ?expression: Prism::node?) -> SplatNode + + # Create a new StatementsNode node. + # -- + # : (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer, ?body: Array[Prism::node]) -> StatementsNode + def statements_node: (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer, ?body: Array[Prism::node]) -> StatementsNode + + # Create a new StringNode node. + # -- + # : (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer, ?opening_loc: Location?, ?content_loc: Location, ?closing_loc: Location?, ?unescaped: String) -> StringNode + def string_node: (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer, ?opening_loc: Location?, ?content_loc: Location, ?closing_loc: Location?, ?unescaped: String) -> StringNode + + # Create a new SuperNode node. + # -- + # : (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer, ?keyword_loc: Location, ?lparen_loc: Location?, ?arguments: ArgumentsNode?, ?rparen_loc: Location?, ?block: (BlockNode | BlockArgumentNode)?) -> SuperNode + 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)?) -> SuperNode + + # Create a new SymbolNode node. + # -- + # : (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer, ?opening_loc: Location?, ?value_loc: Location?, ?closing_loc: Location?, ?unescaped: String) -> SymbolNode + def symbol_node: (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer, ?opening_loc: Location?, ?value_loc: Location?, ?closing_loc: Location?, ?unescaped: String) -> SymbolNode + + # Create a new TrueNode node. + # -- + # : (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer) -> TrueNode + def true_node: (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer) -> TrueNode + + # Create a new UndefNode node. + # -- + # : (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer, ?names: Array[SymbolNode | InterpolatedSymbolNode], ?keyword_loc: Location) -> UndefNode + def undef_node: (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer, ?names: Array[SymbolNode | InterpolatedSymbolNode], ?keyword_loc: Location) -> UndefNode + + # Create a new UnlessNode 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 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 + + # Create a new UntilNode 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 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 + + # Create a new WhenNode 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 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 + + # Create a new WhileNode 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 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 + + # Create a new XStringNode node. + # -- + # : (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer, ?opening_loc: Location, ?content_loc: Location, ?closing_loc: Location, ?unescaped: String) -> XStringNode + 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 + + # Create a new YieldNode node. + # -- + # : (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer, ?keyword_loc: Location, ?lparen_loc: Location?, ?arguments: ArgumentsNode?, ?rparen_loc: Location?) -> YieldNode + def yield_node: (?source: Source, ?node_id: Integer, ?location: Location, ?flags: Integer, ?keyword_loc: Location, ?lparen_loc: Location?, ?arguments: ArgumentsNode?, ?rparen_loc: Location?) -> YieldNode + + # Retrieve the value of one of the ArgumentsNodeFlags flags. + # -- + # : (Symbol name) -> Integer + def arguments_node_flag: (Symbol name) -> Integer + + # Retrieve the value of one of the ArrayNodeFlags flags. + # -- + # : (Symbol name) -> Integer + def array_node_flag: (Symbol name) -> Integer + + # Retrieve the value of one of the CallNodeFlags flags. + # -- + # : (Symbol name) -> Integer + def call_node_flag: (Symbol name) -> Integer + + # Retrieve the value of one of the EncodingFlags flags. + # -- + # : (Symbol name) -> Integer + def encoding_flag: (Symbol name) -> Integer + + # Retrieve the value of one of the IntegerBaseFlags flags. + # -- + # : (Symbol name) -> Integer + def integer_base_flag: (Symbol name) -> Integer + + # Retrieve the value of one of the InterpolatedStringNodeFlags flags. + # -- + # : (Symbol name) -> Integer + def interpolated_string_node_flag: (Symbol name) -> Integer + + # Retrieve the value of one of the KeywordHashNodeFlags flags. + # -- + # : (Symbol name) -> Integer + def keyword_hash_node_flag: (Symbol name) -> Integer + + # Retrieve the value of one of the LoopFlags flags. + # -- + # : (Symbol name) -> Integer + def loop_flag: (Symbol name) -> Integer + + # Retrieve the value of one of the ParameterFlags flags. + # -- + # : (Symbol name) -> Integer + def parameter_flag: (Symbol name) -> Integer + + # Retrieve the value of one of the ParenthesesNodeFlags flags. + # -- + # : (Symbol name) -> Integer + def parentheses_node_flag: (Symbol name) -> Integer + + # Retrieve the value of one of the RangeFlags flags. + # -- + # : (Symbol name) -> Integer + def range_flag: (Symbol name) -> Integer + + # Retrieve the value of one of the RegularExpressionFlags flags. + # -- + # : (Symbol name) -> Integer + def regular_expression_flag: (Symbol name) -> Integer + + # Retrieve the value of one of the ShareableConstantNodeFlags flags. + # -- + # : (Symbol name) -> Integer + def shareable_constant_node_flag: (Symbol name) -> Integer + + # Retrieve the value of one of the StringFlags flags. + # -- + # : (Symbol name) -> Integer + def string_flag: (Symbol name) -> Integer + + # Retrieve the value of one of the SymbolFlags flags. + # -- + # : (Symbol name) -> Integer + def symbol_flag: (Symbol name) -> Integer + + private + + # The default source object that gets attached to nodes and locations if no + # source is specified. + # -- + # : () -> Source + def default_source: () -> Source + + # The default location object that gets attached to nodes if no location is + # specified, which uses the given source. + # -- + # : () -> Location + def default_location: () -> Location + + # The default node that gets attached to nodes if no node is specified for a + # required node field. + # -- + # : (Source source, Location location) -> node + def default_node: (Source source, Location location) -> node + + private + + # Build the newline byte offset array for the given source string. + # -- + # : (String source) -> Array[Integer] + def build_offsets: (String source) -> Array[Integer] + end +end diff --git a/sig/generated/prism/inspect_visitor.rbs b/sig/generated/prism/inspect_visitor.rbs new file mode 100644 index 0000000000..1219427ceb --- /dev/null +++ b/sig/generated/prism/inspect_visitor.rbs @@ -0,0 +1,509 @@ +# Generated from lib/prism/inspect_visitor.rb with RBS::Inline + +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: String + + # : (String value) -> void + def initialize: (String value) -> void + end + + # The current prefix string. + # :stopdoc: + attr_reader indent: String + + # The list of commands that we need to execute in order to compose the + # final string. + # : stopdoc: + attr_reader commands: Array[[ String | node | Replace, String ]] + + # : (?String indent) -> void + def initialize: (?String indent) -> void + + # Compose an inspect string for the given node. + # -- + # : (node node) -> String + def self.compose: (node node) -> String + + # Compose the final string. + # -- + # : () -> String + def compose: () -> String + + # : (AliasGlobalVariableNode node) -> void + def visit_alias_global_variable_node: (AliasGlobalVariableNode node) -> void + + # : (AliasMethodNode node) -> void + def visit_alias_method_node: (AliasMethodNode node) -> void + + # : (AlternationPatternNode node) -> void + def visit_alternation_pattern_node: (AlternationPatternNode node) -> void + + # : (AndNode node) -> void + def visit_and_node: (AndNode node) -> void + + # : (ArgumentsNode node) -> void + def visit_arguments_node: (ArgumentsNode node) -> void + + # : (ArrayNode node) -> void + def visit_array_node: (ArrayNode node) -> void + + # : (ArrayPatternNode node) -> void + def visit_array_pattern_node: (ArrayPatternNode node) -> void + + # : (AssocNode node) -> void + def visit_assoc_node: (AssocNode node) -> void + + # : (AssocSplatNode node) -> void + def visit_assoc_splat_node: (AssocSplatNode node) -> void + + # : (BackReferenceReadNode node) -> void + def visit_back_reference_read_node: (BackReferenceReadNode node) -> void + + # : (BeginNode node) -> void + def visit_begin_node: (BeginNode node) -> void + + # : (BlockArgumentNode node) -> void + def visit_block_argument_node: (BlockArgumentNode node) -> void + + # : (BlockLocalVariableNode node) -> void + def visit_block_local_variable_node: (BlockLocalVariableNode node) -> void + + # : (BlockNode node) -> void + def visit_block_node: (BlockNode node) -> void + + # : (BlockParameterNode node) -> void + def visit_block_parameter_node: (BlockParameterNode node) -> void + + # : (BlockParametersNode node) -> void + def visit_block_parameters_node: (BlockParametersNode node) -> void + + # : (BreakNode node) -> void + def visit_break_node: (BreakNode node) -> void + + # : (CallAndWriteNode node) -> void + def visit_call_and_write_node: (CallAndWriteNode node) -> void + + # : (CallNode node) -> void + def visit_call_node: (CallNode node) -> void + + # : (CallOperatorWriteNode node) -> void + def visit_call_operator_write_node: (CallOperatorWriteNode node) -> void + + # : (CallOrWriteNode node) -> void + def visit_call_or_write_node: (CallOrWriteNode node) -> void + + # : (CallTargetNode node) -> void + def visit_call_target_node: (CallTargetNode node) -> void + + # : (CapturePatternNode node) -> void + def visit_capture_pattern_node: (CapturePatternNode node) -> void + + # : (CaseMatchNode node) -> void + def visit_case_match_node: (CaseMatchNode node) -> void + + # : (CaseNode node) -> void + def visit_case_node: (CaseNode node) -> void + + # : (ClassNode node) -> void + def visit_class_node: (ClassNode node) -> void + + # : (ClassVariableAndWriteNode node) -> void + def visit_class_variable_and_write_node: (ClassVariableAndWriteNode node) -> void + + # : (ClassVariableOperatorWriteNode node) -> void + def visit_class_variable_operator_write_node: (ClassVariableOperatorWriteNode node) -> void + + # : (ClassVariableOrWriteNode node) -> void + def visit_class_variable_or_write_node: (ClassVariableOrWriteNode node) -> void + + # : (ClassVariableReadNode node) -> void + def visit_class_variable_read_node: (ClassVariableReadNode node) -> void + + # : (ClassVariableTargetNode node) -> void + def visit_class_variable_target_node: (ClassVariableTargetNode node) -> void + + # : (ClassVariableWriteNode node) -> void + def visit_class_variable_write_node: (ClassVariableWriteNode node) -> void + + # : (ConstantAndWriteNode node) -> void + def visit_constant_and_write_node: (ConstantAndWriteNode node) -> void + + # : (ConstantOperatorWriteNode node) -> void + def visit_constant_operator_write_node: (ConstantOperatorWriteNode node) -> void + + # : (ConstantOrWriteNode node) -> void + def visit_constant_or_write_node: (ConstantOrWriteNode node) -> void + + # : (ConstantPathAndWriteNode node) -> void + def visit_constant_path_and_write_node: (ConstantPathAndWriteNode node) -> void + + # : (ConstantPathNode node) -> void + def visit_constant_path_node: (ConstantPathNode node) -> void + + # : (ConstantPathOperatorWriteNode node) -> void + def visit_constant_path_operator_write_node: (ConstantPathOperatorWriteNode node) -> void + + # : (ConstantPathOrWriteNode node) -> void + def visit_constant_path_or_write_node: (ConstantPathOrWriteNode node) -> void + + # : (ConstantPathTargetNode node) -> void + def visit_constant_path_target_node: (ConstantPathTargetNode node) -> void + + # : (ConstantPathWriteNode node) -> void + def visit_constant_path_write_node: (ConstantPathWriteNode node) -> void + + # : (ConstantReadNode node) -> void + def visit_constant_read_node: (ConstantReadNode node) -> void + + # : (ConstantTargetNode node) -> void + def visit_constant_target_node: (ConstantTargetNode node) -> void + + # : (ConstantWriteNode node) -> void + def visit_constant_write_node: (ConstantWriteNode node) -> void + + # : (DefNode node) -> void + def visit_def_node: (DefNode node) -> void + + # : (DefinedNode node) -> void + def visit_defined_node: (DefinedNode node) -> void + + # : (ElseNode node) -> void + def visit_else_node: (ElseNode node) -> void + + # : (EmbeddedStatementsNode node) -> void + def visit_embedded_statements_node: (EmbeddedStatementsNode node) -> void + + # : (EmbeddedVariableNode node) -> void + def visit_embedded_variable_node: (EmbeddedVariableNode node) -> void + + # : (EnsureNode node) -> void + def visit_ensure_node: (EnsureNode node) -> void + + # : (FalseNode node) -> void + def visit_false_node: (FalseNode node) -> void + + # : (FindPatternNode node) -> void + def visit_find_pattern_node: (FindPatternNode node) -> void + + # : (FlipFlopNode node) -> void + def visit_flip_flop_node: (FlipFlopNode node) -> void + + # : (FloatNode node) -> void + def visit_float_node: (FloatNode node) -> void + + # : (ForNode node) -> void + def visit_for_node: (ForNode node) -> void + + # : (ForwardingArgumentsNode node) -> void + def visit_forwarding_arguments_node: (ForwardingArgumentsNode node) -> void + + # : (ForwardingParameterNode node) -> void + def visit_forwarding_parameter_node: (ForwardingParameterNode node) -> void + + # : (ForwardingSuperNode node) -> void + def visit_forwarding_super_node: (ForwardingSuperNode node) -> void + + # : (GlobalVariableAndWriteNode node) -> void + def visit_global_variable_and_write_node: (GlobalVariableAndWriteNode node) -> void + + # : (GlobalVariableOperatorWriteNode node) -> void + def visit_global_variable_operator_write_node: (GlobalVariableOperatorWriteNode node) -> void + + # : (GlobalVariableOrWriteNode node) -> void + def visit_global_variable_or_write_node: (GlobalVariableOrWriteNode node) -> void + + # : (GlobalVariableReadNode node) -> void + def visit_global_variable_read_node: (GlobalVariableReadNode node) -> void + + # : (GlobalVariableTargetNode node) -> void + def visit_global_variable_target_node: (GlobalVariableTargetNode node) -> void + + # : (GlobalVariableWriteNode node) -> void + def visit_global_variable_write_node: (GlobalVariableWriteNode node) -> void + + # : (HashNode node) -> void + def visit_hash_node: (HashNode node) -> void + + # : (HashPatternNode node) -> void + def visit_hash_pattern_node: (HashPatternNode node) -> void + + # : (IfNode node) -> void + def visit_if_node: (IfNode node) -> void + + # : (ImaginaryNode node) -> void + def visit_imaginary_node: (ImaginaryNode node) -> void + + # : (ImplicitNode node) -> void + def visit_implicit_node: (ImplicitNode node) -> void + + # : (ImplicitRestNode node) -> void + def visit_implicit_rest_node: (ImplicitRestNode node) -> void + + # : (InNode node) -> void + def visit_in_node: (InNode node) -> void + + # : (IndexAndWriteNode node) -> void + def visit_index_and_write_node: (IndexAndWriteNode node) -> void + + # : (IndexOperatorWriteNode node) -> void + def visit_index_operator_write_node: (IndexOperatorWriteNode node) -> void + + # : (IndexOrWriteNode node) -> void + def visit_index_or_write_node: (IndexOrWriteNode node) -> void + + # : (IndexTargetNode node) -> void + def visit_index_target_node: (IndexTargetNode node) -> void + + # : (InstanceVariableAndWriteNode node) -> void + def visit_instance_variable_and_write_node: (InstanceVariableAndWriteNode node) -> void + + # : (InstanceVariableOperatorWriteNode node) -> void + def visit_instance_variable_operator_write_node: (InstanceVariableOperatorWriteNode node) -> void + + # : (InstanceVariableOrWriteNode node) -> void + def visit_instance_variable_or_write_node: (InstanceVariableOrWriteNode node) -> void + + # : (InstanceVariableReadNode node) -> void + def visit_instance_variable_read_node: (InstanceVariableReadNode node) -> void + + # : (InstanceVariableTargetNode node) -> void + def visit_instance_variable_target_node: (InstanceVariableTargetNode node) -> void + + # : (InstanceVariableWriteNode node) -> void + def visit_instance_variable_write_node: (InstanceVariableWriteNode node) -> void + + # : (IntegerNode node) -> void + def visit_integer_node: (IntegerNode node) -> void + + # : (InterpolatedMatchLastLineNode node) -> void + def visit_interpolated_match_last_line_node: (InterpolatedMatchLastLineNode node) -> void + + # : (InterpolatedRegularExpressionNode node) -> void + def visit_interpolated_regular_expression_node: (InterpolatedRegularExpressionNode node) -> void + + # : (InterpolatedStringNode node) -> void + def visit_interpolated_string_node: (InterpolatedStringNode node) -> void + + # : (InterpolatedSymbolNode node) -> void + def visit_interpolated_symbol_node: (InterpolatedSymbolNode node) -> void + + # : (InterpolatedXStringNode node) -> void + def visit_interpolated_x_string_node: (InterpolatedXStringNode node) -> void + + # : (ItLocalVariableReadNode node) -> void + def visit_it_local_variable_read_node: (ItLocalVariableReadNode node) -> void + + # : (ItParametersNode node) -> void + def visit_it_parameters_node: (ItParametersNode node) -> void + + # : (KeywordHashNode node) -> void + def visit_keyword_hash_node: (KeywordHashNode node) -> void + + # : (KeywordRestParameterNode node) -> void + def visit_keyword_rest_parameter_node: (KeywordRestParameterNode node) -> void + + # : (LambdaNode node) -> void + def visit_lambda_node: (LambdaNode node) -> void + + # : (LocalVariableAndWriteNode node) -> void + def visit_local_variable_and_write_node: (LocalVariableAndWriteNode node) -> void + + # : (LocalVariableOperatorWriteNode node) -> void + def visit_local_variable_operator_write_node: (LocalVariableOperatorWriteNode node) -> void + + # : (LocalVariableOrWriteNode node) -> void + def visit_local_variable_or_write_node: (LocalVariableOrWriteNode node) -> void + + # : (LocalVariableReadNode node) -> void + def visit_local_variable_read_node: (LocalVariableReadNode node) -> void + + # : (LocalVariableTargetNode node) -> void + def visit_local_variable_target_node: (LocalVariableTargetNode node) -> void + + # : (LocalVariableWriteNode node) -> void + def visit_local_variable_write_node: (LocalVariableWriteNode node) -> void + + # : (MatchLastLineNode node) -> void + def visit_match_last_line_node: (MatchLastLineNode node) -> void + + # : (MatchPredicateNode node) -> void + def visit_match_predicate_node: (MatchPredicateNode node) -> void + + # : (MatchRequiredNode node) -> void + def visit_match_required_node: (MatchRequiredNode node) -> void + + # : (MatchWriteNode node) -> void + def visit_match_write_node: (MatchWriteNode node) -> void + + # : (MissingNode node) -> void + def visit_missing_node: (MissingNode node) -> void + + # : (ModuleNode node) -> void + def visit_module_node: (ModuleNode node) -> void + + # : (MultiTargetNode node) -> void + def visit_multi_target_node: (MultiTargetNode node) -> void + + # : (MultiWriteNode node) -> void + def visit_multi_write_node: (MultiWriteNode node) -> void + + # : (NextNode node) -> void + def visit_next_node: (NextNode node) -> void + + # : (NilNode node) -> void + def visit_nil_node: (NilNode node) -> void + + # : (NoBlockParameterNode node) -> void + def visit_no_block_parameter_node: (NoBlockParameterNode node) -> void + + # : (NoKeywordsParameterNode node) -> void + def visit_no_keywords_parameter_node: (NoKeywordsParameterNode node) -> void + + # : (NumberedParametersNode node) -> void + def visit_numbered_parameters_node: (NumberedParametersNode node) -> void + + # : (NumberedReferenceReadNode node) -> void + def visit_numbered_reference_read_node: (NumberedReferenceReadNode node) -> void + + # : (OptionalKeywordParameterNode node) -> void + def visit_optional_keyword_parameter_node: (OptionalKeywordParameterNode node) -> void + + # : (OptionalParameterNode node) -> void + def visit_optional_parameter_node: (OptionalParameterNode node) -> void + + # : (OrNode node) -> void + def visit_or_node: (OrNode node) -> void + + # : (ParametersNode node) -> void + def visit_parameters_node: (ParametersNode node) -> void + + # : (ParenthesesNode node) -> void + def visit_parentheses_node: (ParenthesesNode node) -> void + + # : (PinnedExpressionNode node) -> void + def visit_pinned_expression_node: (PinnedExpressionNode node) -> void + + # : (PinnedVariableNode node) -> void + def visit_pinned_variable_node: (PinnedVariableNode node) -> void + + # : (PostExecutionNode node) -> void + def visit_post_execution_node: (PostExecutionNode node) -> void + + # : (PreExecutionNode node) -> void + def visit_pre_execution_node: (PreExecutionNode node) -> void + + # : (ProgramNode node) -> void + def visit_program_node: (ProgramNode node) -> void + + # : (RangeNode node) -> void + def visit_range_node: (RangeNode node) -> void + + # : (RationalNode node) -> void + def visit_rational_node: (RationalNode node) -> void + + # : (RedoNode node) -> void + def visit_redo_node: (RedoNode node) -> void + + # : (RegularExpressionNode node) -> void + def visit_regular_expression_node: (RegularExpressionNode node) -> void + + # : (RequiredKeywordParameterNode node) -> void + def visit_required_keyword_parameter_node: (RequiredKeywordParameterNode node) -> void + + # : (RequiredParameterNode node) -> void + def visit_required_parameter_node: (RequiredParameterNode node) -> void + + # : (RescueModifierNode node) -> void + def visit_rescue_modifier_node: (RescueModifierNode node) -> void + + # : (RescueNode node) -> void + def visit_rescue_node: (RescueNode node) -> void + + # : (RestParameterNode node) -> void + def visit_rest_parameter_node: (RestParameterNode node) -> void + + # : (RetryNode node) -> void + def visit_retry_node: (RetryNode node) -> void + + # : (ReturnNode node) -> void + def visit_return_node: (ReturnNode node) -> void + + # : (SelfNode node) -> void + def visit_self_node: (SelfNode node) -> void + + # : (ShareableConstantNode node) -> void + def visit_shareable_constant_node: (ShareableConstantNode node) -> void + + # : (SingletonClassNode node) -> void + def visit_singleton_class_node: (SingletonClassNode node) -> void + + # : (SourceEncodingNode node) -> void + def visit_source_encoding_node: (SourceEncodingNode node) -> void + + # : (SourceFileNode node) -> void + def visit_source_file_node: (SourceFileNode node) -> void + + # : (SourceLineNode node) -> void + def visit_source_line_node: (SourceLineNode node) -> void + + # : (SplatNode node) -> void + def visit_splat_node: (SplatNode node) -> void + + # : (StatementsNode node) -> void + def visit_statements_node: (StatementsNode node) -> void + + # : (StringNode node) -> void + def visit_string_node: (StringNode node) -> void + + # : (SuperNode node) -> void + def visit_super_node: (SuperNode node) -> void + + # : (SymbolNode node) -> void + def visit_symbol_node: (SymbolNode node) -> void + + # : (TrueNode node) -> void + def visit_true_node: (TrueNode node) -> void + + # : (UndefNode node) -> void + def visit_undef_node: (UndefNode node) -> void + + # : (UnlessNode node) -> void + def visit_unless_node: (UnlessNode node) -> void + + # : (UntilNode node) -> void + def visit_until_node: (UntilNode node) -> void + + # : (WhenNode node) -> void + def visit_when_node: (WhenNode node) -> void + + # : (WhileNode node) -> void + def visit_while_node: (WhileNode node) -> void + + # : (XStringNode node) -> void + def visit_x_string_node: (XStringNode node) -> void + + # : (YieldNode node) -> void + def visit_yield_node: (YieldNode node) -> void + + private + + # Compose a header for the given node. + # -- + # : (String name, node node) -> String + def inspect_node: (String name, node node) -> String + + # Compose a string representing the given inner location field. + # -- + # : (Location? location) -> String + def inspect_location: (Location? location) -> String + end +end diff --git a/sig/generated/prism/lex_compat.rbs b/sig/generated/prism/lex_compat.rbs new file mode 100644 index 0000000000..707a96b9a8 --- /dev/null +++ b/sig/generated/prism/lex_compat.rbs @@ -0,0 +1,160 @@ +# Generated from lib/prism/lex_compat.rb with RBS::Inline + +module Prism + module Translation + class Ripper + EXPR_NONE: Integer + EXPR_BEG: Integer + EXPR_MID: Integer + EXPR_END: Integer + EXPR_CLASS: Integer + EXPR_VALUE: Integer + EXPR_ARG: Integer + EXPR_CMDARG: Integer + EXPR_ENDARG: Integer + EXPR_ENDFN: Integer + + class Lexer < Ripper + class State + def self.[]: (Integer value) -> State + end + end + end + end + + # 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 + # A token produced by the Ripper lexer that Prism is replicating. + type lex_compat_token = [ [ Integer, Integer ], Symbol, String, untyped ] + + # 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: Array[lex_compat_token] + + # Create a new lex compat result object with the given values. + # -- + # : (Array[lex_compat_token] value, Array[Comment] comments, Array[MagicComment] magic_comments, Location? data_loc, Array[ParseError] errors, Array[ParseWarning] warnings, bool continuable, Source source) -> void + def initialize: (Array[lex_compat_token] value, Array[Comment] comments, Array[MagicComment] magic_comments, Location? data_loc, Array[ParseError] errors, Array[ParseWarning] warnings, bool continuable, Source source) -> void + + # Implement the hash pattern matching interface for Result. + # -- + # : (Array[Symbol]? keys) -> Hash[Symbol, untyped] + def deconstruct_keys: (Array[Symbol]? keys) -> Hash[Symbol, untyped] + 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: untyped + + # 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 + # 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: Array[lex_compat_token] + + # : () -> void + def initialize: () -> void + + # : (lex_compat_token token) -> void + def <<: (lex_compat_token token) -> void + + # : () -> Array[lex_compat_token] + def to_a: () -> Array[lex_compat_token] + 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: bool + + attr_reader tokens: Array[lex_compat_token] + + # : (bool split) -> void + def initialize: (bool split) -> void + + # : (lex_compat_token token) -> void + def <<: (lex_compat_token token) -> void + + # : () -> Array[lex_compat_token] + def to_a: () -> Array[lex_compat_token] + 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: ::Integer + + attr_reader tokens: Array[lex_compat_token] + + attr_reader dedent_next: bool + + attr_reader dedent: Integer? + + attr_reader embexpr_balance: Integer + + @ended_on_newline: bool + + # : () -> void + def initialize: () -> void + + # 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. + # + # : (lex_compat_token token) -> void + def <<: (lex_compat_token token) -> void + + # : () -> Array[lex_compat_token] + def to_a: () -> Array[lex_compat_token] + end + + # Here we will split between the two types of heredocs and return the + # object that will store their tokens. + # -- + # : (lex_compat_token opening) -> (PlainHeredoc | DashHeredoc | DedentingHeredoc) + def self.build: (lex_compat_token opening) -> (PlainHeredoc | DashHeredoc | DedentingHeredoc) + end + + # 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: untyped + + attr_reader options: Hash[Symbol, untyped] + + @source: String + + # : (String source, **untyped options) -> void + def initialize: (String source, **untyped options) -> void + + # : () -> Result + def result: () -> Result + + private + + # : (Array[lex_compat_token] tokens, Source source, Location? data_loc, bool bom, Token? eof_token) -> Array[lex_compat_token] + def post_process_tokens: (Array[lex_compat_token] tokens, Source source, Location? data_loc, bool bom, Token? eof_token) -> Array[lex_compat_token] + end +end diff --git a/sig/generated/prism/mutation_compiler.rbs b/sig/generated/prism/mutation_compiler.rbs new file mode 100644 index 0000000000..e79f254372 --- /dev/null +++ b/sig/generated/prism/mutation_compiler.rbs @@ -0,0 +1,464 @@ +# Generated from lib/prism/mutation_compiler.rb with RBS::Inline + +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 + # : (AliasGlobalVariableNode) -> node? + def visit_alias_global_variable_node: (AliasGlobalVariableNode) -> node? + + # : (AliasMethodNode) -> node? + def visit_alias_method_node: (AliasMethodNode) -> node? + + # : (AlternationPatternNode) -> node? + def visit_alternation_pattern_node: (AlternationPatternNode) -> node? + + # : (AndNode) -> node? + def visit_and_node: (AndNode) -> node? + + # : (ArgumentsNode) -> node? + def visit_arguments_node: (ArgumentsNode) -> node? + + # : (ArrayNode) -> node? + def visit_array_node: (ArrayNode) -> node? + + # : (ArrayPatternNode) -> node? + def visit_array_pattern_node: (ArrayPatternNode) -> node? + + # : (AssocNode) -> node? + def visit_assoc_node: (AssocNode) -> node? + + # : (AssocSplatNode) -> node? + def visit_assoc_splat_node: (AssocSplatNode) -> node? + + # : (BackReferenceReadNode) -> node? + def visit_back_reference_read_node: (BackReferenceReadNode) -> node? + + # : (BeginNode) -> node? + def visit_begin_node: (BeginNode) -> node? + + # : (BlockArgumentNode) -> node? + def visit_block_argument_node: (BlockArgumentNode) -> node? + + # : (BlockLocalVariableNode) -> node? + def visit_block_local_variable_node: (BlockLocalVariableNode) -> node? + + # : (BlockNode) -> node? + def visit_block_node: (BlockNode) -> node? + + # : (BlockParameterNode) -> node? + def visit_block_parameter_node: (BlockParameterNode) -> node? + + # : (BlockParametersNode) -> node? + def visit_block_parameters_node: (BlockParametersNode) -> node? + + # : (BreakNode) -> node? + def visit_break_node: (BreakNode) -> node? + + # : (CallAndWriteNode) -> node? + def visit_call_and_write_node: (CallAndWriteNode) -> node? + + # : (CallNode) -> node? + def visit_call_node: (CallNode) -> node? + + # : (CallOperatorWriteNode) -> node? + def visit_call_operator_write_node: (CallOperatorWriteNode) -> node? + + # : (CallOrWriteNode) -> node? + def visit_call_or_write_node: (CallOrWriteNode) -> node? + + # : (CallTargetNode) -> node? + def visit_call_target_node: (CallTargetNode) -> node? + + # : (CapturePatternNode) -> node? + def visit_capture_pattern_node: (CapturePatternNode) -> node? + + # : (CaseMatchNode) -> node? + def visit_case_match_node: (CaseMatchNode) -> node? + + # : (CaseNode) -> node? + def visit_case_node: (CaseNode) -> node? + + # : (ClassNode) -> node? + def visit_class_node: (ClassNode) -> node? + + # : (ClassVariableAndWriteNode) -> node? + def visit_class_variable_and_write_node: (ClassVariableAndWriteNode) -> node? + + # : (ClassVariableOperatorWriteNode) -> node? + def visit_class_variable_operator_write_node: (ClassVariableOperatorWriteNode) -> node? + + # : (ClassVariableOrWriteNode) -> node? + def visit_class_variable_or_write_node: (ClassVariableOrWriteNode) -> node? + + # : (ClassVariableReadNode) -> node? + def visit_class_variable_read_node: (ClassVariableReadNode) -> node? + + # : (ClassVariableTargetNode) -> node? + def visit_class_variable_target_node: (ClassVariableTargetNode) -> node? + + # : (ClassVariableWriteNode) -> node? + def visit_class_variable_write_node: (ClassVariableWriteNode) -> node? + + # : (ConstantAndWriteNode) -> node? + def visit_constant_and_write_node: (ConstantAndWriteNode) -> node? + + # : (ConstantOperatorWriteNode) -> node? + def visit_constant_operator_write_node: (ConstantOperatorWriteNode) -> node? + + # : (ConstantOrWriteNode) -> node? + def visit_constant_or_write_node: (ConstantOrWriteNode) -> node? + + # : (ConstantPathAndWriteNode) -> node? + def visit_constant_path_and_write_node: (ConstantPathAndWriteNode) -> node? + + # : (ConstantPathNode) -> node? + def visit_constant_path_node: (ConstantPathNode) -> node? + + # : (ConstantPathOperatorWriteNode) -> node? + def visit_constant_path_operator_write_node: (ConstantPathOperatorWriteNode) -> node? + + # : (ConstantPathOrWriteNode) -> node? + def visit_constant_path_or_write_node: (ConstantPathOrWriteNode) -> node? + + # : (ConstantPathTargetNode) -> node? + def visit_constant_path_target_node: (ConstantPathTargetNode) -> node? + + # : (ConstantPathWriteNode) -> node? + def visit_constant_path_write_node: (ConstantPathWriteNode) -> node? + + # : (ConstantReadNode) -> node? + def visit_constant_read_node: (ConstantReadNode) -> node? + + # : (ConstantTargetNode) -> node? + def visit_constant_target_node: (ConstantTargetNode) -> node? + + # : (ConstantWriteNode) -> node? + def visit_constant_write_node: (ConstantWriteNode) -> node? + + # : (DefNode) -> node? + def visit_def_node: (DefNode) -> node? + + # : (DefinedNode) -> node? + def visit_defined_node: (DefinedNode) -> node? + + # : (ElseNode) -> node? + def visit_else_node: (ElseNode) -> node? + + # : (EmbeddedStatementsNode) -> node? + def visit_embedded_statements_node: (EmbeddedStatementsNode) -> node? + + # : (EmbeddedVariableNode) -> node? + def visit_embedded_variable_node: (EmbeddedVariableNode) -> node? + + # : (EnsureNode) -> node? + def visit_ensure_node: (EnsureNode) -> node? + + # : (FalseNode) -> node? + def visit_false_node: (FalseNode) -> node? + + # : (FindPatternNode) -> node? + def visit_find_pattern_node: (FindPatternNode) -> node? + + # : (FlipFlopNode) -> node? + def visit_flip_flop_node: (FlipFlopNode) -> node? + + # : (FloatNode) -> node? + def visit_float_node: (FloatNode) -> node? + + # : (ForNode) -> node? + def visit_for_node: (ForNode) -> node? + + # : (ForwardingArgumentsNode) -> node? + def visit_forwarding_arguments_node: (ForwardingArgumentsNode) -> node? + + # : (ForwardingParameterNode) -> node? + def visit_forwarding_parameter_node: (ForwardingParameterNode) -> node? + + # : (ForwardingSuperNode) -> node? + def visit_forwarding_super_node: (ForwardingSuperNode) -> node? + + # : (GlobalVariableAndWriteNode) -> node? + def visit_global_variable_and_write_node: (GlobalVariableAndWriteNode) -> node? + + # : (GlobalVariableOperatorWriteNode) -> node? + def visit_global_variable_operator_write_node: (GlobalVariableOperatorWriteNode) -> node? + + # : (GlobalVariableOrWriteNode) -> node? + def visit_global_variable_or_write_node: (GlobalVariableOrWriteNode) -> node? + + # : (GlobalVariableReadNode) -> node? + def visit_global_variable_read_node: (GlobalVariableReadNode) -> node? + + # : (GlobalVariableTargetNode) -> node? + def visit_global_variable_target_node: (GlobalVariableTargetNode) -> node? + + # : (GlobalVariableWriteNode) -> node? + def visit_global_variable_write_node: (GlobalVariableWriteNode) -> node? + + # : (HashNode) -> node? + def visit_hash_node: (HashNode) -> node? + + # : (HashPatternNode) -> node? + def visit_hash_pattern_node: (HashPatternNode) -> node? + + # : (IfNode) -> node? + def visit_if_node: (IfNode) -> node? + + # : (ImaginaryNode) -> node? + def visit_imaginary_node: (ImaginaryNode) -> node? + + # : (ImplicitNode) -> node? + def visit_implicit_node: (ImplicitNode) -> node? + + # : (ImplicitRestNode) -> node? + def visit_implicit_rest_node: (ImplicitRestNode) -> node? + + # : (InNode) -> node? + def visit_in_node: (InNode) -> node? + + # : (IndexAndWriteNode) -> node? + def visit_index_and_write_node: (IndexAndWriteNode) -> node? + + # : (IndexOperatorWriteNode) -> node? + def visit_index_operator_write_node: (IndexOperatorWriteNode) -> node? + + # : (IndexOrWriteNode) -> node? + def visit_index_or_write_node: (IndexOrWriteNode) -> node? + + # : (IndexTargetNode) -> node? + def visit_index_target_node: (IndexTargetNode) -> node? + + # : (InstanceVariableAndWriteNode) -> node? + def visit_instance_variable_and_write_node: (InstanceVariableAndWriteNode) -> node? + + # : (InstanceVariableOperatorWriteNode) -> node? + def visit_instance_variable_operator_write_node: (InstanceVariableOperatorWriteNode) -> node? + + # : (InstanceVariableOrWriteNode) -> node? + def visit_instance_variable_or_write_node: (InstanceVariableOrWriteNode) -> node? + + # : (InstanceVariableReadNode) -> node? + def visit_instance_variable_read_node: (InstanceVariableReadNode) -> node? + + # : (InstanceVariableTargetNode) -> node? + def visit_instance_variable_target_node: (InstanceVariableTargetNode) -> node? + + # : (InstanceVariableWriteNode) -> node? + def visit_instance_variable_write_node: (InstanceVariableWriteNode) -> node? + + # : (IntegerNode) -> node? + def visit_integer_node: (IntegerNode) -> node? + + # : (InterpolatedMatchLastLineNode) -> node? + def visit_interpolated_match_last_line_node: (InterpolatedMatchLastLineNode) -> node? + + # : (InterpolatedRegularExpressionNode) -> node? + def visit_interpolated_regular_expression_node: (InterpolatedRegularExpressionNode) -> node? + + # : (InterpolatedStringNode) -> node? + def visit_interpolated_string_node: (InterpolatedStringNode) -> node? + + # : (InterpolatedSymbolNode) -> node? + def visit_interpolated_symbol_node: (InterpolatedSymbolNode) -> node? + + # : (InterpolatedXStringNode) -> node? + def visit_interpolated_x_string_node: (InterpolatedXStringNode) -> node? + + # : (ItLocalVariableReadNode) -> node? + def visit_it_local_variable_read_node: (ItLocalVariableReadNode) -> node? + + # : (ItParametersNode) -> node? + def visit_it_parameters_node: (ItParametersNode) -> node? + + # : (KeywordHashNode) -> node? + def visit_keyword_hash_node: (KeywordHashNode) -> node? + + # : (KeywordRestParameterNode) -> node? + def visit_keyword_rest_parameter_node: (KeywordRestParameterNode) -> node? + + # : (LambdaNode) -> node? + def visit_lambda_node: (LambdaNode) -> node? + + # : (LocalVariableAndWriteNode) -> node? + def visit_local_variable_and_write_node: (LocalVariableAndWriteNode) -> node? + + # : (LocalVariableOperatorWriteNode) -> node? + def visit_local_variable_operator_write_node: (LocalVariableOperatorWriteNode) -> node? + + # : (LocalVariableOrWriteNode) -> node? + def visit_local_variable_or_write_node: (LocalVariableOrWriteNode) -> node? + + # : (LocalVariableReadNode) -> node? + def visit_local_variable_read_node: (LocalVariableReadNode) -> node? + + # : (LocalVariableTargetNode) -> node? + def visit_local_variable_target_node: (LocalVariableTargetNode) -> node? + + # : (LocalVariableWriteNode) -> node? + def visit_local_variable_write_node: (LocalVariableWriteNode) -> node? + + # : (MatchLastLineNode) -> node? + def visit_match_last_line_node: (MatchLastLineNode) -> node? + + # : (MatchPredicateNode) -> node? + def visit_match_predicate_node: (MatchPredicateNode) -> node? + + # : (MatchRequiredNode) -> node? + def visit_match_required_node: (MatchRequiredNode) -> node? + + # : (MatchWriteNode) -> node? + def visit_match_write_node: (MatchWriteNode) -> node? + + # : (MissingNode) -> node? + def visit_missing_node: (MissingNode) -> node? + + # : (ModuleNode) -> node? + def visit_module_node: (ModuleNode) -> node? + + # : (MultiTargetNode) -> node? + def visit_multi_target_node: (MultiTargetNode) -> node? + + # : (MultiWriteNode) -> node? + def visit_multi_write_node: (MultiWriteNode) -> node? + + # : (NextNode) -> node? + def visit_next_node: (NextNode) -> node? + + # : (NilNode) -> node? + def visit_nil_node: (NilNode) -> node? + + # : (NoBlockParameterNode) -> node? + def visit_no_block_parameter_node: (NoBlockParameterNode) -> node? + + # : (NoKeywordsParameterNode) -> node? + def visit_no_keywords_parameter_node: (NoKeywordsParameterNode) -> node? + + # : (NumberedParametersNode) -> node? + def visit_numbered_parameters_node: (NumberedParametersNode) -> node? + + # : (NumberedReferenceReadNode) -> node? + def visit_numbered_reference_read_node: (NumberedReferenceReadNode) -> node? + + # : (OptionalKeywordParameterNode) -> node? + def visit_optional_keyword_parameter_node: (OptionalKeywordParameterNode) -> node? + + # : (OptionalParameterNode) -> node? + def visit_optional_parameter_node: (OptionalParameterNode) -> node? + + # : (OrNode) -> node? + def visit_or_node: (OrNode) -> node? + + # : (ParametersNode) -> node? + def visit_parameters_node: (ParametersNode) -> node? + + # : (ParenthesesNode) -> node? + def visit_parentheses_node: (ParenthesesNode) -> node? + + # : (PinnedExpressionNode) -> node? + def visit_pinned_expression_node: (PinnedExpressionNode) -> node? + + # : (PinnedVariableNode) -> node? + def visit_pinned_variable_node: (PinnedVariableNode) -> node? + + # : (PostExecutionNode) -> node? + def visit_post_execution_node: (PostExecutionNode) -> node? + + # : (PreExecutionNode) -> node? + def visit_pre_execution_node: (PreExecutionNode) -> node? + + # : (ProgramNode) -> node? + def visit_program_node: (ProgramNode) -> node? + + # : (RangeNode) -> node? + def visit_range_node: (RangeNode) -> node? + + # : (RationalNode) -> node? + def visit_rational_node: (RationalNode) -> node? + + # : (RedoNode) -> node? + def visit_redo_node: (RedoNode) -> node? + + # : (RegularExpressionNode) -> node? + def visit_regular_expression_node: (RegularExpressionNode) -> node? + + # : (RequiredKeywordParameterNode) -> node? + def visit_required_keyword_parameter_node: (RequiredKeywordParameterNode) -> node? + + # : (RequiredParameterNode) -> node? + def visit_required_parameter_node: (RequiredParameterNode) -> node? + + # : (RescueModifierNode) -> node? + def visit_rescue_modifier_node: (RescueModifierNode) -> node? + + # : (RescueNode) -> node? + def visit_rescue_node: (RescueNode) -> node? + + # : (RestParameterNode) -> node? + def visit_rest_parameter_node: (RestParameterNode) -> node? + + # : (RetryNode) -> node? + def visit_retry_node: (RetryNode) -> node? + + # : (ReturnNode) -> node? + def visit_return_node: (ReturnNode) -> node? + + # : (SelfNode) -> node? + def visit_self_node: (SelfNode) -> node? + + # : (ShareableConstantNode) -> node? + def visit_shareable_constant_node: (ShareableConstantNode) -> node? + + # : (SingletonClassNode) -> node? + def visit_singleton_class_node: (SingletonClassNode) -> node? + + # : (SourceEncodingNode) -> node? + def visit_source_encoding_node: (SourceEncodingNode) -> node? + + # : (SourceFileNode) -> node? + def visit_source_file_node: (SourceFileNode) -> node? + + # : (SourceLineNode) -> node? + def visit_source_line_node: (SourceLineNode) -> node? + + # : (SplatNode) -> node? + def visit_splat_node: (SplatNode) -> node? + + # : (StatementsNode) -> node? + def visit_statements_node: (StatementsNode) -> node? + + # : (StringNode) -> node? + def visit_string_node: (StringNode) -> node? + + # : (SuperNode) -> node? + def visit_super_node: (SuperNode) -> node? + + # : (SymbolNode) -> node? + def visit_symbol_node: (SymbolNode) -> node? + + # : (TrueNode) -> node? + def visit_true_node: (TrueNode) -> node? + + # : (UndefNode) -> node? + def visit_undef_node: (UndefNode) -> node? + + # : (UnlessNode) -> node? + def visit_unless_node: (UnlessNode) -> node? + + # : (UntilNode) -> node? + def visit_until_node: (UntilNode) -> node? + + # : (WhenNode) -> node? + def visit_when_node: (WhenNode) -> node? + + # : (WhileNode) -> node? + def visit_while_node: (WhileNode) -> node? + + # : (XStringNode) -> node? + def visit_x_string_node: (XStringNode) -> node? + + # : (YieldNode) -> node? + def visit_yield_node: (YieldNode) -> node? + end +end diff --git a/sig/generated/prism/node.rbs b/sig/generated/prism/node.rbs new file mode 100644 index 0000000000..94b8967bd4 --- /dev/null +++ b/sig/generated/prism/node.rbs @@ -0,0 +1,20895 @@ +# Generated from lib/prism/node.rb with RBS::Inline + +module Prism + interface _Repository + def enter: (Integer node_id, Symbol field_name) -> Relocation::Entry + end + + interface _Node + def deconstruct: () -> Array[Prism::node?] + + def inspect: () -> String + end + + type node = Node & _Node + + # 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. + # :stopdoc: + attr_reader source: 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: Integer + + @location: Location | Integer + + # Save this node using a saved source so that it can be retrieved later. + # -- + # : (_Repository repository) -> Relocation::Entry + def save: (_Repository repository) -> Relocation::Entry + + # A Location instance that represents the location of this node in the + # source. + # -- + # : () -> Location + def location: () -> Location + + # Save the location using a saved source so that it can be retrieved later. + # -- + # : (_Repository repository) -> Relocation::Entry + def save_location: (_Repository repository) -> Relocation::Entry + + # Delegates to [`start_line`](rdoc-ref:Location#start_line) of the associated location object. + # -- + # : () -> Integer + def start_line: () -> Integer + + # Delegates to [`end_line`](rdoc-ref:Location#end_line) of the associated location object. + # -- + # : () -> Integer + def end_line: () -> Integer + + # Delegates to [`start_offset`](rdoc-ref:Location#start_offset) of the associated location object. + # -- + # : () -> Integer + def start_offset: () -> Integer + + # Delegates to [`end_offset`](rdoc-ref:Location#end_offset) of the associated location object. + # -- + # : () -> Integer + def end_offset: () -> Integer + + # Delegates to [`start_character_offset`](rdoc-ref:Location#start_character_offset) + # of the associated location object. + # -- + # : () -> Integer + def start_character_offset: () -> Integer + + # Delegates to [`end_character_offset`](rdoc-ref:Location#end_character_offset) + # of the associated location object. + # -- + # : () -> Integer + def end_character_offset: () -> Integer + + # Delegates to [`cached_start_code_units_offset`](rdoc-ref:Location#cached_start_code_units_offset) + # of the associated location object. + # -- + # : (_CodeUnitsCache cache) -> Integer + def cached_start_code_units_offset: (_CodeUnitsCache cache) -> Integer + + # Delegates to [`cached_end_code_units_offset`](rdoc-ref:Location#cached_end_code_units_offset) + # of the associated location object. + # -- + # : (_CodeUnitsCache cache) -> Integer + def cached_end_code_units_offset: (_CodeUnitsCache cache) -> Integer + + # Delegates to [`start_column`](rdoc-ref:Location#start_column) of the associated location object. + # -- + # : () -> Integer + def start_column: () -> Integer + + # Delegates to [`end_column`](rdoc-ref:Location#end_column) of the associated location object. + # -- + # : () -> Integer + def end_column: () -> Integer + + # Delegates to [`start_character_column`](rdoc-ref:Location#start_character_column) + # of the associated location object. + # -- + # : () -> Integer + def start_character_column: () -> Integer + + # Delegates to [`end_character_column`](rdoc-ref:Location#end_character_column) + # of the associated location object. + # -- + # : () -> Integer + def end_character_column: () -> Integer + + # Delegates to [`cached_start_code_units_column`](rdoc-ref:Location#cached_start_code_units_column) + # of the associated location object. + # -- + # : (_CodeUnitsCache cache) -> Integer + def cached_start_code_units_column: (_CodeUnitsCache cache) -> Integer + + # Delegates to [`cached_end_code_units_column`](rdoc-ref:Location#cached_end_code_units_column) + # of the associated location object. + # -- + # : (_CodeUnitsCache cache) -> Integer + def cached_end_code_units_column: (_CodeUnitsCache cache) -> Integer + + # Delegates to [`leading_comments`](rdoc-ref:Location#leading_comments) of the associated location object. + # -- + # : () -> Array[Comment] + def leading_comments: () -> Array[Comment] + + # Delegates to [`trailing_comments`](rdoc-ref:Location#trailing_comments) of the associated location object. + # -- + # : () -> Array[Comment] + def trailing_comments: () -> Array[Comment] + + # Delegates to [`comments`](rdoc-ref:Location#comments) of the associated location object. + # -- + # : () -> Array[Comment] + def comments: () -> Array[Comment] + + # Returns all of the lines of the source code associated with this node. + # -- + # : () -> Array[String] + def source_lines: () -> Array[String] + + # 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. + # -- + # : () -> String + def slice: () -> String + + # 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. + # -- + # : () -> String + def slice_lines: () -> String + + # An bitset of flags for this node. There are certain flags that are common + # for all nodes, and then some nodes have specific flags. + # :stopdoc: + attr_reader flags: Integer + + # Returns true if the node has the newline flag set. + # -- + # : () -> bool + def newline?: () -> bool + + # Returns true if the node has the static literal flag set. + # -- + # : () -> bool + def static_literal?: () -> bool + + # Similar to inspect, but respects the current level of indentation given by + # the pretty print object. + # -- + # : (PP q) -> void + def pretty_print: (PP q) -> void + + # Convert this node into a graphviz dot graph string. + # -- + # : () -> String + def to_dot: () -> String + + # 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. + # -- + # : (Integer line, Integer column) -> Array[node] + def tunnel: (Integer line, Integer column) -> Array[node] + + # 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 } + # -- + # : () { (node) -> bool } -> node? + def breadth_first_search: () { (node) -> bool } -> node? + + 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) } + # -- + # : () { (node) -> bool } -> Array[node] + def breadth_first_search_all: () { (node) -> bool } -> Array[node] + + 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. + # -- + # : () -> Array[Reflection::Field] + def self.fields: () -> Array[Reflection::Field] + + # Accepts a visitor and calls back into the specialized visit function. + # -- + # : (_Visitor visitor) -> untyped + def accept: (_Visitor visitor) -> untyped + + # Returns an array of child nodes, including `nil`s in the place of optional + # nodes that were not present. + # -- + # : () -> Array[node?] + def child_nodes: () -> Array[node?] + + 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. + # -- + # : () -> Enumerator[node, void] + # : () { (node) -> void } -> void + def each_child_node: () -> Enumerator[node, void] + | () { (node) -> void } -> void + + # Returns an array of child nodes, excluding any `nil`s in the place of + # optional nodes that were not present. + # -- + # : () -> Array[node] + def compact_child_nodes: () -> Array[node] + + # Returns an array of child nodes and locations that could potentially have + # comments attached to them. + # -- + # : () -> Array[node | Location] + def comment_targets: () -> Array[node | Location] + + # Returns a string representation of the node. + # -- + # : () -> String + def inspect: () -> String + + # 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. + # -- + # : () -> Symbol + def type: () -> Symbol + + # 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. + # -- + # : () -> Symbol + def self.type: () -> Symbol + end + + # Represents the use of the `alias` keyword to alias a global variable. + # + # alias $foo $bar + # ^^^^^^^^^^^^^^^ + class AliasGlobalVariableNode < Node + @keyword_loc: Location + + @old_name: GlobalVariableReadNode | BackReferenceReadNode | NumberedReferenceReadNode | SymbolNode | MissingNode + + @new_name: GlobalVariableReadNode | BackReferenceReadNode | NumberedReferenceReadNode + + # Initialize a new AliasGlobalVariableNode node. + # -- + # : (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 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 + + # See Node.accept. + # -- + # : (_Visitor visitor) -> untyped + def accept: (_Visitor visitor) -> untyped + + # See Node.child_nodes. + # -- + # : () -> Array[node?] + def child_nodes: () -> Array[node?] + + # See Node.each_child_node. + # -- + # : () -> Enumerator[node, void] + # : () { (node) -> void } -> void + def each_child_node: () -> Enumerator[node, void] + | () { (node) -> void } -> void + + # See Node.compact_child_nodes. + # -- + # : () -> Array[node] + def compact_child_nodes: () -> Array[node] + + # See Node.comment_targets. + # -- + # : () -> Array[node | Location] + def comment_targets: () -> Array[node | Location] + + # :call-seq: + # copy(**fields) -> AliasGlobalVariableNode + # + # Creates a copy of self with the given fields, using self as the template. + # -- + # : (?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: Integer, ?location: Location, ?flags: Integer, ?new_name: GlobalVariableReadNode | BackReferenceReadNode | NumberedReferenceReadNode, ?old_name: GlobalVariableReadNode | BackReferenceReadNode | NumberedReferenceReadNode | SymbolNode | MissingNode, ?keyword_loc: Location) -> AliasGlobalVariableNode + + alias deconstruct child_nodes + + # : (Array[Symbol]? keys) -> Hash[Symbol, untyped] + def deconstruct_keys: (Array[Symbol]? keys) -> Hash[Symbol, untyped] + + # See `Node#type`. + # -- + # : () -> :alias_global_variable_node + def type: () -> :alias_global_variable_node + + # See `Node.type`. + # -- + # : () -> :alias_global_variable_node + def self.type: () -> :alias_global_variable_node + + # : () -> String + def inspect: () -> String + + # :call-seq: + # new_name -> GlobalVariableReadNode | BackReferenceReadNode | NumberedReferenceReadNode + # + # Represents the new name of the global variable that can be used after aliasing. + # + # alias $foo $bar + # ^^^^ + # -- + # : () -> (GlobalVariableReadNode | BackReferenceReadNode | NumberedReferenceReadNode) + def new_name: () -> (GlobalVariableReadNode | BackReferenceReadNode | NumberedReferenceReadNode) + + # :call-seq: + # old_name -> GlobalVariableReadNode | BackReferenceReadNode | NumberedReferenceReadNode | SymbolNode | MissingNode + # + # Represents the old name of the global variable that can be used before aliasing. + # + # alias $foo $bar + # ^^^^ + # -- + # : () -> (GlobalVariableReadNode | BackReferenceReadNode | NumberedReferenceReadNode | SymbolNode | MissingNode) + def old_name: () -> (GlobalVariableReadNode | BackReferenceReadNode | NumberedReferenceReadNode | SymbolNode | MissingNode) + + # :category: Locations + # :call-seq: + # keyword_loc -> Location + # + # The Location of the `alias` keyword. + # + # alias $foo $bar + # ^^^^^ + # -- + # : () -> Location + def keyword_loc: () -> Location + + # :category: Repository + # Save the keyword_loc location using the given saved source so that + # it can be retrieved later. + # -- + # : (_Repository repository) -> Relocation::Entry + def save_keyword_loc: (_Repository repository) -> Relocation::Entry + + # :call-seq: + # keyword -> String + # + # Slice the location of keyword_loc from the source. + # -- + # : () -> String + def keyword: () -> String + + # : (untyped other) -> boolish + def ===: (untyped other) -> boolish + end + + # Represents the use of the `alias` keyword to alias a method. + # + # alias foo bar + # ^^^^^^^^^^^^^ + class AliasMethodNode < Node + @keyword_loc: Location + + @old_name: SymbolNode | InterpolatedSymbolNode | GlobalVariableReadNode | MissingNode + + @new_name: SymbolNode | InterpolatedSymbolNode + + # Initialize a new AliasMethodNode node. + # -- + # : (Source source, Integer node_id, Location location, Integer flags, (SymbolNode | InterpolatedSymbolNode) new_name, (SymbolNode | InterpolatedSymbolNode | GlobalVariableReadNode | MissingNode) old_name, Location keyword_loc) -> 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 + + # See Node.accept. + # -- + # : (_Visitor visitor) -> untyped + def accept: (_Visitor visitor) -> untyped + + # See Node.child_nodes. + # -- + # : () -> Array[node?] + def child_nodes: () -> Array[node?] + + # See Node.each_child_node. + # -- + # : () -> Enumerator[node, void] + # : () { (node) -> void } -> void + def each_child_node: () -> Enumerator[node, void] + | () { (node) -> void } -> void + + # See Node.compact_child_nodes. + # -- + # : () -> Array[node] + def compact_child_nodes: () -> Array[node] + + # See Node.comment_targets. + # -- + # : () -> Array[node | Location] + def comment_targets: () -> Array[node | Location] + + # :call-seq: + # copy(**fields) -> AliasMethodNode + # + # Creates a copy of self with the given fields, using self as the template. + # -- + # : (?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: Integer, ?location: Location, ?flags: Integer, ?new_name: SymbolNode | InterpolatedSymbolNode, ?old_name: SymbolNode | InterpolatedSymbolNode | GlobalVariableReadNode | MissingNode, ?keyword_loc: Location) -> AliasMethodNode + + alias deconstruct child_nodes + + # : (Array[Symbol]? keys) -> Hash[Symbol, untyped] + def deconstruct_keys: (Array[Symbol]? keys) -> Hash[Symbol, untyped] + + # See `Node#type`. + # -- + # : () -> :alias_method_node + def type: () -> :alias_method_node + + # See `Node.type`. + # -- + # : () -> :alias_method_node + def self.type: () -> :alias_method_node + + # : () -> String + def inspect: () -> String + + # :call-seq: + # new_name -> SymbolNode | InterpolatedSymbolNode + # + # Represents the new name of the method that will be aliased. + # + # alias foo bar + # ^^^ + # + # alias :foo :bar + # ^^^^ + # + # alias :"#{foo}" :"#{bar}" + # ^^^^^^^^^ + # -- + # : () -> (SymbolNode | InterpolatedSymbolNode) + def new_name: () -> (SymbolNode | InterpolatedSymbolNode) + + # :call-seq: + # old_name -> SymbolNode | InterpolatedSymbolNode | GlobalVariableReadNode | MissingNode + # + # Represents the old name of the method that will be aliased. + # + # alias foo bar + # ^^^ + # + # alias :foo :bar + # ^^^^ + # + # alias :"#{foo}" :"#{bar}" + # ^^^^^^^^^ + # -- + # : () -> (SymbolNode | InterpolatedSymbolNode | GlobalVariableReadNode | MissingNode) + def old_name: () -> (SymbolNode | InterpolatedSymbolNode | GlobalVariableReadNode | MissingNode) + + # :category: Locations + # :call-seq: + # keyword_loc -> Location + # + # Represents the Location of the `alias` keyword. + # + # alias foo bar + # ^^^^^ + # -- + # : () -> Location + def keyword_loc: () -> Location + + # :category: Repository + # Save the keyword_loc location using the given saved source so that + # it can be retrieved later. + # -- + # : (_Repository repository) -> Relocation::Entry + def save_keyword_loc: (_Repository repository) -> Relocation::Entry + + # :call-seq: + # keyword -> String + # + # Slice the location of keyword_loc from the source. + # -- + # : () -> String + def keyword: () -> String + + # : (untyped other) -> boolish + def ===: (untyped other) -> boolish + end + + # Represents an alternation pattern in pattern matching. + # + # foo => bar | baz + # ^^^^^^^^^ + class AlternationPatternNode < Node + @operator_loc: Location + + @right: Prism::node + + @left: Prism::node + + # Initialize a new AlternationPatternNode node. + # -- + # : (Source source, Integer node_id, Location location, Integer flags, Prism::node left, Prism::node right, Location operator_loc) -> void + def initialize: (Source source, Integer node_id, Location location, Integer flags, Prism::node left, Prism::node right, Location operator_loc) -> void + + # See Node.accept. + # -- + # : (_Visitor visitor) -> untyped + def accept: (_Visitor visitor) -> untyped + + # See Node.child_nodes. + # -- + # : () -> Array[node?] + def child_nodes: () -> Array[node?] + + # See Node.each_child_node. + # -- + # : () -> Enumerator[node, void] + # : () { (node) -> void } -> void + def each_child_node: () -> Enumerator[node, void] + | () { (node) -> void } -> void + + # See Node.compact_child_nodes. + # -- + # : () -> Array[node] + def compact_child_nodes: () -> Array[node] + + # See Node.comment_targets. + # -- + # : () -> Array[node | Location] + def comment_targets: () -> Array[node | Location] + + # :call-seq: + # copy(**fields) -> AlternationPatternNode + # + # Creates a copy of self with the given fields, using self as the template. + # -- + # : (?node_id: Integer, ?location: Location, ?flags: Integer, ?left: Prism::node, ?right: Prism::node, ?operator_loc: Location) -> AlternationPatternNode + def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?left: Prism::node, ?right: Prism::node, ?operator_loc: Location) -> AlternationPatternNode + + alias deconstruct child_nodes + + # : (Array[Symbol]? keys) -> Hash[Symbol, untyped] + def deconstruct_keys: (Array[Symbol]? keys) -> Hash[Symbol, untyped] + + # See `Node#type`. + # -- + # : () -> :alternation_pattern_node + def type: () -> :alternation_pattern_node + + # See `Node.type`. + # -- + # : () -> :alternation_pattern_node + def self.type: () -> :alternation_pattern_node + + # : () -> String + def inspect: () -> String + + # :call-seq: + # left -> Node + # + # Represents the left side of the expression. + # + # foo => bar | baz + # ^^^ + # -- + # : () -> Prism::node + def left: () -> Prism::node + + # :call-seq: + # right -> Node + # + # Represents the right side of the expression. + # + # foo => bar | baz + # ^^^ + # -- + # : () -> Prism::node + def right: () -> Prism::node + + # :category: Locations + # :call-seq: + # operator_loc -> Location + # + # Represents the alternation operator Location. + # + # foo => bar | baz + # ^ + # -- + # : () -> Location + def operator_loc: () -> Location + + # :category: Repository + # Save the operator_loc location using the given saved source so that + # it can be retrieved later. + # -- + # : (_Repository repository) -> Relocation::Entry + def save_operator_loc: (_Repository repository) -> Relocation::Entry + + # :call-seq: + # operator -> String + # + # Slice the location of operator_loc from the source. + # -- + # : () -> String + def operator: () -> String + + # : (untyped other) -> boolish + def ===: (untyped other) -> boolish + end + + # Represents the use of the `&&` operator or the `and` keyword. + # + # left and right + # ^^^^^^^^^^^^^^ + class AndNode < Node + @operator_loc: Location + + @right: Prism::node + + @left: Prism::node + + # Initialize a new AndNode node. + # -- + # : (Source source, Integer node_id, Location location, Integer flags, Prism::node left, Prism::node right, Location operator_loc) -> void + def initialize: (Source source, Integer node_id, Location location, Integer flags, Prism::node left, Prism::node right, Location operator_loc) -> void + + # See Node.accept. + # -- + # : (_Visitor visitor) -> untyped + def accept: (_Visitor visitor) -> untyped + + # See Node.child_nodes. + # -- + # : () -> Array[node?] + def child_nodes: () -> Array[node?] + + # See Node.each_child_node. + # -- + # : () -> Enumerator[node, void] + # : () { (node) -> void } -> void + def each_child_node: () -> Enumerator[node, void] + | () { (node) -> void } -> void + + # See Node.compact_child_nodes. + # -- + # : () -> Array[node] + def compact_child_nodes: () -> Array[node] + + # See Node.comment_targets. + # -- + # : () -> Array[node | Location] + def comment_targets: () -> Array[node | Location] + + # :call-seq: + # copy(**fields) -> AndNode + # + # Creates a copy of self with the given fields, using self as the template. + # -- + # : (?node_id: Integer, ?location: Location, ?flags: Integer, ?left: Prism::node, ?right: Prism::node, ?operator_loc: Location) -> AndNode + def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?left: Prism::node, ?right: Prism::node, ?operator_loc: Location) -> AndNode + + alias deconstruct child_nodes + + # : (Array[Symbol]? keys) -> Hash[Symbol, untyped] + def deconstruct_keys: (Array[Symbol]? keys) -> Hash[Symbol, untyped] + + # See `Node#type`. + # -- + # : () -> :and_node + def type: () -> :and_node + + # See `Node.type`. + # -- + # : () -> :and_node + def self.type: () -> :and_node + + # : () -> String + def inspect: () -> String + + # :call-seq: + # left -> Node + # + # 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 + # ^ + # -- + # : () -> Prism::node + def left: () -> Prism::node + + # :call-seq: + # right -> Node + # + # Represents the right side of the expression. + # + # left && right + # ^^^^^ + # + # 1 and 2 + # ^ + # -- + # : () -> Prism::node + def right: () -> Prism::node + + # :category: Locations + # :call-seq: + # operator_loc -> Location + # + # The Location of the `and` keyword or the `&&` operator. + # + # left and right + # ^^^ + # -- + # : () -> Location + def operator_loc: () -> Location + + # :category: Repository + # Save the operator_loc location using the given saved source so that + # it can be retrieved later. + # -- + # : (_Repository repository) -> Relocation::Entry + def save_operator_loc: (_Repository repository) -> Relocation::Entry + + # :call-seq: + # operator -> String + # + # Slice the location of operator_loc from the source. + # -- + # : () -> String + def operator: () -> String + + # : (untyped other) -> boolish + def ===: (untyped other) -> boolish + end + + # Represents a set of arguments to a method or a keyword. + # + # return foo, bar, baz + # ^^^^^^^^^^^^^ + class ArgumentsNode < Node + @arguments: Array[Prism::node] + + # Initialize a new ArgumentsNode node. + # -- + # : (Source source, Integer node_id, Location location, Integer flags, Array[Prism::node] arguments) -> void + def initialize: (Source source, Integer node_id, Location location, Integer flags, Array[Prism::node] arguments) -> void + + # See Node.accept. + # -- + # : (_Visitor visitor) -> untyped + def accept: (_Visitor visitor) -> untyped + + # See Node.child_nodes. + # -- + # : () -> Array[node?] + def child_nodes: () -> Array[node?] + + # See Node.each_child_node. + # -- + # : () -> Enumerator[node, void] + # : () { (node) -> void } -> void + def each_child_node: () -> Enumerator[node, void] + | () { (node) -> void } -> void + + # See Node.compact_child_nodes. + # -- + # : () -> Array[node] + def compact_child_nodes: () -> Array[node] + + # See Node.comment_targets. + # -- + # : () -> Array[node | Location] + def comment_targets: () -> Array[node | Location] + + # :call-seq: + # copy(**fields) -> ArgumentsNode + # + # Creates a copy of self with the given fields, using self as the template. + # -- + # : (?node_id: Integer, ?location: Location, ?flags: Integer, ?arguments: Array[Prism::node]) -> ArgumentsNode + def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?arguments: Array[Prism::node]) -> ArgumentsNode + + alias deconstruct child_nodes + + # : (Array[Symbol]? keys) -> Hash[Symbol, untyped] + def deconstruct_keys: (Array[Symbol]? keys) -> Hash[Symbol, untyped] + + # See `Node#type`. + # -- + # : () -> :arguments_node + def type: () -> :arguments_node + + # See `Node.type`. + # -- + # : () -> :arguments_node + def self.type: () -> :arguments_node + + # : () -> String + def inspect: () -> String + + # :category: Flags + # if the arguments contain forwarding + # -- + # : () -> bool + def contains_forwarding?: () -> bool + + # :category: Flags + # if the arguments contain keywords + # -- + # : () -> bool + def contains_keywords?: () -> bool + + # :category: Flags + # if the arguments contain a keyword splat + # -- + # : () -> bool + def contains_keyword_splat?: () -> bool + + # :category: Flags + # if the arguments contain a splat + # -- + # : () -> bool + def contains_splat?: () -> bool + + # :category: Flags + # if the arguments contain multiple splats + # -- + # : () -> bool + def contains_multiple_splats?: () -> bool + + # :call-seq: + # arguments -> Array[Node] + # + # 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) + # ^^^^^^^^ + # -- + # : () -> Array[Prism::node] + def arguments: () -> Array[Prism::node] + + # : (untyped other) -> boolish + def ===: (untyped other) -> boolish + 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 + @closing_loc: Location? + + @opening_loc: Location? + + @elements: Array[Prism::node] + + # Initialize a new ArrayNode node. + # -- + # : (Source source, Integer node_id, Location location, Integer flags, Array[Prism::node] elements, Location? opening_loc, Location? closing_loc) -> void + def initialize: (Source source, Integer node_id, Location location, Integer flags, Array[Prism::node] elements, Location? opening_loc, Location? closing_loc) -> void + + # See Node.accept. + # -- + # : (_Visitor visitor) -> untyped + def accept: (_Visitor visitor) -> untyped + + # See Node.child_nodes. + # -- + # : () -> Array[node?] + def child_nodes: () -> Array[node?] + + # See Node.each_child_node. + # -- + # : () -> Enumerator[node, void] + # : () { (node) -> void } -> void + def each_child_node: () -> Enumerator[node, void] + | () { (node) -> void } -> void + + # See Node.compact_child_nodes. + # -- + # : () -> Array[node] + def compact_child_nodes: () -> Array[node] + + # See Node.comment_targets. + # -- + # : () -> Array[node | Location] + def comment_targets: () -> Array[node | Location] + + # :call-seq: + # copy(**fields) -> ArrayNode + # + # Creates a copy of self with the given fields, using self as the template. + # -- + # : (?node_id: Integer, ?location: Location, ?flags: Integer, ?elements: Array[Prism::node], ?opening_loc: Location?, ?closing_loc: Location?) -> ArrayNode + def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?elements: Array[Prism::node], ?opening_loc: Location?, ?closing_loc: Location?) -> ArrayNode + + alias deconstruct child_nodes + + # : (Array[Symbol]? keys) -> Hash[Symbol, untyped] + def deconstruct_keys: (Array[Symbol]? keys) -> Hash[Symbol, untyped] + + # See `Node#type`. + # -- + # : () -> :array_node + def type: () -> :array_node + + # See `Node.type`. + # -- + # : () -> :array_node + def self.type: () -> :array_node + + # : () -> String + def inspect: () -> String + + # :category: Flags + # if array contains splat nodes + # -- + # : () -> bool + def contains_splat?: () -> bool + + # :call-seq: + # elements -> Array[Node] + # + # 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. + # -- + # : () -> Array[Prism::node] + def elements: () -> Array[Prism::node] + + # :category: Locations + # :call-seq: + # opening_loc -> Location | nil + # + # 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 + # -- + # : () -> Location? + def opening_loc: () -> Location? + + # :category: Repository + # Save the opening_loc location using the given saved source so that + # it can be retrieved later. + # -- + # : (_Repository repository) -> Relocation::Entry? + def save_opening_loc: (_Repository repository) -> Relocation::Entry? + + # :category: Locations + # :call-seq: + # closing_loc -> Location | nil + # + # 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 + # -- + # : () -> Location? + def closing_loc: () -> Location? + + # :category: Repository + # Save the closing_loc location using the given saved source so that + # it can be retrieved later. + # -- + # : (_Repository repository) -> Relocation::Entry? + def save_closing_loc: (_Repository repository) -> Relocation::Entry? + + # :call-seq: + # opening -> String | nil + # + # Slice the location of opening_loc from the source. + # -- + # : () -> String? + def opening: () -> String? + + # :call-seq: + # closing -> String | nil + # + # Slice the location of closing_loc from the source. + # -- + # : () -> String? + def closing: () -> String? + + # : (untyped other) -> boolish + def ===: (untyped other) -> boolish + 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 + @closing_loc: Location? + + @opening_loc: Location? + + @posts: Array[Prism::node] + + @rest: Prism::node? + + @requireds: Array[Prism::node] + + @constant: (ConstantPathNode | ConstantReadNode)? + + # Initialize a new ArrayPatternNode node. + # -- + # : (Source source, Integer node_id, Location location, Integer flags, (ConstantPathNode | ConstantReadNode)? constant, Array[Prism::node] requireds, Prism::node? rest, Array[Prism::node] posts, Location? opening_loc, Location? closing_loc) -> void + def initialize: (Source source, Integer node_id, Location location, Integer flags, (ConstantPathNode | ConstantReadNode)? constant, Array[Prism::node] requireds, Prism::node? rest, Array[Prism::node] posts, Location? opening_loc, Location? closing_loc) -> void + + # See Node.accept. + # -- + # : (_Visitor visitor) -> untyped + def accept: (_Visitor visitor) -> untyped + + # See Node.child_nodes. + # -- + # : () -> Array[node?] + def child_nodes: () -> Array[node?] + + # See Node.each_child_node. + # -- + # : () -> Enumerator[node, void] + # : () { (node) -> void } -> void + def each_child_node: () -> Enumerator[node, void] + | () { (node) -> void } -> void + + # See Node.compact_child_nodes. + # -- + # : () -> Array[node] + def compact_child_nodes: () -> Array[node] + + # See Node.comment_targets. + # -- + # : () -> Array[node | Location] + def comment_targets: () -> Array[node | Location] + + # :call-seq: + # copy(**fields) -> ArrayPatternNode + # + # Creates a copy of self with the given fields, using self as the template. + # -- + # : (?node_id: Integer, ?location: Location, ?flags: Integer, ?constant: (ConstantPathNode | ConstantReadNode)?, ?requireds: Array[Prism::node], ?rest: Prism::node?, ?posts: Array[Prism::node], ?opening_loc: Location?, ?closing_loc: Location?) -> ArrayPatternNode + def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?constant: (ConstantPathNode | ConstantReadNode)?, ?requireds: Array[Prism::node], ?rest: Prism::node?, ?posts: Array[Prism::node], ?opening_loc: Location?, ?closing_loc: Location?) -> ArrayPatternNode + + alias deconstruct child_nodes + + # : (Array[Symbol]? keys) -> Hash[Symbol, untyped] + def deconstruct_keys: (Array[Symbol]? keys) -> Hash[Symbol, untyped] + + # See `Node#type`. + # -- + # : () -> :array_pattern_node + def type: () -> :array_pattern_node + + # See `Node.type`. + # -- + # : () -> :array_pattern_node + def self.type: () -> :array_pattern_node + + # : () -> String + def inspect: () -> String + + # :call-seq: + # constant -> ConstantPathNode | ConstantReadNode | nil + # + # Represents the optional constant preceding the Array + # + # foo in Bar[] + # ^^^ + # + # foo in Bar[1, 2, 3] + # ^^^ + # + # foo in Bar::Baz[1, 2, 3] + # ^^^^^^^^ + # -- + # : () -> (ConstantPathNode | ConstantReadNode)? + def constant: () -> (ConstantPathNode | ConstantReadNode)? + + # :call-seq: + # requireds -> Array[Node] + # + # Represents the required elements of the array pattern. + # + # foo in [1, 2] + # ^ ^ + # -- + # : () -> Array[Prism::node] + def requireds: () -> Array[Prism::node] + + # :call-seq: + # rest -> Node | nil + # + # Represents the rest element of the array pattern. + # + # foo in *bar + # ^^^^ + # -- + # : () -> Prism::node? + def rest: () -> Prism::node? + + # :call-seq: + # posts -> Array[Node] + # + # Represents the elements after the rest element of the array pattern. + # + # foo in *bar, baz + # ^^^ + # -- + # : () -> Array[Prism::node] + def posts: () -> Array[Prism::node] + + # :category: Locations + # :call-seq: + # opening_loc -> Location | nil + # + # Represents the opening Location of the array pattern. + # + # foo in [1, 2] + # ^ + # -- + # : () -> Location? + def opening_loc: () -> Location? + + # :category: Repository + # Save the opening_loc location using the given saved source so that + # it can be retrieved later. + # -- + # : (_Repository repository) -> Relocation::Entry? + def save_opening_loc: (_Repository repository) -> Relocation::Entry? + + # :category: Locations + # :call-seq: + # closing_loc -> Location | nil + # + # Represents the closing Location of the array pattern. + # + # foo in [1, 2] + # ^ + # -- + # : () -> Location? + def closing_loc: () -> Location? + + # :category: Repository + # Save the closing_loc location using the given saved source so that + # it can be retrieved later. + # -- + # : (_Repository repository) -> Relocation::Entry? + def save_closing_loc: (_Repository repository) -> Relocation::Entry? + + # :call-seq: + # opening -> String | nil + # + # Slice the location of opening_loc from the source. + # -- + # : () -> String? + def opening: () -> String? + + # :call-seq: + # closing -> String | nil + # + # Slice the location of closing_loc from the source. + # -- + # : () -> String? + def closing: () -> String? + + # : (untyped other) -> boolish + def ===: (untyped other) -> boolish + end + + # Represents a hash key/value pair. + # + # { a => b } + # ^^^^^^ + class AssocNode < Node + @operator_loc: Location? + + @value: Prism::node + + @key: Prism::node + + # Initialize a new AssocNode node. + # -- + # : (Source source, Integer node_id, Location location, Integer flags, Prism::node key, Prism::node value, Location? operator_loc) -> void + def initialize: (Source source, Integer node_id, Location location, Integer flags, Prism::node key, Prism::node value, Location? operator_loc) -> void + + # See Node.accept. + # -- + # : (_Visitor visitor) -> untyped + def accept: (_Visitor visitor) -> untyped + + # See Node.child_nodes. + # -- + # : () -> Array[node?] + def child_nodes: () -> Array[node?] + + # See Node.each_child_node. + # -- + # : () -> Enumerator[node, void] + # : () { (node) -> void } -> void + def each_child_node: () -> Enumerator[node, void] + | () { (node) -> void } -> void + + # See Node.compact_child_nodes. + # -- + # : () -> Array[node] + def compact_child_nodes: () -> Array[node] + + # See Node.comment_targets. + # -- + # : () -> Array[node | Location] + def comment_targets: () -> Array[node | Location] + + # :call-seq: + # copy(**fields) -> AssocNode + # + # Creates a copy of self with the given fields, using self as the template. + # -- + # : (?node_id: Integer, ?location: Location, ?flags: Integer, ?key: Prism::node, ?value: Prism::node, ?operator_loc: Location?) -> AssocNode + def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?key: Prism::node, ?value: Prism::node, ?operator_loc: Location?) -> AssocNode + + alias deconstruct child_nodes + + # : (Array[Symbol]? keys) -> Hash[Symbol, untyped] + def deconstruct_keys: (Array[Symbol]? keys) -> Hash[Symbol, untyped] + + # See `Node#type`. + # -- + # : () -> :assoc_node + def type: () -> :assoc_node + + # See `Node.type`. + # -- + # : () -> :assoc_node + def self.type: () -> :assoc_node + + # : () -> String + def inspect: () -> String + + # :call-seq: + # key -> Node + # + # 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 } + # ^^^^^^^^^^ + # -- + # : () -> Prism::node + def key: () -> Prism::node + + # :call-seq: + # value -> Node + # + # 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 } + # ^ + # -- + # : () -> Prism::node + def value: () -> Prism::node + + # :category: Locations + # :call-seq: + # operator_loc -> Location | nil + # + # The Location of the `=>` operator, if present. + # + # { foo => bar } + # ^^ + # -- + # : () -> Location? + def operator_loc: () -> Location? + + # :category: Repository + # Save the operator_loc location using the given saved source so that + # it can be retrieved later. + # -- + # : (_Repository repository) -> Relocation::Entry? + def save_operator_loc: (_Repository repository) -> Relocation::Entry? + + # :call-seq: + # operator -> String | nil + # + # Slice the location of operator_loc from the source. + # -- + # : () -> String? + def operator: () -> String? + + # : (untyped other) -> boolish + def ===: (untyped other) -> boolish + end + + # Represents a splat in a hash literal. + # + # { **foo } + # ^^^^^ + class AssocSplatNode < Node + @operator_loc: Location + + @value: Prism::node? + + # Initialize a new AssocSplatNode node. + # -- + # : (Source source, Integer node_id, Location location, Integer flags, Prism::node? value, Location operator_loc) -> void + def initialize: (Source source, Integer node_id, Location location, Integer flags, Prism::node? value, Location operator_loc) -> void + + # See Node.accept. + # -- + # : (_Visitor visitor) -> untyped + def accept: (_Visitor visitor) -> untyped + + # See Node.child_nodes. + # -- + # : () -> Array[node?] + def child_nodes: () -> Array[node?] + + # See Node.each_child_node. + # -- + # : () -> Enumerator[node, void] + # : () { (node) -> void } -> void + def each_child_node: () -> Enumerator[node, void] + | () { (node) -> void } -> void + + # See Node.compact_child_nodes. + # -- + # : () -> Array[node] + def compact_child_nodes: () -> Array[node] + + # See Node.comment_targets. + # -- + # : () -> Array[node | Location] + def comment_targets: () -> Array[node | Location] + + # :call-seq: + # copy(**fields) -> AssocSplatNode + # + # Creates a copy of self with the given fields, using self as the template. + # -- + # : (?node_id: Integer, ?location: Location, ?flags: Integer, ?value: Prism::node?, ?operator_loc: Location) -> AssocSplatNode + def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?value: Prism::node?, ?operator_loc: Location) -> AssocSplatNode + + alias deconstruct child_nodes + + # : (Array[Symbol]? keys) -> Hash[Symbol, untyped] + def deconstruct_keys: (Array[Symbol]? keys) -> Hash[Symbol, untyped] + + # See `Node#type`. + # -- + # : () -> :assoc_splat_node + def type: () -> :assoc_splat_node + + # See `Node.type`. + # -- + # : () -> :assoc_splat_node + def self.type: () -> :assoc_splat_node + + # : () -> String + def inspect: () -> String + + # :call-seq: + # value -> Node | nil + # + # The value to be splatted, if present. Will be missing when keyword rest argument forwarding is used. + # + # { **foo } + # ^^^ + # -- + # : () -> Prism::node? + def value: () -> Prism::node? + + # :category: Locations + # :call-seq: + # operator_loc -> Location + # + # The Location of the `**` operator. + # + # { **x } + # ^^ + # -- + # : () -> Location + def operator_loc: () -> Location + + # :category: Repository + # Save the operator_loc location using the given saved source so that + # it can be retrieved later. + # -- + # : (_Repository repository) -> Relocation::Entry + def save_operator_loc: (_Repository repository) -> Relocation::Entry + + # :call-seq: + # operator -> String + # + # Slice the location of operator_loc from the source. + # -- + # : () -> String + def operator: () -> String + + # : (untyped other) -> boolish + def ===: (untyped other) -> boolish + end + + # Represents reading a reference to a field in the previous match. + # + # $' + # ^^ + class BackReferenceReadNode < Node + @name: Symbol + + # Initialize a new BackReferenceReadNode node. + # -- + # : (Source source, Integer node_id, Location location, Integer flags, Symbol name) -> void + def initialize: (Source source, Integer node_id, Location location, Integer flags, Symbol name) -> void + + # See Node.accept. + # -- + # : (_Visitor visitor) -> untyped + def accept: (_Visitor visitor) -> untyped + + # See Node.child_nodes. + # -- + # : () -> Array[node?] + def child_nodes: () -> Array[node?] + + # See Node.each_child_node. + # -- + # : () -> Enumerator[node, void] + # : () { (node) -> void } -> void + def each_child_node: () -> Enumerator[node, void] + | () { (node) -> void } -> void + + # See Node.compact_child_nodes. + # -- + # : () -> Array[node] + def compact_child_nodes: () -> Array[node] + + # See Node.comment_targets. + # -- + # : () -> Array[node | Location] + def comment_targets: () -> Array[node | Location] + + # :call-seq: + # copy(**fields) -> BackReferenceReadNode + # + # Creates a copy of self with the given fields, using self as the template. + # -- + # : (?node_id: Integer, ?location: Location, ?flags: Integer, ?name: Symbol) -> BackReferenceReadNode + def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?name: Symbol) -> BackReferenceReadNode + + alias deconstruct child_nodes + + # : (Array[Symbol]? keys) -> Hash[Symbol, untyped] + def deconstruct_keys: (Array[Symbol]? keys) -> Hash[Symbol, untyped] + + # See `Node#type`. + # -- + # : () -> :back_reference_read_node + def type: () -> :back_reference_read_node + + # See `Node.type`. + # -- + # : () -> :back_reference_read_node + def self.type: () -> :back_reference_read_node + + # : () -> String + def inspect: () -> String + + # :call-seq: + # name -> Symbol + # + # The name of the back-reference variable, including the leading `$`. + # + # $& # name `:$&` + # + # $+ # name `:$+` + # -- + # : () -> Symbol + def name: () -> Symbol + + # : (untyped other) -> boolish + def ===: (untyped other) -> boolish + end + + # Represents a begin statement. + # + # begin + # foo + # end + # ^^^^^ + class BeginNode < Node + @end_keyword_loc: Location? + + @ensure_clause: EnsureNode? + + @else_clause: ElseNode? + + @rescue_clause: RescueNode? + + @statements: StatementsNode? + + @begin_keyword_loc: Location? + + # Initialize a new BeginNode node. + # -- + # : (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 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 + + # See Node.accept. + # -- + # : (_Visitor visitor) -> untyped + def accept: (_Visitor visitor) -> untyped + + # See Node.child_nodes. + # -- + # : () -> Array[node?] + def child_nodes: () -> Array[node?] + + # See Node.each_child_node. + # -- + # : () -> Enumerator[node, void] + # : () { (node) -> void } -> void + def each_child_node: () -> Enumerator[node, void] + | () { (node) -> void } -> void + + # See Node.compact_child_nodes. + # -- + # : () -> Array[node] + def compact_child_nodes: () -> Array[node] + + # See Node.comment_targets. + # -- + # : () -> Array[node | Location] + def comment_targets: () -> Array[node | Location] + + # :call-seq: + # copy(**fields) -> BeginNode + # + # Creates a copy of self with the given fields, using self as the template. + # -- + # : (?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: 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 + + alias deconstruct child_nodes + + # : (Array[Symbol]? keys) -> Hash[Symbol, untyped] + def deconstruct_keys: (Array[Symbol]? keys) -> Hash[Symbol, untyped] + + # See `Node#type`. + # -- + # : () -> :begin_node + def type: () -> :begin_node + + # See `Node.type`. + # -- + # : () -> :begin_node + def self.type: () -> :begin_node + + # : () -> String + def inspect: () -> String + + # :category: Locations + # :call-seq: + # begin_keyword_loc -> Location | nil + # + # Represents the Location of the `begin` keyword. + # + # begin x end + # ^^^^^ + # -- + # : () -> Location? + def begin_keyword_loc: () -> Location? + + # :category: Repository + # Save the begin_keyword_loc location using the given saved source so that + # it can be retrieved later. + # -- + # : (_Repository repository) -> Relocation::Entry? + def save_begin_keyword_loc: (_Repository repository) -> Relocation::Entry? + + # :call-seq: + # statements -> StatementsNode | nil + # + # Represents the statements within the begin block. + # + # begin x end + # ^ + # -- + # : () -> StatementsNode? + def statements: () -> StatementsNode? + + # :call-seq: + # rescue_clause -> RescueNode | nil + # + # Represents the rescue clause within the begin block. + # + # begin x; rescue y; end + # ^^^^^^^^ + # -- + # : () -> RescueNode? + def rescue_clause: () -> RescueNode? + + # :call-seq: + # else_clause -> ElseNode | nil + # + # Represents the else clause within the begin block. + # + # begin x; rescue y; else z; end + # ^^^^^^^^^^^ + # -- + # : () -> ElseNode? + def else_clause: () -> ElseNode? + + # :call-seq: + # ensure_clause -> EnsureNode | nil + # + # Represents the ensure clause within the begin block. + # + # begin x; ensure y; end + # ^^^^^^^^ + # -- + # : () -> EnsureNode? + def ensure_clause: () -> EnsureNode? + + # :category: Locations + # :call-seq: + # end_keyword_loc -> Location | nil + # + # Represents the Location of the `end` keyword. + # + # begin x end + # ^^^ + # -- + # : () -> Location? + def end_keyword_loc: () -> Location? + + # :category: Repository + # Save the end_keyword_loc location using the given saved source so that + # it can be retrieved later. + # -- + # : (_Repository repository) -> Relocation::Entry? + def save_end_keyword_loc: (_Repository repository) -> Relocation::Entry? + + # :call-seq: + # begin_keyword -> String | nil + # + # Slice the location of begin_keyword_loc from the source. + # -- + # : () -> String? + def begin_keyword: () -> String? + + # :call-seq: + # end_keyword -> String | nil + # + # Slice the location of end_keyword_loc from the source. + # -- + # : () -> String? + def end_keyword: () -> String? + + # : (untyped other) -> boolish + def ===: (untyped other) -> boolish + end + + # Represents a block argument using `&`. + # + # bar(&args) + # ^^^^^ + class BlockArgumentNode < Node + @operator_loc: Location + + @expression: Prism::node? + + # Initialize a new BlockArgumentNode node. + # -- + # : (Source source, Integer node_id, Location location, Integer flags, Prism::node? expression, Location operator_loc) -> void + def initialize: (Source source, Integer node_id, Location location, Integer flags, Prism::node? expression, Location operator_loc) -> void + + # See Node.accept. + # -- + # : (_Visitor visitor) -> untyped + def accept: (_Visitor visitor) -> untyped + + # See Node.child_nodes. + # -- + # : () -> Array[node?] + def child_nodes: () -> Array[node?] + + # See Node.each_child_node. + # -- + # : () -> Enumerator[node, void] + # : () { (node) -> void } -> void + def each_child_node: () -> Enumerator[node, void] + | () { (node) -> void } -> void + + # See Node.compact_child_nodes. + # -- + # : () -> Array[node] + def compact_child_nodes: () -> Array[node] + + # See Node.comment_targets. + # -- + # : () -> Array[node | Location] + def comment_targets: () -> Array[node | Location] + + # :call-seq: + # copy(**fields) -> BlockArgumentNode + # + # Creates a copy of self with the given fields, using self as the template. + # -- + # : (?node_id: Integer, ?location: Location, ?flags: Integer, ?expression: Prism::node?, ?operator_loc: Location) -> BlockArgumentNode + def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?expression: Prism::node?, ?operator_loc: Location) -> BlockArgumentNode + + alias deconstruct child_nodes + + # : (Array[Symbol]? keys) -> Hash[Symbol, untyped] + def deconstruct_keys: (Array[Symbol]? keys) -> Hash[Symbol, untyped] + + # See `Node#type`. + # -- + # : () -> :block_argument_node + def type: () -> :block_argument_node + + # See `Node.type`. + # -- + # : () -> :block_argument_node + def self.type: () -> :block_argument_node + + # : () -> String + def inspect: () -> String + + # :call-seq: + # expression -> Node | nil + # + # 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) + # ^^^^ + # -- + # : () -> Prism::node? + def expression: () -> Prism::node? + + # :category: Locations + # :call-seq: + # operator_loc -> Location + # + # Represents the Location of the `&` operator. + # + # foo(&args) + # ^ + # -- + # : () -> Location + def operator_loc: () -> Location + + # :category: Repository + # Save the operator_loc location using the given saved source so that + # it can be retrieved later. + # -- + # : (_Repository repository) -> Relocation::Entry + def save_operator_loc: (_Repository repository) -> Relocation::Entry + + # :call-seq: + # operator -> String + # + # Slice the location of operator_loc from the source. + # -- + # : () -> String + def operator: () -> String + + # : (untyped other) -> boolish + def ===: (untyped other) -> boolish + end + + # Represents a block local variable. + # + # a { |; b| } + # ^ + class BlockLocalVariableNode < Node + @name: Symbol + + # Initialize a new BlockLocalVariableNode node. + # -- + # : (Source source, Integer node_id, Location location, Integer flags, Symbol name) -> void + def initialize: (Source source, Integer node_id, Location location, Integer flags, Symbol name) -> void + + # See Node.accept. + # -- + # : (_Visitor visitor) -> untyped + def accept: (_Visitor visitor) -> untyped + + # See Node.child_nodes. + # -- + # : () -> Array[node?] + def child_nodes: () -> Array[node?] + + # See Node.each_child_node. + # -- + # : () -> Enumerator[node, void] + # : () { (node) -> void } -> void + def each_child_node: () -> Enumerator[node, void] + | () { (node) -> void } -> void + + # See Node.compact_child_nodes. + # -- + # : () -> Array[node] + def compact_child_nodes: () -> Array[node] + + # See Node.comment_targets. + # -- + # : () -> Array[node | Location] + def comment_targets: () -> Array[node | Location] + + # :call-seq: + # copy(**fields) -> BlockLocalVariableNode + # + # Creates a copy of self with the given fields, using self as the template. + # -- + # : (?node_id: Integer, ?location: Location, ?flags: Integer, ?name: Symbol) -> BlockLocalVariableNode + def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?name: Symbol) -> BlockLocalVariableNode + + alias deconstruct child_nodes + + # : (Array[Symbol]? keys) -> Hash[Symbol, untyped] + def deconstruct_keys: (Array[Symbol]? keys) -> Hash[Symbol, untyped] + + # See `Node#type`. + # -- + # : () -> :block_local_variable_node + def type: () -> :block_local_variable_node + + # See `Node.type`. + # -- + # : () -> :block_local_variable_node + def self.type: () -> :block_local_variable_node + + # : () -> String + def inspect: () -> String + + # :category: Flags + # a parameter name that has been repeated in the method signature + # -- + # : () -> bool + def repeated_parameter?: () -> bool + + # :call-seq: + # name -> Symbol + # + # The name of the block local variable. + # + # a { |; b| } # name `:b` + # ^ + # -- + # : () -> Symbol + def name: () -> Symbol + + # : (untyped other) -> boolish + def ===: (untyped other) -> boolish + end + + # Represents a block of ruby code. + # + # [1, 2, 3].each { |i| puts x } + # ^^^^^^^^^^^^^^ + class BlockNode < Node + @closing_loc: Location + + @opening_loc: Location + + @body: (StatementsNode | BeginNode)? + + @parameters: (BlockParametersNode | NumberedParametersNode | ItParametersNode)? + + @locals: Array[Symbol] + + # Initialize a new BlockNode node. + # -- + # : (Source source, Integer node_id, Location location, Integer flags, Array[Symbol] locals, (BlockParametersNode | NumberedParametersNode | ItParametersNode)? parameters, (StatementsNode | BeginNode)? body, Location opening_loc, Location closing_loc) -> void + def initialize: (Source source, Integer node_id, Location location, Integer flags, Array[Symbol] locals, (BlockParametersNode | NumberedParametersNode | ItParametersNode)? parameters, (StatementsNode | BeginNode)? body, Location opening_loc, Location closing_loc) -> void + + # See Node.accept. + # -- + # : (_Visitor visitor) -> untyped + def accept: (_Visitor visitor) -> untyped + + # See Node.child_nodes. + # -- + # : () -> Array[node?] + def child_nodes: () -> Array[node?] + + # See Node.each_child_node. + # -- + # : () -> Enumerator[node, void] + # : () { (node) -> void } -> void + def each_child_node: () -> Enumerator[node, void] + | () { (node) -> void } -> void + + # See Node.compact_child_nodes. + # -- + # : () -> Array[node] + def compact_child_nodes: () -> Array[node] + + # See Node.comment_targets. + # -- + # : () -> Array[node | Location] + def comment_targets: () -> Array[node | Location] + + # :call-seq: + # copy(**fields) -> BlockNode + # + # Creates a copy of self with the given fields, using self as the template. + # -- + # : (?node_id: Integer, ?location: Location, ?flags: Integer, ?locals: Array[Symbol], ?parameters: (BlockParametersNode | NumberedParametersNode | ItParametersNode)?, ?body: (StatementsNode | BeginNode)?, ?opening_loc: Location, ?closing_loc: Location) -> BlockNode + def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?locals: Array[Symbol], ?parameters: (BlockParametersNode | NumberedParametersNode | ItParametersNode)?, ?body: (StatementsNode | BeginNode)?, ?opening_loc: Location, ?closing_loc: Location) -> BlockNode + + alias deconstruct child_nodes + + # : (Array[Symbol]? keys) -> Hash[Symbol, untyped] + def deconstruct_keys: (Array[Symbol]? keys) -> Hash[Symbol, untyped] + + # See `Node#type`. + # -- + # : () -> :block_node + def type: () -> :block_node + + # See `Node.type`. + # -- + # : () -> :block_node + def self.type: () -> :block_node + + # : () -> String + def inspect: () -> String + + # :call-seq: + # locals -> Array[Symbol] + # + # The local variables declared in the block. + # + # [1, 2, 3].each { |i| puts x } # locals: [:i] + # ^ + # -- + # : () -> Array[Symbol] + def locals: () -> Array[Symbol] + + # :call-seq: + # parameters -> BlockParametersNode | NumberedParametersNode | ItParametersNode | nil + # + # The parameters of the block. + # + # [1, 2, 3].each { |i| puts x } + # ^^^ + # [1, 2, 3].each { puts _1 } + # ^^^^^^^^^^^ + # [1, 2, 3].each { puts it } + # ^^^^^^^^^^^ + # -- + # : () -> (BlockParametersNode | NumberedParametersNode | ItParametersNode)? + def parameters: () -> (BlockParametersNode | NumberedParametersNode | ItParametersNode)? + + # :call-seq: + # body -> StatementsNode | BeginNode | nil + # + # The body of the block. + # + # [1, 2, 3].each { |i| puts x } + # ^^^^^^ + # -- + # : () -> (StatementsNode | BeginNode)? + def body: () -> (StatementsNode | BeginNode)? + + # :category: Locations + # :call-seq: + # opening_loc -> Location + # + # Represents the Location of the opening `{` or `do`. + # + # [1, 2, 3].each { |i| puts x } + # ^ + # -- + # : () -> Location + def opening_loc: () -> Location + + # :category: Repository + # Save the opening_loc location using the given saved source so that + # it can be retrieved later. + # -- + # : (_Repository repository) -> Relocation::Entry + def save_opening_loc: (_Repository repository) -> Relocation::Entry + + # :category: Locations + # :call-seq: + # closing_loc -> Location + # + # Represents the Location of the closing `}` or `end`. + # + # [1, 2, 3].each { |i| puts x } + # ^ + # -- + # : () -> Location + def closing_loc: () -> Location + + # :category: Repository + # Save the closing_loc location using the given saved source so that + # it can be retrieved later. + # -- + # : (_Repository repository) -> Relocation::Entry + def save_closing_loc: (_Repository repository) -> Relocation::Entry + + # :call-seq: + # opening -> String + # + # Slice the location of opening_loc from the source. + # -- + # : () -> String + def opening: () -> String + + # :call-seq: + # closing -> String + # + # Slice the location of closing_loc from the source. + # -- + # : () -> String + def closing: () -> String + + # : (untyped other) -> boolish + def ===: (untyped other) -> boolish + end + + # Represents a block parameter of a method, block, or lambda definition. + # + # def a(&b) + # ^^ + # end + class BlockParameterNode < Node + @operator_loc: Location + + @name_loc: Location? + + @name: Symbol? + + # Initialize a new BlockParameterNode node. + # -- + # : (Source source, Integer node_id, Location location, Integer flags, Symbol? name, Location? name_loc, Location operator_loc) -> void + def initialize: (Source source, Integer node_id, Location location, Integer flags, Symbol? name, Location? name_loc, Location operator_loc) -> void + + # See Node.accept. + # -- + # : (_Visitor visitor) -> untyped + def accept: (_Visitor visitor) -> untyped + + # See Node.child_nodes. + # -- + # : () -> Array[node?] + def child_nodes: () -> Array[node?] + + # See Node.each_child_node. + # -- + # : () -> Enumerator[node, void] + # : () { (node) -> void } -> void + def each_child_node: () -> Enumerator[node, void] + | () { (node) -> void } -> void + + # See Node.compact_child_nodes. + # -- + # : () -> Array[node] + def compact_child_nodes: () -> Array[node] + + # See Node.comment_targets. + # -- + # : () -> Array[node | Location] + def comment_targets: () -> Array[node | Location] + + # :call-seq: + # copy(**fields) -> BlockParameterNode + # + # Creates a copy of self with the given fields, using self as the template. + # -- + # : (?node_id: Integer, ?location: Location, ?flags: Integer, ?name: Symbol?, ?name_loc: Location?, ?operator_loc: Location) -> BlockParameterNode + def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?name: Symbol?, ?name_loc: Location?, ?operator_loc: Location) -> BlockParameterNode + + alias deconstruct child_nodes + + # : (Array[Symbol]? keys) -> Hash[Symbol, untyped] + def deconstruct_keys: (Array[Symbol]? keys) -> Hash[Symbol, untyped] + + # See `Node#type`. + # -- + # : () -> :block_parameter_node + def type: () -> :block_parameter_node + + # See `Node.type`. + # -- + # : () -> :block_parameter_node + def self.type: () -> :block_parameter_node + + # : () -> String + def inspect: () -> String + + # :category: Flags + # a parameter name that has been repeated in the method signature + # -- + # : () -> bool + def repeated_parameter?: () -> bool + + # :call-seq: + # name -> Symbol | nil + # + # The name of the block parameter. + # + # def a(&b) # name `:b` + # ^ + # end + # -- + # : () -> Symbol? + def name: () -> Symbol? + + # :category: Locations + # :call-seq: + # name_loc -> Location | nil + # + # Represents the Location of the block parameter name. + # + # def a(&b) + # ^ + # -- + # : () -> Location? + def name_loc: () -> Location? + + # :category: Repository + # Save the name_loc location using the given saved source so that + # it can be retrieved later. + # -- + # : (_Repository repository) -> Relocation::Entry? + def save_name_loc: (_Repository repository) -> Relocation::Entry? + + # :category: Locations + # :call-seq: + # operator_loc -> Location + # + # Represents the Location of the `&` operator. + # + # def a(&b) + # ^ + # end + # -- + # : () -> Location + def operator_loc: () -> Location + + # :category: Repository + # Save the operator_loc location using the given saved source so that + # it can be retrieved later. + # -- + # : (_Repository repository) -> Relocation::Entry + def save_operator_loc: (_Repository repository) -> Relocation::Entry + + # :call-seq: + # operator -> String + # + # Slice the location of operator_loc from the source. + # -- + # : () -> String + def operator: () -> String + + # : (untyped other) -> boolish + def ===: (untyped other) -> boolish + end + + # Represents a block's parameters declaration. + # + # -> (a, b = 1; local) { } + # ^^^^^^^^^^^^^^^^^ + # + # foo do |a, b = 1; local| + # ^^^^^^^^^^^^^^^^^ + # end + class BlockParametersNode < Node + @closing_loc: Location? + + @opening_loc: Location? + + @locals: Array[BlockLocalVariableNode] + + @parameters: ParametersNode? + + # Initialize a new BlockParametersNode node. + # -- + # : (Source source, Integer node_id, Location location, Integer flags, ParametersNode? parameters, Array[BlockLocalVariableNode] locals, Location? opening_loc, Location? closing_loc) -> void + def initialize: (Source source, Integer node_id, Location location, Integer flags, ParametersNode? parameters, Array[BlockLocalVariableNode] locals, Location? opening_loc, Location? closing_loc) -> void + + # See Node.accept. + # -- + # : (_Visitor visitor) -> untyped + def accept: (_Visitor visitor) -> untyped + + # See Node.child_nodes. + # -- + # : () -> Array[node?] + def child_nodes: () -> Array[node?] + + # See Node.each_child_node. + # -- + # : () -> Enumerator[node, void] + # : () { (node) -> void } -> void + def each_child_node: () -> Enumerator[node, void] + | () { (node) -> void } -> void + + # See Node.compact_child_nodes. + # -- + # : () -> Array[node] + def compact_child_nodes: () -> Array[node] + + # See Node.comment_targets. + # -- + # : () -> Array[node | Location] + def comment_targets: () -> Array[node | Location] + + # :call-seq: + # copy(**fields) -> BlockParametersNode + # + # Creates a copy of self with the given fields, using self as the template. + # -- + # : (?node_id: Integer, ?location: Location, ?flags: Integer, ?parameters: ParametersNode?, ?locals: Array[BlockLocalVariableNode], ?opening_loc: Location?, ?closing_loc: Location?) -> BlockParametersNode + def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?parameters: ParametersNode?, ?locals: Array[BlockLocalVariableNode], ?opening_loc: Location?, ?closing_loc: Location?) -> BlockParametersNode + + alias deconstruct child_nodes + + # : (Array[Symbol]? keys) -> Hash[Symbol, untyped] + def deconstruct_keys: (Array[Symbol]? keys) -> Hash[Symbol, untyped] + + # See `Node#type`. + # -- + # : () -> :block_parameters_node + def type: () -> :block_parameters_node + + # See `Node.type`. + # -- + # : () -> :block_parameters_node + def self.type: () -> :block_parameters_node + + # : () -> String + def inspect: () -> String + + # :call-seq: + # parameters -> ParametersNode | nil + # + # Represents the parameters of the block. + # + # -> (a, b = 1; local) { } + # ^^^^^^^^ + # + # foo do |a, b = 1; local| + # ^^^^^^^^ + # end + # -- + # : () -> ParametersNode? + def parameters: () -> ParametersNode? + + # :call-seq: + # locals -> Array[BlockLocalVariableNode] + # + # Represents the local variables of the block. + # + # -> (a, b = 1; local) { } + # ^^^^^ + # + # foo do |a, b = 1; local| + # ^^^^^ + # end + # -- + # : () -> Array[BlockLocalVariableNode] + def locals: () -> Array[BlockLocalVariableNode] + + # :category: Locations + # :call-seq: + # opening_loc -> Location | nil + # + # Represents the opening Location of the block parameters. + # + # -> (a, b = 1; local) { } + # ^ + # + # foo do |a, b = 1; local| + # ^ + # end + # -- + # : () -> Location? + def opening_loc: () -> Location? + + # :category: Repository + # Save the opening_loc location using the given saved source so that + # it can be retrieved later. + # -- + # : (_Repository repository) -> Relocation::Entry? + def save_opening_loc: (_Repository repository) -> Relocation::Entry? + + # :category: Locations + # :call-seq: + # closing_loc -> Location | nil + # + # Represents the closing Location of the block parameters. + # + # -> (a, b = 1; local) { } + # ^ + # + # foo do |a, b = 1; local| + # ^ + # end + # -- + # : () -> Location? + def closing_loc: () -> Location? + + # :category: Repository + # Save the closing_loc location using the given saved source so that + # it can be retrieved later. + # -- + # : (_Repository repository) -> Relocation::Entry? + def save_closing_loc: (_Repository repository) -> Relocation::Entry? + + # :call-seq: + # opening -> String | nil + # + # Slice the location of opening_loc from the source. + # -- + # : () -> String? + def opening: () -> String? + + # :call-seq: + # closing -> String | nil + # + # Slice the location of closing_loc from the source. + # -- + # : () -> String? + def closing: () -> String? + + # : (untyped other) -> boolish + def ===: (untyped other) -> boolish + end + + # Represents the use of the `break` keyword. + # + # break foo + # ^^^^^^^^^ + class BreakNode < Node + @keyword_loc: Location + + @arguments: ArgumentsNode? + + # Initialize a new BreakNode node. + # -- + # : (Source source, Integer node_id, Location location, Integer flags, ArgumentsNode? arguments, Location keyword_loc) -> void + def initialize: (Source source, Integer node_id, Location location, Integer flags, ArgumentsNode? arguments, Location keyword_loc) -> void + + # See Node.accept. + # -- + # : (_Visitor visitor) -> untyped + def accept: (_Visitor visitor) -> untyped + + # See Node.child_nodes. + # -- + # : () -> Array[node?] + def child_nodes: () -> Array[node?] + + # See Node.each_child_node. + # -- + # : () -> Enumerator[node, void] + # : () { (node) -> void } -> void + def each_child_node: () -> Enumerator[node, void] + | () { (node) -> void } -> void + + # See Node.compact_child_nodes. + # -- + # : () -> Array[node] + def compact_child_nodes: () -> Array[node] + + # See Node.comment_targets. + # -- + # : () -> Array[node | Location] + def comment_targets: () -> Array[node | Location] + + # :call-seq: + # copy(**fields) -> BreakNode + # + # Creates a copy of self with the given fields, using self as the template. + # -- + # : (?node_id: Integer, ?location: Location, ?flags: Integer, ?arguments: ArgumentsNode?, ?keyword_loc: Location) -> BreakNode + def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?arguments: ArgumentsNode?, ?keyword_loc: Location) -> BreakNode + + alias deconstruct child_nodes + + # : (Array[Symbol]? keys) -> Hash[Symbol, untyped] + def deconstruct_keys: (Array[Symbol]? keys) -> Hash[Symbol, untyped] + + # See `Node#type`. + # -- + # : () -> :break_node + def type: () -> :break_node + + # See `Node.type`. + # -- + # : () -> :break_node + def self.type: () -> :break_node + + # : () -> String + def inspect: () -> String + + # :call-seq: + # arguments -> ArgumentsNode | nil + # + # 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 + # ^^^ + # -- + # : () -> ArgumentsNode? + def arguments: () -> ArgumentsNode? + + # :category: Locations + # :call-seq: + # keyword_loc -> Location + # + # The Location of the `break` keyword. + # + # break foo + # ^^^^^ + # -- + # : () -> Location + def keyword_loc: () -> Location + + # :category: Repository + # Save the keyword_loc location using the given saved source so that + # it can be retrieved later. + # -- + # : (_Repository repository) -> Relocation::Entry + def save_keyword_loc: (_Repository repository) -> Relocation::Entry + + # :call-seq: + # keyword -> String + # + # Slice the location of keyword_loc from the source. + # -- + # : () -> String + def keyword: () -> String + + # : (untyped other) -> boolish + def ===: (untyped other) -> boolish + end + + # Represents the use of the `&&=` operator on a call. + # + # foo.bar &&= value + # ^^^^^^^^^^^^^^^^^ + class CallAndWriteNode < Node + @value: Prism::node + + @operator_loc: Location + + @write_name: Symbol + + @read_name: Symbol + + @message_loc: Location? + + @call_operator_loc: Location? + + @receiver: Prism::node? + + # Initialize a new CallAndWriteNode node. + # -- + # : (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 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 + + # See Node.accept. + # -- + # : (_Visitor visitor) -> untyped + def accept: (_Visitor visitor) -> untyped + + # See Node.child_nodes. + # -- + # : () -> Array[node?] + def child_nodes: () -> Array[node?] + + # See Node.each_child_node. + # -- + # : () -> Enumerator[node, void] + # : () { (node) -> void } -> void + def each_child_node: () -> Enumerator[node, void] + | () { (node) -> void } -> void + + # See Node.compact_child_nodes. + # -- + # : () -> Array[node] + def compact_child_nodes: () -> Array[node] + + # See Node.comment_targets. + # -- + # : () -> Array[node | Location] + def comment_targets: () -> Array[node | Location] + + # :call-seq: + # copy(**fields) -> CallAndWriteNode + # + # Creates a copy of self with the given fields, using self as the template. + # -- + # : (?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: 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 + + alias deconstruct child_nodes + + # : (Array[Symbol]? keys) -> Hash[Symbol, untyped] + def deconstruct_keys: (Array[Symbol]? keys) -> Hash[Symbol, untyped] + + # See `Node#type`. + # -- + # : () -> :call_and_write_node + def type: () -> :call_and_write_node + + # See `Node.type`. + # -- + # : () -> :call_and_write_node + def self.type: () -> :call_and_write_node + + # : () -> String + def inspect: () -> String + + # :category: Flags + # &. operator + # -- + # : () -> bool + def safe_navigation?: () -> bool + + # :category: Flags + # a call that could have been a local variable + # -- + # : () -> bool + def variable_call?: () -> bool + + # :category: Flags + # a call that is an attribute write, so the value being written should be returned + # -- + # : () -> bool + def attribute_write?: () -> bool + + # :category: Flags + # a call that ignores method visibility + # -- + # : () -> bool + def ignore_visibility?: () -> bool + + # :call-seq: + # receiver -> Node | nil + # + # 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 + # ^^^ + # -- + # : () -> Prism::node? + def receiver: () -> Prism::node? + + # :category: Locations + # :call-seq: + # call_operator_loc -> Location | nil + # + # Represents the Location of the call operator. + # + # foo.bar &&= value + # ^ + # -- + # : () -> Location? + def call_operator_loc: () -> Location? + + # :category: Repository + # Save the call_operator_loc location using the given saved source so that + # it can be retrieved later. + # -- + # : (_Repository repository) -> Relocation::Entry? + def save_call_operator_loc: (_Repository repository) -> Relocation::Entry? + + # :category: Locations + # :call-seq: + # message_loc -> Location | nil + # + # Represents the Location of the message. + # + # foo.bar &&= value + # ^^^ + # -- + # : () -> Location? + def message_loc: () -> Location? + + # :category: Repository + # Save the message_loc location using the given saved source so that + # it can be retrieved later. + # -- + # : (_Repository repository) -> Relocation::Entry? + def save_message_loc: (_Repository repository) -> Relocation::Entry? + + # :call-seq: + # read_name -> Symbol + # + # Represents the name of the method being called. + # + # foo.bar &&= value # read_name `:bar` + # ^^^ + # -- + # : () -> Symbol + def read_name: () -> Symbol + + # :call-seq: + # write_name -> Symbol + # + # Represents the name of the method being written to. + # + # foo.bar &&= value # write_name `:bar=` + # ^^^ + # -- + # : () -> Symbol + def write_name: () -> Symbol + + # :category: Locations + # :call-seq: + # operator_loc -> Location + # + # Represents the Location of the operator. + # + # foo.bar &&= value + # ^^^ + # -- + # : () -> Location + def operator_loc: () -> Location + + # :category: Repository + # Save the operator_loc location using the given saved source so that + # it can be retrieved later. + # -- + # : (_Repository repository) -> Relocation::Entry + def save_operator_loc: (_Repository repository) -> Relocation::Entry + + # :call-seq: + # value -> Node + # + # Represents the value being assigned. + # + # foo.bar &&= value + # ^^^^^ + # -- + # : () -> Prism::node + def value: () -> Prism::node + + # :call-seq: + # call_operator -> String | nil + # + # Slice the location of call_operator_loc from the source. + # -- + # : () -> String? + def call_operator: () -> String? + + # :call-seq: + # message -> String | nil + # + # Slice the location of message_loc from the source. + # -- + # : () -> String? + def message: () -> String? + + # :call-seq: + # operator -> String + # + # Slice the location of operator_loc from the source. + # -- + # : () -> String + def operator: () -> String + + # : (untyped other) -> boolish + def ===: (untyped other) -> boolish + 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 + @block: (BlockNode | BlockArgumentNode)? + + @equal_loc: Location? + + @closing_loc: Location? + + @arguments: ArgumentsNode? + + @opening_loc: Location? + + @message_loc: Location? + + @name: Symbol + + @call_operator_loc: Location? + + @receiver: Prism::node? + + # Initialize a new CallNode node. + # -- + # : (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)? block) -> 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)? block) -> void + + # See Node.accept. + # -- + # : (_Visitor visitor) -> untyped + def accept: (_Visitor visitor) -> untyped + + # See Node.child_nodes. + # -- + # : () -> Array[node?] + def child_nodes: () -> Array[node?] + + # See Node.each_child_node. + # -- + # : () -> Enumerator[node, void] + # : () { (node) -> void } -> void + def each_child_node: () -> Enumerator[node, void] + | () { (node) -> void } -> void + + # See Node.compact_child_nodes. + # -- + # : () -> Array[node] + def compact_child_nodes: () -> Array[node] + + # See Node.comment_targets. + # -- + # : () -> Array[node | Location] + def comment_targets: () -> Array[node | Location] + + # :call-seq: + # copy(**fields) -> CallNode + # + # Creates a copy of self with the given fields, using self as the template. + # -- + # : (?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)?) -> CallNode + 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)?) -> CallNode + + alias deconstruct child_nodes + + # : (Array[Symbol]? keys) -> Hash[Symbol, untyped] + def deconstruct_keys: (Array[Symbol]? keys) -> Hash[Symbol, untyped] + + # See `Node#type`. + # -- + # : () -> :call_node + def type: () -> :call_node + + # See `Node.type`. + # -- + # : () -> :call_node + def self.type: () -> :call_node + + # : () -> String + def inspect: () -> String + + # :category: Flags + # &. operator + # -- + # : () -> bool + def safe_navigation?: () -> bool + + # :category: Flags + # a call that could have been a local variable + # -- + # : () -> bool + def variable_call?: () -> bool + + # :category: Flags + # a call that is an attribute write, so the value being written should be returned + # -- + # : () -> bool + def attribute_write?: () -> bool + + # :category: Flags + # a call that ignores method visibility + # -- + # : () -> bool + def ignore_visibility?: () -> bool + + # :call-seq: + # receiver -> Node | nil + # + # 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 + # ^^^ + # -- + # : () -> Prism::node? + def receiver: () -> Prism::node? + + # :category: Locations + # :call-seq: + # call_operator_loc -> Location | nil + # + # Represents the Location of the call operator. + # + # foo.bar + # ^ + # + # foo&.bar + # ^^ + # -- + # : () -> Location? + def call_operator_loc: () -> Location? + + # :category: Repository + # Save the call_operator_loc location using the given saved source so that + # it can be retrieved later. + # -- + # : (_Repository repository) -> Relocation::Entry? + def save_call_operator_loc: (_Repository repository) -> Relocation::Entry? + + # :call-seq: + # name -> Symbol + # + # Represents the name of the method being called. + # + # foo.bar # name `:foo` + # ^^^ + # -- + # : () -> Symbol + def name: () -> Symbol + + # :category: Locations + # :call-seq: + # message_loc -> Location | nil + # + # Represents the Location of the message. + # + # foo.bar + # ^^^ + # -- + # : () -> Location? + def message_loc: () -> Location? + + # :category: Repository + # Save the message_loc location using the given saved source so that + # it can be retrieved later. + # -- + # : (_Repository repository) -> Relocation::Entry? + def save_message_loc: (_Repository repository) -> Relocation::Entry? + + # :category: Locations + # :call-seq: + # opening_loc -> Location | nil + # + # Represents the Location of the left parenthesis. + # + # foo(bar) + # ^ + # -- + # : () -> Location? + def opening_loc: () -> Location? + + # :category: Repository + # Save the opening_loc location using the given saved source so that + # it can be retrieved later. + # -- + # : (_Repository repository) -> Relocation::Entry? + def save_opening_loc: (_Repository repository) -> Relocation::Entry? + + # :call-seq: + # arguments -> ArgumentsNode | nil + # + # 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) + # ^^^ + # -- + # : () -> ArgumentsNode? + def arguments: () -> ArgumentsNode? + + # :category: Locations + # :call-seq: + # closing_loc -> Location | nil + # + # Represents the Location of the right parenthesis. + # + # foo(bar) + # ^ + # -- + # : () -> Location? + def closing_loc: () -> Location? + + # :category: Repository + # Save the closing_loc location using the given saved source so that + # it can be retrieved later. + # -- + # : (_Repository repository) -> Relocation::Entry? + def save_closing_loc: (_Repository repository) -> Relocation::Entry? + + # :category: Locations + # :call-seq: + # equal_loc -> Location | nil + # + # Represents the Location of the equal sign, in the case that this is an attribute write. + # + # foo.bar = value + # ^ + # + # foo[bar] = value + # ^ + # -- + # : () -> Location? + def equal_loc: () -> Location? + + # :category: Repository + # Save the equal_loc location using the given saved source so that + # it can be retrieved later. + # -- + # : (_Repository repository) -> Relocation::Entry? + def save_equal_loc: (_Repository repository) -> Relocation::Entry? + + # :call-seq: + # block -> BlockNode | BlockArgumentNode | nil + # + # Represents the block that is being passed to the method. + # + # foo { |a| a } + # ^^^^^^^^^ + # -- + # : () -> (BlockNode | BlockArgumentNode)? + def block: () -> (BlockNode | BlockArgumentNode)? + + # :call-seq: + # call_operator -> String | nil + # + # Slice the location of call_operator_loc from the source. + # -- + # : () -> String? + def call_operator: () -> String? + + # :call-seq: + # message -> String | nil + # + # Slice the location of message_loc from the source. + # -- + # : () -> String? + def message: () -> String? + + # :call-seq: + # opening -> String | nil + # + # Slice the location of opening_loc from the source. + # -- + # : () -> String? + def opening: () -> String? + + # :call-seq: + # closing -> String | nil + # + # Slice the location of closing_loc from the source. + # -- + # : () -> String? + def closing: () -> String? + + # :call-seq: + # equal -> String | nil + # + # Slice the location of equal_loc from the source. + # -- + # : () -> String? + def equal: () -> String? + + # : (untyped other) -> boolish + def ===: (untyped other) -> boolish + end + + # Represents the use of an assignment operator on a call. + # + # foo.bar += baz + # ^^^^^^^^^^^^^^ + class CallOperatorWriteNode < Node + @value: Prism::node + + @binary_operator_loc: Location + + @binary_operator: Symbol + + @write_name: Symbol + + @read_name: Symbol + + @message_loc: Location? + + @call_operator_loc: Location? + + @receiver: Prism::node? + + # Initialize a new CallOperatorWriteNode node. + # -- + # : (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 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 + + # See Node.accept. + # -- + # : (_Visitor visitor) -> untyped + def accept: (_Visitor visitor) -> untyped + + # See Node.child_nodes. + # -- + # : () -> Array[node?] + def child_nodes: () -> Array[node?] + + # See Node.each_child_node. + # -- + # : () -> Enumerator[node, void] + # : () { (node) -> void } -> void + def each_child_node: () -> Enumerator[node, void] + | () { (node) -> void } -> void + + # See Node.compact_child_nodes. + # -- + # : () -> Array[node] + def compact_child_nodes: () -> Array[node] + + # See Node.comment_targets. + # -- + # : () -> Array[node | Location] + def comment_targets: () -> Array[node | Location] + + # :call-seq: + # copy(**fields) -> CallOperatorWriteNode + # + # Creates a copy of self with the given fields, using self as the template. + # -- + # : (?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: 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 + + alias deconstruct child_nodes + + # : (Array[Symbol]? keys) -> Hash[Symbol, untyped] + def deconstruct_keys: (Array[Symbol]? keys) -> Hash[Symbol, untyped] + + # See `Node#type`. + # -- + # : () -> :call_operator_write_node + def type: () -> :call_operator_write_node + + # See `Node.type`. + # -- + # : () -> :call_operator_write_node + def self.type: () -> :call_operator_write_node + + # : () -> String + def inspect: () -> String + + # :category: Flags + # &. operator + # -- + # : () -> bool + def safe_navigation?: () -> bool + + # :category: Flags + # a call that could have been a local variable + # -- + # : () -> bool + def variable_call?: () -> bool + + # :category: Flags + # a call that is an attribute write, so the value being written should be returned + # -- + # : () -> bool + def attribute_write?: () -> bool + + # :category: Flags + # a call that ignores method visibility + # -- + # : () -> bool + def ignore_visibility?: () -> bool + + # :call-seq: + # receiver -> Node | nil + # + # 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 + # ^^^ + # -- + # : () -> Prism::node? + def receiver: () -> Prism::node? + + # :category: Locations + # :call-seq: + # call_operator_loc -> Location | nil + # + # Represents the Location of the call operator. + # + # foo.bar += value + # ^ + # -- + # : () -> Location? + def call_operator_loc: () -> Location? + + # :category: Repository + # Save the call_operator_loc location using the given saved source so that + # it can be retrieved later. + # -- + # : (_Repository repository) -> Relocation::Entry? + def save_call_operator_loc: (_Repository repository) -> Relocation::Entry? + + # :category: Locations + # :call-seq: + # message_loc -> Location | nil + # + # Represents the Location of the message. + # + # foo.bar += value + # ^^^ + # -- + # : () -> Location? + def message_loc: () -> Location? + + # :category: Repository + # Save the message_loc location using the given saved source so that + # it can be retrieved later. + # -- + # : (_Repository repository) -> Relocation::Entry? + def save_message_loc: (_Repository repository) -> Relocation::Entry? + + # :call-seq: + # read_name -> Symbol + # + # Represents the name of the method being called. + # + # foo.bar += value # read_name `:bar` + # ^^^ + # -- + # : () -> Symbol + def read_name: () -> Symbol + + # :call-seq: + # write_name -> Symbol + # + # Represents the name of the method being written to. + # + # foo.bar += value # write_name `:bar=` + # ^^^ + # -- + # : () -> Symbol + def write_name: () -> Symbol + + # :call-seq: + # binary_operator -> Symbol + # + # Represents the binary operator being used. + # + # foo.bar += value # binary_operator `:+` + # ^ + # -- + # : () -> Symbol + def binary_operator: () -> Symbol + + # :category: Locations + # :call-seq: + # binary_operator_loc -> Location + # + # Represents the Location of the binary operator. + # + # foo.bar += value + # ^^ + # -- + # : () -> Location + def binary_operator_loc: () -> Location + + # :category: Repository + # Save the binary_operator_loc location using the given saved source so that + # it can be retrieved later. + # -- + # : (_Repository repository) -> Relocation::Entry + def save_binary_operator_loc: (_Repository repository) -> Relocation::Entry + + # :call-seq: + # value -> Node + # + # Represents the value being assigned. + # + # foo.bar += value + # ^^^^^ + # -- + # : () -> Prism::node + def value: () -> Prism::node + + # :call-seq: + # call_operator -> String | nil + # + # Slice the location of call_operator_loc from the source. + # -- + # : () -> String? + def call_operator: () -> String? + + # :call-seq: + # message -> String | nil + # + # Slice the location of message_loc from the source. + # -- + # : () -> String? + def message: () -> String? + + # : (untyped other) -> boolish + def ===: (untyped other) -> boolish + end + + # Represents the use of the `||=` operator on a call. + # + # foo.bar ||= value + # ^^^^^^^^^^^^^^^^^ + class CallOrWriteNode < Node + @value: Prism::node + + @operator_loc: Location + + @write_name: Symbol + + @read_name: Symbol + + @message_loc: Location? + + @call_operator_loc: Location? + + @receiver: Prism::node? + + # Initialize a new CallOrWriteNode node. + # -- + # : (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 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 + + # See Node.accept. + # -- + # : (_Visitor visitor) -> untyped + def accept: (_Visitor visitor) -> untyped + + # See Node.child_nodes. + # -- + # : () -> Array[node?] + def child_nodes: () -> Array[node?] + + # See Node.each_child_node. + # -- + # : () -> Enumerator[node, void] + # : () { (node) -> void } -> void + def each_child_node: () -> Enumerator[node, void] + | () { (node) -> void } -> void + + # See Node.compact_child_nodes. + # -- + # : () -> Array[node] + def compact_child_nodes: () -> Array[node] + + # See Node.comment_targets. + # -- + # : () -> Array[node | Location] + def comment_targets: () -> Array[node | Location] + + # :call-seq: + # copy(**fields) -> CallOrWriteNode + # + # Creates a copy of self with the given fields, using self as the template. + # -- + # : (?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: 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 + + alias deconstruct child_nodes + + # : (Array[Symbol]? keys) -> Hash[Symbol, untyped] + def deconstruct_keys: (Array[Symbol]? keys) -> Hash[Symbol, untyped] + + # See `Node#type`. + # -- + # : () -> :call_or_write_node + def type: () -> :call_or_write_node + + # See `Node.type`. + # -- + # : () -> :call_or_write_node + def self.type: () -> :call_or_write_node + + # : () -> String + def inspect: () -> String + + # :category: Flags + # &. operator + # -- + # : () -> bool + def safe_navigation?: () -> bool + + # :category: Flags + # a call that could have been a local variable + # -- + # : () -> bool + def variable_call?: () -> bool + + # :category: Flags + # a call that is an attribute write, so the value being written should be returned + # -- + # : () -> bool + def attribute_write?: () -> bool + + # :category: Flags + # a call that ignores method visibility + # -- + # : () -> bool + def ignore_visibility?: () -> bool + + # :call-seq: + # receiver -> Node | nil + # + # 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 + # ^^^ + # -- + # : () -> Prism::node? + def receiver: () -> Prism::node? + + # :category: Locations + # :call-seq: + # call_operator_loc -> Location | nil + # + # Represents the Location of the call operator. + # + # foo.bar ||= value + # ^ + # -- + # : () -> Location? + def call_operator_loc: () -> Location? + + # :category: Repository + # Save the call_operator_loc location using the given saved source so that + # it can be retrieved later. + # -- + # : (_Repository repository) -> Relocation::Entry? + def save_call_operator_loc: (_Repository repository) -> Relocation::Entry? + + # :category: Locations + # :call-seq: + # message_loc -> Location | nil + # + # Represents the Location of the message. + # + # foo.bar ||= value + # ^^^ + # -- + # : () -> Location? + def message_loc: () -> Location? + + # :category: Repository + # Save the message_loc location using the given saved source so that + # it can be retrieved later. + # -- + # : (_Repository repository) -> Relocation::Entry? + def save_message_loc: (_Repository repository) -> Relocation::Entry? + + # :call-seq: + # read_name -> Symbol + # + # Represents the name of the method being called. + # + # foo.bar ||= value # read_name `:bar` + # ^^^ + # -- + # : () -> Symbol + def read_name: () -> Symbol + + # :call-seq: + # write_name -> Symbol + # + # Represents the name of the method being written to. + # + # foo.bar ||= value # write_name `:bar=` + # ^^^ + # -- + # : () -> Symbol + def write_name: () -> Symbol + + # :category: Locations + # :call-seq: + # operator_loc -> Location + # + # Represents the Location of the operator. + # + # foo.bar ||= value + # ^^^ + # -- + # : () -> Location + def operator_loc: () -> Location + + # :category: Repository + # Save the operator_loc location using the given saved source so that + # it can be retrieved later. + # -- + # : (_Repository repository) -> Relocation::Entry + def save_operator_loc: (_Repository repository) -> Relocation::Entry + + # :call-seq: + # value -> Node + # + # Represents the value being assigned. + # + # foo.bar ||= value + # ^^^^^ + # -- + # : () -> Prism::node + def value: () -> Prism::node + + # :call-seq: + # call_operator -> String | nil + # + # Slice the location of call_operator_loc from the source. + # -- + # : () -> String? + def call_operator: () -> String? + + # :call-seq: + # message -> String | nil + # + # Slice the location of message_loc from the source. + # -- + # : () -> String? + def message: () -> String? + + # :call-seq: + # operator -> String + # + # Slice the location of operator_loc from the source. + # -- + # : () -> String + def operator: () -> String + + # : (untyped other) -> boolish + def ===: (untyped other) -> boolish + 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 + @message_loc: Location + + @name: Symbol + + @call_operator_loc: Location + + @receiver: Prism::node + + # Initialize a new CallTargetNode node. + # -- + # : (Source source, Integer node_id, Location location, Integer flags, Prism::node receiver, Location call_operator_loc, Symbol name, Location message_loc) -> 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 + + # See Node.accept. + # -- + # : (_Visitor visitor) -> untyped + def accept: (_Visitor visitor) -> untyped + + # See Node.child_nodes. + # -- + # : () -> Array[node?] + def child_nodes: () -> Array[node?] + + # See Node.each_child_node. + # -- + # : () -> Enumerator[node, void] + # : () { (node) -> void } -> void + def each_child_node: () -> Enumerator[node, void] + | () { (node) -> void } -> void + + # See Node.compact_child_nodes. + # -- + # : () -> Array[node] + def compact_child_nodes: () -> Array[node] + + # See Node.comment_targets. + # -- + # : () -> Array[node | Location] + def comment_targets: () -> Array[node | Location] + + # :call-seq: + # copy(**fields) -> CallTargetNode + # + # Creates a copy of self with the given fields, using self as the template. + # -- + # : (?node_id: Integer, ?location: Location, ?flags: Integer, ?receiver: Prism::node, ?call_operator_loc: Location, ?name: Symbol, ?message_loc: Location) -> CallTargetNode + def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?receiver: Prism::node, ?call_operator_loc: Location, ?name: Symbol, ?message_loc: Location) -> CallTargetNode + + alias deconstruct child_nodes + + # : (Array[Symbol]? keys) -> Hash[Symbol, untyped] + def deconstruct_keys: (Array[Symbol]? keys) -> Hash[Symbol, untyped] + + # See `Node#type`. + # -- + # : () -> :call_target_node + def type: () -> :call_target_node + + # See `Node.type`. + # -- + # : () -> :call_target_node + def self.type: () -> :call_target_node + + # : () -> String + def inspect: () -> String + + # :category: Flags + # &. operator + # -- + # : () -> bool + def safe_navigation?: () -> bool + + # :category: Flags + # a call that could have been a local variable + # -- + # : () -> bool + def variable_call?: () -> bool + + # :category: Flags + # a call that is an attribute write, so the value being written should be returned + # -- + # : () -> bool + def attribute_write?: () -> bool + + # :category: Flags + # a call that ignores method visibility + # -- + # : () -> bool + def ignore_visibility?: () -> bool + + # :call-seq: + # receiver -> Node + # + # 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 + # ^^^ + # -- + # : () -> Prism::node + def receiver: () -> Prism::node + + # :category: Locations + # :call-seq: + # call_operator_loc -> Location + # + # Represents the Location of the call operator. + # + # foo.bar = 1 + # ^ + # -- + # : () -> Location + def call_operator_loc: () -> Location + + # :category: Repository + # Save the call_operator_loc location using the given saved source so that + # it can be retrieved later. + # -- + # : (_Repository repository) -> Relocation::Entry + def save_call_operator_loc: (_Repository repository) -> Relocation::Entry + + # :call-seq: + # name -> Symbol + # + # Represents the name of the method being called. + # + # foo.bar = 1 # name `:foo` + # ^^^ + # -- + # : () -> Symbol + def name: () -> Symbol + + # :category: Locations + # :call-seq: + # message_loc -> Location + # + # Represents the Location of the message. + # + # foo.bar = 1 + # ^^^ + # -- + # : () -> Location + def message_loc: () -> Location + + # :category: Repository + # Save the message_loc location using the given saved source so that + # it can be retrieved later. + # -- + # : (_Repository repository) -> Relocation::Entry + def save_message_loc: (_Repository repository) -> Relocation::Entry + + # :call-seq: + # call_operator -> String + # + # Slice the location of call_operator_loc from the source. + # -- + # : () -> String + def call_operator: () -> String + + # :call-seq: + # message -> String + # + # Slice the location of message_loc from the source. + # -- + # : () -> String + def message: () -> String + + # : (untyped other) -> boolish + def ===: (untyped other) -> boolish + end + + # Represents assigning to a local variable in pattern matching. + # + # foo => [bar => baz] + # ^^^^^^^^^^ + class CapturePatternNode < Node + @operator_loc: Location + + @target: LocalVariableTargetNode + + @value: Prism::node + + # Initialize a new CapturePatternNode node. + # -- + # : (Source source, Integer node_id, Location location, Integer flags, Prism::node value, LocalVariableTargetNode target, Location operator_loc) -> void + def initialize: (Source source, Integer node_id, Location location, Integer flags, Prism::node value, LocalVariableTargetNode target, Location operator_loc) -> void + + # See Node.accept. + # -- + # : (_Visitor visitor) -> untyped + def accept: (_Visitor visitor) -> untyped + + # See Node.child_nodes. + # -- + # : () -> Array[node?] + def child_nodes: () -> Array[node?] + + # See Node.each_child_node. + # -- + # : () -> Enumerator[node, void] + # : () { (node) -> void } -> void + def each_child_node: () -> Enumerator[node, void] + | () { (node) -> void } -> void + + # See Node.compact_child_nodes. + # -- + # : () -> Array[node] + def compact_child_nodes: () -> Array[node] + + # See Node.comment_targets. + # -- + # : () -> Array[node | Location] + def comment_targets: () -> Array[node | Location] + + # :call-seq: + # copy(**fields) -> CapturePatternNode + # + # Creates a copy of self with the given fields, using self as the template. + # -- + # : (?node_id: Integer, ?location: Location, ?flags: Integer, ?value: Prism::node, ?target: LocalVariableTargetNode, ?operator_loc: Location) -> CapturePatternNode + def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?value: Prism::node, ?target: LocalVariableTargetNode, ?operator_loc: Location) -> CapturePatternNode + + alias deconstruct child_nodes + + # : (Array[Symbol]? keys) -> Hash[Symbol, untyped] + def deconstruct_keys: (Array[Symbol]? keys) -> Hash[Symbol, untyped] + + # See `Node#type`. + # -- + # : () -> :capture_pattern_node + def type: () -> :capture_pattern_node + + # See `Node.type`. + # -- + # : () -> :capture_pattern_node + def self.type: () -> :capture_pattern_node + + # : () -> String + def inspect: () -> String + + # :call-seq: + # value -> Node + # + # Represents the value to capture. + # + # foo => bar + # ^^^ + # -- + # : () -> Prism::node + def value: () -> Prism::node + + # :call-seq: + # target -> LocalVariableTargetNode + # + # Represents the target of the capture. + # + # foo => bar + # ^^^ + # -- + # : () -> LocalVariableTargetNode + def target: () -> LocalVariableTargetNode + + # :category: Locations + # :call-seq: + # operator_loc -> Location + # + # Represents the Location of the `=>` operator. + # + # foo => bar + # ^^ + # -- + # : () -> Location + def operator_loc: () -> Location + + # :category: Repository + # Save the operator_loc location using the given saved source so that + # it can be retrieved later. + # -- + # : (_Repository repository) -> Relocation::Entry + def save_operator_loc: (_Repository repository) -> Relocation::Entry + + # :call-seq: + # operator -> String + # + # Slice the location of operator_loc from the source. + # -- + # : () -> String + def operator: () -> String + + # : (untyped other) -> boolish + def ===: (untyped other) -> boolish + end + + # Represents the use of a case statement for pattern matching. + # + # case true + # in false + # end + # ^^^^^^^^^ + class CaseMatchNode < Node + @end_keyword_loc: Location + + @case_keyword_loc: Location + + @else_clause: ElseNode? + + @conditions: Array[InNode] + + @predicate: Prism::node? + + # Initialize a new CaseMatchNode node. + # -- + # : (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 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 + + # See Node.accept. + # -- + # : (_Visitor visitor) -> untyped + def accept: (_Visitor visitor) -> untyped + + # See Node.child_nodes. + # -- + # : () -> Array[node?] + def child_nodes: () -> Array[node?] + + # See Node.each_child_node. + # -- + # : () -> Enumerator[node, void] + # : () { (node) -> void } -> void + def each_child_node: () -> Enumerator[node, void] + | () { (node) -> void } -> void + + # See Node.compact_child_nodes. + # -- + # : () -> Array[node] + def compact_child_nodes: () -> Array[node] + + # See Node.comment_targets. + # -- + # : () -> Array[node | Location] + def comment_targets: () -> Array[node | Location] + + # :call-seq: + # copy(**fields) -> CaseMatchNode + # + # Creates a copy of self with the given fields, using self as the template. + # -- + # : (?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: Integer, ?location: Location, ?flags: Integer, ?predicate: Prism::node?, ?conditions: Array[InNode], ?else_clause: ElseNode?, ?case_keyword_loc: Location, ?end_keyword_loc: Location) -> CaseMatchNode + + alias deconstruct child_nodes + + # : (Array[Symbol]? keys) -> Hash[Symbol, untyped] + def deconstruct_keys: (Array[Symbol]? keys) -> Hash[Symbol, untyped] + + # See `Node#type`. + # -- + # : () -> :case_match_node + def type: () -> :case_match_node + + # See `Node.type`. + # -- + # : () -> :case_match_node + def self.type: () -> :case_match_node + + # : () -> String + def inspect: () -> String + + # :call-seq: + # predicate -> Node | nil + # + # 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 + # ^^^^ + # -- + # : () -> Prism::node? + def predicate: () -> Prism::node? + + # :call-seq: + # conditions -> Array[InNode] + # + # Represents the conditions of the case match. + # + # case true; in false; end + # ^^^^^^^^ + # -- + # : () -> Array[InNode] + def conditions: () -> Array[InNode] + + # :call-seq: + # else_clause -> ElseNode | nil + # + # Represents the else clause of the case match. + # + # case true; in false; else; end + # ^^^^^^^^^ + # -- + # : () -> ElseNode? + def else_clause: () -> ElseNode? + + # :category: Locations + # :call-seq: + # case_keyword_loc -> Location + # + # Represents the Location of the `case` keyword. + # + # case true; in false; end + # ^^^^ + # -- + # : () -> Location + def case_keyword_loc: () -> Location + + # :category: Repository + # Save the case_keyword_loc location using the given saved source so that + # it can be retrieved later. + # -- + # : (_Repository repository) -> Relocation::Entry + def save_case_keyword_loc: (_Repository repository) -> Relocation::Entry + + # :category: Locations + # :call-seq: + # end_keyword_loc -> Location + # + # Represents the Location of the `end` keyword. + # + # case true; in false; end + # ^^^ + # -- + # : () -> Location + def end_keyword_loc: () -> Location + + # :category: Repository + # Save the end_keyword_loc location using the given saved source so that + # it can be retrieved later. + # -- + # : (_Repository repository) -> Relocation::Entry + def save_end_keyword_loc: (_Repository repository) -> Relocation::Entry + + # :call-seq: + # case_keyword -> String + # + # Slice the location of case_keyword_loc from the source. + # -- + # : () -> String + def case_keyword: () -> String + + # :call-seq: + # end_keyword -> String + # + # Slice the location of end_keyword_loc from the source. + # -- + # : () -> String + def end_keyword: () -> String + + # : (untyped other) -> boolish + def ===: (untyped other) -> boolish + end + + # Represents the use of a case statement. + # + # case true + # when false + # end + # ^^^^^^^^^^ + class CaseNode < Node + @end_keyword_loc: Location + + @case_keyword_loc: Location + + @else_clause: ElseNode? + + @conditions: Array[WhenNode] + + @predicate: Prism::node? + + # Initialize a new CaseNode node. + # -- + # : (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 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 + + # See Node.accept. + # -- + # : (_Visitor visitor) -> untyped + def accept: (_Visitor visitor) -> untyped + + # See Node.child_nodes. + # -- + # : () -> Array[node?] + def child_nodes: () -> Array[node?] + + # See Node.each_child_node. + # -- + # : () -> Enumerator[node, void] + # : () { (node) -> void } -> void + def each_child_node: () -> Enumerator[node, void] + | () { (node) -> void } -> void + + # See Node.compact_child_nodes. + # -- + # : () -> Array[node] + def compact_child_nodes: () -> Array[node] + + # See Node.comment_targets. + # -- + # : () -> Array[node | Location] + def comment_targets: () -> Array[node | Location] + + # :call-seq: + # copy(**fields) -> CaseNode + # + # Creates a copy of self with the given fields, using self as the template. + # -- + # : (?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: Integer, ?location: Location, ?flags: Integer, ?predicate: Prism::node?, ?conditions: Array[WhenNode], ?else_clause: ElseNode?, ?case_keyword_loc: Location, ?end_keyword_loc: Location) -> CaseNode + + alias deconstruct child_nodes + + # : (Array[Symbol]? keys) -> Hash[Symbol, untyped] + def deconstruct_keys: (Array[Symbol]? keys) -> Hash[Symbol, untyped] + + # See `Node#type`. + # -- + # : () -> :case_node + def type: () -> :case_node + + # See `Node.type`. + # -- + # : () -> :case_node + def self.type: () -> :case_node + + # : () -> String + def inspect: () -> String + + # :call-seq: + # predicate -> Node | nil + # + # 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 + # ^^^^ + # -- + # : () -> Prism::node? + def predicate: () -> Prism::node? + + # :call-seq: + # conditions -> Array[WhenNode] + # + # Represents the conditions of the case statement. + # + # case true; when false; end + # ^^^^^^^^^^ + # -- + # : () -> Array[WhenNode] + def conditions: () -> Array[WhenNode] + + # :call-seq: + # else_clause -> ElseNode | nil + # + # Represents the else clause of the case statement. + # + # case true; when false; else; end + # ^^^^^^^^^ + # -- + # : () -> ElseNode? + def else_clause: () -> ElseNode? + + # :category: Locations + # :call-seq: + # case_keyword_loc -> Location + # + # Represents the Location of the `case` keyword. + # + # case true; when false; end + # ^^^^ + # -- + # : () -> Location + def case_keyword_loc: () -> Location + + # :category: Repository + # Save the case_keyword_loc location using the given saved source so that + # it can be retrieved later. + # -- + # : (_Repository repository) -> Relocation::Entry + def save_case_keyword_loc: (_Repository repository) -> Relocation::Entry + + # :category: Locations + # :call-seq: + # end_keyword_loc -> Location + # + # Represents the Location of the `end` keyword. + # + # case true; when false; end + # ^^^ + # -- + # : () -> Location + def end_keyword_loc: () -> Location + + # :category: Repository + # Save the end_keyword_loc location using the given saved source so that + # it can be retrieved later. + # -- + # : (_Repository repository) -> Relocation::Entry + def save_end_keyword_loc: (_Repository repository) -> Relocation::Entry + + # :call-seq: + # case_keyword -> String + # + # Slice the location of case_keyword_loc from the source. + # -- + # : () -> String + def case_keyword: () -> String + + # :call-seq: + # end_keyword -> String + # + # Slice the location of end_keyword_loc from the source. + # -- + # : () -> String + def end_keyword: () -> String + + # : (untyped other) -> boolish + def ===: (untyped other) -> boolish + end + + # Represents a class declaration involving the `class` keyword. + # + # class Foo end + # ^^^^^^^^^^^^^ + class ClassNode < Node + @name: Symbol + + @end_keyword_loc: Location + + @body: (StatementsNode | BeginNode)? + + @superclass: Prism::node? + + @inheritance_operator_loc: Location? + + @constant_path: ConstantReadNode | ConstantPathNode | CallNode + + @class_keyword_loc: Location + + @locals: Array[Symbol] + + # Initialize a new ClassNode node. + # -- + # : (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)? body, Location end_keyword_loc, Symbol name) -> 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)? body, Location end_keyword_loc, Symbol name) -> void + + # See Node.accept. + # -- + # : (_Visitor visitor) -> untyped + def accept: (_Visitor visitor) -> untyped + + # See Node.child_nodes. + # -- + # : () -> Array[node?] + def child_nodes: () -> Array[node?] + + # See Node.each_child_node. + # -- + # : () -> Enumerator[node, void] + # : () { (node) -> void } -> void + def each_child_node: () -> Enumerator[node, void] + | () { (node) -> void } -> void + + # See Node.compact_child_nodes. + # -- + # : () -> Array[node] + def compact_child_nodes: () -> Array[node] + + # See Node.comment_targets. + # -- + # : () -> Array[node | Location] + def comment_targets: () -> Array[node | Location] + + # :call-seq: + # copy(**fields) -> ClassNode + # + # Creates a copy of self with the given fields, using self as the template. + # -- + # : (?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)?, ?end_keyword_loc: Location, ?name: Symbol) -> ClassNode + 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)?, ?end_keyword_loc: Location, ?name: Symbol) -> ClassNode + + alias deconstruct child_nodes + + # : (Array[Symbol]? keys) -> Hash[Symbol, untyped] + def deconstruct_keys: (Array[Symbol]? keys) -> Hash[Symbol, untyped] + + # See `Node#type`. + # -- + # : () -> :class_node + def type: () -> :class_node + + # See `Node.type`. + # -- + # : () -> :class_node + def self.type: () -> :class_node + + # : () -> String + def inspect: () -> String + + # :call-seq: + # locals -> Array[Symbol] + # + # Returns the `locals` attribute. + # -- + # : () -> Array[Symbol] + def locals: () -> Array[Symbol] + + # :category: Locations + # :call-seq: + # class_keyword_loc -> Location + # + # Represents the Location of the `class` keyword. + # + # class Foo end + # ^^^^^ + # -- + # : () -> Location + def class_keyword_loc: () -> Location + + # :category: Repository + # Save the class_keyword_loc location using the given saved source so that + # it can be retrieved later. + # -- + # : (_Repository repository) -> Relocation::Entry + def save_class_keyword_loc: (_Repository repository) -> Relocation::Entry + + # :call-seq: + # constant_path -> ConstantReadNode | ConstantPathNode | CallNode + # + # Returns the `constant_path` attribute. + # -- + # : () -> (ConstantReadNode | ConstantPathNode | CallNode) + def constant_path: () -> (ConstantReadNode | ConstantPathNode | CallNode) + + # :category: Locations + # :call-seq: + # inheritance_operator_loc -> Location | nil + # + # Represents the Location of the `<` operator. + # + # class Foo < Bar + # ^ + # -- + # : () -> Location? + def inheritance_operator_loc: () -> Location? + + # :category: Repository + # Save the inheritance_operator_loc location using the given saved source so that + # it can be retrieved later. + # -- + # : (_Repository repository) -> Relocation::Entry? + def save_inheritance_operator_loc: (_Repository repository) -> Relocation::Entry? + + # :call-seq: + # superclass -> Node | nil + # + # Represents the superclass of the class. + # + # class Foo < Bar + # ^^^ + # -- + # : () -> Prism::node? + def superclass: () -> Prism::node? + + # :call-seq: + # body -> StatementsNode | BeginNode | nil + # + # Represents the body of the class. + # + # class Foo; bar; end + # ^^^ + # -- + # : () -> (StatementsNode | BeginNode)? + def body: () -> (StatementsNode | BeginNode)? + + # :category: Locations + # :call-seq: + # end_keyword_loc -> Location + # + # Represents the Location of the `end` keyword. + # + # class Foo end + # ^^^ + # -- + # : () -> Location + def end_keyword_loc: () -> Location + + # :category: Repository + # Save the end_keyword_loc location using the given saved source so that + # it can be retrieved later. + # -- + # : (_Repository repository) -> Relocation::Entry + def save_end_keyword_loc: (_Repository repository) -> Relocation::Entry + + # :call-seq: + # name -> Symbol + # + # The name of the class. + # + # class Foo end # name `:Foo` + # -- + # : () -> Symbol + def name: () -> Symbol + + # :call-seq: + # class_keyword -> String + # + # Slice the location of class_keyword_loc from the source. + # -- + # : () -> String + def class_keyword: () -> String + + # :call-seq: + # inheritance_operator -> String | nil + # + # Slice the location of inheritance_operator_loc from the source. + # -- + # : () -> String? + def inheritance_operator: () -> String? + + # :call-seq: + # end_keyword -> String + # + # Slice the location of end_keyword_loc from the source. + # -- + # : () -> String + def end_keyword: () -> String + + # : (untyped other) -> boolish + def ===: (untyped other) -> boolish + end + + # Represents the use of the `&&=` operator for assignment to a class variable. + # + # @@target &&= value + # ^^^^^^^^^^^^^^^^^^ + class ClassVariableAndWriteNode < Node + @value: Prism::node + + @operator_loc: Location + + @name_loc: Location + + @name: Symbol + + # Initialize a new ClassVariableAndWriteNode node. + # -- + # : (Source source, Integer node_id, Location location, Integer flags, Symbol name, Location name_loc, Location operator_loc, Prism::node value) -> void + def initialize: (Source source, Integer node_id, Location location, Integer flags, Symbol name, Location name_loc, Location operator_loc, Prism::node value) -> void + + # See Node.accept. + # -- + # : (_Visitor visitor) -> untyped + def accept: (_Visitor visitor) -> untyped + + # See Node.child_nodes. + # -- + # : () -> Array[node?] + def child_nodes: () -> Array[node?] + + # See Node.each_child_node. + # -- + # : () -> Enumerator[node, void] + # : () { (node) -> void } -> void + def each_child_node: () -> Enumerator[node, void] + | () { (node) -> void } -> void + + # See Node.compact_child_nodes. + # -- + # : () -> Array[node] + def compact_child_nodes: () -> Array[node] + + # See Node.comment_targets. + # -- + # : () -> Array[node | Location] + def comment_targets: () -> Array[node | Location] + + # :call-seq: + # copy(**fields) -> ClassVariableAndWriteNode + # + # Creates a copy of self with the given fields, using self as the template. + # -- + # : (?node_id: Integer, ?location: Location, ?flags: Integer, ?name: Symbol, ?name_loc: Location, ?operator_loc: Location, ?value: Prism::node) -> ClassVariableAndWriteNode + def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?name: Symbol, ?name_loc: Location, ?operator_loc: Location, ?value: Prism::node) -> ClassVariableAndWriteNode + + alias deconstruct child_nodes + + # : (Array[Symbol]? keys) -> Hash[Symbol, untyped] + def deconstruct_keys: (Array[Symbol]? keys) -> Hash[Symbol, untyped] + + # See `Node#type`. + # -- + # : () -> :class_variable_and_write_node + def type: () -> :class_variable_and_write_node + + # See `Node.type`. + # -- + # : () -> :class_variable_and_write_node + def self.type: () -> :class_variable_and_write_node + + # : () -> String + def inspect: () -> String + + # :call-seq: + # name -> Symbol + # + # 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` + # ^^^^^^^^ + # -- + # : () -> Symbol + def name: () -> Symbol + + # :category: Locations + # :call-seq: + # name_loc -> Location + # + # Represents the Location of the variable name. + # + # @@target &&= value + # ^^^^^^^^ + # -- + # : () -> Location + def name_loc: () -> Location + + # :category: Repository + # Save the name_loc location using the given saved source so that + # it can be retrieved later. + # -- + # : (_Repository repository) -> Relocation::Entry + def save_name_loc: (_Repository repository) -> Relocation::Entry + + # :category: Locations + # :call-seq: + # operator_loc -> Location + # + # Represents the Location of the `&&=` operator. + # + # @@target &&= value + # ^^^ + # -- + # : () -> Location + def operator_loc: () -> Location + + # :category: Repository + # Save the operator_loc location using the given saved source so that + # it can be retrieved later. + # -- + # : (_Repository repository) -> Relocation::Entry + def save_operator_loc: (_Repository repository) -> Relocation::Entry + + # :call-seq: + # value -> Node + # + # 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 + # ^^^^^ + # -- + # : () -> Prism::node + def value: () -> Prism::node + + # :call-seq: + # operator -> String + # + # Slice the location of operator_loc from the source. + # -- + # : () -> String + def operator: () -> String + + # : (untyped other) -> boolish + def ===: (untyped other) -> boolish + end + + # Represents assigning to a class variable using an operator that isn't `=`. + # + # @@target += value + # ^^^^^^^^^^^^^^^^^ + class ClassVariableOperatorWriteNode < Node + @binary_operator: Symbol + + @value: Prism::node + + @binary_operator_loc: Location + + @name_loc: Location + + @name: Symbol + + # Initialize a new ClassVariableOperatorWriteNode node. + # -- + # : (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 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 + + # See Node.accept. + # -- + # : (_Visitor visitor) -> untyped + def accept: (_Visitor visitor) -> untyped + + # See Node.child_nodes. + # -- + # : () -> Array[node?] + def child_nodes: () -> Array[node?] + + # See Node.each_child_node. + # -- + # : () -> Enumerator[node, void] + # : () { (node) -> void } -> void + def each_child_node: () -> Enumerator[node, void] + | () { (node) -> void } -> void + + # See Node.compact_child_nodes. + # -- + # : () -> Array[node] + def compact_child_nodes: () -> Array[node] + + # See Node.comment_targets. + # -- + # : () -> Array[node | Location] + def comment_targets: () -> Array[node | Location] + + # :call-seq: + # copy(**fields) -> ClassVariableOperatorWriteNode + # + # Creates a copy of self with the given fields, using self as the template. + # -- + # : (?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: Integer, ?location: Location, ?flags: Integer, ?name: Symbol, ?name_loc: Location, ?binary_operator_loc: Location, ?value: Prism::node, ?binary_operator: Symbol) -> ClassVariableOperatorWriteNode + + alias deconstruct child_nodes + + # : (Array[Symbol]? keys) -> Hash[Symbol, untyped] + def deconstruct_keys: (Array[Symbol]? keys) -> Hash[Symbol, untyped] + + # See `Node#type`. + # -- + # : () -> :class_variable_operator_write_node + def type: () -> :class_variable_operator_write_node + + # See `Node.type`. + # -- + # : () -> :class_variable_operator_write_node + def self.type: () -> :class_variable_operator_write_node + + # : () -> String + def inspect: () -> String + + # :call-seq: + # name -> Symbol + # + # Returns the `name` attribute. + # -- + # : () -> Symbol + def name: () -> Symbol + + # :category: Locations + # :call-seq: + # name_loc -> Location + # + # Returns the Location represented by `name_loc`. + # -- + # : () -> Location + def name_loc: () -> Location + + # :category: Repository + # Save the name_loc location using the given saved source so that + # it can be retrieved later. + # -- + # : (_Repository repository) -> Relocation::Entry + def save_name_loc: (_Repository repository) -> Relocation::Entry + + # :category: Locations + # :call-seq: + # binary_operator_loc -> Location + # + # Returns the Location represented by `binary_operator_loc`. + # -- + # : () -> Location + def binary_operator_loc: () -> Location + + # :category: Repository + # Save the binary_operator_loc location using the given saved source so that + # it can be retrieved later. + # -- + # : (_Repository repository) -> Relocation::Entry + def save_binary_operator_loc: (_Repository repository) -> Relocation::Entry + + # :call-seq: + # value -> Node + # + # Returns the `value` attribute. + # -- + # : () -> Prism::node + def value: () -> Prism::node + + # :call-seq: + # binary_operator -> Symbol + # + # Returns the `binary_operator` attribute. + # -- + # : () -> Symbol + def binary_operator: () -> Symbol + + # : (untyped other) -> boolish + def ===: (untyped other) -> boolish + end + + # Represents the use of the `||=` operator for assignment to a class variable. + # + # @@target ||= value + # ^^^^^^^^^^^^^^^^^^ + class ClassVariableOrWriteNode < Node + @value: Prism::node + + @operator_loc: Location + + @name_loc: Location + + @name: Symbol + + # Initialize a new ClassVariableOrWriteNode node. + # -- + # : (Source source, Integer node_id, Location location, Integer flags, Symbol name, Location name_loc, Location operator_loc, Prism::node value) -> void + def initialize: (Source source, Integer node_id, Location location, Integer flags, Symbol name, Location name_loc, Location operator_loc, Prism::node value) -> void + + # See Node.accept. + # -- + # : (_Visitor visitor) -> untyped + def accept: (_Visitor visitor) -> untyped + + # See Node.child_nodes. + # -- + # : () -> Array[node?] + def child_nodes: () -> Array[node?] + + # See Node.each_child_node. + # -- + # : () -> Enumerator[node, void] + # : () { (node) -> void } -> void + def each_child_node: () -> Enumerator[node, void] + | () { (node) -> void } -> void + + # See Node.compact_child_nodes. + # -- + # : () -> Array[node] + def compact_child_nodes: () -> Array[node] + + # See Node.comment_targets. + # -- + # : () -> Array[node | Location] + def comment_targets: () -> Array[node | Location] + + # :call-seq: + # copy(**fields) -> ClassVariableOrWriteNode + # + # Creates a copy of self with the given fields, using self as the template. + # -- + # : (?node_id: Integer, ?location: Location, ?flags: Integer, ?name: Symbol, ?name_loc: Location, ?operator_loc: Location, ?value: Prism::node) -> ClassVariableOrWriteNode + def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?name: Symbol, ?name_loc: Location, ?operator_loc: Location, ?value: Prism::node) -> ClassVariableOrWriteNode + + alias deconstruct child_nodes + + # : (Array[Symbol]? keys) -> Hash[Symbol, untyped] + def deconstruct_keys: (Array[Symbol]? keys) -> Hash[Symbol, untyped] + + # See `Node#type`. + # -- + # : () -> :class_variable_or_write_node + def type: () -> :class_variable_or_write_node + + # See `Node.type`. + # -- + # : () -> :class_variable_or_write_node + def self.type: () -> :class_variable_or_write_node + + # : () -> String + def inspect: () -> String + + # :call-seq: + # name -> Symbol + # + # Returns the `name` attribute. + # -- + # : () -> Symbol + def name: () -> Symbol + + # :category: Locations + # :call-seq: + # name_loc -> Location + # + # Returns the Location represented by `name_loc`. + # -- + # : () -> Location + def name_loc: () -> Location + + # :category: Repository + # Save the name_loc location using the given saved source so that + # it can be retrieved later. + # -- + # : (_Repository repository) -> Relocation::Entry + def save_name_loc: (_Repository repository) -> Relocation::Entry + + # :category: Locations + # :call-seq: + # operator_loc -> Location + # + # Returns the Location represented by `operator_loc`. + # -- + # : () -> Location + def operator_loc: () -> Location + + # :category: Repository + # Save the operator_loc location using the given saved source so that + # it can be retrieved later. + # -- + # : (_Repository repository) -> Relocation::Entry + def save_operator_loc: (_Repository repository) -> Relocation::Entry + + # :call-seq: + # value -> Node + # + # Returns the `value` attribute. + # -- + # : () -> Prism::node + def value: () -> Prism::node + + # :call-seq: + # operator -> String + # + # Slice the location of operator_loc from the source. + # -- + # : () -> String + def operator: () -> String + + # : (untyped other) -> boolish + def ===: (untyped other) -> boolish + end + + # Represents referencing a class variable. + # + # @@foo + # ^^^^^ + class ClassVariableReadNode < Node + @name: Symbol + + # Initialize a new ClassVariableReadNode node. + # -- + # : (Source source, Integer node_id, Location location, Integer flags, Symbol name) -> void + def initialize: (Source source, Integer node_id, Location location, Integer flags, Symbol name) -> void + + # See Node.accept. + # -- + # : (_Visitor visitor) -> untyped + def accept: (_Visitor visitor) -> untyped + + # See Node.child_nodes. + # -- + # : () -> Array[node?] + def child_nodes: () -> Array[node?] + + # See Node.each_child_node. + # -- + # : () -> Enumerator[node, void] + # : () { (node) -> void } -> void + def each_child_node: () -> Enumerator[node, void] + | () { (node) -> void } -> void + + # See Node.compact_child_nodes. + # -- + # : () -> Array[node] + def compact_child_nodes: () -> Array[node] + + # See Node.comment_targets. + # -- + # : () -> Array[node | Location] + def comment_targets: () -> Array[node | Location] + + # :call-seq: + # copy(**fields) -> ClassVariableReadNode + # + # Creates a copy of self with the given fields, using self as the template. + # -- + # : (?node_id: Integer, ?location: Location, ?flags: Integer, ?name: Symbol) -> ClassVariableReadNode + def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?name: Symbol) -> ClassVariableReadNode + + alias deconstruct child_nodes + + # : (Array[Symbol]? keys) -> Hash[Symbol, untyped] + def deconstruct_keys: (Array[Symbol]? keys) -> Hash[Symbol, untyped] + + # See `Node#type`. + # -- + # : () -> :class_variable_read_node + def type: () -> :class_variable_read_node + + # See `Node.type`. + # -- + # : () -> :class_variable_read_node + def self.type: () -> :class_variable_read_node + + # : () -> String + def inspect: () -> String + + # :call-seq: + # name -> Symbol + # + # 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` + # -- + # : () -> Symbol + def name: () -> Symbol + + # : (untyped other) -> boolish + def ===: (untyped other) -> boolish + end + + # Represents writing to a class variable in a context that doesn't have an explicit value. + # + # @@foo, @@bar = baz + # ^^^^^ ^^^^^ + class ClassVariableTargetNode < Node + @name: Symbol + + # Initialize a new ClassVariableTargetNode node. + # -- + # : (Source source, Integer node_id, Location location, Integer flags, Symbol name) -> void + def initialize: (Source source, Integer node_id, Location location, Integer flags, Symbol name) -> void + + # See Node.accept. + # -- + # : (_Visitor visitor) -> untyped + def accept: (_Visitor visitor) -> untyped + + # See Node.child_nodes. + # -- + # : () -> Array[node?] + def child_nodes: () -> Array[node?] + + # See Node.each_child_node. + # -- + # : () -> Enumerator[node, void] + # : () { (node) -> void } -> void + def each_child_node: () -> Enumerator[node, void] + | () { (node) -> void } -> void + + # See Node.compact_child_nodes. + # -- + # : () -> Array[node] + def compact_child_nodes: () -> Array[node] + + # See Node.comment_targets. + # -- + # : () -> Array[node | Location] + def comment_targets: () -> Array[node | Location] + + # :call-seq: + # copy(**fields) -> ClassVariableTargetNode + # + # Creates a copy of self with the given fields, using self as the template. + # -- + # : (?node_id: Integer, ?location: Location, ?flags: Integer, ?name: Symbol) -> ClassVariableTargetNode + def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?name: Symbol) -> ClassVariableTargetNode + + alias deconstruct child_nodes + + # : (Array[Symbol]? keys) -> Hash[Symbol, untyped] + def deconstruct_keys: (Array[Symbol]? keys) -> Hash[Symbol, untyped] + + # See `Node#type`. + # -- + # : () -> :class_variable_target_node + def type: () -> :class_variable_target_node + + # See `Node.type`. + # -- + # : () -> :class_variable_target_node + def self.type: () -> :class_variable_target_node + + # : () -> String + def inspect: () -> String + + # :call-seq: + # name -> Symbol + # + # Returns the `name` attribute. + # -- + # : () -> Symbol + def name: () -> Symbol + + # : (untyped other) -> boolish + def ===: (untyped other) -> boolish + end + + # Represents writing to a class variable. + # + # @@foo = 1 + # ^^^^^^^^^ + class ClassVariableWriteNode < Node + @operator_loc: Location + + @value: Prism::node + + @name_loc: Location + + @name: Symbol + + # Initialize a new ClassVariableWriteNode node. + # -- + # : (Source source, Integer node_id, Location location, Integer flags, Symbol name, Location name_loc, Prism::node value, Location operator_loc) -> void + def initialize: (Source source, Integer node_id, Location location, Integer flags, Symbol name, Location name_loc, Prism::node value, Location operator_loc) -> void + + # See Node.accept. + # -- + # : (_Visitor visitor) -> untyped + def accept: (_Visitor visitor) -> untyped + + # See Node.child_nodes. + # -- + # : () -> Array[node?] + def child_nodes: () -> Array[node?] + + # See Node.each_child_node. + # -- + # : () -> Enumerator[node, void] + # : () { (node) -> void } -> void + def each_child_node: () -> Enumerator[node, void] + | () { (node) -> void } -> void + + # See Node.compact_child_nodes. + # -- + # : () -> Array[node] + def compact_child_nodes: () -> Array[node] + + # See Node.comment_targets. + # -- + # : () -> Array[node | Location] + def comment_targets: () -> Array[node | Location] + + # :call-seq: + # copy(**fields) -> ClassVariableWriteNode + # + # Creates a copy of self with the given fields, using self as the template. + # -- + # : (?node_id: Integer, ?location: Location, ?flags: Integer, ?name: Symbol, ?name_loc: Location, ?value: Prism::node, ?operator_loc: Location) -> ClassVariableWriteNode + def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?name: Symbol, ?name_loc: Location, ?value: Prism::node, ?operator_loc: Location) -> ClassVariableWriteNode + + alias deconstruct child_nodes + + # : (Array[Symbol]? keys) -> Hash[Symbol, untyped] + def deconstruct_keys: (Array[Symbol]? keys) -> Hash[Symbol, untyped] + + # See `Node#type`. + # -- + # : () -> :class_variable_write_node + def type: () -> :class_variable_write_node + + # See `Node.type`. + # -- + # : () -> :class_variable_write_node + def self.type: () -> :class_variable_write_node + + # : () -> String + def inspect: () -> String + + # :call-seq: + # name -> Symbol + # + # 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` + # -- + # : () -> Symbol + def name: () -> Symbol + + # :category: Locations + # :call-seq: + # name_loc -> Location + # + # The Location of the variable name. + # + # @@foo = :bar + # ^^^^^ + # -- + # : () -> Location + def name_loc: () -> Location + + # :category: Repository + # Save the name_loc location using the given saved source so that + # it can be retrieved later. + # -- + # : (_Repository repository) -> Relocation::Entry + def save_name_loc: (_Repository repository) -> Relocation::Entry + + # :call-seq: + # value -> Node + # + # 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 + # ^^^ + # -- + # : () -> Prism::node + def value: () -> Prism::node + + # :category: Locations + # :call-seq: + # operator_loc -> Location + # + # The Location of the `=` operator. + # + # @@foo = :bar + # ^ + # -- + # : () -> Location + def operator_loc: () -> Location + + # :category: Repository + # Save the operator_loc location using the given saved source so that + # it can be retrieved later. + # -- + # : (_Repository repository) -> Relocation::Entry + def save_operator_loc: (_Repository repository) -> Relocation::Entry + + # :call-seq: + # operator -> String + # + # Slice the location of operator_loc from the source. + # -- + # : () -> String + def operator: () -> String + + # : (untyped other) -> boolish + def ===: (untyped other) -> boolish + end + + # Represents the use of the `&&=` operator for assignment to a constant. + # + # Target &&= value + # ^^^^^^^^^^^^^^^^ + class ConstantAndWriteNode < Node + @value: Prism::node + + @operator_loc: Location + + @name_loc: Location + + @name: Symbol + + # Initialize a new ConstantAndWriteNode node. + # -- + # : (Source source, Integer node_id, Location location, Integer flags, Symbol name, Location name_loc, Location operator_loc, Prism::node value) -> void + def initialize: (Source source, Integer node_id, Location location, Integer flags, Symbol name, Location name_loc, Location operator_loc, Prism::node value) -> void + + # See Node.accept. + # -- + # : (_Visitor visitor) -> untyped + def accept: (_Visitor visitor) -> untyped + + # See Node.child_nodes. + # -- + # : () -> Array[node?] + def child_nodes: () -> Array[node?] + + # See Node.each_child_node. + # -- + # : () -> Enumerator[node, void] + # : () { (node) -> void } -> void + def each_child_node: () -> Enumerator[node, void] + | () { (node) -> void } -> void + + # See Node.compact_child_nodes. + # -- + # : () -> Array[node] + def compact_child_nodes: () -> Array[node] + + # See Node.comment_targets. + # -- + # : () -> Array[node | Location] + def comment_targets: () -> Array[node | Location] + + # :call-seq: + # copy(**fields) -> ConstantAndWriteNode + # + # Creates a copy of self with the given fields, using self as the template. + # -- + # : (?node_id: Integer, ?location: Location, ?flags: Integer, ?name: Symbol, ?name_loc: Location, ?operator_loc: Location, ?value: Prism::node) -> ConstantAndWriteNode + def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?name: Symbol, ?name_loc: Location, ?operator_loc: Location, ?value: Prism::node) -> ConstantAndWriteNode + + alias deconstruct child_nodes + + # : (Array[Symbol]? keys) -> Hash[Symbol, untyped] + def deconstruct_keys: (Array[Symbol]? keys) -> Hash[Symbol, untyped] + + # See `Node#type`. + # -- + # : () -> :constant_and_write_node + def type: () -> :constant_and_write_node + + # See `Node.type`. + # -- + # : () -> :constant_and_write_node + def self.type: () -> :constant_and_write_node + + # : () -> String + def inspect: () -> String + + # :call-seq: + # name -> Symbol + # + # Returns the `name` attribute. + # -- + # : () -> Symbol + def name: () -> Symbol + + # :category: Locations + # :call-seq: + # name_loc -> Location + # + # Returns the Location represented by `name_loc`. + # -- + # : () -> Location + def name_loc: () -> Location + + # :category: Repository + # Save the name_loc location using the given saved source so that + # it can be retrieved later. + # -- + # : (_Repository repository) -> Relocation::Entry + def save_name_loc: (_Repository repository) -> Relocation::Entry + + # :category: Locations + # :call-seq: + # operator_loc -> Location + # + # Returns the Location represented by `operator_loc`. + # -- + # : () -> Location + def operator_loc: () -> Location + + # :category: Repository + # Save the operator_loc location using the given saved source so that + # it can be retrieved later. + # -- + # : (_Repository repository) -> Relocation::Entry + def save_operator_loc: (_Repository repository) -> Relocation::Entry + + # :call-seq: + # value -> Node + # + # Returns the `value` attribute. + # -- + # : () -> Prism::node + def value: () -> Prism::node + + # :call-seq: + # operator -> String + # + # Slice the location of operator_loc from the source. + # -- + # : () -> String + def operator: () -> String + + # : (untyped other) -> boolish + def ===: (untyped other) -> boolish + end + + # Represents assigning to a constant using an operator that isn't `=`. + # + # Target += value + # ^^^^^^^^^^^^^^^ + class ConstantOperatorWriteNode < Node + @binary_operator: Symbol + + @value: Prism::node + + @binary_operator_loc: Location + + @name_loc: Location + + @name: Symbol + + # Initialize a new ConstantOperatorWriteNode node. + # -- + # : (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 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 + + # See Node.accept. + # -- + # : (_Visitor visitor) -> untyped + def accept: (_Visitor visitor) -> untyped + + # See Node.child_nodes. + # -- + # : () -> Array[node?] + def child_nodes: () -> Array[node?] + + # See Node.each_child_node. + # -- + # : () -> Enumerator[node, void] + # : () { (node) -> void } -> void + def each_child_node: () -> Enumerator[node, void] + | () { (node) -> void } -> void + + # See Node.compact_child_nodes. + # -- + # : () -> Array[node] + def compact_child_nodes: () -> Array[node] + + # See Node.comment_targets. + # -- + # : () -> Array[node | Location] + def comment_targets: () -> Array[node | Location] + + # :call-seq: + # copy(**fields) -> ConstantOperatorWriteNode + # + # Creates a copy of self with the given fields, using self as the template. + # -- + # : (?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: Integer, ?location: Location, ?flags: Integer, ?name: Symbol, ?name_loc: Location, ?binary_operator_loc: Location, ?value: Prism::node, ?binary_operator: Symbol) -> ConstantOperatorWriteNode + + alias deconstruct child_nodes + + # : (Array[Symbol]? keys) -> Hash[Symbol, untyped] + def deconstruct_keys: (Array[Symbol]? keys) -> Hash[Symbol, untyped] + + # See `Node#type`. + # -- + # : () -> :constant_operator_write_node + def type: () -> :constant_operator_write_node + + # See `Node.type`. + # -- + # : () -> :constant_operator_write_node + def self.type: () -> :constant_operator_write_node + + # : () -> String + def inspect: () -> String + + # :call-seq: + # name -> Symbol + # + # Returns the `name` attribute. + # -- + # : () -> Symbol + def name: () -> Symbol + + # :category: Locations + # :call-seq: + # name_loc -> Location + # + # Returns the Location represented by `name_loc`. + # -- + # : () -> Location + def name_loc: () -> Location + + # :category: Repository + # Save the name_loc location using the given saved source so that + # it can be retrieved later. + # -- + # : (_Repository repository) -> Relocation::Entry + def save_name_loc: (_Repository repository) -> Relocation::Entry + + # :category: Locations + # :call-seq: + # binary_operator_loc -> Location + # + # Returns the Location represented by `binary_operator_loc`. + # -- + # : () -> Location + def binary_operator_loc: () -> Location + + # :category: Repository + # Save the binary_operator_loc location using the given saved source so that + # it can be retrieved later. + # -- + # : (_Repository repository) -> Relocation::Entry + def save_binary_operator_loc: (_Repository repository) -> Relocation::Entry + + # :call-seq: + # value -> Node + # + # Returns the `value` attribute. + # -- + # : () -> Prism::node + def value: () -> Prism::node + + # :call-seq: + # binary_operator -> Symbol + # + # Returns the `binary_operator` attribute. + # -- + # : () -> Symbol + def binary_operator: () -> Symbol + + # : (untyped other) -> boolish + def ===: (untyped other) -> boolish + end + + # Represents the use of the `||=` operator for assignment to a constant. + # + # Target ||= value + # ^^^^^^^^^^^^^^^^ + class ConstantOrWriteNode < Node + @value: Prism::node + + @operator_loc: Location + + @name_loc: Location + + @name: Symbol + + # Initialize a new ConstantOrWriteNode node. + # -- + # : (Source source, Integer node_id, Location location, Integer flags, Symbol name, Location name_loc, Location operator_loc, Prism::node value) -> void + def initialize: (Source source, Integer node_id, Location location, Integer flags, Symbol name, Location name_loc, Location operator_loc, Prism::node value) -> void + + # See Node.accept. + # -- + # : (_Visitor visitor) -> untyped + def accept: (_Visitor visitor) -> untyped + + # See Node.child_nodes. + # -- + # : () -> Array[node?] + def child_nodes: () -> Array[node?] + + # See Node.each_child_node. + # -- + # : () -> Enumerator[node, void] + # : () { (node) -> void } -> void + def each_child_node: () -> Enumerator[node, void] + | () { (node) -> void } -> void + + # See Node.compact_child_nodes. + # -- + # : () -> Array[node] + def compact_child_nodes: () -> Array[node] + + # See Node.comment_targets. + # -- + # : () -> Array[node | Location] + def comment_targets: () -> Array[node | Location] + + # :call-seq: + # copy(**fields) -> ConstantOrWriteNode + # + # Creates a copy of self with the given fields, using self as the template. + # -- + # : (?node_id: Integer, ?location: Location, ?flags: Integer, ?name: Symbol, ?name_loc: Location, ?operator_loc: Location, ?value: Prism::node) -> ConstantOrWriteNode + def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?name: Symbol, ?name_loc: Location, ?operator_loc: Location, ?value: Prism::node) -> ConstantOrWriteNode + + alias deconstruct child_nodes + + # : (Array[Symbol]? keys) -> Hash[Symbol, untyped] + def deconstruct_keys: (Array[Symbol]? keys) -> Hash[Symbol, untyped] + + # See `Node#type`. + # -- + # : () -> :constant_or_write_node + def type: () -> :constant_or_write_node + + # See `Node.type`. + # -- + # : () -> :constant_or_write_node + def self.type: () -> :constant_or_write_node + + # : () -> String + def inspect: () -> String + + # :call-seq: + # name -> Symbol + # + # Returns the `name` attribute. + # -- + # : () -> Symbol + def name: () -> Symbol + + # :category: Locations + # :call-seq: + # name_loc -> Location + # + # Returns the Location represented by `name_loc`. + # -- + # : () -> Location + def name_loc: () -> Location + + # :category: Repository + # Save the name_loc location using the given saved source so that + # it can be retrieved later. + # -- + # : (_Repository repository) -> Relocation::Entry + def save_name_loc: (_Repository repository) -> Relocation::Entry + + # :category: Locations + # :call-seq: + # operator_loc -> Location + # + # Returns the Location represented by `operator_loc`. + # -- + # : () -> Location + def operator_loc: () -> Location + + # :category: Repository + # Save the operator_loc location using the given saved source so that + # it can be retrieved later. + # -- + # : (_Repository repository) -> Relocation::Entry + def save_operator_loc: (_Repository repository) -> Relocation::Entry + + # :call-seq: + # value -> Node + # + # Returns the `value` attribute. + # -- + # : () -> Prism::node + def value: () -> Prism::node + + # :call-seq: + # operator -> String + # + # Slice the location of operator_loc from the source. + # -- + # : () -> String + def operator: () -> String + + # : (untyped other) -> boolish + def ===: (untyped other) -> boolish + end + + # Represents the use of the `&&=` operator for assignment to a constant path. + # + # Parent::Child &&= value + # ^^^^^^^^^^^^^^^^^^^^^^^ + class ConstantPathAndWriteNode < Node + @value: Prism::node + + @operator_loc: Location + + @target: ConstantPathNode + + # Initialize a new ConstantPathAndWriteNode node. + # -- + # : (Source source, Integer node_id, Location location, Integer flags, ConstantPathNode target, Location operator_loc, Prism::node value) -> void + def initialize: (Source source, Integer node_id, Location location, Integer flags, ConstantPathNode target, Location operator_loc, Prism::node value) -> void + + # See Node.accept. + # -- + # : (_Visitor visitor) -> untyped + def accept: (_Visitor visitor) -> untyped + + # See Node.child_nodes. + # -- + # : () -> Array[node?] + def child_nodes: () -> Array[node?] + + # See Node.each_child_node. + # -- + # : () -> Enumerator[node, void] + # : () { (node) -> void } -> void + def each_child_node: () -> Enumerator[node, void] + | () { (node) -> void } -> void + + # See Node.compact_child_nodes. + # -- + # : () -> Array[node] + def compact_child_nodes: () -> Array[node] + + # See Node.comment_targets. + # -- + # : () -> Array[node | Location] + def comment_targets: () -> Array[node | Location] + + # :call-seq: + # copy(**fields) -> ConstantPathAndWriteNode + # + # Creates a copy of self with the given fields, using self as the template. + # -- + # : (?node_id: Integer, ?location: Location, ?flags: Integer, ?target: ConstantPathNode, ?operator_loc: Location, ?value: Prism::node) -> ConstantPathAndWriteNode + def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?target: ConstantPathNode, ?operator_loc: Location, ?value: Prism::node) -> ConstantPathAndWriteNode + + alias deconstruct child_nodes + + # : (Array[Symbol]? keys) -> Hash[Symbol, untyped] + def deconstruct_keys: (Array[Symbol]? keys) -> Hash[Symbol, untyped] + + # See `Node#type`. + # -- + # : () -> :constant_path_and_write_node + def type: () -> :constant_path_and_write_node + + # See `Node.type`. + # -- + # : () -> :constant_path_and_write_node + def self.type: () -> :constant_path_and_write_node + + # : () -> String + def inspect: () -> String + + # :call-seq: + # target -> ConstantPathNode + # + # Returns the `target` attribute. + # -- + # : () -> ConstantPathNode + def target: () -> ConstantPathNode + + # :category: Locations + # :call-seq: + # operator_loc -> Location + # + # Returns the Location represented by `operator_loc`. + # -- + # : () -> Location + def operator_loc: () -> Location + + # :category: Repository + # Save the operator_loc location using the given saved source so that + # it can be retrieved later. + # -- + # : (_Repository repository) -> Relocation::Entry + def save_operator_loc: (_Repository repository) -> Relocation::Entry + + # :call-seq: + # value -> Node + # + # Returns the `value` attribute. + # -- + # : () -> Prism::node + def value: () -> Prism::node + + # :call-seq: + # operator -> String + # + # Slice the location of operator_loc from the source. + # -- + # : () -> String + def operator: () -> String + + # : (untyped other) -> boolish + def ===: (untyped other) -> boolish + end + + # Represents accessing a constant through a path of `::` operators. + # + # Foo::Bar + # ^^^^^^^^ + class ConstantPathNode < Node + @name_loc: Location + + @delimiter_loc: Location + + @name: Symbol? + + @parent: Prism::node? + + # Initialize a new ConstantPathNode node. + # -- + # : (Source source, Integer node_id, Location location, Integer flags, Prism::node? parent, Symbol? name, Location delimiter_loc, Location name_loc) -> void + def initialize: (Source source, Integer node_id, Location location, Integer flags, Prism::node? parent, Symbol? name, Location delimiter_loc, Location name_loc) -> void + + # See Node.accept. + # -- + # : (_Visitor visitor) -> untyped + def accept: (_Visitor visitor) -> untyped + + # See Node.child_nodes. + # -- + # : () -> Array[node?] + def child_nodes: () -> Array[node?] + + # See Node.each_child_node. + # -- + # : () -> Enumerator[node, void] + # : () { (node) -> void } -> void + def each_child_node: () -> Enumerator[node, void] + | () { (node) -> void } -> void + + # See Node.compact_child_nodes. + # -- + # : () -> Array[node] + def compact_child_nodes: () -> Array[node] + + # See Node.comment_targets. + # -- + # : () -> Array[node | Location] + def comment_targets: () -> Array[node | Location] + + # :call-seq: + # copy(**fields) -> ConstantPathNode + # + # Creates a copy of self with the given fields, using self as the template. + # -- + # : (?node_id: Integer, ?location: Location, ?flags: Integer, ?parent: Prism::node?, ?name: Symbol?, ?delimiter_loc: Location, ?name_loc: Location) -> ConstantPathNode + def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?parent: Prism::node?, ?name: Symbol?, ?delimiter_loc: Location, ?name_loc: Location) -> ConstantPathNode + + alias deconstruct child_nodes + + # : (Array[Symbol]? keys) -> Hash[Symbol, untyped] + def deconstruct_keys: (Array[Symbol]? keys) -> Hash[Symbol, untyped] + + # See `Node#type`. + # -- + # : () -> :constant_path_node + def type: () -> :constant_path_node + + # See `Node.type`. + # -- + # : () -> :constant_path_node + def self.type: () -> :constant_path_node + + # : () -> String + def inspect: () -> String + + # :call-seq: + # parent -> Node | nil + # + # 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 + # ^^^ + # -- + # : () -> Prism::node? + def parent: () -> Prism::node? + + # :call-seq: + # name -> Symbol | nil + # + # The name of the constant being accessed. This could be `nil` in the event of a syntax error. + # -- + # : () -> Symbol? + def name: () -> Symbol? + + # :category: Locations + # :call-seq: + # delimiter_loc -> Location + # + # The Location of the `::` delimiter. + # + # ::Foo + # ^^ + # + # One::Two + # ^^ + # -- + # : () -> Location + def delimiter_loc: () -> Location + + # :category: Repository + # Save the delimiter_loc location using the given saved source so that + # it can be retrieved later. + # -- + # : (_Repository repository) -> Relocation::Entry + def save_delimiter_loc: (_Repository repository) -> Relocation::Entry + + # :category: Locations + # :call-seq: + # name_loc -> Location + # + # The Location of the name of the constant. + # + # ::Foo + # ^^^ + # + # One::Two + # ^^^ + # -- + # : () -> Location + def name_loc: () -> Location + + # :category: Repository + # Save the name_loc location using the given saved source so that + # it can be retrieved later. + # -- + # : (_Repository repository) -> Relocation::Entry + def save_name_loc: (_Repository repository) -> Relocation::Entry + + # :call-seq: + # delimiter -> String + # + # Slice the location of delimiter_loc from the source. + # -- + # : () -> String + def delimiter: () -> String + + # : (untyped other) -> boolish + def ===: (untyped other) -> boolish + end + + # Represents assigning to a constant path using an operator that isn't `=`. + # + # Parent::Child += value + # ^^^^^^^^^^^^^^^^^^^^^^ + class ConstantPathOperatorWriteNode < Node + @binary_operator: Symbol + + @value: Prism::node + + @binary_operator_loc: Location + + @target: ConstantPathNode + + # Initialize a new ConstantPathOperatorWriteNode node. + # -- + # : (Source source, Integer node_id, Location location, Integer flags, ConstantPathNode target, Location binary_operator_loc, Prism::node value, Symbol binary_operator) -> 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 + + # See Node.accept. + # -- + # : (_Visitor visitor) -> untyped + def accept: (_Visitor visitor) -> untyped + + # See Node.child_nodes. + # -- + # : () -> Array[node?] + def child_nodes: () -> Array[node?] + + # See Node.each_child_node. + # -- + # : () -> Enumerator[node, void] + # : () { (node) -> void } -> void + def each_child_node: () -> Enumerator[node, void] + | () { (node) -> void } -> void + + # See Node.compact_child_nodes. + # -- + # : () -> Array[node] + def compact_child_nodes: () -> Array[node] + + # See Node.comment_targets. + # -- + # : () -> Array[node | Location] + def comment_targets: () -> Array[node | Location] + + # :call-seq: + # copy(**fields) -> ConstantPathOperatorWriteNode + # + # Creates a copy of self with the given fields, using self as the template. + # -- + # : (?node_id: Integer, ?location: Location, ?flags: Integer, ?target: ConstantPathNode, ?binary_operator_loc: Location, ?value: Prism::node, ?binary_operator: Symbol) -> ConstantPathOperatorWriteNode + def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?target: ConstantPathNode, ?binary_operator_loc: Location, ?value: Prism::node, ?binary_operator: Symbol) -> ConstantPathOperatorWriteNode + + alias deconstruct child_nodes + + # : (Array[Symbol]? keys) -> Hash[Symbol, untyped] + def deconstruct_keys: (Array[Symbol]? keys) -> Hash[Symbol, untyped] + + # See `Node#type`. + # -- + # : () -> :constant_path_operator_write_node + def type: () -> :constant_path_operator_write_node + + # See `Node.type`. + # -- + # : () -> :constant_path_operator_write_node + def self.type: () -> :constant_path_operator_write_node + + # : () -> String + def inspect: () -> String + + # :call-seq: + # target -> ConstantPathNode + # + # Returns the `target` attribute. + # -- + # : () -> ConstantPathNode + def target: () -> ConstantPathNode + + # :category: Locations + # :call-seq: + # binary_operator_loc -> Location + # + # Returns the Location represented by `binary_operator_loc`. + # -- + # : () -> Location + def binary_operator_loc: () -> Location + + # :category: Repository + # Save the binary_operator_loc location using the given saved source so that + # it can be retrieved later. + # -- + # : (_Repository repository) -> Relocation::Entry + def save_binary_operator_loc: (_Repository repository) -> Relocation::Entry + + # :call-seq: + # value -> Node + # + # Returns the `value` attribute. + # -- + # : () -> Prism::node + def value: () -> Prism::node + + # :call-seq: + # binary_operator -> Symbol + # + # Returns the `binary_operator` attribute. + # -- + # : () -> Symbol + def binary_operator: () -> Symbol + + # : (untyped other) -> boolish + def ===: (untyped other) -> boolish + end + + # Represents the use of the `||=` operator for assignment to a constant path. + # + # Parent::Child ||= value + # ^^^^^^^^^^^^^^^^^^^^^^^ + class ConstantPathOrWriteNode < Node + @value: Prism::node + + @operator_loc: Location + + @target: ConstantPathNode + + # Initialize a new ConstantPathOrWriteNode node. + # -- + # : (Source source, Integer node_id, Location location, Integer flags, ConstantPathNode target, Location operator_loc, Prism::node value) -> void + def initialize: (Source source, Integer node_id, Location location, Integer flags, ConstantPathNode target, Location operator_loc, Prism::node value) -> void + + # See Node.accept. + # -- + # : (_Visitor visitor) -> untyped + def accept: (_Visitor visitor) -> untyped + + # See Node.child_nodes. + # -- + # : () -> Array[node?] + def child_nodes: () -> Array[node?] + + # See Node.each_child_node. + # -- + # : () -> Enumerator[node, void] + # : () { (node) -> void } -> void + def each_child_node: () -> Enumerator[node, void] + | () { (node) -> void } -> void + + # See Node.compact_child_nodes. + # -- + # : () -> Array[node] + def compact_child_nodes: () -> Array[node] + + # See Node.comment_targets. + # -- + # : () -> Array[node | Location] + def comment_targets: () -> Array[node | Location] + + # :call-seq: + # copy(**fields) -> ConstantPathOrWriteNode + # + # Creates a copy of self with the given fields, using self as the template. + # -- + # : (?node_id: Integer, ?location: Location, ?flags: Integer, ?target: ConstantPathNode, ?operator_loc: Location, ?value: Prism::node) -> ConstantPathOrWriteNode + def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?target: ConstantPathNode, ?operator_loc: Location, ?value: Prism::node) -> ConstantPathOrWriteNode + + alias deconstruct child_nodes + + # : (Array[Symbol]? keys) -> Hash[Symbol, untyped] + def deconstruct_keys: (Array[Symbol]? keys) -> Hash[Symbol, untyped] + + # See `Node#type`. + # -- + # : () -> :constant_path_or_write_node + def type: () -> :constant_path_or_write_node + + # See `Node.type`. + # -- + # : () -> :constant_path_or_write_node + def self.type: () -> :constant_path_or_write_node + + # : () -> String + def inspect: () -> String + + # :call-seq: + # target -> ConstantPathNode + # + # Returns the `target` attribute. + # -- + # : () -> ConstantPathNode + def target: () -> ConstantPathNode + + # :category: Locations + # :call-seq: + # operator_loc -> Location + # + # Returns the Location represented by `operator_loc`. + # -- + # : () -> Location + def operator_loc: () -> Location + + # :category: Repository + # Save the operator_loc location using the given saved source so that + # it can be retrieved later. + # -- + # : (_Repository repository) -> Relocation::Entry + def save_operator_loc: (_Repository repository) -> Relocation::Entry + + # :call-seq: + # value -> Node + # + # Returns the `value` attribute. + # -- + # : () -> Prism::node + def value: () -> Prism::node + + # :call-seq: + # operator -> String + # + # Slice the location of operator_loc from the source. + # -- + # : () -> String + def operator: () -> String + + # : (untyped other) -> boolish + def ===: (untyped other) -> boolish + 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 + @name_loc: Location + + @delimiter_loc: Location + + @name: Symbol? + + @parent: Prism::node? + + # Initialize a new ConstantPathTargetNode node. + # -- + # : (Source source, Integer node_id, Location location, Integer flags, Prism::node? parent, Symbol? name, Location delimiter_loc, Location name_loc) -> void + def initialize: (Source source, Integer node_id, Location location, Integer flags, Prism::node? parent, Symbol? name, Location delimiter_loc, Location name_loc) -> void + + # See Node.accept. + # -- + # : (_Visitor visitor) -> untyped + def accept: (_Visitor visitor) -> untyped + + # See Node.child_nodes. + # -- + # : () -> Array[node?] + def child_nodes: () -> Array[node?] + + # See Node.each_child_node. + # -- + # : () -> Enumerator[node, void] + # : () { (node) -> void } -> void + def each_child_node: () -> Enumerator[node, void] + | () { (node) -> void } -> void + + # See Node.compact_child_nodes. + # -- + # : () -> Array[node] + def compact_child_nodes: () -> Array[node] + + # See Node.comment_targets. + # -- + # : () -> Array[node | Location] + def comment_targets: () -> Array[node | Location] + + # :call-seq: + # copy(**fields) -> ConstantPathTargetNode + # + # Creates a copy of self with the given fields, using self as the template. + # -- + # : (?node_id: Integer, ?location: Location, ?flags: Integer, ?parent: Prism::node?, ?name: Symbol?, ?delimiter_loc: Location, ?name_loc: Location) -> ConstantPathTargetNode + def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?parent: Prism::node?, ?name: Symbol?, ?delimiter_loc: Location, ?name_loc: Location) -> ConstantPathTargetNode + + alias deconstruct child_nodes + + # : (Array[Symbol]? keys) -> Hash[Symbol, untyped] + def deconstruct_keys: (Array[Symbol]? keys) -> Hash[Symbol, untyped] + + # See `Node#type`. + # -- + # : () -> :constant_path_target_node + def type: () -> :constant_path_target_node + + # See `Node.type`. + # -- + # : () -> :constant_path_target_node + def self.type: () -> :constant_path_target_node + + # : () -> String + def inspect: () -> String + + # :call-seq: + # parent -> Node | nil + # + # Returns the `parent` attribute. + # -- + # : () -> Prism::node? + def parent: () -> Prism::node? + + # :call-seq: + # name -> Symbol | nil + # + # Returns the `name` attribute. + # -- + # : () -> Symbol? + def name: () -> Symbol? + + # :category: Locations + # :call-seq: + # delimiter_loc -> Location + # + # Returns the Location represented by `delimiter_loc`. + # -- + # : () -> Location + def delimiter_loc: () -> Location + + # :category: Repository + # Save the delimiter_loc location using the given saved source so that + # it can be retrieved later. + # -- + # : (_Repository repository) -> Relocation::Entry + def save_delimiter_loc: (_Repository repository) -> Relocation::Entry + + # :category: Locations + # :call-seq: + # name_loc -> Location + # + # Returns the Location represented by `name_loc`. + # -- + # : () -> Location + def name_loc: () -> Location + + # :category: Repository + # Save the name_loc location using the given saved source so that + # it can be retrieved later. + # -- + # : (_Repository repository) -> Relocation::Entry + def save_name_loc: (_Repository repository) -> Relocation::Entry + + # :call-seq: + # delimiter -> String + # + # Slice the location of delimiter_loc from the source. + # -- + # : () -> String + def delimiter: () -> String + + # : (untyped other) -> boolish + def ===: (untyped other) -> boolish + end + + # Represents writing to a constant path. + # + # ::Foo = 1 + # ^^^^^^^^^ + # + # Foo::Bar = 1 + # ^^^^^^^^^^^^ + # + # ::Foo::Bar = 1 + # ^^^^^^^^^^^^^^ + class ConstantPathWriteNode < Node + @value: Prism::node + + @operator_loc: Location + + @target: ConstantPathNode + + # Initialize a new ConstantPathWriteNode node. + # -- + # : (Source source, Integer node_id, Location location, Integer flags, ConstantPathNode target, Location operator_loc, Prism::node value) -> void + def initialize: (Source source, Integer node_id, Location location, Integer flags, ConstantPathNode target, Location operator_loc, Prism::node value) -> void + + # See Node.accept. + # -- + # : (_Visitor visitor) -> untyped + def accept: (_Visitor visitor) -> untyped + + # See Node.child_nodes. + # -- + # : () -> Array[node?] + def child_nodes: () -> Array[node?] + + # See Node.each_child_node. + # -- + # : () -> Enumerator[node, void] + # : () { (node) -> void } -> void + def each_child_node: () -> Enumerator[node, void] + | () { (node) -> void } -> void + + # See Node.compact_child_nodes. + # -- + # : () -> Array[node] + def compact_child_nodes: () -> Array[node] + + # See Node.comment_targets. + # -- + # : () -> Array[node | Location] + def comment_targets: () -> Array[node | Location] + + # :call-seq: + # copy(**fields) -> ConstantPathWriteNode + # + # Creates a copy of self with the given fields, using self as the template. + # -- + # : (?node_id: Integer, ?location: Location, ?flags: Integer, ?target: ConstantPathNode, ?operator_loc: Location, ?value: Prism::node) -> ConstantPathWriteNode + def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?target: ConstantPathNode, ?operator_loc: Location, ?value: Prism::node) -> ConstantPathWriteNode + + alias deconstruct child_nodes + + # : (Array[Symbol]? keys) -> Hash[Symbol, untyped] + def deconstruct_keys: (Array[Symbol]? keys) -> Hash[Symbol, untyped] + + # See `Node#type`. + # -- + # : () -> :constant_path_write_node + def type: () -> :constant_path_write_node + + # See `Node.type`. + # -- + # : () -> :constant_path_write_node + def self.type: () -> :constant_path_write_node + + # : () -> String + def inspect: () -> String + + # :call-seq: + # target -> ConstantPathNode + # + # A node representing the constant path being written to. + # + # Foo::Bar = 1 + # ^^^^^^^^ + # + # ::Foo = :abc + # ^^^^^ + # -- + # : () -> ConstantPathNode + def target: () -> ConstantPathNode + + # :category: Locations + # :call-seq: + # operator_loc -> Location + # + # The Location of the `=` operator. + # + # ::ABC = 123 + # ^ + # -- + # : () -> Location + def operator_loc: () -> Location + + # :category: Repository + # Save the operator_loc location using the given saved source so that + # it can be retrieved later. + # -- + # : (_Repository repository) -> Relocation::Entry + def save_operator_loc: (_Repository repository) -> Relocation::Entry + + # :call-seq: + # value -> Node + # + # 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 + # ^^^^ + # -- + # : () -> Prism::node + def value: () -> Prism::node + + # :call-seq: + # operator -> String + # + # Slice the location of operator_loc from the source. + # -- + # : () -> String + def operator: () -> String + + # : (untyped other) -> boolish + def ===: (untyped other) -> boolish + end + + # Represents referencing a constant. + # + # Foo + # ^^^ + class ConstantReadNode < Node + @name: Symbol + + # Initialize a new ConstantReadNode node. + # -- + # : (Source source, Integer node_id, Location location, Integer flags, Symbol name) -> void + def initialize: (Source source, Integer node_id, Location location, Integer flags, Symbol name) -> void + + # See Node.accept. + # -- + # : (_Visitor visitor) -> untyped + def accept: (_Visitor visitor) -> untyped + + # See Node.child_nodes. + # -- + # : () -> Array[node?] + def child_nodes: () -> Array[node?] + + # See Node.each_child_node. + # -- + # : () -> Enumerator[node, void] + # : () { (node) -> void } -> void + def each_child_node: () -> Enumerator[node, void] + | () { (node) -> void } -> void + + # See Node.compact_child_nodes. + # -- + # : () -> Array[node] + def compact_child_nodes: () -> Array[node] + + # See Node.comment_targets. + # -- + # : () -> Array[node | Location] + def comment_targets: () -> Array[node | Location] + + # :call-seq: + # copy(**fields) -> ConstantReadNode + # + # Creates a copy of self with the given fields, using self as the template. + # -- + # : (?node_id: Integer, ?location: Location, ?flags: Integer, ?name: Symbol) -> ConstantReadNode + def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?name: Symbol) -> ConstantReadNode + + alias deconstruct child_nodes + + # : (Array[Symbol]? keys) -> Hash[Symbol, untyped] + def deconstruct_keys: (Array[Symbol]? keys) -> Hash[Symbol, untyped] + + # See `Node#type`. + # -- + # : () -> :constant_read_node + def type: () -> :constant_read_node + + # See `Node.type`. + # -- + # : () -> :constant_read_node + def self.type: () -> :constant_read_node + + # : () -> String + def inspect: () -> String + + # :call-seq: + # name -> Symbol + # + # 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` + # -- + # : () -> Symbol + def name: () -> Symbol + + # : (untyped other) -> boolish + def ===: (untyped other) -> boolish + end + + # Represents writing to a constant in a context that doesn't have an explicit value. + # + # Foo, Bar = baz + # ^^^ ^^^ + class ConstantTargetNode < Node + @name: Symbol + + # Initialize a new ConstantTargetNode node. + # -- + # : (Source source, Integer node_id, Location location, Integer flags, Symbol name) -> void + def initialize: (Source source, Integer node_id, Location location, Integer flags, Symbol name) -> void + + # See Node.accept. + # -- + # : (_Visitor visitor) -> untyped + def accept: (_Visitor visitor) -> untyped + + # See Node.child_nodes. + # -- + # : () -> Array[node?] + def child_nodes: () -> Array[node?] + + # See Node.each_child_node. + # -- + # : () -> Enumerator[node, void] + # : () { (node) -> void } -> void + def each_child_node: () -> Enumerator[node, void] + | () { (node) -> void } -> void + + # See Node.compact_child_nodes. + # -- + # : () -> Array[node] + def compact_child_nodes: () -> Array[node] + + # See Node.comment_targets. + # -- + # : () -> Array[node | Location] + def comment_targets: () -> Array[node | Location] + + # :call-seq: + # copy(**fields) -> ConstantTargetNode + # + # Creates a copy of self with the given fields, using self as the template. + # -- + # : (?node_id: Integer, ?location: Location, ?flags: Integer, ?name: Symbol) -> ConstantTargetNode + def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?name: Symbol) -> ConstantTargetNode + + alias deconstruct child_nodes + + # : (Array[Symbol]? keys) -> Hash[Symbol, untyped] + def deconstruct_keys: (Array[Symbol]? keys) -> Hash[Symbol, untyped] + + # See `Node#type`. + # -- + # : () -> :constant_target_node + def type: () -> :constant_target_node + + # See `Node.type`. + # -- + # : () -> :constant_target_node + def self.type: () -> :constant_target_node + + # : () -> String + def inspect: () -> String + + # :call-seq: + # name -> Symbol + # + # Returns the `name` attribute. + # -- + # : () -> Symbol + def name: () -> Symbol + + # : (untyped other) -> boolish + def ===: (untyped other) -> boolish + end + + # Represents writing to a constant. + # + # Foo = 1 + # ^^^^^^^ + class ConstantWriteNode < Node + @operator_loc: Location + + @value: Prism::node + + @name_loc: Location + + @name: Symbol + + # Initialize a new ConstantWriteNode node. + # -- + # : (Source source, Integer node_id, Location location, Integer flags, Symbol name, Location name_loc, Prism::node value, Location operator_loc) -> void + def initialize: (Source source, Integer node_id, Location location, Integer flags, Symbol name, Location name_loc, Prism::node value, Location operator_loc) -> void + + # See Node.accept. + # -- + # : (_Visitor visitor) -> untyped + def accept: (_Visitor visitor) -> untyped + + # See Node.child_nodes. + # -- + # : () -> Array[node?] + def child_nodes: () -> Array[node?] + + # See Node.each_child_node. + # -- + # : () -> Enumerator[node, void] + # : () { (node) -> void } -> void + def each_child_node: () -> Enumerator[node, void] + | () { (node) -> void } -> void + + # See Node.compact_child_nodes. + # -- + # : () -> Array[node] + def compact_child_nodes: () -> Array[node] + + # See Node.comment_targets. + # -- + # : () -> Array[node | Location] + def comment_targets: () -> Array[node | Location] + + # :call-seq: + # copy(**fields) -> ConstantWriteNode + # + # Creates a copy of self with the given fields, using self as the template. + # -- + # : (?node_id: Integer, ?location: Location, ?flags: Integer, ?name: Symbol, ?name_loc: Location, ?value: Prism::node, ?operator_loc: Location) -> ConstantWriteNode + def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?name: Symbol, ?name_loc: Location, ?value: Prism::node, ?operator_loc: Location) -> ConstantWriteNode + + alias deconstruct child_nodes + + # : (Array[Symbol]? keys) -> Hash[Symbol, untyped] + def deconstruct_keys: (Array[Symbol]? keys) -> Hash[Symbol, untyped] + + # See `Node#type`. + # -- + # : () -> :constant_write_node + def type: () -> :constant_write_node + + # See `Node.type`. + # -- + # : () -> :constant_write_node + def self.type: () -> :constant_write_node + + # : () -> String + def inspect: () -> String + + # :call-seq: + # name -> Symbol + # + # 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` + # -- + # : () -> Symbol + def name: () -> Symbol + + # :category: Locations + # :call-seq: + # name_loc -> Location + # + # The Location of the constant name. + # + # FOO = 1 + # ^^^ + # -- + # : () -> Location + def name_loc: () -> Location + + # :category: Repository + # Save the name_loc location using the given saved source so that + # it can be retrieved later. + # -- + # : (_Repository repository) -> Relocation::Entry + def save_name_loc: (_Repository repository) -> Relocation::Entry + + # :call-seq: + # value -> Node + # + # 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 + # ^^^^^^^^^ + # -- + # : () -> Prism::node + def value: () -> Prism::node + + # :category: Locations + # :call-seq: + # operator_loc -> Location + # + # The Location of the `=` operator. + # + # FOO = :bar + # ^ + # -- + # : () -> Location + def operator_loc: () -> Location + + # :category: Repository + # Save the operator_loc location using the given saved source so that + # it can be retrieved later. + # -- + # : (_Repository repository) -> Relocation::Entry + def save_operator_loc: (_Repository repository) -> Relocation::Entry + + # :call-seq: + # operator -> String + # + # Slice the location of operator_loc from the source. + # -- + # : () -> String + def operator: () -> String + + # : (untyped other) -> boolish + def ===: (untyped other) -> boolish + end + + # Represents a method definition. + # + # def method + # end + # ^^^^^^^^^^ + class DefNode < Node + @end_keyword_loc: Location? + + @equal_loc: Location? + + @rparen_loc: Location? + + @lparen_loc: Location? + + @operator_loc: Location? + + @def_keyword_loc: Location + + @locals: Array[Symbol] + + @body: (StatementsNode | BeginNode)? + + @parameters: ParametersNode? + + @receiver: Prism::node? + + @name_loc: Location + + @name: Symbol + + # Initialize a new DefNode node. + # -- + # : (Source source, Integer node_id, Location location, Integer flags, Symbol name, Location name_loc, Prism::node? receiver, ParametersNode? parameters, (StatementsNode | BeginNode)? 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 initialize: (Source source, Integer node_id, Location location, Integer flags, Symbol name, Location name_loc, Prism::node? receiver, ParametersNode? parameters, (StatementsNode | BeginNode)? 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 + + # See Node.accept. + # -- + # : (_Visitor visitor) -> untyped + def accept: (_Visitor visitor) -> untyped + + # See Node.child_nodes. + # -- + # : () -> Array[node?] + def child_nodes: () -> Array[node?] + + # See Node.each_child_node. + # -- + # : () -> Enumerator[node, void] + # : () { (node) -> void } -> void + def each_child_node: () -> Enumerator[node, void] + | () { (node) -> void } -> void + + # See Node.compact_child_nodes. + # -- + # : () -> Array[node] + def compact_child_nodes: () -> Array[node] + + # See Node.comment_targets. + # -- + # : () -> Array[node | Location] + def comment_targets: () -> Array[node | Location] + + # :call-seq: + # copy(**fields) -> DefNode + # + # Creates a copy of self with the given fields, using self as the template. + # -- + # : (?node_id: Integer, ?location: Location, ?flags: Integer, ?name: Symbol, ?name_loc: Location, ?receiver: Prism::node?, ?parameters: ParametersNode?, ?body: (StatementsNode | BeginNode)?, ?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: Integer, ?location: Location, ?flags: Integer, ?name: Symbol, ?name_loc: Location, ?receiver: Prism::node?, ?parameters: ParametersNode?, ?body: (StatementsNode | BeginNode)?, ?locals: Array[Symbol], ?def_keyword_loc: Location, ?operator_loc: Location?, ?lparen_loc: Location?, ?rparen_loc: Location?, ?equal_loc: Location?, ?end_keyword_loc: Location?) -> DefNode + + alias deconstruct child_nodes + + # : (Array[Symbol]? keys) -> Hash[Symbol, untyped] + def deconstruct_keys: (Array[Symbol]? keys) -> Hash[Symbol, untyped] + + # See `Node#type`. + # -- + # : () -> :def_node + def type: () -> :def_node + + # See `Node.type`. + # -- + # : () -> :def_node + def self.type: () -> :def_node + + # : () -> String + def inspect: () -> String + + # :call-seq: + # name -> Symbol + # + # Returns the `name` attribute. + # -- + # : () -> Symbol + def name: () -> Symbol + + # :category: Locations + # :call-seq: + # name_loc -> Location + # + # Returns the Location represented by `name_loc`. + # -- + # : () -> Location + def name_loc: () -> Location + + # :category: Repository + # Save the name_loc location using the given saved source so that + # it can be retrieved later. + # -- + # : (_Repository repository) -> Relocation::Entry + def save_name_loc: (_Repository repository) -> Relocation::Entry + + # :call-seq: + # receiver -> Node | nil + # + # Returns the `receiver` attribute. + # -- + # : () -> Prism::node? + def receiver: () -> Prism::node? + + # :call-seq: + # parameters -> ParametersNode | nil + # + # Returns the `parameters` attribute. + # -- + # : () -> ParametersNode? + def parameters: () -> ParametersNode? + + # :call-seq: + # body -> StatementsNode | BeginNode | nil + # + # Returns the `body` attribute. + # -- + # : () -> (StatementsNode | BeginNode)? + def body: () -> (StatementsNode | BeginNode)? + + # :call-seq: + # locals -> Array[Symbol] + # + # Returns the `locals` attribute. + # -- + # : () -> Array[Symbol] + def locals: () -> Array[Symbol] + + # :category: Locations + # :call-seq: + # def_keyword_loc -> Location + # + # Returns the Location represented by `def_keyword_loc`. + # -- + # : () -> Location + def def_keyword_loc: () -> Location + + # :category: Repository + # Save the def_keyword_loc location using the given saved source so that + # it can be retrieved later. + # -- + # : (_Repository repository) -> Relocation::Entry + def save_def_keyword_loc: (_Repository repository) -> Relocation::Entry + + # :category: Locations + # :call-seq: + # operator_loc -> Location | nil + # + # Returns the Location represented by `operator_loc`. + # -- + # : () -> Location? + def operator_loc: () -> Location? + + # :category: Repository + # Save the operator_loc location using the given saved source so that + # it can be retrieved later. + # -- + # : (_Repository repository) -> Relocation::Entry? + def save_operator_loc: (_Repository repository) -> Relocation::Entry? + + # :category: Locations + # :call-seq: + # lparen_loc -> Location | nil + # + # Returns the Location represented by `lparen_loc`. + # -- + # : () -> Location? + def lparen_loc: () -> Location? + + # :category: Repository + # Save the lparen_loc location using the given saved source so that + # it can be retrieved later. + # -- + # : (_Repository repository) -> Relocation::Entry? + def save_lparen_loc: (_Repository repository) -> Relocation::Entry? + + # :category: Locations + # :call-seq: + # rparen_loc -> Location | nil + # + # Returns the Location represented by `rparen_loc`. + # -- + # : () -> Location? + def rparen_loc: () -> Location? + + # :category: Repository + # Save the rparen_loc location using the given saved source so that + # it can be retrieved later. + # -- + # : (_Repository repository) -> Relocation::Entry? + def save_rparen_loc: (_Repository repository) -> Relocation::Entry? + + # :category: Locations + # :call-seq: + # equal_loc -> Location | nil + # + # Returns the Location represented by `equal_loc`. + # -- + # : () -> Location? + def equal_loc: () -> Location? + + # :category: Repository + # Save the equal_loc location using the given saved source so that + # it can be retrieved later. + # -- + # : (_Repository repository) -> Relocation::Entry? + def save_equal_loc: (_Repository repository) -> Relocation::Entry? + + # :category: Locations + # :call-seq: + # end_keyword_loc -> Location | nil + # + # Returns the Location represented by `end_keyword_loc`. + # -- + # : () -> Location? + def end_keyword_loc: () -> Location? + + # :category: Repository + # Save the end_keyword_loc location using the given saved source so that + # it can be retrieved later. + # -- + # : (_Repository repository) -> Relocation::Entry? + def save_end_keyword_loc: (_Repository repository) -> Relocation::Entry? + + # :call-seq: + # def_keyword -> String + # + # Slice the location of def_keyword_loc from the source. + # -- + # : () -> String + def def_keyword: () -> String + + # :call-seq: + # operator -> String | nil + # + # Slice the location of operator_loc from the source. + # -- + # : () -> String? + def operator: () -> String? + + # :call-seq: + # lparen -> String | nil + # + # Slice the location of lparen_loc from the source. + # -- + # : () -> String? + def lparen: () -> String? + + # :call-seq: + # rparen -> String | nil + # + # Slice the location of rparen_loc from the source. + # -- + # : () -> String? + def rparen: () -> String? + + # :call-seq: + # equal -> String | nil + # + # Slice the location of equal_loc from the source. + # -- + # : () -> String? + def equal: () -> String? + + # :call-seq: + # end_keyword -> String | nil + # + # Slice the location of end_keyword_loc from the source. + # -- + # : () -> String? + def end_keyword: () -> String? + + # : (untyped other) -> boolish + def ===: (untyped other) -> boolish + end + + # Represents the use of the `defined?` keyword. + # + # defined?(a) + # ^^^^^^^^^^^ + class DefinedNode < Node + @keyword_loc: Location + + @rparen_loc: Location? + + @value: Prism::node + + @lparen_loc: Location? + + # Initialize a new DefinedNode node. + # -- + # : (Source source, Integer node_id, Location location, Integer flags, Location? lparen_loc, Prism::node value, Location? rparen_loc, Location keyword_loc) -> 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 + + # See Node.accept. + # -- + # : (_Visitor visitor) -> untyped + def accept: (_Visitor visitor) -> untyped + + # See Node.child_nodes. + # -- + # : () -> Array[node?] + def child_nodes: () -> Array[node?] + + # See Node.each_child_node. + # -- + # : () -> Enumerator[node, void] + # : () { (node) -> void } -> void + def each_child_node: () -> Enumerator[node, void] + | () { (node) -> void } -> void + + # See Node.compact_child_nodes. + # -- + # : () -> Array[node] + def compact_child_nodes: () -> Array[node] + + # See Node.comment_targets. + # -- + # : () -> Array[node | Location] + def comment_targets: () -> Array[node | Location] + + # :call-seq: + # copy(**fields) -> DefinedNode + # + # Creates a copy of self with the given fields, using self as the template. + # -- + # : (?node_id: Integer, ?location: Location, ?flags: Integer, ?lparen_loc: Location?, ?value: Prism::node, ?rparen_loc: Location?, ?keyword_loc: Location) -> DefinedNode + def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?lparen_loc: Location?, ?value: Prism::node, ?rparen_loc: Location?, ?keyword_loc: Location) -> DefinedNode + + alias deconstruct child_nodes + + # : (Array[Symbol]? keys) -> Hash[Symbol, untyped] + def deconstruct_keys: (Array[Symbol]? keys) -> Hash[Symbol, untyped] + + # See `Node#type`. + # -- + # : () -> :defined_node + def type: () -> :defined_node + + # See `Node.type`. + # -- + # : () -> :defined_node + def self.type: () -> :defined_node + + # : () -> String + def inspect: () -> String + + # :category: Locations + # :call-seq: + # lparen_loc -> Location | nil + # + # Returns the Location represented by `lparen_loc`. + # -- + # : () -> Location? + def lparen_loc: () -> Location? + + # :category: Repository + # Save the lparen_loc location using the given saved source so that + # it can be retrieved later. + # -- + # : (_Repository repository) -> Relocation::Entry? + def save_lparen_loc: (_Repository repository) -> Relocation::Entry? + + # :call-seq: + # value -> Node + # + # Returns the `value` attribute. + # -- + # : () -> Prism::node + def value: () -> Prism::node + + # :category: Locations + # :call-seq: + # rparen_loc -> Location | nil + # + # Returns the Location represented by `rparen_loc`. + # -- + # : () -> Location? + def rparen_loc: () -> Location? + + # :category: Repository + # Save the rparen_loc location using the given saved source so that + # it can be retrieved later. + # -- + # : (_Repository repository) -> Relocation::Entry? + def save_rparen_loc: (_Repository repository) -> Relocation::Entry? + + # :category: Locations + # :call-seq: + # keyword_loc -> Location + # + # Returns the Location represented by `keyword_loc`. + # -- + # : () -> Location + def keyword_loc: () -> Location + + # :category: Repository + # Save the keyword_loc location using the given saved source so that + # it can be retrieved later. + # -- + # : (_Repository repository) -> Relocation::Entry + def save_keyword_loc: (_Repository repository) -> Relocation::Entry + + # :call-seq: + # lparen -> String | nil + # + # Slice the location of lparen_loc from the source. + # -- + # : () -> String? + def lparen: () -> String? + + # :call-seq: + # rparen -> String | nil + # + # Slice the location of rparen_loc from the source. + # -- + # : () -> String? + def rparen: () -> String? + + # :call-seq: + # keyword -> String + # + # Slice the location of keyword_loc from the source. + # -- + # : () -> String + def keyword: () -> String + + # : (untyped other) -> boolish + def ===: (untyped other) -> boolish + end + + # Represents an `else` clause in a `case`, `if`, or `unless` statement. + # + # if a then b else c end + # ^^^^^^^^^^ + class ElseNode < Node + @end_keyword_loc: Location? + + @statements: StatementsNode? + + @else_keyword_loc: Location + + # Initialize a new ElseNode node. + # -- + # : (Source source, Integer node_id, Location location, Integer flags, Location else_keyword_loc, StatementsNode? statements, Location? end_keyword_loc) -> void + def initialize: (Source source, Integer node_id, Location location, Integer flags, Location else_keyword_loc, StatementsNode? statements, Location? end_keyword_loc) -> void + + # See Node.accept. + # -- + # : (_Visitor visitor) -> untyped + def accept: (_Visitor visitor) -> untyped + + # See Node.child_nodes. + # -- + # : () -> Array[node?] + def child_nodes: () -> Array[node?] + + # See Node.each_child_node. + # -- + # : () -> Enumerator[node, void] + # : () { (node) -> void } -> void + def each_child_node: () -> Enumerator[node, void] + | () { (node) -> void } -> void + + # See Node.compact_child_nodes. + # -- + # : () -> Array[node] + def compact_child_nodes: () -> Array[node] + + # See Node.comment_targets. + # -- + # : () -> Array[node | Location] + def comment_targets: () -> Array[node | Location] + + # :call-seq: + # copy(**fields) -> ElseNode + # + # Creates a copy of self with the given fields, using self as the template. + # -- + # : (?node_id: Integer, ?location: Location, ?flags: Integer, ?else_keyword_loc: Location, ?statements: StatementsNode?, ?end_keyword_loc: Location?) -> ElseNode + def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?else_keyword_loc: Location, ?statements: StatementsNode?, ?end_keyword_loc: Location?) -> ElseNode + + alias deconstruct child_nodes + + # : (Array[Symbol]? keys) -> Hash[Symbol, untyped] + def deconstruct_keys: (Array[Symbol]? keys) -> Hash[Symbol, untyped] + + # See `Node#type`. + # -- + # : () -> :else_node + def type: () -> :else_node + + # See `Node.type`. + # -- + # : () -> :else_node + def self.type: () -> :else_node + + # : () -> String + def inspect: () -> String + + # :category: Locations + # :call-seq: + # else_keyword_loc -> Location + # + # Returns the Location represented by `else_keyword_loc`. + # -- + # : () -> Location + def else_keyword_loc: () -> Location + + # :category: Repository + # Save the else_keyword_loc location using the given saved source so that + # it can be retrieved later. + # -- + # : (_Repository repository) -> Relocation::Entry + def save_else_keyword_loc: (_Repository repository) -> Relocation::Entry + + # :call-seq: + # statements -> StatementsNode | nil + # + # Returns the `statements` attribute. + # -- + # : () -> StatementsNode? + def statements: () -> StatementsNode? + + # :category: Locations + # :call-seq: + # end_keyword_loc -> Location | nil + # + # Returns the Location represented by `end_keyword_loc`. + # -- + # : () -> Location? + def end_keyword_loc: () -> Location? + + # :category: Repository + # Save the end_keyword_loc location using the given saved source so that + # it can be retrieved later. + # -- + # : (_Repository repository) -> Relocation::Entry? + def save_end_keyword_loc: (_Repository repository) -> Relocation::Entry? + + # :call-seq: + # else_keyword -> String + # + # Slice the location of else_keyword_loc from the source. + # -- + # : () -> String + def else_keyword: () -> String + + # :call-seq: + # end_keyword -> String | nil + # + # Slice the location of end_keyword_loc from the source. + # -- + # : () -> String? + def end_keyword: () -> String? + + # : (untyped other) -> boolish + def ===: (untyped other) -> boolish + end + + # Represents an interpolated set of statements. + # + # "foo #{bar}" + # ^^^^^^ + class EmbeddedStatementsNode < Node + @closing_loc: Location + + @statements: StatementsNode? + + @opening_loc: Location + + # Initialize a new EmbeddedStatementsNode node. + # -- + # : (Source source, Integer node_id, Location location, Integer flags, Location opening_loc, StatementsNode? statements, Location closing_loc) -> void + def initialize: (Source source, Integer node_id, Location location, Integer flags, Location opening_loc, StatementsNode? statements, Location closing_loc) -> void + + # See Node.accept. + # -- + # : (_Visitor visitor) -> untyped + def accept: (_Visitor visitor) -> untyped + + # See Node.child_nodes. + # -- + # : () -> Array[node?] + def child_nodes: () -> Array[node?] + + # See Node.each_child_node. + # -- + # : () -> Enumerator[node, void] + # : () { (node) -> void } -> void + def each_child_node: () -> Enumerator[node, void] + | () { (node) -> void } -> void + + # See Node.compact_child_nodes. + # -- + # : () -> Array[node] + def compact_child_nodes: () -> Array[node] + + # See Node.comment_targets. + # -- + # : () -> Array[node | Location] + def comment_targets: () -> Array[node | Location] + + # :call-seq: + # copy(**fields) -> EmbeddedStatementsNode + # + # Creates a copy of self with the given fields, using self as the template. + # -- + # : (?node_id: Integer, ?location: Location, ?flags: Integer, ?opening_loc: Location, ?statements: StatementsNode?, ?closing_loc: Location) -> EmbeddedStatementsNode + def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?opening_loc: Location, ?statements: StatementsNode?, ?closing_loc: Location) -> EmbeddedStatementsNode + + alias deconstruct child_nodes + + # : (Array[Symbol]? keys) -> Hash[Symbol, untyped] + def deconstruct_keys: (Array[Symbol]? keys) -> Hash[Symbol, untyped] + + # See `Node#type`. + # -- + # : () -> :embedded_statements_node + def type: () -> :embedded_statements_node + + # See `Node.type`. + # -- + # : () -> :embedded_statements_node + def self.type: () -> :embedded_statements_node + + # : () -> String + def inspect: () -> String + + # :category: Locations + # :call-seq: + # opening_loc -> Location + # + # Returns the Location represented by `opening_loc`. + # -- + # : () -> Location + def opening_loc: () -> Location + + # :category: Repository + # Save the opening_loc location using the given saved source so that + # it can be retrieved later. + # -- + # : (_Repository repository) -> Relocation::Entry + def save_opening_loc: (_Repository repository) -> Relocation::Entry + + # :call-seq: + # statements -> StatementsNode | nil + # + # Returns the `statements` attribute. + # -- + # : () -> StatementsNode? + def statements: () -> StatementsNode? + + # :category: Locations + # :call-seq: + # closing_loc -> Location + # + # Returns the Location represented by `closing_loc`. + # -- + # : () -> Location + def closing_loc: () -> Location + + # :category: Repository + # Save the closing_loc location using the given saved source so that + # it can be retrieved later. + # -- + # : (_Repository repository) -> Relocation::Entry + def save_closing_loc: (_Repository repository) -> Relocation::Entry + + # :call-seq: + # opening -> String + # + # Slice the location of opening_loc from the source. + # -- + # : () -> String + def opening: () -> String + + # :call-seq: + # closing -> String + # + # Slice the location of closing_loc from the source. + # -- + # : () -> String + def closing: () -> String + + # : (untyped other) -> boolish + def ===: (untyped other) -> boolish + end + + # Represents an interpolated variable. + # + # "foo #@bar" + # ^^^^^ + class EmbeddedVariableNode < Node + @variable: InstanceVariableReadNode | ClassVariableReadNode | GlobalVariableReadNode | BackReferenceReadNode | NumberedReferenceReadNode + + @operator_loc: Location + + # Initialize a new EmbeddedVariableNode node. + # -- + # : (Source source, Integer node_id, Location location, Integer flags, Location operator_loc, (InstanceVariableReadNode | ClassVariableReadNode | GlobalVariableReadNode | BackReferenceReadNode | NumberedReferenceReadNode) variable) -> void + def initialize: (Source source, Integer node_id, Location location, Integer flags, Location operator_loc, InstanceVariableReadNode | ClassVariableReadNode | GlobalVariableReadNode | BackReferenceReadNode | NumberedReferenceReadNode variable) -> void + + # See Node.accept. + # -- + # : (_Visitor visitor) -> untyped + def accept: (_Visitor visitor) -> untyped + + # See Node.child_nodes. + # -- + # : () -> Array[node?] + def child_nodes: () -> Array[node?] + + # See Node.each_child_node. + # -- + # : () -> Enumerator[node, void] + # : () { (node) -> void } -> void + def each_child_node: () -> Enumerator[node, void] + | () { (node) -> void } -> void + + # See Node.compact_child_nodes. + # -- + # : () -> Array[node] + def compact_child_nodes: () -> Array[node] + + # See Node.comment_targets. + # -- + # : () -> Array[node | Location] + def comment_targets: () -> Array[node | Location] + + # :call-seq: + # copy(**fields) -> EmbeddedVariableNode + # + # Creates a copy of self with the given fields, using self as the template. + # -- + # : (?node_id: Integer, ?location: Location, ?flags: Integer, ?operator_loc: Location, ?variable: (InstanceVariableReadNode | ClassVariableReadNode | GlobalVariableReadNode | BackReferenceReadNode | NumberedReferenceReadNode)) -> EmbeddedVariableNode + def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?operator_loc: Location, ?variable: InstanceVariableReadNode | ClassVariableReadNode | GlobalVariableReadNode | BackReferenceReadNode | NumberedReferenceReadNode) -> EmbeddedVariableNode + + alias deconstruct child_nodes + + # : (Array[Symbol]? keys) -> Hash[Symbol, untyped] + def deconstruct_keys: (Array[Symbol]? keys) -> Hash[Symbol, untyped] + + # See `Node#type`. + # -- + # : () -> :embedded_variable_node + def type: () -> :embedded_variable_node + + # See `Node.type`. + # -- + # : () -> :embedded_variable_node + def self.type: () -> :embedded_variable_node + + # : () -> String + def inspect: () -> String + + # :category: Locations + # :call-seq: + # operator_loc -> Location + # + # Returns the Location represented by `operator_loc`. + # -- + # : () -> Location + def operator_loc: () -> Location + + # :category: Repository + # Save the operator_loc location using the given saved source so that + # it can be retrieved later. + # -- + # : (_Repository repository) -> Relocation::Entry + def save_operator_loc: (_Repository repository) -> Relocation::Entry + + # :call-seq: + # variable -> InstanceVariableReadNode | ClassVariableReadNode | GlobalVariableReadNode | BackReferenceReadNode | NumberedReferenceReadNode + # + # Returns the `variable` attribute. + # -- + # : () -> (InstanceVariableReadNode | ClassVariableReadNode | GlobalVariableReadNode | BackReferenceReadNode | NumberedReferenceReadNode) + def variable: () -> (InstanceVariableReadNode | ClassVariableReadNode | GlobalVariableReadNode | BackReferenceReadNode | NumberedReferenceReadNode) + + # :call-seq: + # operator -> String + # + # Slice the location of operator_loc from the source. + # -- + # : () -> String + def operator: () -> String + + # : (untyped other) -> boolish + def ===: (untyped other) -> boolish + end + + # Represents an `ensure` clause in a `begin` statement. + # + # begin + # foo + # ensure + # ^^^^^^ + # bar + # end + class EnsureNode < Node + @end_keyword_loc: Location + + @statements: StatementsNode? + + @ensure_keyword_loc: Location + + # Initialize a new EnsureNode node. + # -- + # : (Source source, Integer node_id, Location location, Integer flags, Location ensure_keyword_loc, StatementsNode? statements, Location end_keyword_loc) -> void + def initialize: (Source source, Integer node_id, Location location, Integer flags, Location ensure_keyword_loc, StatementsNode? statements, Location end_keyword_loc) -> void + + # See Node.accept. + # -- + # : (_Visitor visitor) -> untyped + def accept: (_Visitor visitor) -> untyped + + # See Node.child_nodes. + # -- + # : () -> Array[node?] + def child_nodes: () -> Array[node?] + + # See Node.each_child_node. + # -- + # : () -> Enumerator[node, void] + # : () { (node) -> void } -> void + def each_child_node: () -> Enumerator[node, void] + | () { (node) -> void } -> void + + # See Node.compact_child_nodes. + # -- + # : () -> Array[node] + def compact_child_nodes: () -> Array[node] + + # See Node.comment_targets. + # -- + # : () -> Array[node | Location] + def comment_targets: () -> Array[node | Location] + + # :call-seq: + # copy(**fields) -> EnsureNode + # + # Creates a copy of self with the given fields, using self as the template. + # -- + # : (?node_id: Integer, ?location: Location, ?flags: Integer, ?ensure_keyword_loc: Location, ?statements: StatementsNode?, ?end_keyword_loc: Location) -> EnsureNode + def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?ensure_keyword_loc: Location, ?statements: StatementsNode?, ?end_keyword_loc: Location) -> EnsureNode + + alias deconstruct child_nodes + + # : (Array[Symbol]? keys) -> Hash[Symbol, untyped] + def deconstruct_keys: (Array[Symbol]? keys) -> Hash[Symbol, untyped] + + # See `Node#type`. + # -- + # : () -> :ensure_node + def type: () -> :ensure_node + + # See `Node.type`. + # -- + # : () -> :ensure_node + def self.type: () -> :ensure_node + + # : () -> String + def inspect: () -> String + + # :category: Locations + # :call-seq: + # ensure_keyword_loc -> Location + # + # Returns the Location represented by `ensure_keyword_loc`. + # -- + # : () -> Location + def ensure_keyword_loc: () -> Location + + # :category: Repository + # Save the ensure_keyword_loc location using the given saved source so that + # it can be retrieved later. + # -- + # : (_Repository repository) -> Relocation::Entry + def save_ensure_keyword_loc: (_Repository repository) -> Relocation::Entry + + # :call-seq: + # statements -> StatementsNode | nil + # + # Returns the `statements` attribute. + # -- + # : () -> StatementsNode? + def statements: () -> StatementsNode? + + # :category: Locations + # :call-seq: + # end_keyword_loc -> Location + # + # Returns the Location represented by `end_keyword_loc`. + # -- + # : () -> Location + def end_keyword_loc: () -> Location + + # :category: Repository + # Save the end_keyword_loc location using the given saved source so that + # it can be retrieved later. + # -- + # : (_Repository repository) -> Relocation::Entry + def save_end_keyword_loc: (_Repository repository) -> Relocation::Entry + + # :call-seq: + # ensure_keyword -> String + # + # Slice the location of ensure_keyword_loc from the source. + # -- + # : () -> String + def ensure_keyword: () -> String + + # :call-seq: + # end_keyword -> String + # + # Slice the location of end_keyword_loc from the source. + # -- + # : () -> String + def end_keyword: () -> String + + # : (untyped other) -> boolish + def ===: (untyped other) -> boolish + end + + # Represents the use of the literal `false` keyword. + # + # false + # ^^^^^ + class FalseNode < Node + # Initialize a new FalseNode node. + # -- + # : (Source source, Integer node_id, Location location, Integer flags, ) -> void + def initialize: (Source source, Integer node_id, Location location, Integer flags) -> void + + # See Node.accept. + # -- + # : (_Visitor visitor) -> untyped + def accept: (_Visitor visitor) -> untyped + + # See Node.child_nodes. + # -- + # : () -> Array[node?] + def child_nodes: () -> Array[node?] + + # See Node.each_child_node. + # -- + # : () -> Enumerator[node, void] + # : () { (node) -> void } -> void + def each_child_node: () -> Enumerator[node, void] + | () { (node) -> void } -> void + + # See Node.compact_child_nodes. + # -- + # : () -> Array[node] + def compact_child_nodes: () -> Array[node] + + # See Node.comment_targets. + # -- + # : () -> Array[node | Location] + def comment_targets: () -> Array[node | Location] + + # :call-seq: + # copy(**fields) -> FalseNode + # + # Creates a copy of self with the given fields, using self as the template. + # -- + # : (?node_id: Integer, ?location: Location, ?flags: Integer, ) -> FalseNode + def copy: (?node_id: Integer, ?location: Location, ?flags: Integer) -> FalseNode + + alias deconstruct child_nodes + + # : (Array[Symbol]? keys) -> Hash[Symbol, untyped] + def deconstruct_keys: (Array[Symbol]? keys) -> Hash[Symbol, untyped] + + # See `Node#type`. + # -- + # : () -> :false_node + def type: () -> :false_node + + # See `Node.type`. + # -- + # : () -> :false_node + def self.type: () -> :false_node + + # : () -> String + def inspect: () -> String + + # : (untyped other) -> boolish + def ===: (untyped other) -> boolish + 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 + @closing_loc: Location? + + @opening_loc: Location? + + @right: SplatNode | MissingNode + + @requireds: Array[Prism::node] + + @left: SplatNode + + @constant: (ConstantPathNode | ConstantReadNode)? + + # Initialize a new FindPatternNode node. + # -- + # : (Source source, Integer node_id, Location location, Integer flags, (ConstantPathNode | ConstantReadNode)? constant, SplatNode left, Array[Prism::node] requireds, (SplatNode | MissingNode) right, Location? opening_loc, Location? closing_loc) -> void + def initialize: (Source source, Integer node_id, Location location, Integer flags, (ConstantPathNode | ConstantReadNode)? constant, SplatNode left, Array[Prism::node] requireds, SplatNode | MissingNode right, Location? opening_loc, Location? closing_loc) -> void + + # See Node.accept. + # -- + # : (_Visitor visitor) -> untyped + def accept: (_Visitor visitor) -> untyped + + # See Node.child_nodes. + # -- + # : () -> Array[node?] + def child_nodes: () -> Array[node?] + + # See Node.each_child_node. + # -- + # : () -> Enumerator[node, void] + # : () { (node) -> void } -> void + def each_child_node: () -> Enumerator[node, void] + | () { (node) -> void } -> void + + # See Node.compact_child_nodes. + # -- + # : () -> Array[node] + def compact_child_nodes: () -> Array[node] + + # See Node.comment_targets. + # -- + # : () -> Array[node | Location] + def comment_targets: () -> Array[node | Location] + + # :call-seq: + # copy(**fields) -> FindPatternNode + # + # Creates a copy of self with the given fields, using self as the template. + # -- + # : (?node_id: Integer, ?location: Location, ?flags: Integer, ?constant: (ConstantPathNode | ConstantReadNode)?, ?left: SplatNode, ?requireds: Array[Prism::node], ?right: (SplatNode | MissingNode), ?opening_loc: Location?, ?closing_loc: Location?) -> FindPatternNode + def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?constant: (ConstantPathNode | ConstantReadNode)?, ?left: SplatNode, ?requireds: Array[Prism::node], ?right: SplatNode | MissingNode, ?opening_loc: Location?, ?closing_loc: Location?) -> FindPatternNode + + alias deconstruct child_nodes + + # : (Array[Symbol]? keys) -> Hash[Symbol, untyped] + def deconstruct_keys: (Array[Symbol]? keys) -> Hash[Symbol, untyped] + + # See `Node#type`. + # -- + # : () -> :find_pattern_node + def type: () -> :find_pattern_node + + # See `Node.type`. + # -- + # : () -> :find_pattern_node + def self.type: () -> :find_pattern_node + + # : () -> String + def inspect: () -> String + + # :call-seq: + # constant -> ConstantPathNode | ConstantReadNode | nil + # + # Represents the optional constant preceding the pattern + # + # foo in Foo(*bar, baz, *qux) + # ^^^ + # -- + # : () -> (ConstantPathNode | ConstantReadNode)? + def constant: () -> (ConstantPathNode | ConstantReadNode)? + + # :call-seq: + # left -> SplatNode + # + # Represents the first wildcard node in the pattern. + # + # foo in *bar, baz, *qux + # ^^^^ + # + # foo in Foo(*bar, baz, *qux) + # ^^^^ + # -- + # : () -> SplatNode + def left: () -> SplatNode + + # :call-seq: + # requireds -> Array[Node] + # + # Represents the nodes in between the wildcards. + # + # foo in *bar, baz, *qux + # ^^^ + # + # foo in Foo(*bar, baz, 1, *qux) + # ^^^^^^ + # -- + # : () -> Array[Prism::node] + def requireds: () -> Array[Prism::node] + + # :call-seq: + # right -> SplatNode | MissingNode + # + # Represents the second wildcard node in the pattern. + # + # foo in *bar, baz, *qux + # ^^^^ + # + # foo in Foo(*bar, baz, *qux) + # ^^^^ + # -- + # : () -> (SplatNode | MissingNode) + def right: () -> (SplatNode | MissingNode) + + # :category: Locations + # :call-seq: + # opening_loc -> Location | nil + # + # The Location of the opening brace. + # + # foo in [*bar, baz, *qux] + # ^ + # + # foo in Foo(*bar, baz, *qux) + # ^ + # -- + # : () -> Location? + def opening_loc: () -> Location? + + # :category: Repository + # Save the opening_loc location using the given saved source so that + # it can be retrieved later. + # -- + # : (_Repository repository) -> Relocation::Entry? + def save_opening_loc: (_Repository repository) -> Relocation::Entry? + + # :category: Locations + # :call-seq: + # closing_loc -> Location | nil + # + # The Location of the closing brace. + # + # foo in [*bar, baz, *qux] + # ^ + # + # foo in Foo(*bar, baz, *qux) + # ^ + # -- + # : () -> Location? + def closing_loc: () -> Location? + + # :category: Repository + # Save the closing_loc location using the given saved source so that + # it can be retrieved later. + # -- + # : (_Repository repository) -> Relocation::Entry? + def save_closing_loc: (_Repository repository) -> Relocation::Entry? + + # :call-seq: + # opening -> String | nil + # + # Slice the location of opening_loc from the source. + # -- + # : () -> String? + def opening: () -> String? + + # :call-seq: + # closing -> String | nil + # + # Slice the location of closing_loc from the source. + # -- + # : () -> String? + def closing: () -> String? + + # : (untyped other) -> boolish + def ===: (untyped other) -> boolish + end + + # Represents the use of the `..` or `...` operators to create flip flops. + # + # baz if foo .. bar + # ^^^^^^^^^^ + class FlipFlopNode < Node + @operator_loc: Location + + @right: Prism::node? + + @left: Prism::node? + + # Initialize a new FlipFlopNode node. + # -- + # : (Source source, Integer node_id, Location location, Integer flags, Prism::node? left, Prism::node? right, Location operator_loc) -> void + def initialize: (Source source, Integer node_id, Location location, Integer flags, Prism::node? left, Prism::node? right, Location operator_loc) -> void + + # See Node.accept. + # -- + # : (_Visitor visitor) -> untyped + def accept: (_Visitor visitor) -> untyped + + # See Node.child_nodes. + # -- + # : () -> Array[node?] + def child_nodes: () -> Array[node?] + + # See Node.each_child_node. + # -- + # : () -> Enumerator[node, void] + # : () { (node) -> void } -> void + def each_child_node: () -> Enumerator[node, void] + | () { (node) -> void } -> void + + # See Node.compact_child_nodes. + # -- + # : () -> Array[node] + def compact_child_nodes: () -> Array[node] + + # See Node.comment_targets. + # -- + # : () -> Array[node | Location] + def comment_targets: () -> Array[node | Location] + + # :call-seq: + # copy(**fields) -> FlipFlopNode + # + # Creates a copy of self with the given fields, using self as the template. + # -- + # : (?node_id: Integer, ?location: Location, ?flags: Integer, ?left: Prism::node?, ?right: Prism::node?, ?operator_loc: Location) -> FlipFlopNode + def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?left: Prism::node?, ?right: Prism::node?, ?operator_loc: Location) -> FlipFlopNode + + alias deconstruct child_nodes + + # : (Array[Symbol]? keys) -> Hash[Symbol, untyped] + def deconstruct_keys: (Array[Symbol]? keys) -> Hash[Symbol, untyped] + + # See `Node#type`. + # -- + # : () -> :flip_flop_node + def type: () -> :flip_flop_node + + # See `Node.type`. + # -- + # : () -> :flip_flop_node + def self.type: () -> :flip_flop_node + + # : () -> String + def inspect: () -> String + + # :category: Flags + # ... operator + # -- + # : () -> bool + def exclude_end?: () -> bool + + # :call-seq: + # left -> Node | nil + # + # Returns the `left` attribute. + # -- + # : () -> Prism::node? + def left: () -> Prism::node? + + # :call-seq: + # right -> Node | nil + # + # Returns the `right` attribute. + # -- + # : () -> Prism::node? + def right: () -> Prism::node? + + # :category: Locations + # :call-seq: + # operator_loc -> Location + # + # Returns the Location represented by `operator_loc`. + # -- + # : () -> Location + def operator_loc: () -> Location + + # :category: Repository + # Save the operator_loc location using the given saved source so that + # it can be retrieved later. + # -- + # : (_Repository repository) -> Relocation::Entry + def save_operator_loc: (_Repository repository) -> Relocation::Entry + + # :call-seq: + # operator -> String + # + # Slice the location of operator_loc from the source. + # -- + # : () -> String + def operator: () -> String + + # : (untyped other) -> boolish + def ===: (untyped other) -> boolish + end + + # Represents a floating point number literal. + # + # 1.0 + # ^^^ + class FloatNode < Node + @value: Float + + # Initialize a new FloatNode node. + # -- + # : (Source source, Integer node_id, Location location, Integer flags, Float value) -> void + def initialize: (Source source, Integer node_id, Location location, Integer flags, Float value) -> void + + # See Node.accept. + # -- + # : (_Visitor visitor) -> untyped + def accept: (_Visitor visitor) -> untyped + + # See Node.child_nodes. + # -- + # : () -> Array[node?] + def child_nodes: () -> Array[node?] + + # See Node.each_child_node. + # -- + # : () -> Enumerator[node, void] + # : () { (node) -> void } -> void + def each_child_node: () -> Enumerator[node, void] + | () { (node) -> void } -> void + + # See Node.compact_child_nodes. + # -- + # : () -> Array[node] + def compact_child_nodes: () -> Array[node] + + # See Node.comment_targets. + # -- + # : () -> Array[node | Location] + def comment_targets: () -> Array[node | Location] + + # :call-seq: + # copy(**fields) -> FloatNode + # + # Creates a copy of self with the given fields, using self as the template. + # -- + # : (?node_id: Integer, ?location: Location, ?flags: Integer, ?value: Float) -> FloatNode + def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?value: Float) -> FloatNode + + alias deconstruct child_nodes + + # : (Array[Symbol]? keys) -> Hash[Symbol, untyped] + def deconstruct_keys: (Array[Symbol]? keys) -> Hash[Symbol, untyped] + + # See `Node#type`. + # -- + # : () -> :float_node + def type: () -> :float_node + + # See `Node.type`. + # -- + # : () -> :float_node + def self.type: () -> :float_node + + # : () -> String + def inspect: () -> String + + # :call-seq: + # value -> Float + # + # The value of the floating point number as a Float. + # -- + # : () -> Float + def value: () -> Float + + # : (untyped other) -> boolish + def ===: (untyped other) -> boolish + end + + # Represents the use of the `for` keyword. + # + # for i in a end + # ^^^^^^^^^^^^^^ + class ForNode < Node + @end_keyword_loc: Location + + @do_keyword_loc: Location? + + @in_keyword_loc: Location + + @for_keyword_loc: Location + + @statements: StatementsNode? + + @collection: Prism::node + + @index: LocalVariableTargetNode | InstanceVariableTargetNode | ClassVariableTargetNode | GlobalVariableTargetNode | ConstantTargetNode | ConstantPathTargetNode | CallTargetNode | IndexTargetNode | MultiTargetNode | BackReferenceReadNode | NumberedReferenceReadNode | MissingNode + + # Initialize a new ForNode node. + # -- + # : (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 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 + + # See Node.accept. + # -- + # : (_Visitor visitor) -> untyped + def accept: (_Visitor visitor) -> untyped + + # See Node.child_nodes. + # -- + # : () -> Array[node?] + def child_nodes: () -> Array[node?] + + # See Node.each_child_node. + # -- + # : () -> Enumerator[node, void] + # : () { (node) -> void } -> void + def each_child_node: () -> Enumerator[node, void] + | () { (node) -> void } -> void + + # See Node.compact_child_nodes. + # -- + # : () -> Array[node] + def compact_child_nodes: () -> Array[node] + + # See Node.comment_targets. + # -- + # : () -> Array[node | Location] + def comment_targets: () -> Array[node | Location] + + # :call-seq: + # copy(**fields) -> ForNode + # + # Creates a copy of self with the given fields, using self as the template. + # -- + # : (?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: 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 + + alias deconstruct child_nodes + + # : (Array[Symbol]? keys) -> Hash[Symbol, untyped] + def deconstruct_keys: (Array[Symbol]? keys) -> Hash[Symbol, untyped] + + # See `Node#type`. + # -- + # : () -> :for_node + def type: () -> :for_node + + # See `Node.type`. + # -- + # : () -> :for_node + def self.type: () -> :for_node + + # : () -> String + def inspect: () -> String + + # :call-seq: + # index -> LocalVariableTargetNode | InstanceVariableTargetNode | ClassVariableTargetNode | GlobalVariableTargetNode | ConstantTargetNode | ConstantPathTargetNode | CallTargetNode | IndexTargetNode | MultiTargetNode | BackReferenceReadNode | NumberedReferenceReadNode | MissingNode + # + # The index expression for `for` loops. + # + # for i in a end + # ^ + # -- + # : () -> (LocalVariableTargetNode | InstanceVariableTargetNode | ClassVariableTargetNode | GlobalVariableTargetNode | ConstantTargetNode | ConstantPathTargetNode | CallTargetNode | IndexTargetNode | MultiTargetNode | BackReferenceReadNode | NumberedReferenceReadNode | MissingNode) + def index: () -> (LocalVariableTargetNode | InstanceVariableTargetNode | ClassVariableTargetNode | GlobalVariableTargetNode | ConstantTargetNode | ConstantPathTargetNode | CallTargetNode | IndexTargetNode | MultiTargetNode | BackReferenceReadNode | NumberedReferenceReadNode | MissingNode) + + # :call-seq: + # collection -> Node + # + # The collection to iterate over. + # + # for i in a end + # ^ + # -- + # : () -> Prism::node + def collection: () -> Prism::node + + # :call-seq: + # statements -> StatementsNode | nil + # + # Represents the body of statements to execute for each iteration of the loop. + # + # for i in a + # foo(i) + # ^^^^^^ + # end + # -- + # : () -> StatementsNode? + def statements: () -> StatementsNode? + + # :category: Locations + # :call-seq: + # for_keyword_loc -> Location + # + # The Location of the `for` keyword. + # + # for i in a end + # ^^^ + # -- + # : () -> Location + def for_keyword_loc: () -> Location + + # :category: Repository + # Save the for_keyword_loc location using the given saved source so that + # it can be retrieved later. + # -- + # : (_Repository repository) -> Relocation::Entry + def save_for_keyword_loc: (_Repository repository) -> Relocation::Entry + + # :category: Locations + # :call-seq: + # in_keyword_loc -> Location + # + # The Location of the `in` keyword. + # + # for i in a end + # ^^ + # -- + # : () -> Location + def in_keyword_loc: () -> Location + + # :category: Repository + # Save the in_keyword_loc location using the given saved source so that + # it can be retrieved later. + # -- + # : (_Repository repository) -> Relocation::Entry + def save_in_keyword_loc: (_Repository repository) -> Relocation::Entry + + # :category: Locations + # :call-seq: + # do_keyword_loc -> Location | nil + # + # The Location of the `do` keyword, if present. + # + # for i in a do end + # ^^ + # -- + # : () -> Location? + def do_keyword_loc: () -> Location? + + # :category: Repository + # Save the do_keyword_loc location using the given saved source so that + # it can be retrieved later. + # -- + # : (_Repository repository) -> Relocation::Entry? + def save_do_keyword_loc: (_Repository repository) -> Relocation::Entry? + + # :category: Locations + # :call-seq: + # end_keyword_loc -> Location + # + # The Location of the `end` keyword. + # + # for i in a end + # ^^^ + # -- + # : () -> Location + def end_keyword_loc: () -> Location + + # :category: Repository + # Save the end_keyword_loc location using the given saved source so that + # it can be retrieved later. + # -- + # : (_Repository repository) -> Relocation::Entry + def save_end_keyword_loc: (_Repository repository) -> Relocation::Entry + + # :call-seq: + # for_keyword -> String + # + # Slice the location of for_keyword_loc from the source. + # -- + # : () -> String + def for_keyword: () -> String + + # :call-seq: + # in_keyword -> String + # + # Slice the location of in_keyword_loc from the source. + # -- + # : () -> String + def in_keyword: () -> String + + # :call-seq: + # do_keyword -> String | nil + # + # Slice the location of do_keyword_loc from the source. + # -- + # : () -> String? + def do_keyword: () -> String? + + # :call-seq: + # end_keyword -> String + # + # Slice the location of end_keyword_loc from the source. + # -- + # : () -> String + def end_keyword: () -> String + + # : (untyped other) -> boolish + def ===: (untyped other) -> boolish + end + + # Represents forwarding all arguments to this method to another method. + # + # def foo(...) + # bar(...) + # ^^^ + # end + class ForwardingArgumentsNode < Node + # Initialize a new ForwardingArgumentsNode node. + # -- + # : (Source source, Integer node_id, Location location, Integer flags, ) -> void + def initialize: (Source source, Integer node_id, Location location, Integer flags) -> void + + # See Node.accept. + # -- + # : (_Visitor visitor) -> untyped + def accept: (_Visitor visitor) -> untyped + + # See Node.child_nodes. + # -- + # : () -> Array[node?] + def child_nodes: () -> Array[node?] + + # See Node.each_child_node. + # -- + # : () -> Enumerator[node, void] + # : () { (node) -> void } -> void + def each_child_node: () -> Enumerator[node, void] + | () { (node) -> void } -> void + + # See Node.compact_child_nodes. + # -- + # : () -> Array[node] + def compact_child_nodes: () -> Array[node] + + # See Node.comment_targets. + # -- + # : () -> Array[node | Location] + def comment_targets: () -> Array[node | Location] + + # :call-seq: + # copy(**fields) -> ForwardingArgumentsNode + # + # Creates a copy of self with the given fields, using self as the template. + # -- + # : (?node_id: Integer, ?location: Location, ?flags: Integer, ) -> ForwardingArgumentsNode + def copy: (?node_id: Integer, ?location: Location, ?flags: Integer) -> ForwardingArgumentsNode + + alias deconstruct child_nodes + + # : (Array[Symbol]? keys) -> Hash[Symbol, untyped] + def deconstruct_keys: (Array[Symbol]? keys) -> Hash[Symbol, untyped] + + # See `Node#type`. + # -- + # : () -> :forwarding_arguments_node + def type: () -> :forwarding_arguments_node + + # See `Node.type`. + # -- + # : () -> :forwarding_arguments_node + def self.type: () -> :forwarding_arguments_node + + # : () -> String + def inspect: () -> String + + # : (untyped other) -> boolish + def ===: (untyped other) -> boolish + 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. + # -- + # : (Source source, Integer node_id, Location location, Integer flags, ) -> void + def initialize: (Source source, Integer node_id, Location location, Integer flags) -> void + + # See Node.accept. + # -- + # : (_Visitor visitor) -> untyped + def accept: (_Visitor visitor) -> untyped + + # See Node.child_nodes. + # -- + # : () -> Array[node?] + def child_nodes: () -> Array[node?] + + # See Node.each_child_node. + # -- + # : () -> Enumerator[node, void] + # : () { (node) -> void } -> void + def each_child_node: () -> Enumerator[node, void] + | () { (node) -> void } -> void + + # See Node.compact_child_nodes. + # -- + # : () -> Array[node] + def compact_child_nodes: () -> Array[node] + + # See Node.comment_targets. + # -- + # : () -> Array[node | Location] + def comment_targets: () -> Array[node | Location] + + # :call-seq: + # copy(**fields) -> ForwardingParameterNode + # + # Creates a copy of self with the given fields, using self as the template. + # -- + # : (?node_id: Integer, ?location: Location, ?flags: Integer, ) -> ForwardingParameterNode + def copy: (?node_id: Integer, ?location: Location, ?flags: Integer) -> ForwardingParameterNode + + alias deconstruct child_nodes + + # : (Array[Symbol]? keys) -> Hash[Symbol, untyped] + def deconstruct_keys: (Array[Symbol]? keys) -> Hash[Symbol, untyped] + + # See `Node#type`. + # -- + # : () -> :forwarding_parameter_node + def type: () -> :forwarding_parameter_node + + # See `Node.type`. + # -- + # : () -> :forwarding_parameter_node + def self.type: () -> :forwarding_parameter_node + + # : () -> String + def inspect: () -> String + + # : (untyped other) -> boolish + def ===: (untyped other) -> boolish + 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 + @block: BlockNode? + + # Initialize a new ForwardingSuperNode node. + # -- + # : (Source source, Integer node_id, Location location, Integer flags, BlockNode? block) -> void + def initialize: (Source source, Integer node_id, Location location, Integer flags, BlockNode? block) -> void + + # See Node.accept. + # -- + # : (_Visitor visitor) -> untyped + def accept: (_Visitor visitor) -> untyped + + # See Node.child_nodes. + # -- + # : () -> Array[node?] + def child_nodes: () -> Array[node?] + + # See Node.each_child_node. + # -- + # : () -> Enumerator[node, void] + # : () { (node) -> void } -> void + def each_child_node: () -> Enumerator[node, void] + | () { (node) -> void } -> void + + # See Node.compact_child_nodes. + # -- + # : () -> Array[node] + def compact_child_nodes: () -> Array[node] + + # See Node.comment_targets. + # -- + # : () -> Array[node | Location] + def comment_targets: () -> Array[node | Location] + + # :call-seq: + # copy(**fields) -> ForwardingSuperNode + # + # Creates a copy of self with the given fields, using self as the template. + # -- + # : (?node_id: Integer, ?location: Location, ?flags: Integer, ?block: BlockNode?) -> ForwardingSuperNode + def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?block: BlockNode?) -> ForwardingSuperNode + + alias deconstruct child_nodes + + # : (Array[Symbol]? keys) -> Hash[Symbol, untyped] + def deconstruct_keys: (Array[Symbol]? keys) -> Hash[Symbol, untyped] + + # See `Node#type`. + # -- + # : () -> :forwarding_super_node + def type: () -> :forwarding_super_node + + # See `Node.type`. + # -- + # : () -> :forwarding_super_node + def self.type: () -> :forwarding_super_node + + # : () -> String + def inspect: () -> String + + # :call-seq: + # block -> BlockNode | nil + # + # All other arguments are forwarded as normal, except the original block is replaced with the new block. + # -- + # : () -> BlockNode? + def block: () -> BlockNode? + + # : (untyped other) -> boolish + def ===: (untyped other) -> boolish + end + + # Represents the use of the `&&=` operator for assignment to a global variable. + # + # $target &&= value + # ^^^^^^^^^^^^^^^^^ + class GlobalVariableAndWriteNode < Node + @value: Prism::node + + @operator_loc: Location + + @name_loc: Location + + @name: Symbol + + # Initialize a new GlobalVariableAndWriteNode node. + # -- + # : (Source source, Integer node_id, Location location, Integer flags, Symbol name, Location name_loc, Location operator_loc, Prism::node value) -> void + def initialize: (Source source, Integer node_id, Location location, Integer flags, Symbol name, Location name_loc, Location operator_loc, Prism::node value) -> void + + # See Node.accept. + # -- + # : (_Visitor visitor) -> untyped + def accept: (_Visitor visitor) -> untyped + + # See Node.child_nodes. + # -- + # : () -> Array[node?] + def child_nodes: () -> Array[node?] + + # See Node.each_child_node. + # -- + # : () -> Enumerator[node, void] + # : () { (node) -> void } -> void + def each_child_node: () -> Enumerator[node, void] + | () { (node) -> void } -> void + + # See Node.compact_child_nodes. + # -- + # : () -> Array[node] + def compact_child_nodes: () -> Array[node] + + # See Node.comment_targets. + # -- + # : () -> Array[node | Location] + def comment_targets: () -> Array[node | Location] + + # :call-seq: + # copy(**fields) -> GlobalVariableAndWriteNode + # + # Creates a copy of self with the given fields, using self as the template. + # -- + # : (?node_id: Integer, ?location: Location, ?flags: Integer, ?name: Symbol, ?name_loc: Location, ?operator_loc: Location, ?value: Prism::node) -> GlobalVariableAndWriteNode + def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?name: Symbol, ?name_loc: Location, ?operator_loc: Location, ?value: Prism::node) -> GlobalVariableAndWriteNode + + alias deconstruct child_nodes + + # : (Array[Symbol]? keys) -> Hash[Symbol, untyped] + def deconstruct_keys: (Array[Symbol]? keys) -> Hash[Symbol, untyped] + + # See `Node#type`. + # -- + # : () -> :global_variable_and_write_node + def type: () -> :global_variable_and_write_node + + # See `Node.type`. + # -- + # : () -> :global_variable_and_write_node + def self.type: () -> :global_variable_and_write_node + + # : () -> String + def inspect: () -> String + + # :call-seq: + # name -> Symbol + # + # Returns the `name` attribute. + # -- + # : () -> Symbol + def name: () -> Symbol + + # :category: Locations + # :call-seq: + # name_loc -> Location + # + # Returns the Location represented by `name_loc`. + # -- + # : () -> Location + def name_loc: () -> Location + + # :category: Repository + # Save the name_loc location using the given saved source so that + # it can be retrieved later. + # -- + # : (_Repository repository) -> Relocation::Entry + def save_name_loc: (_Repository repository) -> Relocation::Entry + + # :category: Locations + # :call-seq: + # operator_loc -> Location + # + # Returns the Location represented by `operator_loc`. + # -- + # : () -> Location + def operator_loc: () -> Location + + # :category: Repository + # Save the operator_loc location using the given saved source so that + # it can be retrieved later. + # -- + # : (_Repository repository) -> Relocation::Entry + def save_operator_loc: (_Repository repository) -> Relocation::Entry + + # :call-seq: + # value -> Node + # + # Returns the `value` attribute. + # -- + # : () -> Prism::node + def value: () -> Prism::node + + # :call-seq: + # operator -> String + # + # Slice the location of operator_loc from the source. + # -- + # : () -> String + def operator: () -> String + + # : (untyped other) -> boolish + def ===: (untyped other) -> boolish + end + + # Represents assigning to a global variable using an operator that isn't `=`. + # + # $target += value + # ^^^^^^^^^^^^^^^^ + class GlobalVariableOperatorWriteNode < Node + @binary_operator: Symbol + + @value: Prism::node + + @binary_operator_loc: Location + + @name_loc: Location + + @name: Symbol + + # Initialize a new GlobalVariableOperatorWriteNode node. + # -- + # : (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 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 + + # See Node.accept. + # -- + # : (_Visitor visitor) -> untyped + def accept: (_Visitor visitor) -> untyped + + # See Node.child_nodes. + # -- + # : () -> Array[node?] + def child_nodes: () -> Array[node?] + + # See Node.each_child_node. + # -- + # : () -> Enumerator[node, void] + # : () { (node) -> void } -> void + def each_child_node: () -> Enumerator[node, void] + | () { (node) -> void } -> void + + # See Node.compact_child_nodes. + # -- + # : () -> Array[node] + def compact_child_nodes: () -> Array[node] + + # See Node.comment_targets. + # -- + # : () -> Array[node | Location] + def comment_targets: () -> Array[node | Location] + + # :call-seq: + # copy(**fields) -> GlobalVariableOperatorWriteNode + # + # Creates a copy of self with the given fields, using self as the template. + # -- + # : (?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: Integer, ?location: Location, ?flags: Integer, ?name: Symbol, ?name_loc: Location, ?binary_operator_loc: Location, ?value: Prism::node, ?binary_operator: Symbol) -> GlobalVariableOperatorWriteNode + + alias deconstruct child_nodes + + # : (Array[Symbol]? keys) -> Hash[Symbol, untyped] + def deconstruct_keys: (Array[Symbol]? keys) -> Hash[Symbol, untyped] + + # See `Node#type`. + # -- + # : () -> :global_variable_operator_write_node + def type: () -> :global_variable_operator_write_node + + # See `Node.type`. + # -- + # : () -> :global_variable_operator_write_node + def self.type: () -> :global_variable_operator_write_node + + # : () -> String + def inspect: () -> String + + # :call-seq: + # name -> Symbol + # + # Returns the `name` attribute. + # -- + # : () -> Symbol + def name: () -> Symbol + + # :category: Locations + # :call-seq: + # name_loc -> Location + # + # Returns the Location represented by `name_loc`. + # -- + # : () -> Location + def name_loc: () -> Location + + # :category: Repository + # Save the name_loc location using the given saved source so that + # it can be retrieved later. + # -- + # : (_Repository repository) -> Relocation::Entry + def save_name_loc: (_Repository repository) -> Relocation::Entry + + # :category: Locations + # :call-seq: + # binary_operator_loc -> Location + # + # Returns the Location represented by `binary_operator_loc`. + # -- + # : () -> Location + def binary_operator_loc: () -> Location + + # :category: Repository + # Save the binary_operator_loc location using the given saved source so that + # it can be retrieved later. + # -- + # : (_Repository repository) -> Relocation::Entry + def save_binary_operator_loc: (_Repository repository) -> Relocation::Entry + + # :call-seq: + # value -> Node + # + # Returns the `value` attribute. + # -- + # : () -> Prism::node + def value: () -> Prism::node + + # :call-seq: + # binary_operator -> Symbol + # + # Returns the `binary_operator` attribute. + # -- + # : () -> Symbol + def binary_operator: () -> Symbol + + # : (untyped other) -> boolish + def ===: (untyped other) -> boolish + end + + # Represents the use of the `||=` operator for assignment to a global variable. + # + # $target ||= value + # ^^^^^^^^^^^^^^^^^ + class GlobalVariableOrWriteNode < Node + @value: Prism::node + + @operator_loc: Location + + @name_loc: Location + + @name: Symbol + + # Initialize a new GlobalVariableOrWriteNode node. + # -- + # : (Source source, Integer node_id, Location location, Integer flags, Symbol name, Location name_loc, Location operator_loc, Prism::node value) -> void + def initialize: (Source source, Integer node_id, Location location, Integer flags, Symbol name, Location name_loc, Location operator_loc, Prism::node value) -> void + + # See Node.accept. + # -- + # : (_Visitor visitor) -> untyped + def accept: (_Visitor visitor) -> untyped + + # See Node.child_nodes. + # -- + # : () -> Array[node?] + def child_nodes: () -> Array[node?] + + # See Node.each_child_node. + # -- + # : () -> Enumerator[node, void] + # : () { (node) -> void } -> void + def each_child_node: () -> Enumerator[node, void] + | () { (node) -> void } -> void + + # See Node.compact_child_nodes. + # -- + # : () -> Array[node] + def compact_child_nodes: () -> Array[node] + + # See Node.comment_targets. + # -- + # : () -> Array[node | Location] + def comment_targets: () -> Array[node | Location] + + # :call-seq: + # copy(**fields) -> GlobalVariableOrWriteNode + # + # Creates a copy of self with the given fields, using self as the template. + # -- + # : (?node_id: Integer, ?location: Location, ?flags: Integer, ?name: Symbol, ?name_loc: Location, ?operator_loc: Location, ?value: Prism::node) -> GlobalVariableOrWriteNode + def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?name: Symbol, ?name_loc: Location, ?operator_loc: Location, ?value: Prism::node) -> GlobalVariableOrWriteNode + + alias deconstruct child_nodes + + # : (Array[Symbol]? keys) -> Hash[Symbol, untyped] + def deconstruct_keys: (Array[Symbol]? keys) -> Hash[Symbol, untyped] + + # See `Node#type`. + # -- + # : () -> :global_variable_or_write_node + def type: () -> :global_variable_or_write_node + + # See `Node.type`. + # -- + # : () -> :global_variable_or_write_node + def self.type: () -> :global_variable_or_write_node + + # : () -> String + def inspect: () -> String + + # :call-seq: + # name -> Symbol + # + # Returns the `name` attribute. + # -- + # : () -> Symbol + def name: () -> Symbol + + # :category: Locations + # :call-seq: + # name_loc -> Location + # + # Returns the Location represented by `name_loc`. + # -- + # : () -> Location + def name_loc: () -> Location + + # :category: Repository + # Save the name_loc location using the given saved source so that + # it can be retrieved later. + # -- + # : (_Repository repository) -> Relocation::Entry + def save_name_loc: (_Repository repository) -> Relocation::Entry + + # :category: Locations + # :call-seq: + # operator_loc -> Location + # + # Returns the Location represented by `operator_loc`. + # -- + # : () -> Location + def operator_loc: () -> Location + + # :category: Repository + # Save the operator_loc location using the given saved source so that + # it can be retrieved later. + # -- + # : (_Repository repository) -> Relocation::Entry + def save_operator_loc: (_Repository repository) -> Relocation::Entry + + # :call-seq: + # value -> Node + # + # Returns the `value` attribute. + # -- + # : () -> Prism::node + def value: () -> Prism::node + + # :call-seq: + # operator -> String + # + # Slice the location of operator_loc from the source. + # -- + # : () -> String + def operator: () -> String + + # : (untyped other) -> boolish + def ===: (untyped other) -> boolish + end + + # Represents referencing a global variable. + # + # $foo + # ^^^^ + class GlobalVariableReadNode < Node + @name: Symbol + + # Initialize a new GlobalVariableReadNode node. + # -- + # : (Source source, Integer node_id, Location location, Integer flags, Symbol name) -> void + def initialize: (Source source, Integer node_id, Location location, Integer flags, Symbol name) -> void + + # See Node.accept. + # -- + # : (_Visitor visitor) -> untyped + def accept: (_Visitor visitor) -> untyped + + # See Node.child_nodes. + # -- + # : () -> Array[node?] + def child_nodes: () -> Array[node?] + + # See Node.each_child_node. + # -- + # : () -> Enumerator[node, void] + # : () { (node) -> void } -> void + def each_child_node: () -> Enumerator[node, void] + | () { (node) -> void } -> void + + # See Node.compact_child_nodes. + # -- + # : () -> Array[node] + def compact_child_nodes: () -> Array[node] + + # See Node.comment_targets. + # -- + # : () -> Array[node | Location] + def comment_targets: () -> Array[node | Location] + + # :call-seq: + # copy(**fields) -> GlobalVariableReadNode + # + # Creates a copy of self with the given fields, using self as the template. + # -- + # : (?node_id: Integer, ?location: Location, ?flags: Integer, ?name: Symbol) -> GlobalVariableReadNode + def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?name: Symbol) -> GlobalVariableReadNode + + alias deconstruct child_nodes + + # : (Array[Symbol]? keys) -> Hash[Symbol, untyped] + def deconstruct_keys: (Array[Symbol]? keys) -> Hash[Symbol, untyped] + + # See `Node#type`. + # -- + # : () -> :global_variable_read_node + def type: () -> :global_variable_read_node + + # See `Node.type`. + # -- + # : () -> :global_variable_read_node + def self.type: () -> :global_variable_read_node + + # : () -> String + def inspect: () -> String + + # :call-seq: + # name -> Symbol + # + # 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` + # -- + # : () -> Symbol + def name: () -> Symbol + + # : (untyped other) -> boolish + def ===: (untyped other) -> boolish + end + + # Represents writing to a global variable in a context that doesn't have an explicit value. + # + # $foo, $bar = baz + # ^^^^ ^^^^ + class GlobalVariableTargetNode < Node + @name: Symbol + + # Initialize a new GlobalVariableTargetNode node. + # -- + # : (Source source, Integer node_id, Location location, Integer flags, Symbol name) -> void + def initialize: (Source source, Integer node_id, Location location, Integer flags, Symbol name) -> void + + # See Node.accept. + # -- + # : (_Visitor visitor) -> untyped + def accept: (_Visitor visitor) -> untyped + + # See Node.child_nodes. + # -- + # : () -> Array[node?] + def child_nodes: () -> Array[node?] + + # See Node.each_child_node. + # -- + # : () -> Enumerator[node, void] + # : () { (node) -> void } -> void + def each_child_node: () -> Enumerator[node, void] + | () { (node) -> void } -> void + + # See Node.compact_child_nodes. + # -- + # : () -> Array[node] + def compact_child_nodes: () -> Array[node] + + # See Node.comment_targets. + # -- + # : () -> Array[node | Location] + def comment_targets: () -> Array[node | Location] + + # :call-seq: + # copy(**fields) -> GlobalVariableTargetNode + # + # Creates a copy of self with the given fields, using self as the template. + # -- + # : (?node_id: Integer, ?location: Location, ?flags: Integer, ?name: Symbol) -> GlobalVariableTargetNode + def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?name: Symbol) -> GlobalVariableTargetNode + + alias deconstruct child_nodes + + # : (Array[Symbol]? keys) -> Hash[Symbol, untyped] + def deconstruct_keys: (Array[Symbol]? keys) -> Hash[Symbol, untyped] + + # See `Node#type`. + # -- + # : () -> :global_variable_target_node + def type: () -> :global_variable_target_node + + # See `Node.type`. + # -- + # : () -> :global_variable_target_node + def self.type: () -> :global_variable_target_node + + # : () -> String + def inspect: () -> String + + # :call-seq: + # name -> Symbol + # + # Returns the `name` attribute. + # -- + # : () -> Symbol + def name: () -> Symbol + + # : (untyped other) -> boolish + def ===: (untyped other) -> boolish + end + + # Represents writing to a global variable. + # + # $foo = 1 + # ^^^^^^^^ + class GlobalVariableWriteNode < Node + @operator_loc: Location + + @value: Prism::node + + @name_loc: Location + + @name: Symbol + + # Initialize a new GlobalVariableWriteNode node. + # -- + # : (Source source, Integer node_id, Location location, Integer flags, Symbol name, Location name_loc, Prism::node value, Location operator_loc) -> void + def initialize: (Source source, Integer node_id, Location location, Integer flags, Symbol name, Location name_loc, Prism::node value, Location operator_loc) -> void + + # See Node.accept. + # -- + # : (_Visitor visitor) -> untyped + def accept: (_Visitor visitor) -> untyped + + # See Node.child_nodes. + # -- + # : () -> Array[node?] + def child_nodes: () -> Array[node?] + + # See Node.each_child_node. + # -- + # : () -> Enumerator[node, void] + # : () { (node) -> void } -> void + def each_child_node: () -> Enumerator[node, void] + | () { (node) -> void } -> void + + # See Node.compact_child_nodes. + # -- + # : () -> Array[node] + def compact_child_nodes: () -> Array[node] + + # See Node.comment_targets. + # -- + # : () -> Array[node | Location] + def comment_targets: () -> Array[node | Location] + + # :call-seq: + # copy(**fields) -> GlobalVariableWriteNode + # + # Creates a copy of self with the given fields, using self as the template. + # -- + # : (?node_id: Integer, ?location: Location, ?flags: Integer, ?name: Symbol, ?name_loc: Location, ?value: Prism::node, ?operator_loc: Location) -> GlobalVariableWriteNode + def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?name: Symbol, ?name_loc: Location, ?value: Prism::node, ?operator_loc: Location) -> GlobalVariableWriteNode + + alias deconstruct child_nodes + + # : (Array[Symbol]? keys) -> Hash[Symbol, untyped] + def deconstruct_keys: (Array[Symbol]? keys) -> Hash[Symbol, untyped] + + # See `Node#type`. + # -- + # : () -> :global_variable_write_node + def type: () -> :global_variable_write_node + + # See `Node.type`. + # -- + # : () -> :global_variable_write_node + def self.type: () -> :global_variable_write_node + + # : () -> String + def inspect: () -> String + + # :call-seq: + # name -> Symbol + # + # 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` + # -- + # : () -> Symbol + def name: () -> Symbol + + # :category: Locations + # :call-seq: + # name_loc -> Location + # + # The Location of the global variable's name. + # + # $foo = :bar + # ^^^^ + # -- + # : () -> Location + def name_loc: () -> Location + + # :category: Repository + # Save the name_loc location using the given saved source so that + # it can be retrieved later. + # -- + # : (_Repository repository) -> Relocation::Entry + def save_name_loc: (_Repository repository) -> Relocation::Entry + + # :call-seq: + # value -> Node + # + # 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 + # ^^^ + # -- + # : () -> Prism::node + def value: () -> Prism::node + + # :category: Locations + # :call-seq: + # operator_loc -> Location + # + # The Location of the `=` operator. + # + # $foo = :bar + # ^ + # -- + # : () -> Location + def operator_loc: () -> Location + + # :category: Repository + # Save the operator_loc location using the given saved source so that + # it can be retrieved later. + # -- + # : (_Repository repository) -> Relocation::Entry + def save_operator_loc: (_Repository repository) -> Relocation::Entry + + # :call-seq: + # operator -> String + # + # Slice the location of operator_loc from the source. + # -- + # : () -> String + def operator: () -> String + + # : (untyped other) -> boolish + def ===: (untyped other) -> boolish + end + + # Represents a hash literal. + # + # { a => b } + # ^^^^^^^^^^ + class HashNode < Node + @closing_loc: Location + + @elements: Array[AssocNode | AssocSplatNode] + + @opening_loc: Location + + # Initialize a new HashNode node. + # -- + # : (Source source, Integer node_id, Location location, Integer flags, Location opening_loc, Array[AssocNode | AssocSplatNode] elements, Location closing_loc) -> void + def initialize: (Source source, Integer node_id, Location location, Integer flags, Location opening_loc, Array[AssocNode | AssocSplatNode] elements, Location closing_loc) -> void + + # See Node.accept. + # -- + # : (_Visitor visitor) -> untyped + def accept: (_Visitor visitor) -> untyped + + # See Node.child_nodes. + # -- + # : () -> Array[node?] + def child_nodes: () -> Array[node?] + + # See Node.each_child_node. + # -- + # : () -> Enumerator[node, void] + # : () { (node) -> void } -> void + def each_child_node: () -> Enumerator[node, void] + | () { (node) -> void } -> void + + # See Node.compact_child_nodes. + # -- + # : () -> Array[node] + def compact_child_nodes: () -> Array[node] + + # See Node.comment_targets. + # -- + # : () -> Array[node | Location] + def comment_targets: () -> Array[node | Location] + + # :call-seq: + # copy(**fields) -> HashNode + # + # Creates a copy of self with the given fields, using self as the template. + # -- + # : (?node_id: Integer, ?location: Location, ?flags: Integer, ?opening_loc: Location, ?elements: Array[AssocNode | AssocSplatNode], ?closing_loc: Location) -> HashNode + def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?opening_loc: Location, ?elements: Array[AssocNode | AssocSplatNode], ?closing_loc: Location) -> HashNode + + alias deconstruct child_nodes + + # : (Array[Symbol]? keys) -> Hash[Symbol, untyped] + def deconstruct_keys: (Array[Symbol]? keys) -> Hash[Symbol, untyped] + + # See `Node#type`. + # -- + # : () -> :hash_node + def type: () -> :hash_node + + # See `Node.type`. + # -- + # : () -> :hash_node + def self.type: () -> :hash_node + + # : () -> String + def inspect: () -> String + + # :category: Locations + # :call-seq: + # opening_loc -> Location + # + # The Location of the opening brace. + # + # { a => b } + # ^ + # -- + # : () -> Location + def opening_loc: () -> Location + + # :category: Repository + # Save the opening_loc location using the given saved source so that + # it can be retrieved later. + # -- + # : (_Repository repository) -> Relocation::Entry + def save_opening_loc: (_Repository repository) -> Relocation::Entry + + # :call-seq: + # elements -> Array[AssocNode | AssocSplatNode] + # + # The elements of the hash. These can be either `AssocNode`s or `AssocSplatNode`s. + # + # { a: b } + # ^^^^ + # + # { **foo } + # ^^^^^ + # -- + # : () -> Array[AssocNode | AssocSplatNode] + def elements: () -> Array[AssocNode | AssocSplatNode] + + # :category: Locations + # :call-seq: + # closing_loc -> Location + # + # The Location of the closing brace. + # + # { a => b } + # ^ + # -- + # : () -> Location + def closing_loc: () -> Location + + # :category: Repository + # Save the closing_loc location using the given saved source so that + # it can be retrieved later. + # -- + # : (_Repository repository) -> Relocation::Entry + def save_closing_loc: (_Repository repository) -> Relocation::Entry + + # :call-seq: + # opening -> String + # + # Slice the location of opening_loc from the source. + # -- + # : () -> String + def opening: () -> String + + # :call-seq: + # closing -> String + # + # Slice the location of closing_loc from the source. + # -- + # : () -> String + def closing: () -> String + + # : (untyped other) -> boolish + def ===: (untyped other) -> boolish + 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 + @closing_loc: Location? + + @opening_loc: Location? + + @rest: (AssocSplatNode | NoKeywordsParameterNode)? + + @elements: Array[AssocNode] + + @constant: (ConstantPathNode | ConstantReadNode)? + + # Initialize a new HashPatternNode node. + # -- + # : (Source source, Integer node_id, Location location, Integer flags, (ConstantPathNode | ConstantReadNode)? constant, Array[AssocNode] elements, (AssocSplatNode | NoKeywordsParameterNode)? rest, Location? opening_loc, Location? closing_loc) -> void + def initialize: (Source source, Integer node_id, Location location, Integer flags, (ConstantPathNode | ConstantReadNode)? constant, Array[AssocNode] elements, (AssocSplatNode | NoKeywordsParameterNode)? rest, Location? opening_loc, Location? closing_loc) -> void + + # See Node.accept. + # -- + # : (_Visitor visitor) -> untyped + def accept: (_Visitor visitor) -> untyped + + # See Node.child_nodes. + # -- + # : () -> Array[node?] + def child_nodes: () -> Array[node?] + + # See Node.each_child_node. + # -- + # : () -> Enumerator[node, void] + # : () { (node) -> void } -> void + def each_child_node: () -> Enumerator[node, void] + | () { (node) -> void } -> void + + # See Node.compact_child_nodes. + # -- + # : () -> Array[node] + def compact_child_nodes: () -> Array[node] + + # See Node.comment_targets. + # -- + # : () -> Array[node | Location] + def comment_targets: () -> Array[node | Location] + + # :call-seq: + # copy(**fields) -> HashPatternNode + # + # Creates a copy of self with the given fields, using self as the template. + # -- + # : (?node_id: Integer, ?location: Location, ?flags: Integer, ?constant: (ConstantPathNode | ConstantReadNode)?, ?elements: Array[AssocNode], ?rest: (AssocSplatNode | NoKeywordsParameterNode)?, ?opening_loc: Location?, ?closing_loc: Location?) -> HashPatternNode + def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?constant: (ConstantPathNode | ConstantReadNode)?, ?elements: Array[AssocNode], ?rest: (AssocSplatNode | NoKeywordsParameterNode)?, ?opening_loc: Location?, ?closing_loc: Location?) -> HashPatternNode + + alias deconstruct child_nodes + + # : (Array[Symbol]? keys) -> Hash[Symbol, untyped] + def deconstruct_keys: (Array[Symbol]? keys) -> Hash[Symbol, untyped] + + # See `Node#type`. + # -- + # : () -> :hash_pattern_node + def type: () -> :hash_pattern_node + + # See `Node.type`. + # -- + # : () -> :hash_pattern_node + def self.type: () -> :hash_pattern_node + + # : () -> String + def inspect: () -> String + + # :call-seq: + # constant -> ConstantPathNode | ConstantReadNode | nil + # + # Represents the optional constant preceding the Hash. + # + # foo => Bar[a: 1, b: 2] + # ^^^ + # + # foo => Bar::Baz[a: 1, b: 2] + # ^^^^^^^^ + # -- + # : () -> (ConstantPathNode | ConstantReadNode)? + def constant: () -> (ConstantPathNode | ConstantReadNode)? + + # :call-seq: + # elements -> Array[AssocNode] + # + # Represents the explicit named hash keys and values. + # + # foo => { a: 1, b:, ** } + # ^^^^^^^^ + # -- + # : () -> Array[AssocNode] + def elements: () -> Array[AssocNode] + + # :call-seq: + # rest -> AssocSplatNode | NoKeywordsParameterNode | nil + # + # 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 } + # ^^^^^ + # -- + # : () -> (AssocSplatNode | NoKeywordsParameterNode)? + def rest: () -> (AssocSplatNode | NoKeywordsParameterNode)? + + # :category: Locations + # :call-seq: + # opening_loc -> Location | nil + # + # The Location of the opening brace. + # + # foo => { a: 1 } + # ^ + # + # foo => Bar[a: 1] + # ^ + # -- + # : () -> Location? + def opening_loc: () -> Location? + + # :category: Repository + # Save the opening_loc location using the given saved source so that + # it can be retrieved later. + # -- + # : (_Repository repository) -> Relocation::Entry? + def save_opening_loc: (_Repository repository) -> Relocation::Entry? + + # :category: Locations + # :call-seq: + # closing_loc -> Location | nil + # + # The Location of the closing brace. + # + # foo => { a: 1 } + # ^ + # + # foo => Bar[a: 1] + # ^ + # -- + # : () -> Location? + def closing_loc: () -> Location? + + # :category: Repository + # Save the closing_loc location using the given saved source so that + # it can be retrieved later. + # -- + # : (_Repository repository) -> Relocation::Entry? + def save_closing_loc: (_Repository repository) -> Relocation::Entry? + + # :call-seq: + # opening -> String | nil + # + # Slice the location of opening_loc from the source. + # -- + # : () -> String? + def opening: () -> String? + + # :call-seq: + # closing -> String | nil + # + # Slice the location of closing_loc from the source. + # -- + # : () -> String? + def closing: () -> String? + + # : (untyped other) -> boolish + def ===: (untyped other) -> boolish + 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 + @end_keyword_loc: Location? + + @subsequent: (ElseNode | IfNode)? + + @statements: StatementsNode? + + @then_keyword_loc: Location? + + @predicate: Prism::node + + @if_keyword_loc: Location? + + # Initialize a new IfNode node. + # -- + # : (Source source, Integer node_id, Location location, Integer flags, Location? if_keyword_loc, Prism::node predicate, Location? then_keyword_loc, StatementsNode? statements, (ElseNode | IfNode)? subsequent, Location? end_keyword_loc) -> 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)? subsequent, Location? end_keyword_loc) -> void + + # See Node.accept. + # -- + # : (_Visitor visitor) -> untyped + def accept: (_Visitor visitor) -> untyped + + # See Node.child_nodes. + # -- + # : () -> Array[node?] + def child_nodes: () -> Array[node?] + + # See Node.each_child_node. + # -- + # : () -> Enumerator[node, void] + # : () { (node) -> void } -> void + def each_child_node: () -> Enumerator[node, void] + | () { (node) -> void } -> void + + # See Node.compact_child_nodes. + # -- + # : () -> Array[node] + def compact_child_nodes: () -> Array[node] + + # See Node.comment_targets. + # -- + # : () -> Array[node | Location] + def comment_targets: () -> Array[node | Location] + + # :call-seq: + # copy(**fields) -> IfNode + # + # Creates a copy of self with the given fields, using self as the template. + # -- + # : (?node_id: Integer, ?location: Location, ?flags: Integer, ?if_keyword_loc: Location?, ?predicate: Prism::node, ?then_keyword_loc: Location?, ?statements: StatementsNode?, ?subsequent: (ElseNode | IfNode)?, ?end_keyword_loc: Location?) -> IfNode + 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)?, ?end_keyword_loc: Location?) -> IfNode + + alias deconstruct child_nodes + + # : (Array[Symbol]? keys) -> Hash[Symbol, untyped] + def deconstruct_keys: (Array[Symbol]? keys) -> Hash[Symbol, untyped] + + # See `Node#type`. + # -- + # : () -> :if_node + def type: () -> :if_node + + # See `Node.type`. + # -- + # : () -> :if_node + def self.type: () -> :if_node + + # : () -> String + def inspect: () -> String + + # :category: Locations + # :call-seq: + # if_keyword_loc -> Location | nil + # + # 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. + # -- + # : () -> Location? + def if_keyword_loc: () -> Location? + + # :category: Repository + # Save the if_keyword_loc location using the given saved source so that + # it can be retrieved later. + # -- + # : (_Repository repository) -> Relocation::Entry? + def save_if_keyword_loc: (_Repository repository) -> Relocation::Entry? + + # :call-seq: + # predicate -> Node + # + # The node for the condition the `IfNode` is testing. + # + # if foo + # ^^^ + # bar + # end + # + # bar if foo + # ^^^ + # + # foo ? bar : baz + # ^^^ + # -- + # : () -> Prism::node + def predicate: () -> Prism::node + + # :category: Locations + # :call-seq: + # then_keyword_loc -> Location | nil + # + # The Location of the `then` keyword (if present) or the `?` in a ternary expression, `nil` otherwise. + # + # if foo then bar end + # ^^^^ + # + # a ? b : c + # ^ + # -- + # : () -> Location? + def then_keyword_loc: () -> Location? + + # :category: Repository + # Save the then_keyword_loc location using the given saved source so that + # it can be retrieved later. + # -- + # : (_Repository repository) -> Relocation::Entry? + def save_then_keyword_loc: (_Repository repository) -> Relocation::Entry? + + # :call-seq: + # statements -> StatementsNode | nil + # + # 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 + # -- + # : () -> StatementsNode? + def statements: () -> StatementsNode? + + # :call-seq: + # subsequent -> ElseNode | IfNode | nil + # + # 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 + # ^^^^^^^^^^^^ + # -- + # : () -> (ElseNode | IfNode)? + def subsequent: () -> (ElseNode | IfNode)? + + # :category: Locations + # :call-seq: + # end_keyword_loc -> Location | nil + # + # The Location of the `end` keyword if present, `nil` otherwise. + # + # if foo + # bar + # end + # ^^^ + # -- + # : () -> Location? + def end_keyword_loc: () -> Location? + + # :category: Repository + # Save the end_keyword_loc location using the given saved source so that + # it can be retrieved later. + # -- + # : (_Repository repository) -> Relocation::Entry? + def save_end_keyword_loc: (_Repository repository) -> Relocation::Entry? + + # :call-seq: + # if_keyword -> String | nil + # + # Slice the location of if_keyword_loc from the source. + # -- + # : () -> String? + def if_keyword: () -> String? + + # :call-seq: + # then_keyword -> String | nil + # + # Slice the location of then_keyword_loc from the source. + # -- + # : () -> String? + def then_keyword: () -> String? + + # :call-seq: + # end_keyword -> String | nil + # + # Slice the location of end_keyword_loc from the source. + # -- + # : () -> String? + def end_keyword: () -> String? + + # : (untyped other) -> boolish + def ===: (untyped other) -> boolish + end + + # Represents an imaginary number literal. + # + # 1.0i + # ^^^^ + class ImaginaryNode < Node + @numeric: FloatNode | IntegerNode | RationalNode + + # Initialize a new ImaginaryNode node. + # -- + # : (Source source, Integer node_id, Location location, Integer flags, (FloatNode | IntegerNode | RationalNode) numeric) -> void + def initialize: (Source source, Integer node_id, Location location, Integer flags, FloatNode | IntegerNode | RationalNode numeric) -> void + + # See Node.accept. + # -- + # : (_Visitor visitor) -> untyped + def accept: (_Visitor visitor) -> untyped + + # See Node.child_nodes. + # -- + # : () -> Array[node?] + def child_nodes: () -> Array[node?] + + # See Node.each_child_node. + # -- + # : () -> Enumerator[node, void] + # : () { (node) -> void } -> void + def each_child_node: () -> Enumerator[node, void] + | () { (node) -> void } -> void + + # See Node.compact_child_nodes. + # -- + # : () -> Array[node] + def compact_child_nodes: () -> Array[node] + + # See Node.comment_targets. + # -- + # : () -> Array[node | Location] + def comment_targets: () -> Array[node | Location] + + # :call-seq: + # copy(**fields) -> ImaginaryNode + # + # Creates a copy of self with the given fields, using self as the template. + # -- + # : (?node_id: Integer, ?location: Location, ?flags: Integer, ?numeric: (FloatNode | IntegerNode | RationalNode)) -> ImaginaryNode + def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?numeric: FloatNode | IntegerNode | RationalNode) -> ImaginaryNode + + alias deconstruct child_nodes + + # : (Array[Symbol]? keys) -> Hash[Symbol, untyped] + def deconstruct_keys: (Array[Symbol]? keys) -> Hash[Symbol, untyped] + + # See `Node#type`. + # -- + # : () -> :imaginary_node + def type: () -> :imaginary_node + + # See `Node.type`. + # -- + # : () -> :imaginary_node + def self.type: () -> :imaginary_node + + # : () -> String + def inspect: () -> String + + # :call-seq: + # numeric -> FloatNode | IntegerNode | RationalNode + # + # Returns the `numeric` attribute. + # -- + # : () -> (FloatNode | IntegerNode | RationalNode) + def numeric: () -> (FloatNode | IntegerNode | RationalNode) + + # : (untyped other) -> boolish + def ===: (untyped other) -> boolish + 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 + @value: LocalVariableReadNode | CallNode | ConstantReadNode | LocalVariableTargetNode + + # Initialize a new ImplicitNode node. + # -- + # : (Source source, Integer node_id, Location location, Integer flags, (LocalVariableReadNode | CallNode | ConstantReadNode | LocalVariableTargetNode) value) -> void + def initialize: (Source source, Integer node_id, Location location, Integer flags, LocalVariableReadNode | CallNode | ConstantReadNode | LocalVariableTargetNode value) -> void + + # See Node.accept. + # -- + # : (_Visitor visitor) -> untyped + def accept: (_Visitor visitor) -> untyped + + # See Node.child_nodes. + # -- + # : () -> Array[node?] + def child_nodes: () -> Array[node?] + + # See Node.each_child_node. + # -- + # : () -> Enumerator[node, void] + # : () { (node) -> void } -> void + def each_child_node: () -> Enumerator[node, void] + | () { (node) -> void } -> void + + # See Node.compact_child_nodes. + # -- + # : () -> Array[node] + def compact_child_nodes: () -> Array[node] + + # See Node.comment_targets. + # -- + # : () -> Array[node | Location] + def comment_targets: () -> Array[node | Location] + + # :call-seq: + # copy(**fields) -> ImplicitNode + # + # Creates a copy of self with the given fields, using self as the template. + # -- + # : (?node_id: Integer, ?location: Location, ?flags: Integer, ?value: (LocalVariableReadNode | CallNode | ConstantReadNode | LocalVariableTargetNode)) -> ImplicitNode + def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?value: LocalVariableReadNode | CallNode | ConstantReadNode | LocalVariableTargetNode) -> ImplicitNode + + alias deconstruct child_nodes + + # : (Array[Symbol]? keys) -> Hash[Symbol, untyped] + def deconstruct_keys: (Array[Symbol]? keys) -> Hash[Symbol, untyped] + + # See `Node#type`. + # -- + # : () -> :implicit_node + def type: () -> :implicit_node + + # See `Node.type`. + # -- + # : () -> :implicit_node + def self.type: () -> :implicit_node + + # : () -> String + def inspect: () -> String + + # :call-seq: + # value -> LocalVariableReadNode | CallNode | ConstantReadNode | LocalVariableTargetNode + # + # Returns the `value` attribute. + # -- + # : () -> (LocalVariableReadNode | CallNode | ConstantReadNode | LocalVariableTargetNode) + def value: () -> (LocalVariableReadNode | CallNode | ConstantReadNode | LocalVariableTargetNode) + + # : (untyped other) -> boolish + def ===: (untyped other) -> boolish + 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. + # -- + # : (Source source, Integer node_id, Location location, Integer flags, ) -> void + def initialize: (Source source, Integer node_id, Location location, Integer flags) -> void + + # See Node.accept. + # -- + # : (_Visitor visitor) -> untyped + def accept: (_Visitor visitor) -> untyped + + # See Node.child_nodes. + # -- + # : () -> Array[node?] + def child_nodes: () -> Array[node?] + + # See Node.each_child_node. + # -- + # : () -> Enumerator[node, void] + # : () { (node) -> void } -> void + def each_child_node: () -> Enumerator[node, void] + | () { (node) -> void } -> void + + # See Node.compact_child_nodes. + # -- + # : () -> Array[node] + def compact_child_nodes: () -> Array[node] + + # See Node.comment_targets. + # -- + # : () -> Array[node | Location] + def comment_targets: () -> Array[node | Location] + + # :call-seq: + # copy(**fields) -> ImplicitRestNode + # + # Creates a copy of self with the given fields, using self as the template. + # -- + # : (?node_id: Integer, ?location: Location, ?flags: Integer, ) -> ImplicitRestNode + def copy: (?node_id: Integer, ?location: Location, ?flags: Integer) -> ImplicitRestNode + + alias deconstruct child_nodes + + # : (Array[Symbol]? keys) -> Hash[Symbol, untyped] + def deconstruct_keys: (Array[Symbol]? keys) -> Hash[Symbol, untyped] + + # See `Node#type`. + # -- + # : () -> :implicit_rest_node + def type: () -> :implicit_rest_node + + # See `Node.type`. + # -- + # : () -> :implicit_rest_node + def self.type: () -> :implicit_rest_node + + # : () -> String + def inspect: () -> String + + # : (untyped other) -> boolish + def ===: (untyped other) -> boolish + end + + # Represents the use of the `in` keyword in a case statement. + # + # case a; in b then c end + # ^^^^^^^^^^^ + class InNode < Node + @then_loc: Location? + + @in_loc: Location + + @statements: StatementsNode? + + @pattern: Prism::node + + # Initialize a new InNode node. + # -- + # : (Source source, Integer node_id, Location location, Integer flags, Prism::node pattern, StatementsNode? statements, Location in_loc, Location? then_loc) -> void + def initialize: (Source source, Integer node_id, Location location, Integer flags, Prism::node pattern, StatementsNode? statements, Location in_loc, Location? then_loc) -> void + + # See Node.accept. + # -- + # : (_Visitor visitor) -> untyped + def accept: (_Visitor visitor) -> untyped + + # See Node.child_nodes. + # -- + # : () -> Array[node?] + def child_nodes: () -> Array[node?] + + # See Node.each_child_node. + # -- + # : () -> Enumerator[node, void] + # : () { (node) -> void } -> void + def each_child_node: () -> Enumerator[node, void] + | () { (node) -> void } -> void + + # See Node.compact_child_nodes. + # -- + # : () -> Array[node] + def compact_child_nodes: () -> Array[node] + + # See Node.comment_targets. + # -- + # : () -> Array[node | Location] + def comment_targets: () -> Array[node | Location] + + # :call-seq: + # copy(**fields) -> InNode + # + # Creates a copy of self with the given fields, using self as the template. + # -- + # : (?node_id: Integer, ?location: Location, ?flags: Integer, ?pattern: Prism::node, ?statements: StatementsNode?, ?in_loc: Location, ?then_loc: Location?) -> InNode + def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?pattern: Prism::node, ?statements: StatementsNode?, ?in_loc: Location, ?then_loc: Location?) -> InNode + + alias deconstruct child_nodes + + # : (Array[Symbol]? keys) -> Hash[Symbol, untyped] + def deconstruct_keys: (Array[Symbol]? keys) -> Hash[Symbol, untyped] + + # See `Node#type`. + # -- + # : () -> :in_node + def type: () -> :in_node + + # See `Node.type`. + # -- + # : () -> :in_node + def self.type: () -> :in_node + + # : () -> String + def inspect: () -> String + + # :call-seq: + # pattern -> Node + # + # Returns the `pattern` attribute. + # -- + # : () -> Prism::node + def pattern: () -> Prism::node + + # :call-seq: + # statements -> StatementsNode | nil + # + # Returns the `statements` attribute. + # -- + # : () -> StatementsNode? + def statements: () -> StatementsNode? + + # :category: Locations + # :call-seq: + # in_loc -> Location + # + # Returns the Location represented by `in_loc`. + # -- + # : () -> Location + def in_loc: () -> Location + + # :category: Repository + # Save the in_loc location using the given saved source so that + # it can be retrieved later. + # -- + # : (_Repository repository) -> Relocation::Entry + def save_in_loc: (_Repository repository) -> Relocation::Entry + + # :category: Locations + # :call-seq: + # then_loc -> Location | nil + # + # Returns the Location represented by `then_loc`. + # -- + # : () -> Location? + def then_loc: () -> Location? + + # :category: Repository + # Save the then_loc location using the given saved source so that + # it can be retrieved later. + # -- + # : (_Repository repository) -> Relocation::Entry? + def save_then_loc: (_Repository repository) -> Relocation::Entry? + + # :call-seq: + # in -> String + # + # Slice the location of in_loc from the source. + # -- + # : () -> String + def in: () -> String + + # :call-seq: + # then -> String | nil + # + # Slice the location of then_loc from the source. + # -- + # : () -> String? + def then: () -> String? + + # : (untyped other) -> boolish + def ===: (untyped other) -> boolish + end + + # Represents the use of the `&&=` operator on a call to the `[]` method. + # + # foo.bar[baz] &&= value + # ^^^^^^^^^^^^^^^^^^^^^^ + class IndexAndWriteNode < Node + @value: Prism::node + + @operator_loc: Location + + @block: BlockArgumentNode? + + @closing_loc: Location + + @arguments: ArgumentsNode? + + @opening_loc: Location + + @call_operator_loc: Location? + + @receiver: Prism::node? + + # Initialize a new IndexAndWriteNode node. + # -- + # : (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 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 + + # See Node.accept. + # -- + # : (_Visitor visitor) -> untyped + def accept: (_Visitor visitor) -> untyped + + # See Node.child_nodes. + # -- + # : () -> Array[node?] + def child_nodes: () -> Array[node?] + + # See Node.each_child_node. + # -- + # : () -> Enumerator[node, void] + # : () { (node) -> void } -> void + def each_child_node: () -> Enumerator[node, void] + | () { (node) -> void } -> void + + # See Node.compact_child_nodes. + # -- + # : () -> Array[node] + def compact_child_nodes: () -> Array[node] + + # See Node.comment_targets. + # -- + # : () -> Array[node | Location] + def comment_targets: () -> Array[node | Location] + + # :call-seq: + # copy(**fields) -> IndexAndWriteNode + # + # Creates a copy of self with the given fields, using self as the template. + # -- + # : (?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: 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 + + alias deconstruct child_nodes + + # : (Array[Symbol]? keys) -> Hash[Symbol, untyped] + def deconstruct_keys: (Array[Symbol]? keys) -> Hash[Symbol, untyped] + + # See `Node#type`. + # -- + # : () -> :index_and_write_node + def type: () -> :index_and_write_node + + # See `Node.type`. + # -- + # : () -> :index_and_write_node + def self.type: () -> :index_and_write_node + + # : () -> String + def inspect: () -> String + + # :category: Flags + # &. operator + # -- + # : () -> bool + def safe_navigation?: () -> bool + + # :category: Flags + # a call that could have been a local variable + # -- + # : () -> bool + def variable_call?: () -> bool + + # :category: Flags + # a call that is an attribute write, so the value being written should be returned + # -- + # : () -> bool + def attribute_write?: () -> bool + + # :category: Flags + # a call that ignores method visibility + # -- + # : () -> bool + def ignore_visibility?: () -> bool + + # :call-seq: + # receiver -> Node | nil + # + # Returns the `receiver` attribute. + # -- + # : () -> Prism::node? + def receiver: () -> Prism::node? + + # :category: Locations + # :call-seq: + # call_operator_loc -> Location | nil + # + # Returns the Location represented by `call_operator_loc`. + # -- + # : () -> Location? + def call_operator_loc: () -> Location? + + # :category: Repository + # Save the call_operator_loc location using the given saved source so that + # it can be retrieved later. + # -- + # : (_Repository repository) -> Relocation::Entry? + def save_call_operator_loc: (_Repository repository) -> Relocation::Entry? + + # :category: Locations + # :call-seq: + # opening_loc -> Location + # + # Returns the Location represented by `opening_loc`. + # -- + # : () -> Location + def opening_loc: () -> Location + + # :category: Repository + # Save the opening_loc location using the given saved source so that + # it can be retrieved later. + # -- + # : (_Repository repository) -> Relocation::Entry + def save_opening_loc: (_Repository repository) -> Relocation::Entry + + # :call-seq: + # arguments -> ArgumentsNode | nil + # + # Returns the `arguments` attribute. + # -- + # : () -> ArgumentsNode? + def arguments: () -> ArgumentsNode? + + # :category: Locations + # :call-seq: + # closing_loc -> Location + # + # Returns the Location represented by `closing_loc`. + # -- + # : () -> Location + def closing_loc: () -> Location + + # :category: Repository + # Save the closing_loc location using the given saved source so that + # it can be retrieved later. + # -- + # : (_Repository repository) -> Relocation::Entry + def save_closing_loc: (_Repository repository) -> Relocation::Entry + + # :call-seq: + # block -> BlockArgumentNode | nil + # + # Returns the `block` attribute. + # -- + # : () -> BlockArgumentNode? + def block: () -> BlockArgumentNode? + + # :category: Locations + # :call-seq: + # operator_loc -> Location + # + # Returns the Location represented by `operator_loc`. + # -- + # : () -> Location + def operator_loc: () -> Location + + # :category: Repository + # Save the operator_loc location using the given saved source so that + # it can be retrieved later. + # -- + # : (_Repository repository) -> Relocation::Entry + def save_operator_loc: (_Repository repository) -> Relocation::Entry + + # :call-seq: + # value -> Node + # + # Returns the `value` attribute. + # -- + # : () -> Prism::node + def value: () -> Prism::node + + # :call-seq: + # call_operator -> String | nil + # + # Slice the location of call_operator_loc from the source. + # -- + # : () -> String? + def call_operator: () -> String? + + # :call-seq: + # opening -> String + # + # Slice the location of opening_loc from the source. + # -- + # : () -> String + def opening: () -> String + + # :call-seq: + # closing -> String + # + # Slice the location of closing_loc from the source. + # -- + # : () -> String + def closing: () -> String + + # :call-seq: + # operator -> String + # + # Slice the location of operator_loc from the source. + # -- + # : () -> String + def operator: () -> String + + # : (untyped other) -> boolish + def ===: (untyped other) -> boolish + end + + # Represents the use of an assignment operator on a call to `[]`. + # + # foo.bar[baz] += value + # ^^^^^^^^^^^^^^^^^^^^^ + class IndexOperatorWriteNode < Node + @value: Prism::node + + @binary_operator_loc: Location + + @binary_operator: Symbol + + @block: BlockArgumentNode? + + @closing_loc: Location + + @arguments: ArgumentsNode? + + @opening_loc: Location + + @call_operator_loc: Location? + + @receiver: Prism::node? + + # Initialize a new IndexOperatorWriteNode node. + # -- + # : (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 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 + + # See Node.accept. + # -- + # : (_Visitor visitor) -> untyped + def accept: (_Visitor visitor) -> untyped + + # See Node.child_nodes. + # -- + # : () -> Array[node?] + def child_nodes: () -> Array[node?] + + # See Node.each_child_node. + # -- + # : () -> Enumerator[node, void] + # : () { (node) -> void } -> void + def each_child_node: () -> Enumerator[node, void] + | () { (node) -> void } -> void + + # See Node.compact_child_nodes. + # -- + # : () -> Array[node] + def compact_child_nodes: () -> Array[node] + + # See Node.comment_targets. + # -- + # : () -> Array[node | Location] + def comment_targets: () -> Array[node | Location] + + # :call-seq: + # copy(**fields) -> IndexOperatorWriteNode + # + # Creates a copy of self with the given fields, using self as the template. + # -- + # : (?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: 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 + + alias deconstruct child_nodes + + # : (Array[Symbol]? keys) -> Hash[Symbol, untyped] + def deconstruct_keys: (Array[Symbol]? keys) -> Hash[Symbol, untyped] + + # See `Node#type`. + # -- + # : () -> :index_operator_write_node + def type: () -> :index_operator_write_node + + # See `Node.type`. + # -- + # : () -> :index_operator_write_node + def self.type: () -> :index_operator_write_node + + # : () -> String + def inspect: () -> String + + # :category: Flags + # &. operator + # -- + # : () -> bool + def safe_navigation?: () -> bool + + # :category: Flags + # a call that could have been a local variable + # -- + # : () -> bool + def variable_call?: () -> bool + + # :category: Flags + # a call that is an attribute write, so the value being written should be returned + # -- + # : () -> bool + def attribute_write?: () -> bool + + # :category: Flags + # a call that ignores method visibility + # -- + # : () -> bool + def ignore_visibility?: () -> bool + + # :call-seq: + # receiver -> Node | nil + # + # Returns the `receiver` attribute. + # -- + # : () -> Prism::node? + def receiver: () -> Prism::node? + + # :category: Locations + # :call-seq: + # call_operator_loc -> Location | nil + # + # Returns the Location represented by `call_operator_loc`. + # -- + # : () -> Location? + def call_operator_loc: () -> Location? + + # :category: Repository + # Save the call_operator_loc location using the given saved source so that + # it can be retrieved later. + # -- + # : (_Repository repository) -> Relocation::Entry? + def save_call_operator_loc: (_Repository repository) -> Relocation::Entry? + + # :category: Locations + # :call-seq: + # opening_loc -> Location + # + # Returns the Location represented by `opening_loc`. + # -- + # : () -> Location + def opening_loc: () -> Location + + # :category: Repository + # Save the opening_loc location using the given saved source so that + # it can be retrieved later. + # -- + # : (_Repository repository) -> Relocation::Entry + def save_opening_loc: (_Repository repository) -> Relocation::Entry + + # :call-seq: + # arguments -> ArgumentsNode | nil + # + # Returns the `arguments` attribute. + # -- + # : () -> ArgumentsNode? + def arguments: () -> ArgumentsNode? + + # :category: Locations + # :call-seq: + # closing_loc -> Location + # + # Returns the Location represented by `closing_loc`. + # -- + # : () -> Location + def closing_loc: () -> Location + + # :category: Repository + # Save the closing_loc location using the given saved source so that + # it can be retrieved later. + # -- + # : (_Repository repository) -> Relocation::Entry + def save_closing_loc: (_Repository repository) -> Relocation::Entry + + # :call-seq: + # block -> BlockArgumentNode | nil + # + # Returns the `block` attribute. + # -- + # : () -> BlockArgumentNode? + def block: () -> BlockArgumentNode? + + # :call-seq: + # binary_operator -> Symbol + # + # Returns the `binary_operator` attribute. + # -- + # : () -> Symbol + def binary_operator: () -> Symbol + + # :category: Locations + # :call-seq: + # binary_operator_loc -> Location + # + # Returns the Location represented by `binary_operator_loc`. + # -- + # : () -> Location + def binary_operator_loc: () -> Location + + # :category: Repository + # Save the binary_operator_loc location using the given saved source so that + # it can be retrieved later. + # -- + # : (_Repository repository) -> Relocation::Entry + def save_binary_operator_loc: (_Repository repository) -> Relocation::Entry + + # :call-seq: + # value -> Node + # + # Returns the `value` attribute. + # -- + # : () -> Prism::node + def value: () -> Prism::node + + # :call-seq: + # call_operator -> String | nil + # + # Slice the location of call_operator_loc from the source. + # -- + # : () -> String? + def call_operator: () -> String? + + # :call-seq: + # opening -> String + # + # Slice the location of opening_loc from the source. + # -- + # : () -> String + def opening: () -> String + + # :call-seq: + # closing -> String + # + # Slice the location of closing_loc from the source. + # -- + # : () -> String + def closing: () -> String + + # : (untyped other) -> boolish + def ===: (untyped other) -> boolish + end + + # Represents the use of the `||=` operator on a call to `[]`. + # + # foo.bar[baz] ||= value + # ^^^^^^^^^^^^^^^^^^^^^^ + class IndexOrWriteNode < Node + @value: Prism::node + + @operator_loc: Location + + @block: BlockArgumentNode? + + @closing_loc: Location + + @arguments: ArgumentsNode? + + @opening_loc: Location + + @call_operator_loc: Location? + + @receiver: Prism::node? + + # Initialize a new IndexOrWriteNode node. + # -- + # : (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 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 + + # See Node.accept. + # -- + # : (_Visitor visitor) -> untyped + def accept: (_Visitor visitor) -> untyped + + # See Node.child_nodes. + # -- + # : () -> Array[node?] + def child_nodes: () -> Array[node?] + + # See Node.each_child_node. + # -- + # : () -> Enumerator[node, void] + # : () { (node) -> void } -> void + def each_child_node: () -> Enumerator[node, void] + | () { (node) -> void } -> void + + # See Node.compact_child_nodes. + # -- + # : () -> Array[node] + def compact_child_nodes: () -> Array[node] + + # See Node.comment_targets. + # -- + # : () -> Array[node | Location] + def comment_targets: () -> Array[node | Location] + + # :call-seq: + # copy(**fields) -> IndexOrWriteNode + # + # Creates a copy of self with the given fields, using self as the template. + # -- + # : (?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: 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 + + alias deconstruct child_nodes + + # : (Array[Symbol]? keys) -> Hash[Symbol, untyped] + def deconstruct_keys: (Array[Symbol]? keys) -> Hash[Symbol, untyped] + + # See `Node#type`. + # -- + # : () -> :index_or_write_node + def type: () -> :index_or_write_node + + # See `Node.type`. + # -- + # : () -> :index_or_write_node + def self.type: () -> :index_or_write_node + + # : () -> String + def inspect: () -> String + + # :category: Flags + # &. operator + # -- + # : () -> bool + def safe_navigation?: () -> bool + + # :category: Flags + # a call that could have been a local variable + # -- + # : () -> bool + def variable_call?: () -> bool + + # :category: Flags + # a call that is an attribute write, so the value being written should be returned + # -- + # : () -> bool + def attribute_write?: () -> bool + + # :category: Flags + # a call that ignores method visibility + # -- + # : () -> bool + def ignore_visibility?: () -> bool + + # :call-seq: + # receiver -> Node | nil + # + # Returns the `receiver` attribute. + # -- + # : () -> Prism::node? + def receiver: () -> Prism::node? + + # :category: Locations + # :call-seq: + # call_operator_loc -> Location | nil + # + # Returns the Location represented by `call_operator_loc`. + # -- + # : () -> Location? + def call_operator_loc: () -> Location? + + # :category: Repository + # Save the call_operator_loc location using the given saved source so that + # it can be retrieved later. + # -- + # : (_Repository repository) -> Relocation::Entry? + def save_call_operator_loc: (_Repository repository) -> Relocation::Entry? + + # :category: Locations + # :call-seq: + # opening_loc -> Location + # + # Returns the Location represented by `opening_loc`. + # -- + # : () -> Location + def opening_loc: () -> Location + + # :category: Repository + # Save the opening_loc location using the given saved source so that + # it can be retrieved later. + # -- + # : (_Repository repository) -> Relocation::Entry + def save_opening_loc: (_Repository repository) -> Relocation::Entry + + # :call-seq: + # arguments -> ArgumentsNode | nil + # + # Returns the `arguments` attribute. + # -- + # : () -> ArgumentsNode? + def arguments: () -> ArgumentsNode? + + # :category: Locations + # :call-seq: + # closing_loc -> Location + # + # Returns the Location represented by `closing_loc`. + # -- + # : () -> Location + def closing_loc: () -> Location + + # :category: Repository + # Save the closing_loc location using the given saved source so that + # it can be retrieved later. + # -- + # : (_Repository repository) -> Relocation::Entry + def save_closing_loc: (_Repository repository) -> Relocation::Entry + + # :call-seq: + # block -> BlockArgumentNode | nil + # + # Returns the `block` attribute. + # -- + # : () -> BlockArgumentNode? + def block: () -> BlockArgumentNode? + + # :category: Locations + # :call-seq: + # operator_loc -> Location + # + # Returns the Location represented by `operator_loc`. + # -- + # : () -> Location + def operator_loc: () -> Location + + # :category: Repository + # Save the operator_loc location using the given saved source so that + # it can be retrieved later. + # -- + # : (_Repository repository) -> Relocation::Entry + def save_operator_loc: (_Repository repository) -> Relocation::Entry + + # :call-seq: + # value -> Node + # + # Returns the `value` attribute. + # -- + # : () -> Prism::node + def value: () -> Prism::node + + # :call-seq: + # call_operator -> String | nil + # + # Slice the location of call_operator_loc from the source. + # -- + # : () -> String? + def call_operator: () -> String? + + # :call-seq: + # opening -> String + # + # Slice the location of opening_loc from the source. + # -- + # : () -> String + def opening: () -> String + + # :call-seq: + # closing -> String + # + # Slice the location of closing_loc from the source. + # -- + # : () -> String + def closing: () -> String + + # :call-seq: + # operator -> String + # + # Slice the location of operator_loc from the source. + # -- + # : () -> String + def operator: () -> String + + # : (untyped other) -> boolish + def ===: (untyped other) -> boolish + end + + # Represents assigning to an index. + # + # foo[bar], = 1 + # ^^^^^^^^ + # + # begin + # rescue => foo[bar] + # ^^^^^^^^ + # end + # + # for foo[bar] in baz do end + # ^^^^^^^^ + class IndexTargetNode < Node + @block: BlockArgumentNode? + + @closing_loc: Location + + @arguments: ArgumentsNode? + + @opening_loc: Location + + @receiver: Prism::node + + # Initialize a new IndexTargetNode node. + # -- + # : (Source source, Integer node_id, Location location, Integer flags, Prism::node receiver, Location opening_loc, ArgumentsNode? arguments, Location closing_loc, BlockArgumentNode? block) -> 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 + + # See Node.accept. + # -- + # : (_Visitor visitor) -> untyped + def accept: (_Visitor visitor) -> untyped + + # See Node.child_nodes. + # -- + # : () -> Array[node?] + def child_nodes: () -> Array[node?] + + # See Node.each_child_node. + # -- + # : () -> Enumerator[node, void] + # : () { (node) -> void } -> void + def each_child_node: () -> Enumerator[node, void] + | () { (node) -> void } -> void + + # See Node.compact_child_nodes. + # -- + # : () -> Array[node] + def compact_child_nodes: () -> Array[node] + + # See Node.comment_targets. + # -- + # : () -> Array[node | Location] + def comment_targets: () -> Array[node | Location] + + # :call-seq: + # copy(**fields) -> IndexTargetNode + # + # Creates a copy of self with the given fields, using self as the template. + # -- + # : (?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: Integer, ?location: Location, ?flags: Integer, ?receiver: Prism::node, ?opening_loc: Location, ?arguments: ArgumentsNode?, ?closing_loc: Location, ?block: BlockArgumentNode?) -> IndexTargetNode + + alias deconstruct child_nodes + + # : (Array[Symbol]? keys) -> Hash[Symbol, untyped] + def deconstruct_keys: (Array[Symbol]? keys) -> Hash[Symbol, untyped] + + # See `Node#type`. + # -- + # : () -> :index_target_node + def type: () -> :index_target_node + + # See `Node.type`. + # -- + # : () -> :index_target_node + def self.type: () -> :index_target_node + + # : () -> String + def inspect: () -> String + + # :category: Flags + # &. operator + # -- + # : () -> bool + def safe_navigation?: () -> bool + + # :category: Flags + # a call that could have been a local variable + # -- + # : () -> bool + def variable_call?: () -> bool + + # :category: Flags + # a call that is an attribute write, so the value being written should be returned + # -- + # : () -> bool + def attribute_write?: () -> bool + + # :category: Flags + # a call that ignores method visibility + # -- + # : () -> bool + def ignore_visibility?: () -> bool + + # :call-seq: + # receiver -> Node + # + # Returns the `receiver` attribute. + # -- + # : () -> Prism::node + def receiver: () -> Prism::node + + # :category: Locations + # :call-seq: + # opening_loc -> Location + # + # Returns the Location represented by `opening_loc`. + # -- + # : () -> Location + def opening_loc: () -> Location + + # :category: Repository + # Save the opening_loc location using the given saved source so that + # it can be retrieved later. + # -- + # : (_Repository repository) -> Relocation::Entry + def save_opening_loc: (_Repository repository) -> Relocation::Entry + + # :call-seq: + # arguments -> ArgumentsNode | nil + # + # Returns the `arguments` attribute. + # -- + # : () -> ArgumentsNode? + def arguments: () -> ArgumentsNode? + + # :category: Locations + # :call-seq: + # closing_loc -> Location + # + # Returns the Location represented by `closing_loc`. + # -- + # : () -> Location + def closing_loc: () -> Location + + # :category: Repository + # Save the closing_loc location using the given saved source so that + # it can be retrieved later. + # -- + # : (_Repository repository) -> Relocation::Entry + def save_closing_loc: (_Repository repository) -> Relocation::Entry + + # :call-seq: + # block -> BlockArgumentNode | nil + # + # Returns the `block` attribute. + # -- + # : () -> BlockArgumentNode? + def block: () -> BlockArgumentNode? + + # :call-seq: + # opening -> String + # + # Slice the location of opening_loc from the source. + # -- + # : () -> String + def opening: () -> String + + # :call-seq: + # closing -> String + # + # Slice the location of closing_loc from the source. + # -- + # : () -> String + def closing: () -> String + + # : (untyped other) -> boolish + def ===: (untyped other) -> boolish + end + + # Represents the use of the `&&=` operator for assignment to an instance variable. + # + # @target &&= value + # ^^^^^^^^^^^^^^^^^ + class InstanceVariableAndWriteNode < Node + @value: Prism::node + + @operator_loc: Location + + @name_loc: Location + + @name: Symbol + + # Initialize a new InstanceVariableAndWriteNode node. + # -- + # : (Source source, Integer node_id, Location location, Integer flags, Symbol name, Location name_loc, Location operator_loc, Prism::node value) -> void + def initialize: (Source source, Integer node_id, Location location, Integer flags, Symbol name, Location name_loc, Location operator_loc, Prism::node value) -> void + + # See Node.accept. + # -- + # : (_Visitor visitor) -> untyped + def accept: (_Visitor visitor) -> untyped + + # See Node.child_nodes. + # -- + # : () -> Array[node?] + def child_nodes: () -> Array[node?] + + # See Node.each_child_node. + # -- + # : () -> Enumerator[node, void] + # : () { (node) -> void } -> void + def each_child_node: () -> Enumerator[node, void] + | () { (node) -> void } -> void + + # See Node.compact_child_nodes. + # -- + # : () -> Array[node] + def compact_child_nodes: () -> Array[node] + + # See Node.comment_targets. + # -- + # : () -> Array[node | Location] + def comment_targets: () -> Array[node | Location] + + # :call-seq: + # copy(**fields) -> InstanceVariableAndWriteNode + # + # Creates a copy of self with the given fields, using self as the template. + # -- + # : (?node_id: Integer, ?location: Location, ?flags: Integer, ?name: Symbol, ?name_loc: Location, ?operator_loc: Location, ?value: Prism::node) -> InstanceVariableAndWriteNode + def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?name: Symbol, ?name_loc: Location, ?operator_loc: Location, ?value: Prism::node) -> InstanceVariableAndWriteNode + + alias deconstruct child_nodes + + # : (Array[Symbol]? keys) -> Hash[Symbol, untyped] + def deconstruct_keys: (Array[Symbol]? keys) -> Hash[Symbol, untyped] + + # See `Node#type`. + # -- + # : () -> :instance_variable_and_write_node + def type: () -> :instance_variable_and_write_node + + # See `Node.type`. + # -- + # : () -> :instance_variable_and_write_node + def self.type: () -> :instance_variable_and_write_node + + # : () -> String + def inspect: () -> String + + # :call-seq: + # name -> Symbol + # + # Returns the `name` attribute. + # -- + # : () -> Symbol + def name: () -> Symbol + + # :category: Locations + # :call-seq: + # name_loc -> Location + # + # Returns the Location represented by `name_loc`. + # -- + # : () -> Location + def name_loc: () -> Location + + # :category: Repository + # Save the name_loc location using the given saved source so that + # it can be retrieved later. + # -- + # : (_Repository repository) -> Relocation::Entry + def save_name_loc: (_Repository repository) -> Relocation::Entry + + # :category: Locations + # :call-seq: + # operator_loc -> Location + # + # Returns the Location represented by `operator_loc`. + # -- + # : () -> Location + def operator_loc: () -> Location + + # :category: Repository + # Save the operator_loc location using the given saved source so that + # it can be retrieved later. + # -- + # : (_Repository repository) -> Relocation::Entry + def save_operator_loc: (_Repository repository) -> Relocation::Entry + + # :call-seq: + # value -> Node + # + # Returns the `value` attribute. + # -- + # : () -> Prism::node + def value: () -> Prism::node + + # :call-seq: + # operator -> String + # + # Slice the location of operator_loc from the source. + # -- + # : () -> String + def operator: () -> String + + # : (untyped other) -> boolish + def ===: (untyped other) -> boolish + end + + # Represents assigning to an instance variable using an operator that isn't `=`. + # + # @target += value + # ^^^^^^^^^^^^^^^^ + class InstanceVariableOperatorWriteNode < Node + @binary_operator: Symbol + + @value: Prism::node + + @binary_operator_loc: Location + + @name_loc: Location + + @name: Symbol + + # Initialize a new InstanceVariableOperatorWriteNode node. + # -- + # : (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 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 + + # See Node.accept. + # -- + # : (_Visitor visitor) -> untyped + def accept: (_Visitor visitor) -> untyped + + # See Node.child_nodes. + # -- + # : () -> Array[node?] + def child_nodes: () -> Array[node?] + + # See Node.each_child_node. + # -- + # : () -> Enumerator[node, void] + # : () { (node) -> void } -> void + def each_child_node: () -> Enumerator[node, void] + | () { (node) -> void } -> void + + # See Node.compact_child_nodes. + # -- + # : () -> Array[node] + def compact_child_nodes: () -> Array[node] + + # See Node.comment_targets. + # -- + # : () -> Array[node | Location] + def comment_targets: () -> Array[node | Location] + + # :call-seq: + # copy(**fields) -> InstanceVariableOperatorWriteNode + # + # Creates a copy of self with the given fields, using self as the template. + # -- + # : (?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: Integer, ?location: Location, ?flags: Integer, ?name: Symbol, ?name_loc: Location, ?binary_operator_loc: Location, ?value: Prism::node, ?binary_operator: Symbol) -> InstanceVariableOperatorWriteNode + + alias deconstruct child_nodes + + # : (Array[Symbol]? keys) -> Hash[Symbol, untyped] + def deconstruct_keys: (Array[Symbol]? keys) -> Hash[Symbol, untyped] + + # See `Node#type`. + # -- + # : () -> :instance_variable_operator_write_node + def type: () -> :instance_variable_operator_write_node + + # See `Node.type`. + # -- + # : () -> :instance_variable_operator_write_node + def self.type: () -> :instance_variable_operator_write_node + + # : () -> String + def inspect: () -> String + + # :call-seq: + # name -> Symbol + # + # Returns the `name` attribute. + # -- + # : () -> Symbol + def name: () -> Symbol + + # :category: Locations + # :call-seq: + # name_loc -> Location + # + # Returns the Location represented by `name_loc`. + # -- + # : () -> Location + def name_loc: () -> Location + + # :category: Repository + # Save the name_loc location using the given saved source so that + # it can be retrieved later. + # -- + # : (_Repository repository) -> Relocation::Entry + def save_name_loc: (_Repository repository) -> Relocation::Entry + + # :category: Locations + # :call-seq: + # binary_operator_loc -> Location + # + # Returns the Location represented by `binary_operator_loc`. + # -- + # : () -> Location + def binary_operator_loc: () -> Location + + # :category: Repository + # Save the binary_operator_loc location using the given saved source so that + # it can be retrieved later. + # -- + # : (_Repository repository) -> Relocation::Entry + def save_binary_operator_loc: (_Repository repository) -> Relocation::Entry + + # :call-seq: + # value -> Node + # + # Returns the `value` attribute. + # -- + # : () -> Prism::node + def value: () -> Prism::node + + # :call-seq: + # binary_operator -> Symbol + # + # Returns the `binary_operator` attribute. + # -- + # : () -> Symbol + def binary_operator: () -> Symbol + + # : (untyped other) -> boolish + def ===: (untyped other) -> boolish + end + + # Represents the use of the `||=` operator for assignment to an instance variable. + # + # @target ||= value + # ^^^^^^^^^^^^^^^^^ + class InstanceVariableOrWriteNode < Node + @value: Prism::node + + @operator_loc: Location + + @name_loc: Location + + @name: Symbol + + # Initialize a new InstanceVariableOrWriteNode node. + # -- + # : (Source source, Integer node_id, Location location, Integer flags, Symbol name, Location name_loc, Location operator_loc, Prism::node value) -> void + def initialize: (Source source, Integer node_id, Location location, Integer flags, Symbol name, Location name_loc, Location operator_loc, Prism::node value) -> void + + # See Node.accept. + # -- + # : (_Visitor visitor) -> untyped + def accept: (_Visitor visitor) -> untyped + + # See Node.child_nodes. + # -- + # : () -> Array[node?] + def child_nodes: () -> Array[node?] + + # See Node.each_child_node. + # -- + # : () -> Enumerator[node, void] + # : () { (node) -> void } -> void + def each_child_node: () -> Enumerator[node, void] + | () { (node) -> void } -> void + + # See Node.compact_child_nodes. + # -- + # : () -> Array[node] + def compact_child_nodes: () -> Array[node] + + # See Node.comment_targets. + # -- + # : () -> Array[node | Location] + def comment_targets: () -> Array[node | Location] + + # :call-seq: + # copy(**fields) -> InstanceVariableOrWriteNode + # + # Creates a copy of self with the given fields, using self as the template. + # -- + # : (?node_id: Integer, ?location: Location, ?flags: Integer, ?name: Symbol, ?name_loc: Location, ?operator_loc: Location, ?value: Prism::node) -> InstanceVariableOrWriteNode + def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?name: Symbol, ?name_loc: Location, ?operator_loc: Location, ?value: Prism::node) -> InstanceVariableOrWriteNode + + alias deconstruct child_nodes + + # : (Array[Symbol]? keys) -> Hash[Symbol, untyped] + def deconstruct_keys: (Array[Symbol]? keys) -> Hash[Symbol, untyped] + + # See `Node#type`. + # -- + # : () -> :instance_variable_or_write_node + def type: () -> :instance_variable_or_write_node + + # See `Node.type`. + # -- + # : () -> :instance_variable_or_write_node + def self.type: () -> :instance_variable_or_write_node + + # : () -> String + def inspect: () -> String + + # :call-seq: + # name -> Symbol + # + # Returns the `name` attribute. + # -- + # : () -> Symbol + def name: () -> Symbol + + # :category: Locations + # :call-seq: + # name_loc -> Location + # + # Returns the Location represented by `name_loc`. + # -- + # : () -> Location + def name_loc: () -> Location + + # :category: Repository + # Save the name_loc location using the given saved source so that + # it can be retrieved later. + # -- + # : (_Repository repository) -> Relocation::Entry + def save_name_loc: (_Repository repository) -> Relocation::Entry + + # :category: Locations + # :call-seq: + # operator_loc -> Location + # + # Returns the Location represented by `operator_loc`. + # -- + # : () -> Location + def operator_loc: () -> Location + + # :category: Repository + # Save the operator_loc location using the given saved source so that + # it can be retrieved later. + # -- + # : (_Repository repository) -> Relocation::Entry + def save_operator_loc: (_Repository repository) -> Relocation::Entry + + # :call-seq: + # value -> Node + # + # Returns the `value` attribute. + # -- + # : () -> Prism::node + def value: () -> Prism::node + + # :call-seq: + # operator -> String + # + # Slice the location of operator_loc from the source. + # -- + # : () -> String + def operator: () -> String + + # : (untyped other) -> boolish + def ===: (untyped other) -> boolish + end + + # Represents referencing an instance variable. + # + # @foo + # ^^^^ + class InstanceVariableReadNode < Node + @name: Symbol + + # Initialize a new InstanceVariableReadNode node. + # -- + # : (Source source, Integer node_id, Location location, Integer flags, Symbol name) -> void + def initialize: (Source source, Integer node_id, Location location, Integer flags, Symbol name) -> void + + # See Node.accept. + # -- + # : (_Visitor visitor) -> untyped + def accept: (_Visitor visitor) -> untyped + + # See Node.child_nodes. + # -- + # : () -> Array[node?] + def child_nodes: () -> Array[node?] + + # See Node.each_child_node. + # -- + # : () -> Enumerator[node, void] + # : () { (node) -> void } -> void + def each_child_node: () -> Enumerator[node, void] + | () { (node) -> void } -> void + + # See Node.compact_child_nodes. + # -- + # : () -> Array[node] + def compact_child_nodes: () -> Array[node] + + # See Node.comment_targets. + # -- + # : () -> Array[node | Location] + def comment_targets: () -> Array[node | Location] + + # :call-seq: + # copy(**fields) -> InstanceVariableReadNode + # + # Creates a copy of self with the given fields, using self as the template. + # -- + # : (?node_id: Integer, ?location: Location, ?flags: Integer, ?name: Symbol) -> InstanceVariableReadNode + def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?name: Symbol) -> InstanceVariableReadNode + + alias deconstruct child_nodes + + # : (Array[Symbol]? keys) -> Hash[Symbol, untyped] + def deconstruct_keys: (Array[Symbol]? keys) -> Hash[Symbol, untyped] + + # See `Node#type`. + # -- + # : () -> :instance_variable_read_node + def type: () -> :instance_variable_read_node + + # See `Node.type`. + # -- + # : () -> :instance_variable_read_node + def self.type: () -> :instance_variable_read_node + + # : () -> String + def inspect: () -> String + + # :call-seq: + # name -> Symbol + # + # 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` + # -- + # : () -> Symbol + def name: () -> Symbol + + # : (untyped other) -> boolish + def ===: (untyped other) -> boolish + end + + # Represents writing to an instance variable in a context that doesn't have an explicit value. + # + # @foo, @bar = baz + # ^^^^ ^^^^ + class InstanceVariableTargetNode < Node + @name: Symbol + + # Initialize a new InstanceVariableTargetNode node. + # -- + # : (Source source, Integer node_id, Location location, Integer flags, Symbol name) -> void + def initialize: (Source source, Integer node_id, Location location, Integer flags, Symbol name) -> void + + # See Node.accept. + # -- + # : (_Visitor visitor) -> untyped + def accept: (_Visitor visitor) -> untyped + + # See Node.child_nodes. + # -- + # : () -> Array[node?] + def child_nodes: () -> Array[node?] + + # See Node.each_child_node. + # -- + # : () -> Enumerator[node, void] + # : () { (node) -> void } -> void + def each_child_node: () -> Enumerator[node, void] + | () { (node) -> void } -> void + + # See Node.compact_child_nodes. + # -- + # : () -> Array[node] + def compact_child_nodes: () -> Array[node] + + # See Node.comment_targets. + # -- + # : () -> Array[node | Location] + def comment_targets: () -> Array[node | Location] + + # :call-seq: + # copy(**fields) -> InstanceVariableTargetNode + # + # Creates a copy of self with the given fields, using self as the template. + # -- + # : (?node_id: Integer, ?location: Location, ?flags: Integer, ?name: Symbol) -> InstanceVariableTargetNode + def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?name: Symbol) -> InstanceVariableTargetNode + + alias deconstruct child_nodes + + # : (Array[Symbol]? keys) -> Hash[Symbol, untyped] + def deconstruct_keys: (Array[Symbol]? keys) -> Hash[Symbol, untyped] + + # See `Node#type`. + # -- + # : () -> :instance_variable_target_node + def type: () -> :instance_variable_target_node + + # See `Node.type`. + # -- + # : () -> :instance_variable_target_node + def self.type: () -> :instance_variable_target_node + + # : () -> String + def inspect: () -> String + + # :call-seq: + # name -> Symbol + # + # Returns the `name` attribute. + # -- + # : () -> Symbol + def name: () -> Symbol + + # : (untyped other) -> boolish + def ===: (untyped other) -> boolish + end + + # Represents writing to an instance variable. + # + # @foo = 1 + # ^^^^^^^^ + class InstanceVariableWriteNode < Node + @operator_loc: Location + + @value: Prism::node + + @name_loc: Location + + @name: Symbol + + # Initialize a new InstanceVariableWriteNode node. + # -- + # : (Source source, Integer node_id, Location location, Integer flags, Symbol name, Location name_loc, Prism::node value, Location operator_loc) -> void + def initialize: (Source source, Integer node_id, Location location, Integer flags, Symbol name, Location name_loc, Prism::node value, Location operator_loc) -> void + + # See Node.accept. + # -- + # : (_Visitor visitor) -> untyped + def accept: (_Visitor visitor) -> untyped + + # See Node.child_nodes. + # -- + # : () -> Array[node?] + def child_nodes: () -> Array[node?] + + # See Node.each_child_node. + # -- + # : () -> Enumerator[node, void] + # : () { (node) -> void } -> void + def each_child_node: () -> Enumerator[node, void] + | () { (node) -> void } -> void + + # See Node.compact_child_nodes. + # -- + # : () -> Array[node] + def compact_child_nodes: () -> Array[node] + + # See Node.comment_targets. + # -- + # : () -> Array[node | Location] + def comment_targets: () -> Array[node | Location] + + # :call-seq: + # copy(**fields) -> InstanceVariableWriteNode + # + # Creates a copy of self with the given fields, using self as the template. + # -- + # : (?node_id: Integer, ?location: Location, ?flags: Integer, ?name: Symbol, ?name_loc: Location, ?value: Prism::node, ?operator_loc: Location) -> InstanceVariableWriteNode + def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?name: Symbol, ?name_loc: Location, ?value: Prism::node, ?operator_loc: Location) -> InstanceVariableWriteNode + + alias deconstruct child_nodes + + # : (Array[Symbol]? keys) -> Hash[Symbol, untyped] + def deconstruct_keys: (Array[Symbol]? keys) -> Hash[Symbol, untyped] + + # See `Node#type`. + # -- + # : () -> :instance_variable_write_node + def type: () -> :instance_variable_write_node + + # See `Node.type`. + # -- + # : () -> :instance_variable_write_node + def self.type: () -> :instance_variable_write_node + + # : () -> String + def inspect: () -> String + + # :call-seq: + # name -> Symbol + # + # 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` + # -- + # : () -> Symbol + def name: () -> Symbol + + # :category: Locations + # :call-seq: + # name_loc -> Location + # + # The Location of the variable name. + # + # @_x = 1 + # ^^^ + # -- + # : () -> Location + def name_loc: () -> Location + + # :category: Repository + # Save the name_loc location using the given saved source so that + # it can be retrieved later. + # -- + # : (_Repository repository) -> Relocation::Entry + def save_name_loc: (_Repository repository) -> Relocation::Entry + + # :call-seq: + # value -> Node + # + # 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 + # ^^^^ + # -- + # : () -> Prism::node + def value: () -> Prism::node + + # :category: Locations + # :call-seq: + # operator_loc -> Location + # + # The Location of the `=` operator. + # + # @x = y + # ^ + # -- + # : () -> Location + def operator_loc: () -> Location + + # :category: Repository + # Save the operator_loc location using the given saved source so that + # it can be retrieved later. + # -- + # : (_Repository repository) -> Relocation::Entry + def save_operator_loc: (_Repository repository) -> Relocation::Entry + + # :call-seq: + # operator -> String + # + # Slice the location of operator_loc from the source. + # -- + # : () -> String + def operator: () -> String + + # : (untyped other) -> boolish + def ===: (untyped other) -> boolish + end + + # Represents an integer number literal. + # + # 1 + # ^ + class IntegerNode < Node + @value: Integer + + # Initialize a new IntegerNode node. + # -- + # : (Source source, Integer node_id, Location location, Integer flags, Integer value) -> void + def initialize: (Source source, Integer node_id, Location location, Integer flags, Integer value) -> void + + # See Node.accept. + # -- + # : (_Visitor visitor) -> untyped + def accept: (_Visitor visitor) -> untyped + + # See Node.child_nodes. + # -- + # : () -> Array[node?] + def child_nodes: () -> Array[node?] + + # See Node.each_child_node. + # -- + # : () -> Enumerator[node, void] + # : () { (node) -> void } -> void + def each_child_node: () -> Enumerator[node, void] + | () { (node) -> void } -> void + + # See Node.compact_child_nodes. + # -- + # : () -> Array[node] + def compact_child_nodes: () -> Array[node] + + # See Node.comment_targets. + # -- + # : () -> Array[node | Location] + def comment_targets: () -> Array[node | Location] + + # :call-seq: + # copy(**fields) -> IntegerNode + # + # Creates a copy of self with the given fields, using self as the template. + # -- + # : (?node_id: Integer, ?location: Location, ?flags: Integer, ?value: Integer) -> IntegerNode + def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?value: Integer) -> IntegerNode + + alias deconstruct child_nodes + + # : (Array[Symbol]? keys) -> Hash[Symbol, untyped] + def deconstruct_keys: (Array[Symbol]? keys) -> Hash[Symbol, untyped] + + # See `Node#type`. + # -- + # : () -> :integer_node + def type: () -> :integer_node + + # See `Node.type`. + # -- + # : () -> :integer_node + def self.type: () -> :integer_node + + # : () -> String + def inspect: () -> String + + # :category: Flags + # 0b prefix + # -- + # : () -> bool + def binary?: () -> bool + + # :category: Flags + # 0d or no prefix + # -- + # : () -> bool + def decimal?: () -> bool + + # :category: Flags + # 0o or 0 prefix + # -- + # : () -> bool + def octal?: () -> bool + + # :category: Flags + # 0x prefix + # -- + # : () -> bool + def hexadecimal?: () -> bool + + # :call-seq: + # value -> Integer + # + # The value of the integer literal as a number. + # -- + # : () -> Integer + def value: () -> Integer + + # : (untyped other) -> boolish + def ===: (untyped other) -> boolish + 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 + @closing_loc: Location + + @parts: Array[StringNode | EmbeddedStatementsNode | EmbeddedVariableNode] + + @opening_loc: Location + + # Initialize a new InterpolatedMatchLastLineNode node. + # -- + # : (Source source, Integer node_id, Location location, Integer flags, Location opening_loc, Array[StringNode | EmbeddedStatementsNode | EmbeddedVariableNode] parts, Location closing_loc) -> void + def initialize: (Source source, Integer node_id, Location location, Integer flags, Location opening_loc, Array[StringNode | EmbeddedStatementsNode | EmbeddedVariableNode] parts, Location closing_loc) -> void + + # See Node.accept. + # -- + # : (_Visitor visitor) -> untyped + def accept: (_Visitor visitor) -> untyped + + # See Node.child_nodes. + # -- + # : () -> Array[node?] + def child_nodes: () -> Array[node?] + + # See Node.each_child_node. + # -- + # : () -> Enumerator[node, void] + # : () { (node) -> void } -> void + def each_child_node: () -> Enumerator[node, void] + | () { (node) -> void } -> void + + # See Node.compact_child_nodes. + # -- + # : () -> Array[node] + def compact_child_nodes: () -> Array[node] + + # See Node.comment_targets. + # -- + # : () -> Array[node | Location] + def comment_targets: () -> Array[node | Location] + + # :call-seq: + # copy(**fields) -> InterpolatedMatchLastLineNode + # + # Creates a copy of self with the given fields, using self as the template. + # -- + # : (?node_id: Integer, ?location: Location, ?flags: Integer, ?opening_loc: Location, ?parts: Array[StringNode | EmbeddedStatementsNode | EmbeddedVariableNode], ?closing_loc: Location) -> InterpolatedMatchLastLineNode + def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?opening_loc: Location, ?parts: Array[StringNode | EmbeddedStatementsNode | EmbeddedVariableNode], ?closing_loc: Location) -> InterpolatedMatchLastLineNode + + alias deconstruct child_nodes + + # : (Array[Symbol]? keys) -> Hash[Symbol, untyped] + def deconstruct_keys: (Array[Symbol]? keys) -> Hash[Symbol, untyped] + + # See `Node#type`. + # -- + # : () -> :interpolated_match_last_line_node + def type: () -> :interpolated_match_last_line_node + + # See `Node.type`. + # -- + # : () -> :interpolated_match_last_line_node + def self.type: () -> :interpolated_match_last_line_node + + # : () -> String + def inspect: () -> String + + # :category: Flags + # i - ignores the case of characters when matching + # -- + # : () -> bool + def ignore_case?: () -> bool + + # :category: Flags + # x - ignores whitespace and allows comments in regular expressions + # -- + # : () -> bool + def extended?: () -> bool + + # :category: Flags + # m - allows $ to match the end of lines within strings + # -- + # : () -> bool + def multi_line?: () -> bool + + # :category: Flags + # o - only interpolates values into the regular expression once + # -- + # : () -> bool + def once?: () -> bool + + # :category: Flags + # e - forces the EUC-JP encoding + # -- + # : () -> bool + def euc_jp?: () -> bool + + # :category: Flags + # n - forces the ASCII-8BIT encoding + # -- + # : () -> bool + def ascii_8bit?: () -> bool + + # :category: Flags + # s - forces the Windows-31J encoding + # -- + # : () -> bool + def windows_31j?: () -> bool + + # :category: Flags + # u - forces the UTF-8 encoding + # -- + # : () -> bool + def utf_8?: () -> bool + + # :category: Flags + # internal bytes forced the encoding to UTF-8 + # -- + # : () -> bool + def forced_utf8_encoding?: () -> bool + + # :category: Flags + # internal bytes forced the encoding to binary + # -- + # : () -> bool + def forced_binary_encoding?: () -> bool + + # :category: Flags + # internal bytes forced the encoding to US-ASCII + # -- + # : () -> bool + def forced_us_ascii_encoding?: () -> bool + + # :category: Locations + # :call-seq: + # opening_loc -> Location + # + # Returns the Location represented by `opening_loc`. + # -- + # : () -> Location + def opening_loc: () -> Location + + # :category: Repository + # Save the opening_loc location using the given saved source so that + # it can be retrieved later. + # -- + # : (_Repository repository) -> Relocation::Entry + def save_opening_loc: (_Repository repository) -> Relocation::Entry + + # :call-seq: + # parts -> Array[StringNode | EmbeddedStatementsNode | EmbeddedVariableNode] + # + # Returns the `parts` attribute. + # -- + # : () -> Array[StringNode | EmbeddedStatementsNode | EmbeddedVariableNode] + def parts: () -> Array[StringNode | EmbeddedStatementsNode | EmbeddedVariableNode] + + # :category: Locations + # :call-seq: + # closing_loc -> Location + # + # Returns the Location represented by `closing_loc`. + # -- + # : () -> Location + def closing_loc: () -> Location + + # :category: Repository + # Save the closing_loc location using the given saved source so that + # it can be retrieved later. + # -- + # : (_Repository repository) -> Relocation::Entry + def save_closing_loc: (_Repository repository) -> Relocation::Entry + + # :call-seq: + # opening -> String + # + # Slice the location of opening_loc from the source. + # -- + # : () -> String + def opening: () -> String + + # :call-seq: + # closing -> String + # + # Slice the location of closing_loc from the source. + # -- + # : () -> String + def closing: () -> String + + # : (untyped other) -> boolish + def ===: (untyped other) -> boolish + end + + # Represents a regular expression literal that contains interpolation. + # + # /foo #{bar} baz/ + # ^^^^^^^^^^^^^^^^ + class InterpolatedRegularExpressionNode < Node + @closing_loc: Location + + @parts: Array[StringNode | EmbeddedStatementsNode | EmbeddedVariableNode] + + @opening_loc: Location + + # Initialize a new InterpolatedRegularExpressionNode node. + # -- + # : (Source source, Integer node_id, Location location, Integer flags, Location opening_loc, Array[StringNode | EmbeddedStatementsNode | EmbeddedVariableNode] parts, Location closing_loc) -> void + def initialize: (Source source, Integer node_id, Location location, Integer flags, Location opening_loc, Array[StringNode | EmbeddedStatementsNode | EmbeddedVariableNode] parts, Location closing_loc) -> void + + # See Node.accept. + # -- + # : (_Visitor visitor) -> untyped + def accept: (_Visitor visitor) -> untyped + + # See Node.child_nodes. + # -- + # : () -> Array[node?] + def child_nodes: () -> Array[node?] + + # See Node.each_child_node. + # -- + # : () -> Enumerator[node, void] + # : () { (node) -> void } -> void + def each_child_node: () -> Enumerator[node, void] + | () { (node) -> void } -> void + + # See Node.compact_child_nodes. + # -- + # : () -> Array[node] + def compact_child_nodes: () -> Array[node] + + # See Node.comment_targets. + # -- + # : () -> Array[node | Location] + def comment_targets: () -> Array[node | Location] + + # :call-seq: + # copy(**fields) -> InterpolatedRegularExpressionNode + # + # Creates a copy of self with the given fields, using self as the template. + # -- + # : (?node_id: Integer, ?location: Location, ?flags: Integer, ?opening_loc: Location, ?parts: Array[StringNode | EmbeddedStatementsNode | EmbeddedVariableNode], ?closing_loc: Location) -> InterpolatedRegularExpressionNode + def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?opening_loc: Location, ?parts: Array[StringNode | EmbeddedStatementsNode | EmbeddedVariableNode], ?closing_loc: Location) -> InterpolatedRegularExpressionNode + + alias deconstruct child_nodes + + # : (Array[Symbol]? keys) -> Hash[Symbol, untyped] + def deconstruct_keys: (Array[Symbol]? keys) -> Hash[Symbol, untyped] + + # See `Node#type`. + # -- + # : () -> :interpolated_regular_expression_node + def type: () -> :interpolated_regular_expression_node + + # See `Node.type`. + # -- + # : () -> :interpolated_regular_expression_node + def self.type: () -> :interpolated_regular_expression_node + + # : () -> String + def inspect: () -> String + + # :category: Flags + # i - ignores the case of characters when matching + # -- + # : () -> bool + def ignore_case?: () -> bool + + # :category: Flags + # x - ignores whitespace and allows comments in regular expressions + # -- + # : () -> bool + def extended?: () -> bool + + # :category: Flags + # m - allows $ to match the end of lines within strings + # -- + # : () -> bool + def multi_line?: () -> bool + + # :category: Flags + # o - only interpolates values into the regular expression once + # -- + # : () -> bool + def once?: () -> bool + + # :category: Flags + # e - forces the EUC-JP encoding + # -- + # : () -> bool + def euc_jp?: () -> bool + + # :category: Flags + # n - forces the ASCII-8BIT encoding + # -- + # : () -> bool + def ascii_8bit?: () -> bool + + # :category: Flags + # s - forces the Windows-31J encoding + # -- + # : () -> bool + def windows_31j?: () -> bool + + # :category: Flags + # u - forces the UTF-8 encoding + # -- + # : () -> bool + def utf_8?: () -> bool + + # :category: Flags + # internal bytes forced the encoding to UTF-8 + # -- + # : () -> bool + def forced_utf8_encoding?: () -> bool + + # :category: Flags + # internal bytes forced the encoding to binary + # -- + # : () -> bool + def forced_binary_encoding?: () -> bool + + # :category: Flags + # internal bytes forced the encoding to US-ASCII + # -- + # : () -> bool + def forced_us_ascii_encoding?: () -> bool + + # :category: Locations + # :call-seq: + # opening_loc -> Location + # + # Returns the Location represented by `opening_loc`. + # -- + # : () -> Location + def opening_loc: () -> Location + + # :category: Repository + # Save the opening_loc location using the given saved source so that + # it can be retrieved later. + # -- + # : (_Repository repository) -> Relocation::Entry + def save_opening_loc: (_Repository repository) -> Relocation::Entry + + # :call-seq: + # parts -> Array[StringNode | EmbeddedStatementsNode | EmbeddedVariableNode] + # + # Returns the `parts` attribute. + # -- + # : () -> Array[StringNode | EmbeddedStatementsNode | EmbeddedVariableNode] + def parts: () -> Array[StringNode | EmbeddedStatementsNode | EmbeddedVariableNode] + + # :category: Locations + # :call-seq: + # closing_loc -> Location + # + # Returns the Location represented by `closing_loc`. + # -- + # : () -> Location + def closing_loc: () -> Location + + # :category: Repository + # Save the closing_loc location using the given saved source so that + # it can be retrieved later. + # -- + # : (_Repository repository) -> Relocation::Entry + def save_closing_loc: (_Repository repository) -> Relocation::Entry + + # :call-seq: + # opening -> String + # + # Slice the location of opening_loc from the source. + # -- + # : () -> String + def opening: () -> String + + # :call-seq: + # closing -> String + # + # Slice the location of closing_loc from the source. + # -- + # : () -> String + def closing: () -> String + + # : (untyped other) -> boolish + def ===: (untyped other) -> boolish + end + + # Represents a string literal that contains interpolation. + # + # "foo #{bar} baz" + # ^^^^^^^^^^^^^^^^ + class InterpolatedStringNode < Node + @closing_loc: Location? + + @parts: Array[StringNode | EmbeddedStatementsNode | EmbeddedVariableNode | InterpolatedStringNode | XStringNode | InterpolatedXStringNode | SymbolNode | InterpolatedSymbolNode] + + @opening_loc: Location? + + # Initialize a new InterpolatedStringNode node. + # -- + # : (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 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 + + # See Node.accept. + # -- + # : (_Visitor visitor) -> untyped + def accept: (_Visitor visitor) -> untyped + + # See Node.child_nodes. + # -- + # : () -> Array[node?] + def child_nodes: () -> Array[node?] + + # See Node.each_child_node. + # -- + # : () -> Enumerator[node, void] + # : () { (node) -> void } -> void + def each_child_node: () -> Enumerator[node, void] + | () { (node) -> void } -> void + + # See Node.compact_child_nodes. + # -- + # : () -> Array[node] + def compact_child_nodes: () -> Array[node] + + # See Node.comment_targets. + # -- + # : () -> Array[node | Location] + def comment_targets: () -> Array[node | Location] + + # :call-seq: + # copy(**fields) -> InterpolatedStringNode + # + # Creates a copy of self with the given fields, using self as the template. + # -- + # : (?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: Integer, ?location: Location, ?flags: Integer, ?opening_loc: Location?, ?parts: Array[StringNode | EmbeddedStatementsNode | EmbeddedVariableNode | InterpolatedStringNode | XStringNode | InterpolatedXStringNode | SymbolNode | InterpolatedSymbolNode], ?closing_loc: Location?) -> InterpolatedStringNode + + alias deconstruct child_nodes + + # : (Array[Symbol]? keys) -> Hash[Symbol, untyped] + def deconstruct_keys: (Array[Symbol]? keys) -> Hash[Symbol, untyped] + + # See `Node#type`. + # -- + # : () -> :interpolated_string_node + def type: () -> :interpolated_string_node + + # See `Node.type`. + # -- + # : () -> :interpolated_string_node + def self.type: () -> :interpolated_string_node + + # : () -> String + def inspect: () -> String + + # :category: Flags + # frozen by virtue of a `frozen_string_literal: true` comment or `--enable-frozen-string-literal`; only for adjacent string literals like `'a' 'b'` + # -- + # : () -> bool + def frozen?: () -> bool + + # :category: Flags + # mutable by virtue of a `frozen_string_literal: false` comment or `--disable-frozen-string-literal`; only for adjacent string literals like `'a' 'b'` + # -- + # : () -> bool + def mutable?: () -> bool + + # :category: Locations + # :call-seq: + # opening_loc -> Location | nil + # + # Returns the Location represented by `opening_loc`. + # -- + # : () -> Location? + def opening_loc: () -> Location? + + # :category: Repository + # Save the opening_loc location using the given saved source so that + # it can be retrieved later. + # -- + # : (_Repository repository) -> Relocation::Entry? + def save_opening_loc: (_Repository repository) -> Relocation::Entry? + + # :call-seq: + # parts -> Array[StringNode | EmbeddedStatementsNode | EmbeddedVariableNode | InterpolatedStringNode | XStringNode | InterpolatedXStringNode | SymbolNode | InterpolatedSymbolNode] + # + # Returns the `parts` attribute. + # -- + # : () -> Array[StringNode | EmbeddedStatementsNode | EmbeddedVariableNode | InterpolatedStringNode | XStringNode | InterpolatedXStringNode | SymbolNode | InterpolatedSymbolNode] + def parts: () -> Array[StringNode | EmbeddedStatementsNode | EmbeddedVariableNode | InterpolatedStringNode | XStringNode | InterpolatedXStringNode | SymbolNode | InterpolatedSymbolNode] + + # :category: Locations + # :call-seq: + # closing_loc -> Location | nil + # + # Returns the Location represented by `closing_loc`. + # -- + # : () -> Location? + def closing_loc: () -> Location? + + # :category: Repository + # Save the closing_loc location using the given saved source so that + # it can be retrieved later. + # -- + # : (_Repository repository) -> Relocation::Entry? + def save_closing_loc: (_Repository repository) -> Relocation::Entry? + + # :call-seq: + # opening -> String | nil + # + # Slice the location of opening_loc from the source. + # -- + # : () -> String? + def opening: () -> String? + + # :call-seq: + # closing -> String | nil + # + # Slice the location of closing_loc from the source. + # -- + # : () -> String? + def closing: () -> String? + + # : (untyped other) -> boolish + def ===: (untyped other) -> boolish + end + + # Represents a symbol literal that contains interpolation. + # + # :"foo #{bar} baz" + # ^^^^^^^^^^^^^^^^^ + class InterpolatedSymbolNode < Node + @closing_loc: Location? + + @parts: Array[StringNode | EmbeddedStatementsNode | EmbeddedVariableNode] + + @opening_loc: Location? + + # Initialize a new InterpolatedSymbolNode node. + # -- + # : (Source source, Integer node_id, Location location, Integer flags, Location? opening_loc, Array[StringNode | EmbeddedStatementsNode | EmbeddedVariableNode] parts, Location? closing_loc) -> void + def initialize: (Source source, Integer node_id, Location location, Integer flags, Location? opening_loc, Array[StringNode | EmbeddedStatementsNode | EmbeddedVariableNode] parts, Location? closing_loc) -> void + + # See Node.accept. + # -- + # : (_Visitor visitor) -> untyped + def accept: (_Visitor visitor) -> untyped + + # See Node.child_nodes. + # -- + # : () -> Array[node?] + def child_nodes: () -> Array[node?] + + # See Node.each_child_node. + # -- + # : () -> Enumerator[node, void] + # : () { (node) -> void } -> void + def each_child_node: () -> Enumerator[node, void] + | () { (node) -> void } -> void + + # See Node.compact_child_nodes. + # -- + # : () -> Array[node] + def compact_child_nodes: () -> Array[node] + + # See Node.comment_targets. + # -- + # : () -> Array[node | Location] + def comment_targets: () -> Array[node | Location] + + # :call-seq: + # copy(**fields) -> InterpolatedSymbolNode + # + # Creates a copy of self with the given fields, using self as the template. + # -- + # : (?node_id: Integer, ?location: Location, ?flags: Integer, ?opening_loc: Location?, ?parts: Array[StringNode | EmbeddedStatementsNode | EmbeddedVariableNode], ?closing_loc: Location?) -> InterpolatedSymbolNode + def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?opening_loc: Location?, ?parts: Array[StringNode | EmbeddedStatementsNode | EmbeddedVariableNode], ?closing_loc: Location?) -> InterpolatedSymbolNode + + alias deconstruct child_nodes + + # : (Array[Symbol]? keys) -> Hash[Symbol, untyped] + def deconstruct_keys: (Array[Symbol]? keys) -> Hash[Symbol, untyped] + + # See `Node#type`. + # -- + # : () -> :interpolated_symbol_node + def type: () -> :interpolated_symbol_node + + # See `Node.type`. + # -- + # : () -> :interpolated_symbol_node + def self.type: () -> :interpolated_symbol_node + + # : () -> String + def inspect: () -> String + + # :category: Locations + # :call-seq: + # opening_loc -> Location | nil + # + # Returns the Location represented by `opening_loc`. + # -- + # : () -> Location? + def opening_loc: () -> Location? + + # :category: Repository + # Save the opening_loc location using the given saved source so that + # it can be retrieved later. + # -- + # : (_Repository repository) -> Relocation::Entry? + def save_opening_loc: (_Repository repository) -> Relocation::Entry? + + # :call-seq: + # parts -> Array[StringNode | EmbeddedStatementsNode | EmbeddedVariableNode] + # + # Returns the `parts` attribute. + # -- + # : () -> Array[StringNode | EmbeddedStatementsNode | EmbeddedVariableNode] + def parts: () -> Array[StringNode | EmbeddedStatementsNode | EmbeddedVariableNode] + + # :category: Locations + # :call-seq: + # closing_loc -> Location | nil + # + # Returns the Location represented by `closing_loc`. + # -- + # : () -> Location? + def closing_loc: () -> Location? + + # :category: Repository + # Save the closing_loc location using the given saved source so that + # it can be retrieved later. + # -- + # : (_Repository repository) -> Relocation::Entry? + def save_closing_loc: (_Repository repository) -> Relocation::Entry? + + # :call-seq: + # opening -> String | nil + # + # Slice the location of opening_loc from the source. + # -- + # : () -> String? + def opening: () -> String? + + # :call-seq: + # closing -> String | nil + # + # Slice the location of closing_loc from the source. + # -- + # : () -> String? + def closing: () -> String? + + # : (untyped other) -> boolish + def ===: (untyped other) -> boolish + end + + # Represents an xstring literal that contains interpolation. + # + # `foo #{bar} baz` + # ^^^^^^^^^^^^^^^^ + class InterpolatedXStringNode < Node + @closing_loc: Location + + @parts: Array[StringNode | EmbeddedStatementsNode | EmbeddedVariableNode] + + @opening_loc: Location + + # Initialize a new InterpolatedXStringNode node. + # -- + # : (Source source, Integer node_id, Location location, Integer flags, Location opening_loc, Array[StringNode | EmbeddedStatementsNode | EmbeddedVariableNode] parts, Location closing_loc) -> void + def initialize: (Source source, Integer node_id, Location location, Integer flags, Location opening_loc, Array[StringNode | EmbeddedStatementsNode | EmbeddedVariableNode] parts, Location closing_loc) -> void + + # See Node.accept. + # -- + # : (_Visitor visitor) -> untyped + def accept: (_Visitor visitor) -> untyped + + # See Node.child_nodes. + # -- + # : () -> Array[node?] + def child_nodes: () -> Array[node?] + + # See Node.each_child_node. + # -- + # : () -> Enumerator[node, void] + # : () { (node) -> void } -> void + def each_child_node: () -> Enumerator[node, void] + | () { (node) -> void } -> void + + # See Node.compact_child_nodes. + # -- + # : () -> Array[node] + def compact_child_nodes: () -> Array[node] + + # See Node.comment_targets. + # -- + # : () -> Array[node | Location] + def comment_targets: () -> Array[node | Location] + + # :call-seq: + # copy(**fields) -> InterpolatedXStringNode + # + # Creates a copy of self with the given fields, using self as the template. + # -- + # : (?node_id: Integer, ?location: Location, ?flags: Integer, ?opening_loc: Location, ?parts: Array[StringNode | EmbeddedStatementsNode | EmbeddedVariableNode], ?closing_loc: Location) -> InterpolatedXStringNode + def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?opening_loc: Location, ?parts: Array[StringNode | EmbeddedStatementsNode | EmbeddedVariableNode], ?closing_loc: Location) -> InterpolatedXStringNode + + alias deconstruct child_nodes + + # : (Array[Symbol]? keys) -> Hash[Symbol, untyped] + def deconstruct_keys: (Array[Symbol]? keys) -> Hash[Symbol, untyped] + + # See `Node#type`. + # -- + # : () -> :interpolated_x_string_node + def type: () -> :interpolated_x_string_node + + # See `Node.type`. + # -- + # : () -> :interpolated_x_string_node + def self.type: () -> :interpolated_x_string_node + + # : () -> String + def inspect: () -> String + + # :category: Locations + # :call-seq: + # opening_loc -> Location + # + # Returns the Location represented by `opening_loc`. + # -- + # : () -> Location + def opening_loc: () -> Location + + # :category: Repository + # Save the opening_loc location using the given saved source so that + # it can be retrieved later. + # -- + # : (_Repository repository) -> Relocation::Entry + def save_opening_loc: (_Repository repository) -> Relocation::Entry + + # :call-seq: + # parts -> Array[StringNode | EmbeddedStatementsNode | EmbeddedVariableNode] + # + # Returns the `parts` attribute. + # -- + # : () -> Array[StringNode | EmbeddedStatementsNode | EmbeddedVariableNode] + def parts: () -> Array[StringNode | EmbeddedStatementsNode | EmbeddedVariableNode] + + # :category: Locations + # :call-seq: + # closing_loc -> Location + # + # Returns the Location represented by `closing_loc`. + # -- + # : () -> Location + def closing_loc: () -> Location + + # :category: Repository + # Save the closing_loc location using the given saved source so that + # it can be retrieved later. + # -- + # : (_Repository repository) -> Relocation::Entry + def save_closing_loc: (_Repository repository) -> Relocation::Entry + + # :call-seq: + # opening -> String + # + # Slice the location of opening_loc from the source. + # -- + # : () -> String + def opening: () -> String + + # :call-seq: + # closing -> String + # + # Slice the location of closing_loc from the source. + # -- + # : () -> String + def closing: () -> String + + # : (untyped other) -> boolish + def ===: (untyped other) -> boolish + end + + # Represents reading from the implicit `it` local variable. + # + # -> { it } + # ^^ + class ItLocalVariableReadNode < Node + # Initialize a new ItLocalVariableReadNode node. + # -- + # : (Source source, Integer node_id, Location location, Integer flags, ) -> void + def initialize: (Source source, Integer node_id, Location location, Integer flags) -> void + + # See Node.accept. + # -- + # : (_Visitor visitor) -> untyped + def accept: (_Visitor visitor) -> untyped + + # See Node.child_nodes. + # -- + # : () -> Array[node?] + def child_nodes: () -> Array[node?] + + # See Node.each_child_node. + # -- + # : () -> Enumerator[node, void] + # : () { (node) -> void } -> void + def each_child_node: () -> Enumerator[node, void] + | () { (node) -> void } -> void + + # See Node.compact_child_nodes. + # -- + # : () -> Array[node] + def compact_child_nodes: () -> Array[node] + + # See Node.comment_targets. + # -- + # : () -> Array[node | Location] + def comment_targets: () -> Array[node | Location] + + # :call-seq: + # copy(**fields) -> ItLocalVariableReadNode + # + # Creates a copy of self with the given fields, using self as the template. + # -- + # : (?node_id: Integer, ?location: Location, ?flags: Integer, ) -> ItLocalVariableReadNode + def copy: (?node_id: Integer, ?location: Location, ?flags: Integer) -> ItLocalVariableReadNode + + alias deconstruct child_nodes + + # : (Array[Symbol]? keys) -> Hash[Symbol, untyped] + def deconstruct_keys: (Array[Symbol]? keys) -> Hash[Symbol, untyped] + + # See `Node#type`. + # -- + # : () -> :it_local_variable_read_node + def type: () -> :it_local_variable_read_node + + # See `Node.type`. + # -- + # : () -> :it_local_variable_read_node + def self.type: () -> :it_local_variable_read_node + + # : () -> String + def inspect: () -> String + + # : (untyped other) -> boolish + def ===: (untyped other) -> boolish + 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. + # -- + # : (Source source, Integer node_id, Location location, Integer flags, ) -> void + def initialize: (Source source, Integer node_id, Location location, Integer flags) -> void + + # See Node.accept. + # -- + # : (_Visitor visitor) -> untyped + def accept: (_Visitor visitor) -> untyped + + # See Node.child_nodes. + # -- + # : () -> Array[node?] + def child_nodes: () -> Array[node?] + + # See Node.each_child_node. + # -- + # : () -> Enumerator[node, void] + # : () { (node) -> void } -> void + def each_child_node: () -> Enumerator[node, void] + | () { (node) -> void } -> void + + # See Node.compact_child_nodes. + # -- + # : () -> Array[node] + def compact_child_nodes: () -> Array[node] + + # See Node.comment_targets. + # -- + # : () -> Array[node | Location] + def comment_targets: () -> Array[node | Location] + + # :call-seq: + # copy(**fields) -> ItParametersNode + # + # Creates a copy of self with the given fields, using self as the template. + # -- + # : (?node_id: Integer, ?location: Location, ?flags: Integer, ) -> ItParametersNode + def copy: (?node_id: Integer, ?location: Location, ?flags: Integer) -> ItParametersNode + + alias deconstruct child_nodes + + # : (Array[Symbol]? keys) -> Hash[Symbol, untyped] + def deconstruct_keys: (Array[Symbol]? keys) -> Hash[Symbol, untyped] + + # See `Node#type`. + # -- + # : () -> :it_parameters_node + def type: () -> :it_parameters_node + + # See `Node.type`. + # -- + # : () -> :it_parameters_node + def self.type: () -> :it_parameters_node + + # : () -> String + def inspect: () -> String + + # : (untyped other) -> boolish + def ===: (untyped other) -> boolish + end + + # Represents a hash literal without opening and closing braces. + # + # foo(a: b) + # ^^^^ + class KeywordHashNode < Node + @elements: Array[AssocNode | AssocSplatNode] + + # Initialize a new KeywordHashNode node. + # -- + # : (Source source, Integer node_id, Location location, Integer flags, Array[AssocNode | AssocSplatNode] elements) -> void + def initialize: (Source source, Integer node_id, Location location, Integer flags, Array[AssocNode | AssocSplatNode] elements) -> void + + # See Node.accept. + # -- + # : (_Visitor visitor) -> untyped + def accept: (_Visitor visitor) -> untyped + + # See Node.child_nodes. + # -- + # : () -> Array[node?] + def child_nodes: () -> Array[node?] + + # See Node.each_child_node. + # -- + # : () -> Enumerator[node, void] + # : () { (node) -> void } -> void + def each_child_node: () -> Enumerator[node, void] + | () { (node) -> void } -> void + + # See Node.compact_child_nodes. + # -- + # : () -> Array[node] + def compact_child_nodes: () -> Array[node] + + # See Node.comment_targets. + # -- + # : () -> Array[node | Location] + def comment_targets: () -> Array[node | Location] + + # :call-seq: + # copy(**fields) -> KeywordHashNode + # + # Creates a copy of self with the given fields, using self as the template. + # -- + # : (?node_id: Integer, ?location: Location, ?flags: Integer, ?elements: Array[AssocNode | AssocSplatNode]) -> KeywordHashNode + def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?elements: Array[AssocNode | AssocSplatNode]) -> KeywordHashNode + + alias deconstruct child_nodes + + # : (Array[Symbol]? keys) -> Hash[Symbol, untyped] + def deconstruct_keys: (Array[Symbol]? keys) -> Hash[Symbol, untyped] + + # See `Node#type`. + # -- + # : () -> :keyword_hash_node + def type: () -> :keyword_hash_node + + # See `Node.type`. + # -- + # : () -> :keyword_hash_node + def self.type: () -> :keyword_hash_node + + # : () -> String + def inspect: () -> String + + # :category: Flags + # a keyword hash which only has `AssocNode` elements all with symbol keys, which means the elements can be treated as keyword arguments + # -- + # : () -> bool + def symbol_keys?: () -> bool + + # :call-seq: + # elements -> Array[AssocNode | AssocSplatNode] + # + # Returns the `elements` attribute. + # -- + # : () -> Array[AssocNode | AssocSplatNode] + def elements: () -> Array[AssocNode | AssocSplatNode] + + # : (untyped other) -> boolish + def ===: (untyped other) -> boolish + end + + # Represents a keyword rest parameter to a method, block, or lambda definition. + # + # def a(**b) + # ^^^ + # end + class KeywordRestParameterNode < Node + @operator_loc: Location + + @name_loc: Location? + + @name: Symbol? + + # Initialize a new KeywordRestParameterNode node. + # -- + # : (Source source, Integer node_id, Location location, Integer flags, Symbol? name, Location? name_loc, Location operator_loc) -> void + def initialize: (Source source, Integer node_id, Location location, Integer flags, Symbol? name, Location? name_loc, Location operator_loc) -> void + + # See Node.accept. + # -- + # : (_Visitor visitor) -> untyped + def accept: (_Visitor visitor) -> untyped + + # See Node.child_nodes. + # -- + # : () -> Array[node?] + def child_nodes: () -> Array[node?] + + # See Node.each_child_node. + # -- + # : () -> Enumerator[node, void] + # : () { (node) -> void } -> void + def each_child_node: () -> Enumerator[node, void] + | () { (node) -> void } -> void + + # See Node.compact_child_nodes. + # -- + # : () -> Array[node] + def compact_child_nodes: () -> Array[node] + + # See Node.comment_targets. + # -- + # : () -> Array[node | Location] + def comment_targets: () -> Array[node | Location] + + # :call-seq: + # copy(**fields) -> KeywordRestParameterNode + # + # Creates a copy of self with the given fields, using self as the template. + # -- + # : (?node_id: Integer, ?location: Location, ?flags: Integer, ?name: Symbol?, ?name_loc: Location?, ?operator_loc: Location) -> KeywordRestParameterNode + def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?name: Symbol?, ?name_loc: Location?, ?operator_loc: Location) -> KeywordRestParameterNode + + alias deconstruct child_nodes + + # : (Array[Symbol]? keys) -> Hash[Symbol, untyped] + def deconstruct_keys: (Array[Symbol]? keys) -> Hash[Symbol, untyped] + + # See `Node#type`. + # -- + # : () -> :keyword_rest_parameter_node + def type: () -> :keyword_rest_parameter_node + + # See `Node.type`. + # -- + # : () -> :keyword_rest_parameter_node + def self.type: () -> :keyword_rest_parameter_node + + # : () -> String + def inspect: () -> String + + # :category: Flags + # a parameter name that has been repeated in the method signature + # -- + # : () -> bool + def repeated_parameter?: () -> bool + + # :call-seq: + # name -> Symbol | nil + # + # Returns the `name` attribute. + # -- + # : () -> Symbol? + def name: () -> Symbol? + + # :category: Locations + # :call-seq: + # name_loc -> Location | nil + # + # Returns the Location represented by `name_loc`. + # -- + # : () -> Location? + def name_loc: () -> Location? + + # :category: Repository + # Save the name_loc location using the given saved source so that + # it can be retrieved later. + # -- + # : (_Repository repository) -> Relocation::Entry? + def save_name_loc: (_Repository repository) -> Relocation::Entry? + + # :category: Locations + # :call-seq: + # operator_loc -> Location + # + # Returns the Location represented by `operator_loc`. + # -- + # : () -> Location + def operator_loc: () -> Location + + # :category: Repository + # Save the operator_loc location using the given saved source so that + # it can be retrieved later. + # -- + # : (_Repository repository) -> Relocation::Entry + def save_operator_loc: (_Repository repository) -> Relocation::Entry + + # :call-seq: + # operator -> String + # + # Slice the location of operator_loc from the source. + # -- + # : () -> String + def operator: () -> String + + # : (untyped other) -> boolish + def ===: (untyped other) -> boolish + end + + # Represents using a lambda literal (not the lambda method call). + # + # ->(value) { value * 2 } + # ^^^^^^^^^^^^^^^^^^^^^^^ + class LambdaNode < Node + @body: (StatementsNode | BeginNode)? + + @parameters: (BlockParametersNode | NumberedParametersNode | ItParametersNode)? + + @closing_loc: Location + + @opening_loc: Location + + @operator_loc: Location + + @locals: Array[Symbol] + + # Initialize a new LambdaNode node. + # -- + # : (Source source, Integer node_id, Location location, Integer flags, Array[Symbol] locals, Location operator_loc, Location opening_loc, Location closing_loc, (BlockParametersNode | NumberedParametersNode | ItParametersNode)? parameters, (StatementsNode | BeginNode)? body) -> 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)? parameters, (StatementsNode | BeginNode)? body) -> void + + # See Node.accept. + # -- + # : (_Visitor visitor) -> untyped + def accept: (_Visitor visitor) -> untyped + + # See Node.child_nodes. + # -- + # : () -> Array[node?] + def child_nodes: () -> Array[node?] + + # See Node.each_child_node. + # -- + # : () -> Enumerator[node, void] + # : () { (node) -> void } -> void + def each_child_node: () -> Enumerator[node, void] + | () { (node) -> void } -> void + + # See Node.compact_child_nodes. + # -- + # : () -> Array[node] + def compact_child_nodes: () -> Array[node] + + # See Node.comment_targets. + # -- + # : () -> Array[node | Location] + def comment_targets: () -> Array[node | Location] + + # :call-seq: + # copy(**fields) -> LambdaNode + # + # Creates a copy of self with the given fields, using self as the template. + # -- + # : (?node_id: Integer, ?location: Location, ?flags: Integer, ?locals: Array[Symbol], ?operator_loc: Location, ?opening_loc: Location, ?closing_loc: Location, ?parameters: (BlockParametersNode | NumberedParametersNode | ItParametersNode)?, ?body: (StatementsNode | BeginNode)?) -> LambdaNode + 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)?, ?body: (StatementsNode | BeginNode)?) -> LambdaNode + + alias deconstruct child_nodes + + # : (Array[Symbol]? keys) -> Hash[Symbol, untyped] + def deconstruct_keys: (Array[Symbol]? keys) -> Hash[Symbol, untyped] + + # See `Node#type`. + # -- + # : () -> :lambda_node + def type: () -> :lambda_node + + # See `Node.type`. + # -- + # : () -> :lambda_node + def self.type: () -> :lambda_node + + # : () -> String + def inspect: () -> String + + # :call-seq: + # locals -> Array[Symbol] + # + # Returns the `locals` attribute. + # -- + # : () -> Array[Symbol] + def locals: () -> Array[Symbol] + + # :category: Locations + # :call-seq: + # operator_loc -> Location + # + # Returns the Location represented by `operator_loc`. + # -- + # : () -> Location + def operator_loc: () -> Location + + # :category: Repository + # Save the operator_loc location using the given saved source so that + # it can be retrieved later. + # -- + # : (_Repository repository) -> Relocation::Entry + def save_operator_loc: (_Repository repository) -> Relocation::Entry + + # :category: Locations + # :call-seq: + # opening_loc -> Location + # + # Returns the Location represented by `opening_loc`. + # -- + # : () -> Location + def opening_loc: () -> Location + + # :category: Repository + # Save the opening_loc location using the given saved source so that + # it can be retrieved later. + # -- + # : (_Repository repository) -> Relocation::Entry + def save_opening_loc: (_Repository repository) -> Relocation::Entry + + # :category: Locations + # :call-seq: + # closing_loc -> Location + # + # Returns the Location represented by `closing_loc`. + # -- + # : () -> Location + def closing_loc: () -> Location + + # :category: Repository + # Save the closing_loc location using the given saved source so that + # it can be retrieved later. + # -- + # : (_Repository repository) -> Relocation::Entry + def save_closing_loc: (_Repository repository) -> Relocation::Entry + + # :call-seq: + # parameters -> BlockParametersNode | NumberedParametersNode | ItParametersNode | nil + # + # Returns the `parameters` attribute. + # -- + # : () -> (BlockParametersNode | NumberedParametersNode | ItParametersNode)? + def parameters: () -> (BlockParametersNode | NumberedParametersNode | ItParametersNode)? + + # :call-seq: + # body -> StatementsNode | BeginNode | nil + # + # Returns the `body` attribute. + # -- + # : () -> (StatementsNode | BeginNode)? + def body: () -> (StatementsNode | BeginNode)? + + # :call-seq: + # operator -> String + # + # Slice the location of operator_loc from the source. + # -- + # : () -> String + def operator: () -> String + + # :call-seq: + # opening -> String + # + # Slice the location of opening_loc from the source. + # -- + # : () -> String + def opening: () -> String + + # :call-seq: + # closing -> String + # + # Slice the location of closing_loc from the source. + # -- + # : () -> String + def closing: () -> String + + # : (untyped other) -> boolish + def ===: (untyped other) -> boolish + end + + # Represents the use of the `&&=` operator for assignment to a local variable. + # + # target &&= value + # ^^^^^^^^^^^^^^^^ + class LocalVariableAndWriteNode < Node + @depth: Integer + + @name: Symbol + + @value: Prism::node + + @operator_loc: Location + + @name_loc: Location + + # Initialize a new LocalVariableAndWriteNode node. + # -- + # : (Source source, Integer node_id, Location location, Integer flags, Location name_loc, Location operator_loc, Prism::node value, Symbol name, Integer depth) -> 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 + + # See Node.accept. + # -- + # : (_Visitor visitor) -> untyped + def accept: (_Visitor visitor) -> untyped + + # See Node.child_nodes. + # -- + # : () -> Array[node?] + def child_nodes: () -> Array[node?] + + # See Node.each_child_node. + # -- + # : () -> Enumerator[node, void] + # : () { (node) -> void } -> void + def each_child_node: () -> Enumerator[node, void] + | () { (node) -> void } -> void + + # See Node.compact_child_nodes. + # -- + # : () -> Array[node] + def compact_child_nodes: () -> Array[node] + + # See Node.comment_targets. + # -- + # : () -> Array[node | Location] + def comment_targets: () -> Array[node | Location] + + # :call-seq: + # copy(**fields) -> LocalVariableAndWriteNode + # + # Creates a copy of self with the given fields, using self as the template. + # -- + # : (?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: Integer, ?location: Location, ?flags: Integer, ?name_loc: Location, ?operator_loc: Location, ?value: Prism::node, ?name: Symbol, ?depth: Integer) -> LocalVariableAndWriteNode + + alias deconstruct child_nodes + + # : (Array[Symbol]? keys) -> Hash[Symbol, untyped] + def deconstruct_keys: (Array[Symbol]? keys) -> Hash[Symbol, untyped] + + # See `Node#type`. + # -- + # : () -> :local_variable_and_write_node + def type: () -> :local_variable_and_write_node + + # See `Node.type`. + # -- + # : () -> :local_variable_and_write_node + def self.type: () -> :local_variable_and_write_node + + # : () -> String + def inspect: () -> String + + # :category: Locations + # :call-seq: + # name_loc -> Location + # + # Returns the Location represented by `name_loc`. + # -- + # : () -> Location + def name_loc: () -> Location + + # :category: Repository + # Save the name_loc location using the given saved source so that + # it can be retrieved later. + # -- + # : (_Repository repository) -> Relocation::Entry + def save_name_loc: (_Repository repository) -> Relocation::Entry + + # :category: Locations + # :call-seq: + # operator_loc -> Location + # + # Returns the Location represented by `operator_loc`. + # -- + # : () -> Location + def operator_loc: () -> Location + + # :category: Repository + # Save the operator_loc location using the given saved source so that + # it can be retrieved later. + # -- + # : (_Repository repository) -> Relocation::Entry + def save_operator_loc: (_Repository repository) -> Relocation::Entry + + # :call-seq: + # value -> Node + # + # Returns the `value` attribute. + # -- + # : () -> Prism::node + def value: () -> Prism::node + + # :call-seq: + # name -> Symbol + # + # Returns the `name` attribute. + # -- + # : () -> Symbol + def name: () -> Symbol + + # :call-seq: + # depth -> Integer + # + # Returns the `depth` attribute. + # -- + # : () -> Integer + def depth: () -> Integer + + # :call-seq: + # operator -> String + # + # Slice the location of operator_loc from the source. + # -- + # : () -> String + def operator: () -> String + + # : (untyped other) -> boolish + def ===: (untyped other) -> boolish + end + + # Represents assigning to a local variable using an operator that isn't `=`. + # + # target += value + # ^^^^^^^^^^^^^^^ + class LocalVariableOperatorWriteNode < Node + @depth: Integer + + @binary_operator: Symbol + + @name: Symbol + + @value: Prism::node + + @binary_operator_loc: Location + + @name_loc: Location + + # Initialize a new LocalVariableOperatorWriteNode node. + # -- + # : (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 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 + + # See Node.accept. + # -- + # : (_Visitor visitor) -> untyped + def accept: (_Visitor visitor) -> untyped + + # See Node.child_nodes. + # -- + # : () -> Array[node?] + def child_nodes: () -> Array[node?] + + # See Node.each_child_node. + # -- + # : () -> Enumerator[node, void] + # : () { (node) -> void } -> void + def each_child_node: () -> Enumerator[node, void] + | () { (node) -> void } -> void + + # See Node.compact_child_nodes. + # -- + # : () -> Array[node] + def compact_child_nodes: () -> Array[node] + + # See Node.comment_targets. + # -- + # : () -> Array[node | Location] + def comment_targets: () -> Array[node | Location] + + # :call-seq: + # copy(**fields) -> LocalVariableOperatorWriteNode + # + # Creates a copy of self with the given fields, using self as the template. + # -- + # : (?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: Integer, ?location: Location, ?flags: Integer, ?name_loc: Location, ?binary_operator_loc: Location, ?value: Prism::node, ?name: Symbol, ?binary_operator: Symbol, ?depth: Integer) -> LocalVariableOperatorWriteNode + + alias deconstruct child_nodes + + # : (Array[Symbol]? keys) -> Hash[Symbol, untyped] + def deconstruct_keys: (Array[Symbol]? keys) -> Hash[Symbol, untyped] + + # See `Node#type`. + # -- + # : () -> :local_variable_operator_write_node + def type: () -> :local_variable_operator_write_node + + # See `Node.type`. + # -- + # : () -> :local_variable_operator_write_node + def self.type: () -> :local_variable_operator_write_node + + # : () -> String + def inspect: () -> String + + # :category: Locations + # :call-seq: + # name_loc -> Location + # + # Returns the Location represented by `name_loc`. + # -- + # : () -> Location + def name_loc: () -> Location + + # :category: Repository + # Save the name_loc location using the given saved source so that + # it can be retrieved later. + # -- + # : (_Repository repository) -> Relocation::Entry + def save_name_loc: (_Repository repository) -> Relocation::Entry + + # :category: Locations + # :call-seq: + # binary_operator_loc -> Location + # + # Returns the Location represented by `binary_operator_loc`. + # -- + # : () -> Location + def binary_operator_loc: () -> Location + + # :category: Repository + # Save the binary_operator_loc location using the given saved source so that + # it can be retrieved later. + # -- + # : (_Repository repository) -> Relocation::Entry + def save_binary_operator_loc: (_Repository repository) -> Relocation::Entry + + # :call-seq: + # value -> Node + # + # Returns the `value` attribute. + # -- + # : () -> Prism::node + def value: () -> Prism::node + + # :call-seq: + # name -> Symbol + # + # Returns the `name` attribute. + # -- + # : () -> Symbol + def name: () -> Symbol + + # :call-seq: + # binary_operator -> Symbol + # + # Returns the `binary_operator` attribute. + # -- + # : () -> Symbol + def binary_operator: () -> Symbol + + # :call-seq: + # depth -> Integer + # + # Returns the `depth` attribute. + # -- + # : () -> Integer + def depth: () -> Integer + + # : (untyped other) -> boolish + def ===: (untyped other) -> boolish + end + + # Represents the use of the `||=` operator for assignment to a local variable. + # + # target ||= value + # ^^^^^^^^^^^^^^^^ + class LocalVariableOrWriteNode < Node + @depth: Integer + + @name: Symbol + + @value: Prism::node + + @operator_loc: Location + + @name_loc: Location + + # Initialize a new LocalVariableOrWriteNode node. + # -- + # : (Source source, Integer node_id, Location location, Integer flags, Location name_loc, Location operator_loc, Prism::node value, Symbol name, Integer depth) -> 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 + + # See Node.accept. + # -- + # : (_Visitor visitor) -> untyped + def accept: (_Visitor visitor) -> untyped + + # See Node.child_nodes. + # -- + # : () -> Array[node?] + def child_nodes: () -> Array[node?] + + # See Node.each_child_node. + # -- + # : () -> Enumerator[node, void] + # : () { (node) -> void } -> void + def each_child_node: () -> Enumerator[node, void] + | () { (node) -> void } -> void + + # See Node.compact_child_nodes. + # -- + # : () -> Array[node] + def compact_child_nodes: () -> Array[node] + + # See Node.comment_targets. + # -- + # : () -> Array[node | Location] + def comment_targets: () -> Array[node | Location] + + # :call-seq: + # copy(**fields) -> LocalVariableOrWriteNode + # + # Creates a copy of self with the given fields, using self as the template. + # -- + # : (?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: Integer, ?location: Location, ?flags: Integer, ?name_loc: Location, ?operator_loc: Location, ?value: Prism::node, ?name: Symbol, ?depth: Integer) -> LocalVariableOrWriteNode + + alias deconstruct child_nodes + + # : (Array[Symbol]? keys) -> Hash[Symbol, untyped] + def deconstruct_keys: (Array[Symbol]? keys) -> Hash[Symbol, untyped] + + # See `Node#type`. + # -- + # : () -> :local_variable_or_write_node + def type: () -> :local_variable_or_write_node + + # See `Node.type`. + # -- + # : () -> :local_variable_or_write_node + def self.type: () -> :local_variable_or_write_node + + # : () -> String + def inspect: () -> String + + # :category: Locations + # :call-seq: + # name_loc -> Location + # + # Returns the Location represented by `name_loc`. + # -- + # : () -> Location + def name_loc: () -> Location + + # :category: Repository + # Save the name_loc location using the given saved source so that + # it can be retrieved later. + # -- + # : (_Repository repository) -> Relocation::Entry + def save_name_loc: (_Repository repository) -> Relocation::Entry + + # :category: Locations + # :call-seq: + # operator_loc -> Location + # + # Returns the Location represented by `operator_loc`. + # -- + # : () -> Location + def operator_loc: () -> Location + + # :category: Repository + # Save the operator_loc location using the given saved source so that + # it can be retrieved later. + # -- + # : (_Repository repository) -> Relocation::Entry + def save_operator_loc: (_Repository repository) -> Relocation::Entry + + # :call-seq: + # value -> Node + # + # Returns the `value` attribute. + # -- + # : () -> Prism::node + def value: () -> Prism::node + + # :call-seq: + # name -> Symbol + # + # Returns the `name` attribute. + # -- + # : () -> Symbol + def name: () -> Symbol + + # :call-seq: + # depth -> Integer + # + # Returns the `depth` attribute. + # -- + # : () -> Integer + def depth: () -> Integer + + # :call-seq: + # operator -> String + # + # Slice the location of operator_loc from the source. + # -- + # : () -> String + def operator: () -> String + + # : (untyped other) -> boolish + def ===: (untyped other) -> boolish + 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 + @depth: Integer + + @name: Symbol + + # Initialize a new LocalVariableReadNode node. + # -- + # : (Source source, Integer node_id, Location location, Integer flags, Symbol name, Integer depth) -> void + def initialize: (Source source, Integer node_id, Location location, Integer flags, Symbol name, Integer depth) -> void + + # See Node.accept. + # -- + # : (_Visitor visitor) -> untyped + def accept: (_Visitor visitor) -> untyped + + # See Node.child_nodes. + # -- + # : () -> Array[node?] + def child_nodes: () -> Array[node?] + + # See Node.each_child_node. + # -- + # : () -> Enumerator[node, void] + # : () { (node) -> void } -> void + def each_child_node: () -> Enumerator[node, void] + | () { (node) -> void } -> void + + # See Node.compact_child_nodes. + # -- + # : () -> Array[node] + def compact_child_nodes: () -> Array[node] + + # See Node.comment_targets. + # -- + # : () -> Array[node | Location] + def comment_targets: () -> Array[node | Location] + + # :call-seq: + # copy(**fields) -> LocalVariableReadNode + # + # Creates a copy of self with the given fields, using self as the template. + # -- + # : (?node_id: Integer, ?location: Location, ?flags: Integer, ?name: Symbol, ?depth: Integer) -> LocalVariableReadNode + def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?name: Symbol, ?depth: Integer) -> LocalVariableReadNode + + alias deconstruct child_nodes + + # : (Array[Symbol]? keys) -> Hash[Symbol, untyped] + def deconstruct_keys: (Array[Symbol]? keys) -> Hash[Symbol, untyped] + + # See `Node#type`. + # -- + # : () -> :local_variable_read_node + def type: () -> :local_variable_read_node + + # See `Node.type`. + # -- + # : () -> :local_variable_read_node + def self.type: () -> :local_variable_read_node + + # : () -> String + def inspect: () -> String + + # :call-seq: + # name -> Symbol + # + # 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` + # -- + # : () -> Symbol + def name: () -> Symbol + + # :call-seq: + # depth -> Integer + # + # 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). + # -- + # : () -> Integer + def depth: () -> Integer + + # : (untyped other) -> boolish + def ===: (untyped other) -> boolish + 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 + @depth: Integer + + @name: Symbol + + # Initialize a new LocalVariableTargetNode node. + # -- + # : (Source source, Integer node_id, Location location, Integer flags, Symbol name, Integer depth) -> void + def initialize: (Source source, Integer node_id, Location location, Integer flags, Symbol name, Integer depth) -> void + + # See Node.accept. + # -- + # : (_Visitor visitor) -> untyped + def accept: (_Visitor visitor) -> untyped + + # See Node.child_nodes. + # -- + # : () -> Array[node?] + def child_nodes: () -> Array[node?] + + # See Node.each_child_node. + # -- + # : () -> Enumerator[node, void] + # : () { (node) -> void } -> void + def each_child_node: () -> Enumerator[node, void] + | () { (node) -> void } -> void + + # See Node.compact_child_nodes. + # -- + # : () -> Array[node] + def compact_child_nodes: () -> Array[node] + + # See Node.comment_targets. + # -- + # : () -> Array[node | Location] + def comment_targets: () -> Array[node | Location] + + # :call-seq: + # copy(**fields) -> LocalVariableTargetNode + # + # Creates a copy of self with the given fields, using self as the template. + # -- + # : (?node_id: Integer, ?location: Location, ?flags: Integer, ?name: Symbol, ?depth: Integer) -> LocalVariableTargetNode + def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?name: Symbol, ?depth: Integer) -> LocalVariableTargetNode + + alias deconstruct child_nodes + + # : (Array[Symbol]? keys) -> Hash[Symbol, untyped] + def deconstruct_keys: (Array[Symbol]? keys) -> Hash[Symbol, untyped] + + # See `Node#type`. + # -- + # : () -> :local_variable_target_node + def type: () -> :local_variable_target_node + + # See `Node.type`. + # -- + # : () -> :local_variable_target_node + def self.type: () -> :local_variable_target_node + + # : () -> String + def inspect: () -> String + + # :call-seq: + # name -> Symbol + # + # Returns the `name` attribute. + # -- + # : () -> Symbol + def name: () -> Symbol + + # :call-seq: + # depth -> Integer + # + # Returns the `depth` attribute. + # -- + # : () -> Integer + def depth: () -> Integer + + # : (untyped other) -> boolish + def ===: (untyped other) -> boolish + end + + # Represents writing to a local variable. + # + # foo = 1 + # ^^^^^^^ + class LocalVariableWriteNode < Node + @operator_loc: Location + + @value: Prism::node + + @name_loc: Location + + @depth: Integer + + @name: Symbol + + # Initialize a new LocalVariableWriteNode node. + # -- + # : (Source source, Integer node_id, Location location, Integer flags, Symbol name, Integer depth, Location name_loc, Prism::node value, Location operator_loc) -> 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 + + # See Node.accept. + # -- + # : (_Visitor visitor) -> untyped + def accept: (_Visitor visitor) -> untyped + + # See Node.child_nodes. + # -- + # : () -> Array[node?] + def child_nodes: () -> Array[node?] + + # See Node.each_child_node. + # -- + # : () -> Enumerator[node, void] + # : () { (node) -> void } -> void + def each_child_node: () -> Enumerator[node, void] + | () { (node) -> void } -> void + + # See Node.compact_child_nodes. + # -- + # : () -> Array[node] + def compact_child_nodes: () -> Array[node] + + # See Node.comment_targets. + # -- + # : () -> Array[node | Location] + def comment_targets: () -> Array[node | Location] + + # :call-seq: + # copy(**fields) -> LocalVariableWriteNode + # + # Creates a copy of self with the given fields, using self as the template. + # -- + # : (?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: Integer, ?location: Location, ?flags: Integer, ?name: Symbol, ?depth: Integer, ?name_loc: Location, ?value: Prism::node, ?operator_loc: Location) -> LocalVariableWriteNode + + alias deconstruct child_nodes + + # : (Array[Symbol]? keys) -> Hash[Symbol, untyped] + def deconstruct_keys: (Array[Symbol]? keys) -> Hash[Symbol, untyped] + + # See `Node#type`. + # -- + # : () -> :local_variable_write_node + def type: () -> :local_variable_write_node + + # See `Node.type`. + # -- + # : () -> :local_variable_write_node + def self.type: () -> :local_variable_write_node + + # : () -> String + def inspect: () -> String + + # :call-seq: + # name -> Symbol + # + # 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` + # -- + # : () -> Symbol + def name: () -> Symbol + + # :call-seq: + # depth -> Integer + # + # 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). + # -- + # : () -> Integer + def depth: () -> Integer + + # :category: Locations + # :call-seq: + # name_loc -> Location + # + # The Location of the variable name. + # + # foo = :bar + # ^^^ + # -- + # : () -> Location + def name_loc: () -> Location + + # :category: Repository + # Save the name_loc location using the given saved source so that + # it can be retrieved later. + # -- + # : (_Repository repository) -> Relocation::Entry + def save_name_loc: (_Repository repository) -> Relocation::Entry + + # :call-seq: + # value -> Node + # + # 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 + # -- + # : () -> Prism::node + def value: () -> Prism::node + + # :category: Locations + # :call-seq: + # operator_loc -> Location + # + # The Location of the `=` operator. + # + # x = :y + # ^ + # -- + # : () -> Location + def operator_loc: () -> Location + + # :category: Repository + # Save the operator_loc location using the given saved source so that + # it can be retrieved later. + # -- + # : (_Repository repository) -> Relocation::Entry + def save_operator_loc: (_Repository repository) -> Relocation::Entry + + # :call-seq: + # operator -> String + # + # Slice the location of operator_loc from the source. + # -- + # : () -> String + def operator: () -> String + + # : (untyped other) -> boolish + def ===: (untyped other) -> boolish + 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 + @unescaped: String + + @closing_loc: Location + + @content_loc: Location + + @opening_loc: Location + + # Initialize a new MatchLastLineNode node. + # -- + # : (Source source, Integer node_id, Location location, Integer flags, Location opening_loc, Location content_loc, Location closing_loc, String unescaped) -> void + def initialize: (Source source, Integer node_id, Location location, Integer flags, Location opening_loc, Location content_loc, Location closing_loc, String unescaped) -> void + + # See Node.accept. + # -- + # : (_Visitor visitor) -> untyped + def accept: (_Visitor visitor) -> untyped + + # See Node.child_nodes. + # -- + # : () -> Array[node?] + def child_nodes: () -> Array[node?] + + # See Node.each_child_node. + # -- + # : () -> Enumerator[node, void] + # : () { (node) -> void } -> void + def each_child_node: () -> Enumerator[node, void] + | () { (node) -> void } -> void + + # See Node.compact_child_nodes. + # -- + # : () -> Array[node] + def compact_child_nodes: () -> Array[node] + + # See Node.comment_targets. + # -- + # : () -> Array[node | Location] + def comment_targets: () -> Array[node | Location] + + # :call-seq: + # copy(**fields) -> MatchLastLineNode + # + # Creates a copy of self with the given fields, using self as the template. + # -- + # : (?node_id: Integer, ?location: Location, ?flags: Integer, ?opening_loc: Location, ?content_loc: Location, ?closing_loc: Location, ?unescaped: String) -> MatchLastLineNode + def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?opening_loc: Location, ?content_loc: Location, ?closing_loc: Location, ?unescaped: String) -> MatchLastLineNode + + alias deconstruct child_nodes + + # : (Array[Symbol]? keys) -> Hash[Symbol, untyped] + def deconstruct_keys: (Array[Symbol]? keys) -> Hash[Symbol, untyped] + + # See `Node#type`. + # -- + # : () -> :match_last_line_node + def type: () -> :match_last_line_node + + # See `Node.type`. + # -- + # : () -> :match_last_line_node + def self.type: () -> :match_last_line_node + + # : () -> String + def inspect: () -> String + + # :category: Flags + # i - ignores the case of characters when matching + # -- + # : () -> bool + def ignore_case?: () -> bool + + # :category: Flags + # x - ignores whitespace and allows comments in regular expressions + # -- + # : () -> bool + def extended?: () -> bool + + # :category: Flags + # m - allows $ to match the end of lines within strings + # -- + # : () -> bool + def multi_line?: () -> bool + + # :category: Flags + # o - only interpolates values into the regular expression once + # -- + # : () -> bool + def once?: () -> bool + + # :category: Flags + # e - forces the EUC-JP encoding + # -- + # : () -> bool + def euc_jp?: () -> bool + + # :category: Flags + # n - forces the ASCII-8BIT encoding + # -- + # : () -> bool + def ascii_8bit?: () -> bool + + # :category: Flags + # s - forces the Windows-31J encoding + # -- + # : () -> bool + def windows_31j?: () -> bool + + # :category: Flags + # u - forces the UTF-8 encoding + # -- + # : () -> bool + def utf_8?: () -> bool + + # :category: Flags + # internal bytes forced the encoding to UTF-8 + # -- + # : () -> bool + def forced_utf8_encoding?: () -> bool + + # :category: Flags + # internal bytes forced the encoding to binary + # -- + # : () -> bool + def forced_binary_encoding?: () -> bool + + # :category: Flags + # internal bytes forced the encoding to US-ASCII + # -- + # : () -> bool + def forced_us_ascii_encoding?: () -> bool + + # :category: Locations + # :call-seq: + # opening_loc -> Location + # + # Returns the Location represented by `opening_loc`. + # -- + # : () -> Location + def opening_loc: () -> Location + + # :category: Repository + # Save the opening_loc location using the given saved source so that + # it can be retrieved later. + # -- + # : (_Repository repository) -> Relocation::Entry + def save_opening_loc: (_Repository repository) -> Relocation::Entry + + # :category: Locations + # :call-seq: + # content_loc -> Location + # + # Returns the Location represented by `content_loc`. + # -- + # : () -> Location + def content_loc: () -> Location + + # :category: Repository + # Save the content_loc location using the given saved source so that + # it can be retrieved later. + # -- + # : (_Repository repository) -> Relocation::Entry + def save_content_loc: (_Repository repository) -> Relocation::Entry + + # :category: Locations + # :call-seq: + # closing_loc -> Location + # + # Returns the Location represented by `closing_loc`. + # -- + # : () -> Location + def closing_loc: () -> Location + + # :category: Repository + # Save the closing_loc location using the given saved source so that + # it can be retrieved later. + # -- + # : (_Repository repository) -> Relocation::Entry + def save_closing_loc: (_Repository repository) -> Relocation::Entry + + # :call-seq: + # unescaped -> String + # + # Returns the `unescaped` attribute. + # -- + # : () -> String + def unescaped: () -> String + + # :call-seq: + # opening -> String + # + # Slice the location of opening_loc from the source. + # -- + # : () -> String + def opening: () -> String + + # :call-seq: + # content -> String + # + # Slice the location of content_loc from the source. + # -- + # : () -> String + def content: () -> String + + # :call-seq: + # closing -> String + # + # Slice the location of closing_loc from the source. + # -- + # : () -> String + def closing: () -> String + + # : (untyped other) -> boolish + def ===: (untyped other) -> boolish + end + + # Represents the use of the modifier `in` operator. + # + # foo in bar + # ^^^^^^^^^^ + class MatchPredicateNode < Node + @operator_loc: Location + + @pattern: Prism::node + + @value: Prism::node + + # Initialize a new MatchPredicateNode node. + # -- + # : (Source source, Integer node_id, Location location, Integer flags, Prism::node value, Prism::node pattern, Location operator_loc) -> void + def initialize: (Source source, Integer node_id, Location location, Integer flags, Prism::node value, Prism::node pattern, Location operator_loc) -> void + + # See Node.accept. + # -- + # : (_Visitor visitor) -> untyped + def accept: (_Visitor visitor) -> untyped + + # See Node.child_nodes. + # -- + # : () -> Array[node?] + def child_nodes: () -> Array[node?] + + # See Node.each_child_node. + # -- + # : () -> Enumerator[node, void] + # : () { (node) -> void } -> void + def each_child_node: () -> Enumerator[node, void] + | () { (node) -> void } -> void + + # See Node.compact_child_nodes. + # -- + # : () -> Array[node] + def compact_child_nodes: () -> Array[node] + + # See Node.comment_targets. + # -- + # : () -> Array[node | Location] + def comment_targets: () -> Array[node | Location] + + # :call-seq: + # copy(**fields) -> MatchPredicateNode + # + # Creates a copy of self with the given fields, using self as the template. + # -- + # : (?node_id: Integer, ?location: Location, ?flags: Integer, ?value: Prism::node, ?pattern: Prism::node, ?operator_loc: Location) -> MatchPredicateNode + def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?value: Prism::node, ?pattern: Prism::node, ?operator_loc: Location) -> MatchPredicateNode + + alias deconstruct child_nodes + + # : (Array[Symbol]? keys) -> Hash[Symbol, untyped] + def deconstruct_keys: (Array[Symbol]? keys) -> Hash[Symbol, untyped] + + # See `Node#type`. + # -- + # : () -> :match_predicate_node + def type: () -> :match_predicate_node + + # See `Node.type`. + # -- + # : () -> :match_predicate_node + def self.type: () -> :match_predicate_node + + # : () -> String + def inspect: () -> String + + # :call-seq: + # value -> Node + # + # Returns the `value` attribute. + # -- + # : () -> Prism::node + def value: () -> Prism::node + + # :call-seq: + # pattern -> Node + # + # Returns the `pattern` attribute. + # -- + # : () -> Prism::node + def pattern: () -> Prism::node + + # :category: Locations + # :call-seq: + # operator_loc -> Location + # + # Returns the Location represented by `operator_loc`. + # -- + # : () -> Location + def operator_loc: () -> Location + + # :category: Repository + # Save the operator_loc location using the given saved source so that + # it can be retrieved later. + # -- + # : (_Repository repository) -> Relocation::Entry + def save_operator_loc: (_Repository repository) -> Relocation::Entry + + # :call-seq: + # operator -> String + # + # Slice the location of operator_loc from the source. + # -- + # : () -> String + def operator: () -> String + + # : (untyped other) -> boolish + def ===: (untyped other) -> boolish + end + + # Represents the use of the `=>` operator. + # + # foo => bar + # ^^^^^^^^^^ + class MatchRequiredNode < Node + @operator_loc: Location + + @pattern: Prism::node + + @value: Prism::node + + # Initialize a new MatchRequiredNode node. + # -- + # : (Source source, Integer node_id, Location location, Integer flags, Prism::node value, Prism::node pattern, Location operator_loc) -> void + def initialize: (Source source, Integer node_id, Location location, Integer flags, Prism::node value, Prism::node pattern, Location operator_loc) -> void + + # See Node.accept. + # -- + # : (_Visitor visitor) -> untyped + def accept: (_Visitor visitor) -> untyped + + # See Node.child_nodes. + # -- + # : () -> Array[node?] + def child_nodes: () -> Array[node?] + + # See Node.each_child_node. + # -- + # : () -> Enumerator[node, void] + # : () { (node) -> void } -> void + def each_child_node: () -> Enumerator[node, void] + | () { (node) -> void } -> void + + # See Node.compact_child_nodes. + # -- + # : () -> Array[node] + def compact_child_nodes: () -> Array[node] + + # See Node.comment_targets. + # -- + # : () -> Array[node | Location] + def comment_targets: () -> Array[node | Location] + + # :call-seq: + # copy(**fields) -> MatchRequiredNode + # + # Creates a copy of self with the given fields, using self as the template. + # -- + # : (?node_id: Integer, ?location: Location, ?flags: Integer, ?value: Prism::node, ?pattern: Prism::node, ?operator_loc: Location) -> MatchRequiredNode + def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?value: Prism::node, ?pattern: Prism::node, ?operator_loc: Location) -> MatchRequiredNode + + alias deconstruct child_nodes + + # : (Array[Symbol]? keys) -> Hash[Symbol, untyped] + def deconstruct_keys: (Array[Symbol]? keys) -> Hash[Symbol, untyped] + + # See `Node#type`. + # -- + # : () -> :match_required_node + def type: () -> :match_required_node + + # See `Node.type`. + # -- + # : () -> :match_required_node + def self.type: () -> :match_required_node + + # : () -> String + def inspect: () -> String + + # :call-seq: + # value -> Node + # + # Represents the left-hand side of the operator. + # + # foo => bar + # ^^^ + # -- + # : () -> Prism::node + def value: () -> Prism::node + + # :call-seq: + # pattern -> Node + # + # 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 + # -- + # : () -> Prism::node + def pattern: () -> Prism::node + + # :category: Locations + # :call-seq: + # operator_loc -> Location + # + # The Location of the operator. + # + # foo => bar + # ^^ + # -- + # : () -> Location + def operator_loc: () -> Location + + # :category: Repository + # Save the operator_loc location using the given saved source so that + # it can be retrieved later. + # -- + # : (_Repository repository) -> Relocation::Entry + def save_operator_loc: (_Repository repository) -> Relocation::Entry + + # :call-seq: + # operator -> String + # + # Slice the location of operator_loc from the source. + # -- + # : () -> String + def operator: () -> String + + # : (untyped other) -> boolish + def ===: (untyped other) -> boolish + end + + # Represents writing local variables using a regular expression match with named capture groups. + # + # /(?bar)/ =~ baz + # ^^^^^^^^^^^^^^^^^^^^ + class MatchWriteNode < Node + @targets: Array[LocalVariableTargetNode] + + @call: CallNode + + # Initialize a new MatchWriteNode node. + # -- + # : (Source source, Integer node_id, Location location, Integer flags, CallNode call, Array[LocalVariableTargetNode] targets) -> void + def initialize: (Source source, Integer node_id, Location location, Integer flags, CallNode call, Array[LocalVariableTargetNode] targets) -> void + + # See Node.accept. + # -- + # : (_Visitor visitor) -> untyped + def accept: (_Visitor visitor) -> untyped + + # See Node.child_nodes. + # -- + # : () -> Array[node?] + def child_nodes: () -> Array[node?] + + # See Node.each_child_node. + # -- + # : () -> Enumerator[node, void] + # : () { (node) -> void } -> void + def each_child_node: () -> Enumerator[node, void] + | () { (node) -> void } -> void + + # See Node.compact_child_nodes. + # -- + # : () -> Array[node] + def compact_child_nodes: () -> Array[node] + + # See Node.comment_targets. + # -- + # : () -> Array[node | Location] + def comment_targets: () -> Array[node | Location] + + # :call-seq: + # copy(**fields) -> MatchWriteNode + # + # Creates a copy of self with the given fields, using self as the template. + # -- + # : (?node_id: Integer, ?location: Location, ?flags: Integer, ?call: CallNode, ?targets: Array[LocalVariableTargetNode]) -> MatchWriteNode + def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?call: CallNode, ?targets: Array[LocalVariableTargetNode]) -> MatchWriteNode + + alias deconstruct child_nodes + + # : (Array[Symbol]? keys) -> Hash[Symbol, untyped] + def deconstruct_keys: (Array[Symbol]? keys) -> Hash[Symbol, untyped] + + # See `Node#type`. + # -- + # : () -> :match_write_node + def type: () -> :match_write_node + + # See `Node.type`. + # -- + # : () -> :match_write_node + def self.type: () -> :match_write_node + + # : () -> String + def inspect: () -> String + + # :call-seq: + # call -> CallNode + # + # Returns the `call` attribute. + # -- + # : () -> CallNode + def call: () -> CallNode + + # :call-seq: + # targets -> Array[LocalVariableTargetNode] + # + # Returns the `targets` attribute. + # -- + # : () -> Array[LocalVariableTargetNode] + def targets: () -> Array[LocalVariableTargetNode] + + # : (untyped other) -> boolish + def ===: (untyped other) -> boolish + end + + # Represents a node that is missing from the source and results in a syntax error. + class MissingNode < Node + # Initialize a new MissingNode node. + # -- + # : (Source source, Integer node_id, Location location, Integer flags, ) -> void + def initialize: (Source source, Integer node_id, Location location, Integer flags) -> void + + # See Node.accept. + # -- + # : (_Visitor visitor) -> untyped + def accept: (_Visitor visitor) -> untyped + + # See Node.child_nodes. + # -- + # : () -> Array[node?] + def child_nodes: () -> Array[node?] + + # See Node.each_child_node. + # -- + # : () -> Enumerator[node, void] + # : () { (node) -> void } -> void + def each_child_node: () -> Enumerator[node, void] + | () { (node) -> void } -> void + + # See Node.compact_child_nodes. + # -- + # : () -> Array[node] + def compact_child_nodes: () -> Array[node] + + # See Node.comment_targets. + # -- + # : () -> Array[node | Location] + def comment_targets: () -> Array[node | Location] + + # :call-seq: + # copy(**fields) -> MissingNode + # + # Creates a copy of self with the given fields, using self as the template. + # -- + # : (?node_id: Integer, ?location: Location, ?flags: Integer, ) -> MissingNode + def copy: (?node_id: Integer, ?location: Location, ?flags: Integer) -> MissingNode + + alias deconstruct child_nodes + + # : (Array[Symbol]? keys) -> Hash[Symbol, untyped] + def deconstruct_keys: (Array[Symbol]? keys) -> Hash[Symbol, untyped] + + # See `Node#type`. + # -- + # : () -> :missing_node + def type: () -> :missing_node + + # See `Node.type`. + # -- + # : () -> :missing_node + def self.type: () -> :missing_node + + # : () -> String + def inspect: () -> String + + # : (untyped other) -> boolish + def ===: (untyped other) -> boolish + end + + # Represents a module declaration involving the `module` keyword. + # + # module Foo end + # ^^^^^^^^^^^^^^ + class ModuleNode < Node + @name: Symbol + + @end_keyword_loc: Location + + @body: (StatementsNode | BeginNode)? + + @constant_path: ConstantReadNode | ConstantPathNode | MissingNode + + @module_keyword_loc: Location + + @locals: Array[Symbol] + + # Initialize a new ModuleNode node. + # -- + # : (Source source, Integer node_id, Location location, Integer flags, Array[Symbol] locals, Location module_keyword_loc, (ConstantReadNode | ConstantPathNode | MissingNode) constant_path, (StatementsNode | BeginNode)? body, Location end_keyword_loc, Symbol name) -> 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)? body, Location end_keyword_loc, Symbol name) -> void + + # See Node.accept. + # -- + # : (_Visitor visitor) -> untyped + def accept: (_Visitor visitor) -> untyped + + # See Node.child_nodes. + # -- + # : () -> Array[node?] + def child_nodes: () -> Array[node?] + + # See Node.each_child_node. + # -- + # : () -> Enumerator[node, void] + # : () { (node) -> void } -> void + def each_child_node: () -> Enumerator[node, void] + | () { (node) -> void } -> void + + # See Node.compact_child_nodes. + # -- + # : () -> Array[node] + def compact_child_nodes: () -> Array[node] + + # See Node.comment_targets. + # -- + # : () -> Array[node | Location] + def comment_targets: () -> Array[node | Location] + + # :call-seq: + # copy(**fields) -> ModuleNode + # + # Creates a copy of self with the given fields, using self as the template. + # -- + # : (?node_id: Integer, ?location: Location, ?flags: Integer, ?locals: Array[Symbol], ?module_keyword_loc: Location, ?constant_path: (ConstantReadNode | ConstantPathNode | MissingNode), ?body: (StatementsNode | BeginNode)?, ?end_keyword_loc: Location, ?name: Symbol) -> ModuleNode + def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?locals: Array[Symbol], ?module_keyword_loc: Location, ?constant_path: ConstantReadNode | ConstantPathNode | MissingNode, ?body: (StatementsNode | BeginNode)?, ?end_keyword_loc: Location, ?name: Symbol) -> ModuleNode + + alias deconstruct child_nodes + + # : (Array[Symbol]? keys) -> Hash[Symbol, untyped] + def deconstruct_keys: (Array[Symbol]? keys) -> Hash[Symbol, untyped] + + # See `Node#type`. + # -- + # : () -> :module_node + def type: () -> :module_node + + # See `Node.type`. + # -- + # : () -> :module_node + def self.type: () -> :module_node + + # : () -> String + def inspect: () -> String + + # :call-seq: + # locals -> Array[Symbol] + # + # Returns the `locals` attribute. + # -- + # : () -> Array[Symbol] + def locals: () -> Array[Symbol] + + # :category: Locations + # :call-seq: + # module_keyword_loc -> Location + # + # Returns the Location represented by `module_keyword_loc`. + # -- + # : () -> Location + def module_keyword_loc: () -> Location + + # :category: Repository + # Save the module_keyword_loc location using the given saved source so that + # it can be retrieved later. + # -- + # : (_Repository repository) -> Relocation::Entry + def save_module_keyword_loc: (_Repository repository) -> Relocation::Entry + + # :call-seq: + # constant_path -> ConstantReadNode | ConstantPathNode | MissingNode + # + # Returns the `constant_path` attribute. + # -- + # : () -> (ConstantReadNode | ConstantPathNode | MissingNode) + def constant_path: () -> (ConstantReadNode | ConstantPathNode | MissingNode) + + # :call-seq: + # body -> StatementsNode | BeginNode | nil + # + # Returns the `body` attribute. + # -- + # : () -> (StatementsNode | BeginNode)? + def body: () -> (StatementsNode | BeginNode)? + + # :category: Locations + # :call-seq: + # end_keyword_loc -> Location + # + # Returns the Location represented by `end_keyword_loc`. + # -- + # : () -> Location + def end_keyword_loc: () -> Location + + # :category: Repository + # Save the end_keyword_loc location using the given saved source so that + # it can be retrieved later. + # -- + # : (_Repository repository) -> Relocation::Entry + def save_end_keyword_loc: (_Repository repository) -> Relocation::Entry + + # :call-seq: + # name -> Symbol + # + # Returns the `name` attribute. + # -- + # : () -> Symbol + def name: () -> Symbol + + # :call-seq: + # module_keyword -> String + # + # Slice the location of module_keyword_loc from the source. + # -- + # : () -> String + def module_keyword: () -> String + + # :call-seq: + # end_keyword -> String + # + # Slice the location of end_keyword_loc from the source. + # -- + # : () -> String + def end_keyword: () -> String + + # : (untyped other) -> boolish + def ===: (untyped other) -> boolish + 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 + @rparen_loc: Location? + + @lparen_loc: Location? + + @rights: Array[LocalVariableTargetNode | InstanceVariableTargetNode | ClassVariableTargetNode | GlobalVariableTargetNode | ConstantTargetNode | ConstantPathTargetNode | CallTargetNode | IndexTargetNode | MultiTargetNode | RequiredParameterNode | BackReferenceReadNode | NumberedReferenceReadNode] + + @rest: (ImplicitRestNode | SplatNode)? + + @lefts: Array[LocalVariableTargetNode | InstanceVariableTargetNode | ClassVariableTargetNode | GlobalVariableTargetNode | ConstantTargetNode | ConstantPathTargetNode | CallTargetNode | IndexTargetNode | MultiTargetNode | RequiredParameterNode | BackReferenceReadNode | NumberedReferenceReadNode] + + # Initialize a new MultiTargetNode node. + # -- + # : (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)? rest, Array[LocalVariableTargetNode | InstanceVariableTargetNode | ClassVariableTargetNode | GlobalVariableTargetNode | ConstantTargetNode | ConstantPathTargetNode | CallTargetNode | IndexTargetNode | MultiTargetNode | RequiredParameterNode | BackReferenceReadNode | NumberedReferenceReadNode] rights, Location? lparen_loc, Location? rparen_loc) -> 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)? rest, Array[LocalVariableTargetNode | InstanceVariableTargetNode | ClassVariableTargetNode | GlobalVariableTargetNode | ConstantTargetNode | ConstantPathTargetNode | CallTargetNode | IndexTargetNode | MultiTargetNode | RequiredParameterNode | BackReferenceReadNode | NumberedReferenceReadNode] rights, Location? lparen_loc, Location? rparen_loc) -> void + + # See Node.accept. + # -- + # : (_Visitor visitor) -> untyped + def accept: (_Visitor visitor) -> untyped + + # See Node.child_nodes. + # -- + # : () -> Array[node?] + def child_nodes: () -> Array[node?] + + # See Node.each_child_node. + # -- + # : () -> Enumerator[node, void] + # : () { (node) -> void } -> void + def each_child_node: () -> Enumerator[node, void] + | () { (node) -> void } -> void + + # See Node.compact_child_nodes. + # -- + # : () -> Array[node] + def compact_child_nodes: () -> Array[node] + + # See Node.comment_targets. + # -- + # : () -> Array[node | Location] + def comment_targets: () -> Array[node | Location] + + # :call-seq: + # copy(**fields) -> MultiTargetNode + # + # Creates a copy of self with the given fields, using self as the template. + # -- + # : (?node_id: Integer, ?location: Location, ?flags: Integer, ?lefts: Array[LocalVariableTargetNode | InstanceVariableTargetNode | ClassVariableTargetNode | GlobalVariableTargetNode | ConstantTargetNode | ConstantPathTargetNode | CallTargetNode | IndexTargetNode | MultiTargetNode | RequiredParameterNode | BackReferenceReadNode | NumberedReferenceReadNode], ?rest: (ImplicitRestNode | SplatNode)?, ?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: Integer, ?location: Location, ?flags: Integer, ?lefts: Array[LocalVariableTargetNode | InstanceVariableTargetNode | ClassVariableTargetNode | GlobalVariableTargetNode | ConstantTargetNode | ConstantPathTargetNode | CallTargetNode | IndexTargetNode | MultiTargetNode | RequiredParameterNode | BackReferenceReadNode | NumberedReferenceReadNode], ?rest: (ImplicitRestNode | SplatNode)?, ?rights: Array[LocalVariableTargetNode | InstanceVariableTargetNode | ClassVariableTargetNode | GlobalVariableTargetNode | ConstantTargetNode | ConstantPathTargetNode | CallTargetNode | IndexTargetNode | MultiTargetNode | RequiredParameterNode | BackReferenceReadNode | NumberedReferenceReadNode], ?lparen_loc: Location?, ?rparen_loc: Location?) -> MultiTargetNode + + alias deconstruct child_nodes + + # : (Array[Symbol]? keys) -> Hash[Symbol, untyped] + def deconstruct_keys: (Array[Symbol]? keys) -> Hash[Symbol, untyped] + + # See `Node#type`. + # -- + # : () -> :multi_target_node + def type: () -> :multi_target_node + + # See `Node.type`. + # -- + # : () -> :multi_target_node + def self.type: () -> :multi_target_node + + # : () -> String + def inspect: () -> String + + # :call-seq: + # lefts -> Array[LocalVariableTargetNode | InstanceVariableTargetNode | ClassVariableTargetNode | GlobalVariableTargetNode | ConstantTargetNode | ConstantPathTargetNode | CallTargetNode | IndexTargetNode | MultiTargetNode | RequiredParameterNode | BackReferenceReadNode | NumberedReferenceReadNode] + # + # 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 + # ^^^^ + # -- + # : () -> Array[LocalVariableTargetNode | InstanceVariableTargetNode | ClassVariableTargetNode | GlobalVariableTargetNode | ConstantTargetNode | ConstantPathTargetNode | CallTargetNode | IndexTargetNode | MultiTargetNode | RequiredParameterNode | BackReferenceReadNode | NumberedReferenceReadNode] + def lefts: () -> Array[LocalVariableTargetNode | InstanceVariableTargetNode | ClassVariableTargetNode | GlobalVariableTargetNode | ConstantTargetNode | ConstantPathTargetNode | CallTargetNode | IndexTargetNode | MultiTargetNode | RequiredParameterNode | BackReferenceReadNode | NumberedReferenceReadNode] + + # :call-seq: + # rest -> ImplicitRestNode | SplatNode | nil + # + # 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 + # ^ + # -- + # : () -> (ImplicitRestNode | SplatNode)? + def rest: () -> (ImplicitRestNode | SplatNode)? + + # :call-seq: + # rights -> Array[LocalVariableTargetNode | InstanceVariableTargetNode | ClassVariableTargetNode | GlobalVariableTargetNode | ConstantTargetNode | ConstantPathTargetNode | CallTargetNode | IndexTargetNode | MultiTargetNode | RequiredParameterNode | BackReferenceReadNode | NumberedReferenceReadNode] + # + # Represents the targets expressions after a splat node. + # + # a, (*, b, c) = 1, 2, 3, 4, 5 + # ^^^^ + # -- + # : () -> Array[LocalVariableTargetNode | InstanceVariableTargetNode | ClassVariableTargetNode | GlobalVariableTargetNode | ConstantTargetNode | ConstantPathTargetNode | CallTargetNode | IndexTargetNode | MultiTargetNode | RequiredParameterNode | BackReferenceReadNode | NumberedReferenceReadNode] + def rights: () -> Array[LocalVariableTargetNode | InstanceVariableTargetNode | ClassVariableTargetNode | GlobalVariableTargetNode | ConstantTargetNode | ConstantPathTargetNode | CallTargetNode | IndexTargetNode | MultiTargetNode | RequiredParameterNode | BackReferenceReadNode | NumberedReferenceReadNode] + + # :category: Locations + # :call-seq: + # lparen_loc -> Location | nil + # + # The Location of the opening parenthesis. + # + # a, (b, c) = 1, 2, 3 + # ^ + # -- + # : () -> Location? + def lparen_loc: () -> Location? + + # :category: Repository + # Save the lparen_loc location using the given saved source so that + # it can be retrieved later. + # -- + # : (_Repository repository) -> Relocation::Entry? + def save_lparen_loc: (_Repository repository) -> Relocation::Entry? + + # :category: Locations + # :call-seq: + # rparen_loc -> Location | nil + # + # The Location of the closing parenthesis. + # + # a, (b, c) = 1, 2, 3 + # ^ + # -- + # : () -> Location? + def rparen_loc: () -> Location? + + # :category: Repository + # Save the rparen_loc location using the given saved source so that + # it can be retrieved later. + # -- + # : (_Repository repository) -> Relocation::Entry? + def save_rparen_loc: (_Repository repository) -> Relocation::Entry? + + # :call-seq: + # lparen -> String | nil + # + # Slice the location of lparen_loc from the source. + # -- + # : () -> String? + def lparen: () -> String? + + # :call-seq: + # rparen -> String | nil + # + # Slice the location of rparen_loc from the source. + # -- + # : () -> String? + def rparen: () -> String? + + # : (untyped other) -> boolish + def ===: (untyped other) -> boolish + end + + # Represents a write to a multi-target expression. + # + # a, b, c = 1, 2, 3 + # ^^^^^^^^^^^^^^^^^ + class MultiWriteNode < Node + @value: Prism::node + + @operator_loc: Location + + @rparen_loc: Location? + + @lparen_loc: Location? + + @rights: Array[LocalVariableTargetNode | InstanceVariableTargetNode | ClassVariableTargetNode | GlobalVariableTargetNode | ConstantTargetNode | ConstantPathTargetNode | CallTargetNode | IndexTargetNode | MultiTargetNode | BackReferenceReadNode | NumberedReferenceReadNode] + + @rest: (ImplicitRestNode | SplatNode)? + + @lefts: Array[LocalVariableTargetNode | InstanceVariableTargetNode | ClassVariableTargetNode | GlobalVariableTargetNode | ConstantTargetNode | ConstantPathTargetNode | CallTargetNode | IndexTargetNode | MultiTargetNode | BackReferenceReadNode | NumberedReferenceReadNode] + + # Initialize a new MultiWriteNode node. + # -- + # : (Source source, Integer node_id, Location location, Integer flags, Array[LocalVariableTargetNode | InstanceVariableTargetNode | ClassVariableTargetNode | GlobalVariableTargetNode | ConstantTargetNode | ConstantPathTargetNode | CallTargetNode | IndexTargetNode | MultiTargetNode | BackReferenceReadNode | NumberedReferenceReadNode] lefts, (ImplicitRestNode | SplatNode)? 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 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)? 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 + + # See Node.accept. + # -- + # : (_Visitor visitor) -> untyped + def accept: (_Visitor visitor) -> untyped + + # See Node.child_nodes. + # -- + # : () -> Array[node?] + def child_nodes: () -> Array[node?] + + # See Node.each_child_node. + # -- + # : () -> Enumerator[node, void] + # : () { (node) -> void } -> void + def each_child_node: () -> Enumerator[node, void] + | () { (node) -> void } -> void + + # See Node.compact_child_nodes. + # -- + # : () -> Array[node] + def compact_child_nodes: () -> Array[node] + + # See Node.comment_targets. + # -- + # : () -> Array[node | Location] + def comment_targets: () -> Array[node | Location] + + # :call-seq: + # copy(**fields) -> MultiWriteNode + # + # Creates a copy of self with the given fields, using self as the template. + # -- + # : (?node_id: Integer, ?location: Location, ?flags: Integer, ?lefts: Array[LocalVariableTargetNode | InstanceVariableTargetNode | ClassVariableTargetNode | GlobalVariableTargetNode | ConstantTargetNode | ConstantPathTargetNode | CallTargetNode | IndexTargetNode | MultiTargetNode | BackReferenceReadNode | NumberedReferenceReadNode], ?rest: (ImplicitRestNode | SplatNode)?, ?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: Integer, ?location: Location, ?flags: Integer, ?lefts: Array[LocalVariableTargetNode | InstanceVariableTargetNode | ClassVariableTargetNode | GlobalVariableTargetNode | ConstantTargetNode | ConstantPathTargetNode | CallTargetNode | IndexTargetNode | MultiTargetNode | BackReferenceReadNode | NumberedReferenceReadNode], ?rest: (ImplicitRestNode | SplatNode)?, ?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 + + alias deconstruct child_nodes + + # : (Array[Symbol]? keys) -> Hash[Symbol, untyped] + def deconstruct_keys: (Array[Symbol]? keys) -> Hash[Symbol, untyped] + + # See `Node#type`. + # -- + # : () -> :multi_write_node + def type: () -> :multi_write_node + + # See `Node.type`. + # -- + # : () -> :multi_write_node + def self.type: () -> :multi_write_node + + # : () -> String + def inspect: () -> String + + # :call-seq: + # lefts -> Array[LocalVariableTargetNode | InstanceVariableTargetNode | ClassVariableTargetNode | GlobalVariableTargetNode | ConstantTargetNode | ConstantPathTargetNode | CallTargetNode | IndexTargetNode | MultiTargetNode | BackReferenceReadNode | NumberedReferenceReadNode] + # + # 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 + # ^^^^^^^ + # -- + # : () -> Array[LocalVariableTargetNode | InstanceVariableTargetNode | ClassVariableTargetNode | GlobalVariableTargetNode | ConstantTargetNode | ConstantPathTargetNode | CallTargetNode | IndexTargetNode | MultiTargetNode | BackReferenceReadNode | NumberedReferenceReadNode] + def lefts: () -> Array[LocalVariableTargetNode | InstanceVariableTargetNode | ClassVariableTargetNode | GlobalVariableTargetNode | ConstantTargetNode | ConstantPathTargetNode | CallTargetNode | IndexTargetNode | MultiTargetNode | BackReferenceReadNode | NumberedReferenceReadNode] + + # :call-seq: + # rest -> ImplicitRestNode | SplatNode | nil + # + # 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 + # ^ + # -- + # : () -> (ImplicitRestNode | SplatNode)? + def rest: () -> (ImplicitRestNode | SplatNode)? + + # :call-seq: + # rights -> Array[LocalVariableTargetNode | InstanceVariableTargetNode | ClassVariableTargetNode | GlobalVariableTargetNode | ConstantTargetNode | ConstantPathTargetNode | CallTargetNode | IndexTargetNode | MultiTargetNode | BackReferenceReadNode | NumberedReferenceReadNode] + # + # Represents the targets expressions after a splat node. + # + # a, *, b, c = 1, 2, 3, 4, 5 + # ^^^^ + # -- + # : () -> Array[LocalVariableTargetNode | InstanceVariableTargetNode | ClassVariableTargetNode | GlobalVariableTargetNode | ConstantTargetNode | ConstantPathTargetNode | CallTargetNode | IndexTargetNode | MultiTargetNode | BackReferenceReadNode | NumberedReferenceReadNode] + def rights: () -> Array[LocalVariableTargetNode | InstanceVariableTargetNode | ClassVariableTargetNode | GlobalVariableTargetNode | ConstantTargetNode | ConstantPathTargetNode | CallTargetNode | IndexTargetNode | MultiTargetNode | BackReferenceReadNode | NumberedReferenceReadNode] + + # :category: Locations + # :call-seq: + # lparen_loc -> Location | nil + # + # The Location of the opening parenthesis. + # + # (a, b, c) = 1, 2, 3 + # ^ + # -- + # : () -> Location? + def lparen_loc: () -> Location? + + # :category: Repository + # Save the lparen_loc location using the given saved source so that + # it can be retrieved later. + # -- + # : (_Repository repository) -> Relocation::Entry? + def save_lparen_loc: (_Repository repository) -> Relocation::Entry? + + # :category: Locations + # :call-seq: + # rparen_loc -> Location | nil + # + # The Location of the closing parenthesis. + # + # (a, b, c) = 1, 2, 3 + # ^ + # -- + # : () -> Location? + def rparen_loc: () -> Location? + + # :category: Repository + # Save the rparen_loc location using the given saved source so that + # it can be retrieved later. + # -- + # : (_Repository repository) -> Relocation::Entry? + def save_rparen_loc: (_Repository repository) -> Relocation::Entry? + + # :category: Locations + # :call-seq: + # operator_loc -> Location + # + # The Location of the operator. + # + # a, b, c = 1, 2, 3 + # ^ + # -- + # : () -> Location + def operator_loc: () -> Location + + # :category: Repository + # Save the operator_loc location using the given saved source so that + # it can be retrieved later. + # -- + # : (_Repository repository) -> Relocation::Entry + def save_operator_loc: (_Repository repository) -> Relocation::Entry + + # :call-seq: + # value -> Node + # + # 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 + # ^^^^^^^ + # -- + # : () -> Prism::node + def value: () -> Prism::node + + # :call-seq: + # lparen -> String | nil + # + # Slice the location of lparen_loc from the source. + # -- + # : () -> String? + def lparen: () -> String? + + # :call-seq: + # rparen -> String | nil + # + # Slice the location of rparen_loc from the source. + # -- + # : () -> String? + def rparen: () -> String? + + # :call-seq: + # operator -> String + # + # Slice the location of operator_loc from the source. + # -- + # : () -> String + def operator: () -> String + + # : (untyped other) -> boolish + def ===: (untyped other) -> boolish + end + + # Represents the use of the `next` keyword. + # + # next 1 + # ^^^^^^ + class NextNode < Node + @keyword_loc: Location + + @arguments: ArgumentsNode? + + # Initialize a new NextNode node. + # -- + # : (Source source, Integer node_id, Location location, Integer flags, ArgumentsNode? arguments, Location keyword_loc) -> void + def initialize: (Source source, Integer node_id, Location location, Integer flags, ArgumentsNode? arguments, Location keyword_loc) -> void + + # See Node.accept. + # -- + # : (_Visitor visitor) -> untyped + def accept: (_Visitor visitor) -> untyped + + # See Node.child_nodes. + # -- + # : () -> Array[node?] + def child_nodes: () -> Array[node?] + + # See Node.each_child_node. + # -- + # : () -> Enumerator[node, void] + # : () { (node) -> void } -> void + def each_child_node: () -> Enumerator[node, void] + | () { (node) -> void } -> void + + # See Node.compact_child_nodes. + # -- + # : () -> Array[node] + def compact_child_nodes: () -> Array[node] + + # See Node.comment_targets. + # -- + # : () -> Array[node | Location] + def comment_targets: () -> Array[node | Location] + + # :call-seq: + # copy(**fields) -> NextNode + # + # Creates a copy of self with the given fields, using self as the template. + # -- + # : (?node_id: Integer, ?location: Location, ?flags: Integer, ?arguments: ArgumentsNode?, ?keyword_loc: Location) -> NextNode + def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?arguments: ArgumentsNode?, ?keyword_loc: Location) -> NextNode + + alias deconstruct child_nodes + + # : (Array[Symbol]? keys) -> Hash[Symbol, untyped] + def deconstruct_keys: (Array[Symbol]? keys) -> Hash[Symbol, untyped] + + # See `Node#type`. + # -- + # : () -> :next_node + def type: () -> :next_node + + # See `Node.type`. + # -- + # : () -> :next_node + def self.type: () -> :next_node + + # : () -> String + def inspect: () -> String + + # :call-seq: + # arguments -> ArgumentsNode | nil + # + # Returns the `arguments` attribute. + # -- + # : () -> ArgumentsNode? + def arguments: () -> ArgumentsNode? + + # :category: Locations + # :call-seq: + # keyword_loc -> Location + # + # Returns the Location represented by `keyword_loc`. + # -- + # : () -> Location + def keyword_loc: () -> Location + + # :category: Repository + # Save the keyword_loc location using the given saved source so that + # it can be retrieved later. + # -- + # : (_Repository repository) -> Relocation::Entry + def save_keyword_loc: (_Repository repository) -> Relocation::Entry + + # :call-seq: + # keyword -> String + # + # Slice the location of keyword_loc from the source. + # -- + # : () -> String + def keyword: () -> String + + # : (untyped other) -> boolish + def ===: (untyped other) -> boolish + end + + # Represents the use of the `nil` keyword. + # + # nil + # ^^^ + class NilNode < Node + # Initialize a new NilNode node. + # -- + # : (Source source, Integer node_id, Location location, Integer flags, ) -> void + def initialize: (Source source, Integer node_id, Location location, Integer flags) -> void + + # See Node.accept. + # -- + # : (_Visitor visitor) -> untyped + def accept: (_Visitor visitor) -> untyped + + # See Node.child_nodes. + # -- + # : () -> Array[node?] + def child_nodes: () -> Array[node?] + + # See Node.each_child_node. + # -- + # : () -> Enumerator[node, void] + # : () { (node) -> void } -> void + def each_child_node: () -> Enumerator[node, void] + | () { (node) -> void } -> void + + # See Node.compact_child_nodes. + # -- + # : () -> Array[node] + def compact_child_nodes: () -> Array[node] + + # See Node.comment_targets. + # -- + # : () -> Array[node | Location] + def comment_targets: () -> Array[node | Location] + + # :call-seq: + # copy(**fields) -> NilNode + # + # Creates a copy of self with the given fields, using self as the template. + # -- + # : (?node_id: Integer, ?location: Location, ?flags: Integer, ) -> NilNode + def copy: (?node_id: Integer, ?location: Location, ?flags: Integer) -> NilNode + + alias deconstruct child_nodes + + # : (Array[Symbol]? keys) -> Hash[Symbol, untyped] + def deconstruct_keys: (Array[Symbol]? keys) -> Hash[Symbol, untyped] + + # See `Node#type`. + # -- + # : () -> :nil_node + def type: () -> :nil_node + + # See `Node.type`. + # -- + # : () -> :nil_node + def self.type: () -> :nil_node + + # : () -> String + def inspect: () -> String + + # : (untyped other) -> boolish + def ===: (untyped other) -> boolish + end + + # Represents the use of `&nil` inside method arguments. + # + # def a(&nil) + # ^^^^ + # end + class NoBlockParameterNode < Node + @keyword_loc: Location + + @operator_loc: Location + + # Initialize a new NoBlockParameterNode node. + # -- + # : (Source source, Integer node_id, Location location, Integer flags, Location operator_loc, Location keyword_loc) -> void + def initialize: (Source source, Integer node_id, Location location, Integer flags, Location operator_loc, Location keyword_loc) -> void + + # See Node.accept. + # -- + # : (_Visitor visitor) -> untyped + def accept: (_Visitor visitor) -> untyped + + # See Node.child_nodes. + # -- + # : () -> Array[node?] + def child_nodes: () -> Array[node?] + + # See Node.each_child_node. + # -- + # : () -> Enumerator[node, void] + # : () { (node) -> void } -> void + def each_child_node: () -> Enumerator[node, void] + | () { (node) -> void } -> void + + # See Node.compact_child_nodes. + # -- + # : () -> Array[node] + def compact_child_nodes: () -> Array[node] + + # See Node.comment_targets. + # -- + # : () -> Array[node | Location] + def comment_targets: () -> Array[node | Location] + + # :call-seq: + # copy(**fields) -> NoBlockParameterNode + # + # Creates a copy of self with the given fields, using self as the template. + # -- + # : (?node_id: Integer, ?location: Location, ?flags: Integer, ?operator_loc: Location, ?keyword_loc: Location) -> NoBlockParameterNode + def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?operator_loc: Location, ?keyword_loc: Location) -> NoBlockParameterNode + + alias deconstruct child_nodes + + # : (Array[Symbol]? keys) -> Hash[Symbol, untyped] + def deconstruct_keys: (Array[Symbol]? keys) -> Hash[Symbol, untyped] + + # See `Node#type`. + # -- + # : () -> :no_block_parameter_node + def type: () -> :no_block_parameter_node + + # See `Node.type`. + # -- + # : () -> :no_block_parameter_node + def self.type: () -> :no_block_parameter_node + + # : () -> String + def inspect: () -> String + + # :category: Locations + # :call-seq: + # operator_loc -> Location + # + # Returns the Location represented by `operator_loc`. + # -- + # : () -> Location + def operator_loc: () -> Location + + # :category: Repository + # Save the operator_loc location using the given saved source so that + # it can be retrieved later. + # -- + # : (_Repository repository) -> Relocation::Entry + def save_operator_loc: (_Repository repository) -> Relocation::Entry + + # :category: Locations + # :call-seq: + # keyword_loc -> Location + # + # Returns the Location represented by `keyword_loc`. + # -- + # : () -> Location + def keyword_loc: () -> Location + + # :category: Repository + # Save the keyword_loc location using the given saved source so that + # it can be retrieved later. + # -- + # : (_Repository repository) -> Relocation::Entry + def save_keyword_loc: (_Repository repository) -> Relocation::Entry + + # :call-seq: + # operator -> String + # + # Slice the location of operator_loc from the source. + # -- + # : () -> String + def operator: () -> String + + # :call-seq: + # keyword -> String + # + # Slice the location of keyword_loc from the source. + # -- + # : () -> String + def keyword: () -> String + + # : (untyped other) -> boolish + def ===: (untyped other) -> boolish + end + + # Represents the use of `**nil` inside method arguments. + # + # def a(**nil) + # ^^^^^ + # end + class NoKeywordsParameterNode < Node + @keyword_loc: Location + + @operator_loc: Location + + # Initialize a new NoKeywordsParameterNode node. + # -- + # : (Source source, Integer node_id, Location location, Integer flags, Location operator_loc, Location keyword_loc) -> void + def initialize: (Source source, Integer node_id, Location location, Integer flags, Location operator_loc, Location keyword_loc) -> void + + # See Node.accept. + # -- + # : (_Visitor visitor) -> untyped + def accept: (_Visitor visitor) -> untyped + + # See Node.child_nodes. + # -- + # : () -> Array[node?] + def child_nodes: () -> Array[node?] + + # See Node.each_child_node. + # -- + # : () -> Enumerator[node, void] + # : () { (node) -> void } -> void + def each_child_node: () -> Enumerator[node, void] + | () { (node) -> void } -> void + + # See Node.compact_child_nodes. + # -- + # : () -> Array[node] + def compact_child_nodes: () -> Array[node] + + # See Node.comment_targets. + # -- + # : () -> Array[node | Location] + def comment_targets: () -> Array[node | Location] + + # :call-seq: + # copy(**fields) -> NoKeywordsParameterNode + # + # Creates a copy of self with the given fields, using self as the template. + # -- + # : (?node_id: Integer, ?location: Location, ?flags: Integer, ?operator_loc: Location, ?keyword_loc: Location) -> NoKeywordsParameterNode + def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?operator_loc: Location, ?keyword_loc: Location) -> NoKeywordsParameterNode + + alias deconstruct child_nodes + + # : (Array[Symbol]? keys) -> Hash[Symbol, untyped] + def deconstruct_keys: (Array[Symbol]? keys) -> Hash[Symbol, untyped] + + # See `Node#type`. + # -- + # : () -> :no_keywords_parameter_node + def type: () -> :no_keywords_parameter_node + + # See `Node.type`. + # -- + # : () -> :no_keywords_parameter_node + def self.type: () -> :no_keywords_parameter_node + + # : () -> String + def inspect: () -> String + + # :category: Locations + # :call-seq: + # operator_loc -> Location + # + # Returns the Location represented by `operator_loc`. + # -- + # : () -> Location + def operator_loc: () -> Location + + # :category: Repository + # Save the operator_loc location using the given saved source so that + # it can be retrieved later. + # -- + # : (_Repository repository) -> Relocation::Entry + def save_operator_loc: (_Repository repository) -> Relocation::Entry + + # :category: Locations + # :call-seq: + # keyword_loc -> Location + # + # Returns the Location represented by `keyword_loc`. + # -- + # : () -> Location + def keyword_loc: () -> Location + + # :category: Repository + # Save the keyword_loc location using the given saved source so that + # it can be retrieved later. + # -- + # : (_Repository repository) -> Relocation::Entry + def save_keyword_loc: (_Repository repository) -> Relocation::Entry + + # :call-seq: + # operator -> String + # + # Slice the location of operator_loc from the source. + # -- + # : () -> String + def operator: () -> String + + # :call-seq: + # keyword -> String + # + # Slice the location of keyword_loc from the source. + # -- + # : () -> String + def keyword: () -> String + + # : (untyped other) -> boolish + def ===: (untyped other) -> boolish + end + + # Represents an implicit set of parameters through the use of numbered parameters within a block or lambda. + # + # -> { _1 + _2 } + # ^^^^^^^^^^^^^^ + class NumberedParametersNode < Node + @maximum: Integer + + # Initialize a new NumberedParametersNode node. + # -- + # : (Source source, Integer node_id, Location location, Integer flags, Integer maximum) -> void + def initialize: (Source source, Integer node_id, Location location, Integer flags, Integer maximum) -> void + + # See Node.accept. + # -- + # : (_Visitor visitor) -> untyped + def accept: (_Visitor visitor) -> untyped + + # See Node.child_nodes. + # -- + # : () -> Array[node?] + def child_nodes: () -> Array[node?] + + # See Node.each_child_node. + # -- + # : () -> Enumerator[node, void] + # : () { (node) -> void } -> void + def each_child_node: () -> Enumerator[node, void] + | () { (node) -> void } -> void + + # See Node.compact_child_nodes. + # -- + # : () -> Array[node] + def compact_child_nodes: () -> Array[node] + + # See Node.comment_targets. + # -- + # : () -> Array[node | Location] + def comment_targets: () -> Array[node | Location] + + # :call-seq: + # copy(**fields) -> NumberedParametersNode + # + # Creates a copy of self with the given fields, using self as the template. + # -- + # : (?node_id: Integer, ?location: Location, ?flags: Integer, ?maximum: Integer) -> NumberedParametersNode + def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?maximum: Integer) -> NumberedParametersNode + + alias deconstruct child_nodes + + # : (Array[Symbol]? keys) -> Hash[Symbol, untyped] + def deconstruct_keys: (Array[Symbol]? keys) -> Hash[Symbol, untyped] + + # See `Node#type`. + # -- + # : () -> :numbered_parameters_node + def type: () -> :numbered_parameters_node + + # See `Node.type`. + # -- + # : () -> :numbered_parameters_node + def self.type: () -> :numbered_parameters_node + + # : () -> String + def inspect: () -> String + + # :call-seq: + # maximum -> Integer + # + # Returns the `maximum` attribute. + # -- + # : () -> Integer + def maximum: () -> Integer + + # : (untyped other) -> boolish + def ===: (untyped other) -> boolish + end + + # Represents reading a numbered reference to a capture in the previous match. + # + # $1 + # ^^ + class NumberedReferenceReadNode < Node + @number: Integer + + # Initialize a new NumberedReferenceReadNode node. + # -- + # : (Source source, Integer node_id, Location location, Integer flags, Integer number) -> void + def initialize: (Source source, Integer node_id, Location location, Integer flags, Integer number) -> void + + # See Node.accept. + # -- + # : (_Visitor visitor) -> untyped + def accept: (_Visitor visitor) -> untyped + + # See Node.child_nodes. + # -- + # : () -> Array[node?] + def child_nodes: () -> Array[node?] + + # See Node.each_child_node. + # -- + # : () -> Enumerator[node, void] + # : () { (node) -> void } -> void + def each_child_node: () -> Enumerator[node, void] + | () { (node) -> void } -> void + + # See Node.compact_child_nodes. + # -- + # : () -> Array[node] + def compact_child_nodes: () -> Array[node] + + # See Node.comment_targets. + # -- + # : () -> Array[node | Location] + def comment_targets: () -> Array[node | Location] + + # :call-seq: + # copy(**fields) -> NumberedReferenceReadNode + # + # Creates a copy of self with the given fields, using self as the template. + # -- + # : (?node_id: Integer, ?location: Location, ?flags: Integer, ?number: Integer) -> NumberedReferenceReadNode + def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?number: Integer) -> NumberedReferenceReadNode + + alias deconstruct child_nodes + + # : (Array[Symbol]? keys) -> Hash[Symbol, untyped] + def deconstruct_keys: (Array[Symbol]? keys) -> Hash[Symbol, untyped] + + # See `Node#type`. + # -- + # : () -> :numbered_reference_read_node + def type: () -> :numbered_reference_read_node + + # See `Node.type`. + # -- + # : () -> :numbered_reference_read_node + def self.type: () -> :numbered_reference_read_node + + # : () -> String + def inspect: () -> String + + # :call-seq: + # number -> Integer + # + # 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` + # -- + # : () -> Integer + def number: () -> Integer + + # : (untyped other) -> boolish + def ===: (untyped other) -> boolish + end + + # Represents an optional keyword parameter to a method, block, or lambda definition. + # + # def a(b: 1) + # ^^^^ + # end + class OptionalKeywordParameterNode < Node + @value: Prism::node + + @name_loc: Location + + @name: Symbol + + # Initialize a new OptionalKeywordParameterNode node. + # -- + # : (Source source, Integer node_id, Location location, Integer flags, Symbol name, Location name_loc, Prism::node value) -> void + def initialize: (Source source, Integer node_id, Location location, Integer flags, Symbol name, Location name_loc, Prism::node value) -> void + + # See Node.accept. + # -- + # : (_Visitor visitor) -> untyped + def accept: (_Visitor visitor) -> untyped + + # See Node.child_nodes. + # -- + # : () -> Array[node?] + def child_nodes: () -> Array[node?] + + # See Node.each_child_node. + # -- + # : () -> Enumerator[node, void] + # : () { (node) -> void } -> void + def each_child_node: () -> Enumerator[node, void] + | () { (node) -> void } -> void + + # See Node.compact_child_nodes. + # -- + # : () -> Array[node] + def compact_child_nodes: () -> Array[node] + + # See Node.comment_targets. + # -- + # : () -> Array[node | Location] + def comment_targets: () -> Array[node | Location] + + # :call-seq: + # copy(**fields) -> OptionalKeywordParameterNode + # + # Creates a copy of self with the given fields, using self as the template. + # -- + # : (?node_id: Integer, ?location: Location, ?flags: Integer, ?name: Symbol, ?name_loc: Location, ?value: Prism::node) -> OptionalKeywordParameterNode + def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?name: Symbol, ?name_loc: Location, ?value: Prism::node) -> OptionalKeywordParameterNode + + alias deconstruct child_nodes + + # : (Array[Symbol]? keys) -> Hash[Symbol, untyped] + def deconstruct_keys: (Array[Symbol]? keys) -> Hash[Symbol, untyped] + + # See `Node#type`. + # -- + # : () -> :optional_keyword_parameter_node + def type: () -> :optional_keyword_parameter_node + + # See `Node.type`. + # -- + # : () -> :optional_keyword_parameter_node + def self.type: () -> :optional_keyword_parameter_node + + # : () -> String + def inspect: () -> String + + # :category: Flags + # a parameter name that has been repeated in the method signature + # -- + # : () -> bool + def repeated_parameter?: () -> bool + + # :call-seq: + # name -> Symbol + # + # Returns the `name` attribute. + # -- + # : () -> Symbol + def name: () -> Symbol + + # :category: Locations + # :call-seq: + # name_loc -> Location + # + # Returns the Location represented by `name_loc`. + # -- + # : () -> Location + def name_loc: () -> Location + + # :category: Repository + # Save the name_loc location using the given saved source so that + # it can be retrieved later. + # -- + # : (_Repository repository) -> Relocation::Entry + def save_name_loc: (_Repository repository) -> Relocation::Entry + + # :call-seq: + # value -> Node + # + # Returns the `value` attribute. + # -- + # : () -> Prism::node + def value: () -> Prism::node + + # : (untyped other) -> boolish + def ===: (untyped other) -> boolish + end + + # Represents an optional parameter to a method, block, or lambda definition. + # + # def a(b = 1) + # ^^^^^ + # end + class OptionalParameterNode < Node + @value: Prism::node + + @operator_loc: Location + + @name_loc: Location + + @name: Symbol + + # Initialize a new OptionalParameterNode node. + # -- + # : (Source source, Integer node_id, Location location, Integer flags, Symbol name, Location name_loc, Location operator_loc, Prism::node value) -> void + def initialize: (Source source, Integer node_id, Location location, Integer flags, Symbol name, Location name_loc, Location operator_loc, Prism::node value) -> void + + # See Node.accept. + # -- + # : (_Visitor visitor) -> untyped + def accept: (_Visitor visitor) -> untyped + + # See Node.child_nodes. + # -- + # : () -> Array[node?] + def child_nodes: () -> Array[node?] + + # See Node.each_child_node. + # -- + # : () -> Enumerator[node, void] + # : () { (node) -> void } -> void + def each_child_node: () -> Enumerator[node, void] + | () { (node) -> void } -> void + + # See Node.compact_child_nodes. + # -- + # : () -> Array[node] + def compact_child_nodes: () -> Array[node] + + # See Node.comment_targets. + # -- + # : () -> Array[node | Location] + def comment_targets: () -> Array[node | Location] + + # :call-seq: + # copy(**fields) -> OptionalParameterNode + # + # Creates a copy of self with the given fields, using self as the template. + # -- + # : (?node_id: Integer, ?location: Location, ?flags: Integer, ?name: Symbol, ?name_loc: Location, ?operator_loc: Location, ?value: Prism::node) -> OptionalParameterNode + def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?name: Symbol, ?name_loc: Location, ?operator_loc: Location, ?value: Prism::node) -> OptionalParameterNode + + alias deconstruct child_nodes + + # : (Array[Symbol]? keys) -> Hash[Symbol, untyped] + def deconstruct_keys: (Array[Symbol]? keys) -> Hash[Symbol, untyped] + + # See `Node#type`. + # -- + # : () -> :optional_parameter_node + def type: () -> :optional_parameter_node + + # See `Node.type`. + # -- + # : () -> :optional_parameter_node + def self.type: () -> :optional_parameter_node + + # : () -> String + def inspect: () -> String + + # :category: Flags + # a parameter name that has been repeated in the method signature + # -- + # : () -> bool + def repeated_parameter?: () -> bool + + # :call-seq: + # name -> Symbol + # + # Returns the `name` attribute. + # -- + # : () -> Symbol + def name: () -> Symbol + + # :category: Locations + # :call-seq: + # name_loc -> Location + # + # Returns the Location represented by `name_loc`. + # -- + # : () -> Location + def name_loc: () -> Location + + # :category: Repository + # Save the name_loc location using the given saved source so that + # it can be retrieved later. + # -- + # : (_Repository repository) -> Relocation::Entry + def save_name_loc: (_Repository repository) -> Relocation::Entry + + # :category: Locations + # :call-seq: + # operator_loc -> Location + # + # Returns the Location represented by `operator_loc`. + # -- + # : () -> Location + def operator_loc: () -> Location + + # :category: Repository + # Save the operator_loc location using the given saved source so that + # it can be retrieved later. + # -- + # : (_Repository repository) -> Relocation::Entry + def save_operator_loc: (_Repository repository) -> Relocation::Entry + + # :call-seq: + # value -> Node + # + # Returns the `value` attribute. + # -- + # : () -> Prism::node + def value: () -> Prism::node + + # :call-seq: + # operator -> String + # + # Slice the location of operator_loc from the source. + # -- + # : () -> String + def operator: () -> String + + # : (untyped other) -> boolish + def ===: (untyped other) -> boolish + end + + # Represents the use of the `||` operator or the `or` keyword. + # + # left or right + # ^^^^^^^^^^^^^ + class OrNode < Node + @operator_loc: Location + + @right: Prism::node + + @left: Prism::node + + # Initialize a new OrNode node. + # -- + # : (Source source, Integer node_id, Location location, Integer flags, Prism::node left, Prism::node right, Location operator_loc) -> void + def initialize: (Source source, Integer node_id, Location location, Integer flags, Prism::node left, Prism::node right, Location operator_loc) -> void + + # See Node.accept. + # -- + # : (_Visitor visitor) -> untyped + def accept: (_Visitor visitor) -> untyped + + # See Node.child_nodes. + # -- + # : () -> Array[node?] + def child_nodes: () -> Array[node?] + + # See Node.each_child_node. + # -- + # : () -> Enumerator[node, void] + # : () { (node) -> void } -> void + def each_child_node: () -> Enumerator[node, void] + | () { (node) -> void } -> void + + # See Node.compact_child_nodes. + # -- + # : () -> Array[node] + def compact_child_nodes: () -> Array[node] + + # See Node.comment_targets. + # -- + # : () -> Array[node | Location] + def comment_targets: () -> Array[node | Location] + + # :call-seq: + # copy(**fields) -> OrNode + # + # Creates a copy of self with the given fields, using self as the template. + # -- + # : (?node_id: Integer, ?location: Location, ?flags: Integer, ?left: Prism::node, ?right: Prism::node, ?operator_loc: Location) -> OrNode + def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?left: Prism::node, ?right: Prism::node, ?operator_loc: Location) -> OrNode + + alias deconstruct child_nodes + + # : (Array[Symbol]? keys) -> Hash[Symbol, untyped] + def deconstruct_keys: (Array[Symbol]? keys) -> Hash[Symbol, untyped] + + # See `Node#type`. + # -- + # : () -> :or_node + def type: () -> :or_node + + # See `Node.type`. + # -- + # : () -> :or_node + def self.type: () -> :or_node + + # : () -> String + def inspect: () -> String + + # :call-seq: + # left -> Node + # + # 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 + # ^ + # -- + # : () -> Prism::node + def left: () -> Prism::node + + # :call-seq: + # right -> Node + # + # Represents the right side of the expression. + # + # left || right + # ^^^^^ + # + # 1 or 2 + # ^ + # -- + # : () -> Prism::node + def right: () -> Prism::node + + # :category: Locations + # :call-seq: + # operator_loc -> Location + # + # The Location of the `or` keyword or the `||` operator. + # + # left or right + # ^^ + # -- + # : () -> Location + def operator_loc: () -> Location + + # :category: Repository + # Save the operator_loc location using the given saved source so that + # it can be retrieved later. + # -- + # : (_Repository repository) -> Relocation::Entry + def save_operator_loc: (_Repository repository) -> Relocation::Entry + + # :call-seq: + # operator -> String + # + # Slice the location of operator_loc from the source. + # -- + # : () -> String + def operator: () -> String + + # : (untyped other) -> boolish + def ===: (untyped other) -> boolish + end + + # Represents the list of parameters on a method, block, or lambda definition. + # + # def a(b, c, d) + # ^^^^^^^ + # end + class ParametersNode < Node + @block: (BlockParameterNode | NoBlockParameterNode)? + + @keyword_rest: (KeywordRestParameterNode | ForwardingParameterNode | NoKeywordsParameterNode)? + + @keywords: Array[RequiredKeywordParameterNode | OptionalKeywordParameterNode] + + @posts: Array[RequiredParameterNode | MultiTargetNode | KeywordRestParameterNode | NoKeywordsParameterNode | ForwardingParameterNode | BlockParameterNode | NoBlockParameterNode] + + @rest: (RestParameterNode | ImplicitRestNode)? + + @optionals: Array[OptionalParameterNode] + + @requireds: Array[RequiredParameterNode | MultiTargetNode] + + # Initialize a new ParametersNode node. + # -- + # : (Source source, Integer node_id, Location location, Integer flags, Array[RequiredParameterNode | MultiTargetNode] requireds, Array[OptionalParameterNode] optionals, (RestParameterNode | ImplicitRestNode)? rest, Array[RequiredParameterNode | MultiTargetNode | KeywordRestParameterNode | NoKeywordsParameterNode | ForwardingParameterNode | BlockParameterNode | NoBlockParameterNode] posts, Array[RequiredKeywordParameterNode | OptionalKeywordParameterNode] keywords, (KeywordRestParameterNode | ForwardingParameterNode | NoKeywordsParameterNode)? keyword_rest, (BlockParameterNode | NoBlockParameterNode)? block) -> void + def initialize: (Source source, Integer node_id, Location location, Integer flags, Array[RequiredParameterNode | MultiTargetNode] requireds, Array[OptionalParameterNode] optionals, (RestParameterNode | ImplicitRestNode)? rest, Array[RequiredParameterNode | MultiTargetNode | KeywordRestParameterNode | NoKeywordsParameterNode | ForwardingParameterNode | BlockParameterNode | NoBlockParameterNode] posts, Array[RequiredKeywordParameterNode | OptionalKeywordParameterNode] keywords, (KeywordRestParameterNode | ForwardingParameterNode | NoKeywordsParameterNode)? keyword_rest, (BlockParameterNode | NoBlockParameterNode)? block) -> void + + # See Node.accept. + # -- + # : (_Visitor visitor) -> untyped + def accept: (_Visitor visitor) -> untyped + + # See Node.child_nodes. + # -- + # : () -> Array[node?] + def child_nodes: () -> Array[node?] + + # See Node.each_child_node. + # -- + # : () -> Enumerator[node, void] + # : () { (node) -> void } -> void + def each_child_node: () -> Enumerator[node, void] + | () { (node) -> void } -> void + + # See Node.compact_child_nodes. + # -- + # : () -> Array[node] + def compact_child_nodes: () -> Array[node] + + # See Node.comment_targets. + # -- + # : () -> Array[node | Location] + def comment_targets: () -> Array[node | Location] + + # :call-seq: + # copy(**fields) -> ParametersNode + # + # Creates a copy of self with the given fields, using self as the template. + # -- + # : (?node_id: Integer, ?location: Location, ?flags: Integer, ?requireds: Array[RequiredParameterNode | MultiTargetNode], ?optionals: Array[OptionalParameterNode], ?rest: (RestParameterNode | ImplicitRestNode)?, ?posts: Array[RequiredParameterNode | MultiTargetNode | KeywordRestParameterNode | NoKeywordsParameterNode | ForwardingParameterNode | BlockParameterNode | NoBlockParameterNode], ?keywords: Array[RequiredKeywordParameterNode | OptionalKeywordParameterNode], ?keyword_rest: (KeywordRestParameterNode | ForwardingParameterNode | NoKeywordsParameterNode)?, ?block: (BlockParameterNode | NoBlockParameterNode)?) -> ParametersNode + def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?requireds: Array[RequiredParameterNode | MultiTargetNode], ?optionals: Array[OptionalParameterNode], ?rest: (RestParameterNode | ImplicitRestNode)?, ?posts: Array[RequiredParameterNode | MultiTargetNode | KeywordRestParameterNode | NoKeywordsParameterNode | ForwardingParameterNode | BlockParameterNode | NoBlockParameterNode], ?keywords: Array[RequiredKeywordParameterNode | OptionalKeywordParameterNode], ?keyword_rest: (KeywordRestParameterNode | ForwardingParameterNode | NoKeywordsParameterNode)?, ?block: (BlockParameterNode | NoBlockParameterNode)?) -> ParametersNode + + alias deconstruct child_nodes + + # : (Array[Symbol]? keys) -> Hash[Symbol, untyped] + def deconstruct_keys: (Array[Symbol]? keys) -> Hash[Symbol, untyped] + + # See `Node#type`. + # -- + # : () -> :parameters_node + def type: () -> :parameters_node + + # See `Node.type`. + # -- + # : () -> :parameters_node + def self.type: () -> :parameters_node + + # : () -> String + def inspect: () -> String + + # :call-seq: + # requireds -> Array[RequiredParameterNode | MultiTargetNode] + # + # Returns the `requireds` attribute. + # -- + # : () -> Array[RequiredParameterNode | MultiTargetNode] + def requireds: () -> Array[RequiredParameterNode | MultiTargetNode] + + # :call-seq: + # optionals -> Array[OptionalParameterNode] + # + # Returns the `optionals` attribute. + # -- + # : () -> Array[OptionalParameterNode] + def optionals: () -> Array[OptionalParameterNode] + + # :call-seq: + # rest -> RestParameterNode | ImplicitRestNode | nil + # + # Returns the `rest` attribute. + # -- + # : () -> (RestParameterNode | ImplicitRestNode)? + def rest: () -> (RestParameterNode | ImplicitRestNode)? + + # :call-seq: + # posts -> Array[RequiredParameterNode | MultiTargetNode | KeywordRestParameterNode | NoKeywordsParameterNode | ForwardingParameterNode | BlockParameterNode | NoBlockParameterNode] + # + # Returns the `posts` attribute. + # -- + # : () -> Array[RequiredParameterNode | MultiTargetNode | KeywordRestParameterNode | NoKeywordsParameterNode | ForwardingParameterNode | BlockParameterNode | NoBlockParameterNode] + def posts: () -> Array[RequiredParameterNode | MultiTargetNode | KeywordRestParameterNode | NoKeywordsParameterNode | ForwardingParameterNode | BlockParameterNode | NoBlockParameterNode] + + # :call-seq: + # keywords -> Array[RequiredKeywordParameterNode | OptionalKeywordParameterNode] + # + # Returns the `keywords` attribute. + # -- + # : () -> Array[RequiredKeywordParameterNode | OptionalKeywordParameterNode] + def keywords: () -> Array[RequiredKeywordParameterNode | OptionalKeywordParameterNode] + + # :call-seq: + # keyword_rest -> KeywordRestParameterNode | ForwardingParameterNode | NoKeywordsParameterNode | nil + # + # Returns the `keyword_rest` attribute. + # -- + # : () -> (KeywordRestParameterNode | ForwardingParameterNode | NoKeywordsParameterNode)? + def keyword_rest: () -> (KeywordRestParameterNode | ForwardingParameterNode | NoKeywordsParameterNode)? + + # :call-seq: + # block -> BlockParameterNode | NoBlockParameterNode | nil + # + # Returns the `block` attribute. + # -- + # : () -> (BlockParameterNode | NoBlockParameterNode)? + def block: () -> (BlockParameterNode | NoBlockParameterNode)? + + # : (untyped other) -> boolish + def ===: (untyped other) -> boolish + end + + # Represents a parenthesized expression + # + # (10 + 34) + # ^^^^^^^^^ + class ParenthesesNode < Node + @closing_loc: Location + + @opening_loc: Location + + @body: Prism::node? + + # Initialize a new ParenthesesNode node. + # -- + # : (Source source, Integer node_id, Location location, Integer flags, Prism::node? body, Location opening_loc, Location closing_loc) -> void + def initialize: (Source source, Integer node_id, Location location, Integer flags, Prism::node? body, Location opening_loc, Location closing_loc) -> void + + # See Node.accept. + # -- + # : (_Visitor visitor) -> untyped + def accept: (_Visitor visitor) -> untyped + + # See Node.child_nodes. + # -- + # : () -> Array[node?] + def child_nodes: () -> Array[node?] + + # See Node.each_child_node. + # -- + # : () -> Enumerator[node, void] + # : () { (node) -> void } -> void + def each_child_node: () -> Enumerator[node, void] + | () { (node) -> void } -> void + + # See Node.compact_child_nodes. + # -- + # : () -> Array[node] + def compact_child_nodes: () -> Array[node] + + # See Node.comment_targets. + # -- + # : () -> Array[node | Location] + def comment_targets: () -> Array[node | Location] + + # :call-seq: + # copy(**fields) -> ParenthesesNode + # + # Creates a copy of self with the given fields, using self as the template. + # -- + # : (?node_id: Integer, ?location: Location, ?flags: Integer, ?body: Prism::node?, ?opening_loc: Location, ?closing_loc: Location) -> ParenthesesNode + def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?body: Prism::node?, ?opening_loc: Location, ?closing_loc: Location) -> ParenthesesNode + + alias deconstruct child_nodes + + # : (Array[Symbol]? keys) -> Hash[Symbol, untyped] + def deconstruct_keys: (Array[Symbol]? keys) -> Hash[Symbol, untyped] + + # See `Node#type`. + # -- + # : () -> :parentheses_node + def type: () -> :parentheses_node + + # See `Node.type`. + # -- + # : () -> :parentheses_node + def self.type: () -> :parentheses_node + + # : () -> String + def inspect: () -> String + + # :category: Flags + # parentheses that contain multiple potentially void statements + # -- + # : () -> bool + def multiple_statements?: () -> bool + + # :call-seq: + # body -> Node | nil + # + # Returns the `body` attribute. + # -- + # : () -> Prism::node? + def body: () -> Prism::node? + + # :category: Locations + # :call-seq: + # opening_loc -> Location + # + # Returns the Location represented by `opening_loc`. + # -- + # : () -> Location + def opening_loc: () -> Location + + # :category: Repository + # Save the opening_loc location using the given saved source so that + # it can be retrieved later. + # -- + # : (_Repository repository) -> Relocation::Entry + def save_opening_loc: (_Repository repository) -> Relocation::Entry + + # :category: Locations + # :call-seq: + # closing_loc -> Location + # + # Returns the Location represented by `closing_loc`. + # -- + # : () -> Location + def closing_loc: () -> Location + + # :category: Repository + # Save the closing_loc location using the given saved source so that + # it can be retrieved later. + # -- + # : (_Repository repository) -> Relocation::Entry + def save_closing_loc: (_Repository repository) -> Relocation::Entry + + # :call-seq: + # opening -> String + # + # Slice the location of opening_loc from the source. + # -- + # : () -> String + def opening: () -> String + + # :call-seq: + # closing -> String + # + # Slice the location of closing_loc from the source. + # -- + # : () -> String + def closing: () -> String + + # : (untyped other) -> boolish + def ===: (untyped other) -> boolish + end + + # Represents the use of the `^` operator for pinning an expression in a pattern matching expression. + # + # foo in ^(bar) + # ^^^^^^ + class PinnedExpressionNode < Node + @rparen_loc: Location + + @lparen_loc: Location + + @operator_loc: Location + + @expression: Prism::node + + # Initialize a new PinnedExpressionNode node. + # -- + # : (Source source, Integer node_id, Location location, Integer flags, Prism::node expression, Location operator_loc, Location lparen_loc, Location rparen_loc) -> 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 + + # See Node.accept. + # -- + # : (_Visitor visitor) -> untyped + def accept: (_Visitor visitor) -> untyped + + # See Node.child_nodes. + # -- + # : () -> Array[node?] + def child_nodes: () -> Array[node?] + + # See Node.each_child_node. + # -- + # : () -> Enumerator[node, void] + # : () { (node) -> void } -> void + def each_child_node: () -> Enumerator[node, void] + | () { (node) -> void } -> void + + # See Node.compact_child_nodes. + # -- + # : () -> Array[node] + def compact_child_nodes: () -> Array[node] + + # See Node.comment_targets. + # -- + # : () -> Array[node | Location] + def comment_targets: () -> Array[node | Location] + + # :call-seq: + # copy(**fields) -> PinnedExpressionNode + # + # Creates a copy of self with the given fields, using self as the template. + # -- + # : (?node_id: Integer, ?location: Location, ?flags: Integer, ?expression: Prism::node, ?operator_loc: Location, ?lparen_loc: Location, ?rparen_loc: Location) -> PinnedExpressionNode + def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?expression: Prism::node, ?operator_loc: Location, ?lparen_loc: Location, ?rparen_loc: Location) -> PinnedExpressionNode + + alias deconstruct child_nodes + + # : (Array[Symbol]? keys) -> Hash[Symbol, untyped] + def deconstruct_keys: (Array[Symbol]? keys) -> Hash[Symbol, untyped] + + # See `Node#type`. + # -- + # : () -> :pinned_expression_node + def type: () -> :pinned_expression_node + + # See `Node.type`. + # -- + # : () -> :pinned_expression_node + def self.type: () -> :pinned_expression_node + + # : () -> String + def inspect: () -> String + + # :call-seq: + # expression -> Node + # + # The expression used in the pinned expression + # + # foo in ^(bar) + # ^^^ + # -- + # : () -> Prism::node + def expression: () -> Prism::node + + # :category: Locations + # :call-seq: + # operator_loc -> Location + # + # The Location of the `^` operator + # + # foo in ^(bar) + # ^ + # -- + # : () -> Location + def operator_loc: () -> Location + + # :category: Repository + # Save the operator_loc location using the given saved source so that + # it can be retrieved later. + # -- + # : (_Repository repository) -> Relocation::Entry + def save_operator_loc: (_Repository repository) -> Relocation::Entry + + # :category: Locations + # :call-seq: + # lparen_loc -> Location + # + # The Location of the opening parenthesis. + # + # foo in ^(bar) + # ^ + # -- + # : () -> Location + def lparen_loc: () -> Location + + # :category: Repository + # Save the lparen_loc location using the given saved source so that + # it can be retrieved later. + # -- + # : (_Repository repository) -> Relocation::Entry + def save_lparen_loc: (_Repository repository) -> Relocation::Entry + + # :category: Locations + # :call-seq: + # rparen_loc -> Location + # + # The Location of the closing parenthesis. + # + # foo in ^(bar) + # ^ + # -- + # : () -> Location + def rparen_loc: () -> Location + + # :category: Repository + # Save the rparen_loc location using the given saved source so that + # it can be retrieved later. + # -- + # : (_Repository repository) -> Relocation::Entry + def save_rparen_loc: (_Repository repository) -> Relocation::Entry + + # :call-seq: + # operator -> String + # + # Slice the location of operator_loc from the source. + # -- + # : () -> String + def operator: () -> String + + # :call-seq: + # lparen -> String + # + # Slice the location of lparen_loc from the source. + # -- + # : () -> String + def lparen: () -> String + + # :call-seq: + # rparen -> String + # + # Slice the location of rparen_loc from the source. + # -- + # : () -> String + def rparen: () -> String + + # : (untyped other) -> boolish + def ===: (untyped other) -> boolish + end + + # Represents the use of the `^` operator for pinning a variable in a pattern matching expression. + # + # foo in ^bar + # ^^^^ + class PinnedVariableNode < Node + @operator_loc: Location + + @variable: LocalVariableReadNode | InstanceVariableReadNode | ClassVariableReadNode | GlobalVariableReadNode | BackReferenceReadNode | NumberedReferenceReadNode | ItLocalVariableReadNode | MissingNode + + # Initialize a new PinnedVariableNode node. + # -- + # : (Source source, Integer node_id, Location location, Integer flags, (LocalVariableReadNode | InstanceVariableReadNode | ClassVariableReadNode | GlobalVariableReadNode | BackReferenceReadNode | NumberedReferenceReadNode | ItLocalVariableReadNode | MissingNode) variable, Location operator_loc) -> 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 + + # See Node.accept. + # -- + # : (_Visitor visitor) -> untyped + def accept: (_Visitor visitor) -> untyped + + # See Node.child_nodes. + # -- + # : () -> Array[node?] + def child_nodes: () -> Array[node?] + + # See Node.each_child_node. + # -- + # : () -> Enumerator[node, void] + # : () { (node) -> void } -> void + def each_child_node: () -> Enumerator[node, void] + | () { (node) -> void } -> void + + # See Node.compact_child_nodes. + # -- + # : () -> Array[node] + def compact_child_nodes: () -> Array[node] + + # See Node.comment_targets. + # -- + # : () -> Array[node | Location] + def comment_targets: () -> Array[node | Location] + + # :call-seq: + # copy(**fields) -> PinnedVariableNode + # + # Creates a copy of self with the given fields, using self as the template. + # -- + # : (?node_id: Integer, ?location: Location, ?flags: Integer, ?variable: (LocalVariableReadNode | InstanceVariableReadNode | ClassVariableReadNode | GlobalVariableReadNode | BackReferenceReadNode | NumberedReferenceReadNode | ItLocalVariableReadNode | MissingNode), ?operator_loc: Location) -> PinnedVariableNode + def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?variable: LocalVariableReadNode | InstanceVariableReadNode | ClassVariableReadNode | GlobalVariableReadNode | BackReferenceReadNode | NumberedReferenceReadNode | ItLocalVariableReadNode | MissingNode, ?operator_loc: Location) -> PinnedVariableNode + + alias deconstruct child_nodes + + # : (Array[Symbol]? keys) -> Hash[Symbol, untyped] + def deconstruct_keys: (Array[Symbol]? keys) -> Hash[Symbol, untyped] + + # See `Node#type`. + # -- + # : () -> :pinned_variable_node + def type: () -> :pinned_variable_node + + # See `Node.type`. + # -- + # : () -> :pinned_variable_node + def self.type: () -> :pinned_variable_node + + # : () -> String + def inspect: () -> String + + # :call-seq: + # variable -> LocalVariableReadNode | InstanceVariableReadNode | ClassVariableReadNode | GlobalVariableReadNode | BackReferenceReadNode | NumberedReferenceReadNode | ItLocalVariableReadNode | MissingNode + # + # The variable used in the pinned expression + # + # foo in ^bar + # ^^^ + # -- + # : () -> (LocalVariableReadNode | InstanceVariableReadNode | ClassVariableReadNode | GlobalVariableReadNode | BackReferenceReadNode | NumberedReferenceReadNode | ItLocalVariableReadNode | MissingNode) + def variable: () -> (LocalVariableReadNode | InstanceVariableReadNode | ClassVariableReadNode | GlobalVariableReadNode | BackReferenceReadNode | NumberedReferenceReadNode | ItLocalVariableReadNode | MissingNode) + + # :category: Locations + # :call-seq: + # operator_loc -> Location + # + # The Location of the `^` operator + # + # foo in ^bar + # ^ + # -- + # : () -> Location + def operator_loc: () -> Location + + # :category: Repository + # Save the operator_loc location using the given saved source so that + # it can be retrieved later. + # -- + # : (_Repository repository) -> Relocation::Entry + def save_operator_loc: (_Repository repository) -> Relocation::Entry + + # :call-seq: + # operator -> String + # + # Slice the location of operator_loc from the source. + # -- + # : () -> String + def operator: () -> String + + # : (untyped other) -> boolish + def ===: (untyped other) -> boolish + end + + # Represents the use of the `END` keyword. + # + # END { foo } + # ^^^^^^^^^^^ + class PostExecutionNode < Node + @closing_loc: Location + + @opening_loc: Location + + @keyword_loc: Location + + @statements: StatementsNode? + + # Initialize a new PostExecutionNode node. + # -- + # : (Source source, Integer node_id, Location location, Integer flags, StatementsNode? statements, Location keyword_loc, Location opening_loc, Location closing_loc) -> void + def initialize: (Source source, Integer node_id, Location location, Integer flags, StatementsNode? statements, Location keyword_loc, Location opening_loc, Location closing_loc) -> void + + # See Node.accept. + # -- + # : (_Visitor visitor) -> untyped + def accept: (_Visitor visitor) -> untyped + + # See Node.child_nodes. + # -- + # : () -> Array[node?] + def child_nodes: () -> Array[node?] + + # See Node.each_child_node. + # -- + # : () -> Enumerator[node, void] + # : () { (node) -> void } -> void + def each_child_node: () -> Enumerator[node, void] + | () { (node) -> void } -> void + + # See Node.compact_child_nodes. + # -- + # : () -> Array[node] + def compact_child_nodes: () -> Array[node] + + # See Node.comment_targets. + # -- + # : () -> Array[node | Location] + def comment_targets: () -> Array[node | Location] + + # :call-seq: + # copy(**fields) -> PostExecutionNode + # + # Creates a copy of self with the given fields, using self as the template. + # -- + # : (?node_id: Integer, ?location: Location, ?flags: Integer, ?statements: StatementsNode?, ?keyword_loc: Location, ?opening_loc: Location, ?closing_loc: Location) -> PostExecutionNode + def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?statements: StatementsNode?, ?keyword_loc: Location, ?opening_loc: Location, ?closing_loc: Location) -> PostExecutionNode + + alias deconstruct child_nodes + + # : (Array[Symbol]? keys) -> Hash[Symbol, untyped] + def deconstruct_keys: (Array[Symbol]? keys) -> Hash[Symbol, untyped] + + # See `Node#type`. + # -- + # : () -> :post_execution_node + def type: () -> :post_execution_node + + # See `Node.type`. + # -- + # : () -> :post_execution_node + def self.type: () -> :post_execution_node + + # : () -> String + def inspect: () -> String + + # :call-seq: + # statements -> StatementsNode | nil + # + # Returns the `statements` attribute. + # -- + # : () -> StatementsNode? + def statements: () -> StatementsNode? + + # :category: Locations + # :call-seq: + # keyword_loc -> Location + # + # Returns the Location represented by `keyword_loc`. + # -- + # : () -> Location + def keyword_loc: () -> Location + + # :category: Repository + # Save the keyword_loc location using the given saved source so that + # it can be retrieved later. + # -- + # : (_Repository repository) -> Relocation::Entry + def save_keyword_loc: (_Repository repository) -> Relocation::Entry + + # :category: Locations + # :call-seq: + # opening_loc -> Location + # + # Returns the Location represented by `opening_loc`. + # -- + # : () -> Location + def opening_loc: () -> Location + + # :category: Repository + # Save the opening_loc location using the given saved source so that + # it can be retrieved later. + # -- + # : (_Repository repository) -> Relocation::Entry + def save_opening_loc: (_Repository repository) -> Relocation::Entry + + # :category: Locations + # :call-seq: + # closing_loc -> Location + # + # Returns the Location represented by `closing_loc`. + # -- + # : () -> Location + def closing_loc: () -> Location + + # :category: Repository + # Save the closing_loc location using the given saved source so that + # it can be retrieved later. + # -- + # : (_Repository repository) -> Relocation::Entry + def save_closing_loc: (_Repository repository) -> Relocation::Entry + + # :call-seq: + # keyword -> String + # + # Slice the location of keyword_loc from the source. + # -- + # : () -> String + def keyword: () -> String + + # :call-seq: + # opening -> String + # + # Slice the location of opening_loc from the source. + # -- + # : () -> String + def opening: () -> String + + # :call-seq: + # closing -> String + # + # Slice the location of closing_loc from the source. + # -- + # : () -> String + def closing: () -> String + + # : (untyped other) -> boolish + def ===: (untyped other) -> boolish + end + + # Represents the use of the `BEGIN` keyword. + # + # BEGIN { foo } + # ^^^^^^^^^^^^^ + class PreExecutionNode < Node + @closing_loc: Location + + @opening_loc: Location + + @keyword_loc: Location + + @statements: StatementsNode? + + # Initialize a new PreExecutionNode node. + # -- + # : (Source source, Integer node_id, Location location, Integer flags, StatementsNode? statements, Location keyword_loc, Location opening_loc, Location closing_loc) -> void + def initialize: (Source source, Integer node_id, Location location, Integer flags, StatementsNode? statements, Location keyword_loc, Location opening_loc, Location closing_loc) -> void + + # See Node.accept. + # -- + # : (_Visitor visitor) -> untyped + def accept: (_Visitor visitor) -> untyped + + # See Node.child_nodes. + # -- + # : () -> Array[node?] + def child_nodes: () -> Array[node?] + + # See Node.each_child_node. + # -- + # : () -> Enumerator[node, void] + # : () { (node) -> void } -> void + def each_child_node: () -> Enumerator[node, void] + | () { (node) -> void } -> void + + # See Node.compact_child_nodes. + # -- + # : () -> Array[node] + def compact_child_nodes: () -> Array[node] + + # See Node.comment_targets. + # -- + # : () -> Array[node | Location] + def comment_targets: () -> Array[node | Location] + + # :call-seq: + # copy(**fields) -> PreExecutionNode + # + # Creates a copy of self with the given fields, using self as the template. + # -- + # : (?node_id: Integer, ?location: Location, ?flags: Integer, ?statements: StatementsNode?, ?keyword_loc: Location, ?opening_loc: Location, ?closing_loc: Location) -> PreExecutionNode + def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?statements: StatementsNode?, ?keyword_loc: Location, ?opening_loc: Location, ?closing_loc: Location) -> PreExecutionNode + + alias deconstruct child_nodes + + # : (Array[Symbol]? keys) -> Hash[Symbol, untyped] + def deconstruct_keys: (Array[Symbol]? keys) -> Hash[Symbol, untyped] + + # See `Node#type`. + # -- + # : () -> :pre_execution_node + def type: () -> :pre_execution_node + + # See `Node.type`. + # -- + # : () -> :pre_execution_node + def self.type: () -> :pre_execution_node + + # : () -> String + def inspect: () -> String + + # :call-seq: + # statements -> StatementsNode | nil + # + # Returns the `statements` attribute. + # -- + # : () -> StatementsNode? + def statements: () -> StatementsNode? + + # :category: Locations + # :call-seq: + # keyword_loc -> Location + # + # Returns the Location represented by `keyword_loc`. + # -- + # : () -> Location + def keyword_loc: () -> Location + + # :category: Repository + # Save the keyword_loc location using the given saved source so that + # it can be retrieved later. + # -- + # : (_Repository repository) -> Relocation::Entry + def save_keyword_loc: (_Repository repository) -> Relocation::Entry + + # :category: Locations + # :call-seq: + # opening_loc -> Location + # + # Returns the Location represented by `opening_loc`. + # -- + # : () -> Location + def opening_loc: () -> Location + + # :category: Repository + # Save the opening_loc location using the given saved source so that + # it can be retrieved later. + # -- + # : (_Repository repository) -> Relocation::Entry + def save_opening_loc: (_Repository repository) -> Relocation::Entry + + # :category: Locations + # :call-seq: + # closing_loc -> Location + # + # Returns the Location represented by `closing_loc`. + # -- + # : () -> Location + def closing_loc: () -> Location + + # :category: Repository + # Save the closing_loc location using the given saved source so that + # it can be retrieved later. + # -- + # : (_Repository repository) -> Relocation::Entry + def save_closing_loc: (_Repository repository) -> Relocation::Entry + + # :call-seq: + # keyword -> String + # + # Slice the location of keyword_loc from the source. + # -- + # : () -> String + def keyword: () -> String + + # :call-seq: + # opening -> String + # + # Slice the location of opening_loc from the source. + # -- + # : () -> String + def opening: () -> String + + # :call-seq: + # closing -> String + # + # Slice the location of closing_loc from the source. + # -- + # : () -> String + def closing: () -> String + + # : (untyped other) -> boolish + def ===: (untyped other) -> boolish + end + + # The top level node of any parse tree. + class ProgramNode < Node + @statements: StatementsNode + + @locals: Array[Symbol] + + # Initialize a new ProgramNode node. + # -- + # : (Source source, Integer node_id, Location location, Integer flags, Array[Symbol] locals, StatementsNode statements) -> void + def initialize: (Source source, Integer node_id, Location location, Integer flags, Array[Symbol] locals, StatementsNode statements) -> void + + # See Node.accept. + # -- + # : (_Visitor visitor) -> untyped + def accept: (_Visitor visitor) -> untyped + + # See Node.child_nodes. + # -- + # : () -> Array[node?] + def child_nodes: () -> Array[node?] + + # See Node.each_child_node. + # -- + # : () -> Enumerator[node, void] + # : () { (node) -> void } -> void + def each_child_node: () -> Enumerator[node, void] + | () { (node) -> void } -> void + + # See Node.compact_child_nodes. + # -- + # : () -> Array[node] + def compact_child_nodes: () -> Array[node] + + # See Node.comment_targets. + # -- + # : () -> Array[node | Location] + def comment_targets: () -> Array[node | Location] + + # :call-seq: + # copy(**fields) -> ProgramNode + # + # Creates a copy of self with the given fields, using self as the template. + # -- + # : (?node_id: Integer, ?location: Location, ?flags: Integer, ?locals: Array[Symbol], ?statements: StatementsNode) -> ProgramNode + def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?locals: Array[Symbol], ?statements: StatementsNode) -> ProgramNode + + alias deconstruct child_nodes + + # : (Array[Symbol]? keys) -> Hash[Symbol, untyped] + def deconstruct_keys: (Array[Symbol]? keys) -> Hash[Symbol, untyped] + + # See `Node#type`. + # -- + # : () -> :program_node + def type: () -> :program_node + + # See `Node.type`. + # -- + # : () -> :program_node + def self.type: () -> :program_node + + # : () -> String + def inspect: () -> String + + # :call-seq: + # locals -> Array[Symbol] + # + # Returns the `locals` attribute. + # -- + # : () -> Array[Symbol] + def locals: () -> Array[Symbol] + + # :call-seq: + # statements -> StatementsNode + # + # Returns the `statements` attribute. + # -- + # : () -> StatementsNode + def statements: () -> StatementsNode + + # : (untyped other) -> boolish + def ===: (untyped other) -> boolish + end + + # Represents the use of the `..` or `...` operators. + # + # 1..2 + # ^^^^ + # + # c if a =~ /left/ ... b =~ /right/ + # ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + class RangeNode < Node + @operator_loc: Location + + @right: Prism::node? + + @left: Prism::node? + + # Initialize a new RangeNode node. + # -- + # : (Source source, Integer node_id, Location location, Integer flags, Prism::node? left, Prism::node? right, Location operator_loc) -> void + def initialize: (Source source, Integer node_id, Location location, Integer flags, Prism::node? left, Prism::node? right, Location operator_loc) -> void + + # See Node.accept. + # -- + # : (_Visitor visitor) -> untyped + def accept: (_Visitor visitor) -> untyped + + # See Node.child_nodes. + # -- + # : () -> Array[node?] + def child_nodes: () -> Array[node?] + + # See Node.each_child_node. + # -- + # : () -> Enumerator[node, void] + # : () { (node) -> void } -> void + def each_child_node: () -> Enumerator[node, void] + | () { (node) -> void } -> void + + # See Node.compact_child_nodes. + # -- + # : () -> Array[node] + def compact_child_nodes: () -> Array[node] + + # See Node.comment_targets. + # -- + # : () -> Array[node | Location] + def comment_targets: () -> Array[node | Location] + + # :call-seq: + # copy(**fields) -> RangeNode + # + # Creates a copy of self with the given fields, using self as the template. + # -- + # : (?node_id: Integer, ?location: Location, ?flags: Integer, ?left: Prism::node?, ?right: Prism::node?, ?operator_loc: Location) -> RangeNode + def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?left: Prism::node?, ?right: Prism::node?, ?operator_loc: Location) -> RangeNode + + alias deconstruct child_nodes + + # : (Array[Symbol]? keys) -> Hash[Symbol, untyped] + def deconstruct_keys: (Array[Symbol]? keys) -> Hash[Symbol, untyped] + + # See `Node#type`. + # -- + # : () -> :range_node + def type: () -> :range_node + + # See `Node.type`. + # -- + # : () -> :range_node + def self.type: () -> :range_node + + # : () -> String + def inspect: () -> String + + # :category: Flags + # ... operator + # -- + # : () -> bool + def exclude_end?: () -> bool + + # :call-seq: + # left -> Node | nil + # + # 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 + # ^^^^^ + # -- + # : () -> Prism::node? + def left: () -> Prism::node? + + # :call-seq: + # right -> Node | nil + # + # 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. + # -- + # : () -> Prism::node? + def right: () -> Prism::node? + + # :category: Locations + # :call-seq: + # operator_loc -> Location + # + # The Location of the `..` or `...` operator. + # -- + # : () -> Location + def operator_loc: () -> Location + + # :category: Repository + # Save the operator_loc location using the given saved source so that + # it can be retrieved later. + # -- + # : (_Repository repository) -> Relocation::Entry + def save_operator_loc: (_Repository repository) -> Relocation::Entry + + # :call-seq: + # operator -> String + # + # Slice the location of operator_loc from the source. + # -- + # : () -> String + def operator: () -> String + + # : (untyped other) -> boolish + def ===: (untyped other) -> boolish + end + + # Represents a rational number literal. + # + # 1.0r + # ^^^^ + class RationalNode < Node + @denominator: Integer + + @numerator: Integer + + # Initialize a new RationalNode node. + # -- + # : (Source source, Integer node_id, Location location, Integer flags, Integer numerator, Integer denominator) -> void + def initialize: (Source source, Integer node_id, Location location, Integer flags, Integer numerator, Integer denominator) -> void + + # See Node.accept. + # -- + # : (_Visitor visitor) -> untyped + def accept: (_Visitor visitor) -> untyped + + # See Node.child_nodes. + # -- + # : () -> Array[node?] + def child_nodes: () -> Array[node?] + + # See Node.each_child_node. + # -- + # : () -> Enumerator[node, void] + # : () { (node) -> void } -> void + def each_child_node: () -> Enumerator[node, void] + | () { (node) -> void } -> void + + # See Node.compact_child_nodes. + # -- + # : () -> Array[node] + def compact_child_nodes: () -> Array[node] + + # See Node.comment_targets. + # -- + # : () -> Array[node | Location] + def comment_targets: () -> Array[node | Location] + + # :call-seq: + # copy(**fields) -> RationalNode + # + # Creates a copy of self with the given fields, using self as the template. + # -- + # : (?node_id: Integer, ?location: Location, ?flags: Integer, ?numerator: Integer, ?denominator: Integer) -> RationalNode + def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?numerator: Integer, ?denominator: Integer) -> RationalNode + + alias deconstruct child_nodes + + # : (Array[Symbol]? keys) -> Hash[Symbol, untyped] + def deconstruct_keys: (Array[Symbol]? keys) -> Hash[Symbol, untyped] + + # See `Node#type`. + # -- + # : () -> :rational_node + def type: () -> :rational_node + + # See `Node.type`. + # -- + # : () -> :rational_node + def self.type: () -> :rational_node + + # : () -> String + def inspect: () -> String + + # :category: Flags + # 0b prefix + # -- + # : () -> bool + def binary?: () -> bool + + # :category: Flags + # 0d or no prefix + # -- + # : () -> bool + def decimal?: () -> bool + + # :category: Flags + # 0o or 0 prefix + # -- + # : () -> bool + def octal?: () -> bool + + # :category: Flags + # 0x prefix + # -- + # : () -> bool + def hexadecimal?: () -> bool + + # :call-seq: + # numerator -> Integer + # + # The numerator of the rational number. + # + # 1.5r # numerator 3 + # -- + # : () -> Integer + def numerator: () -> Integer + + # :call-seq: + # denominator -> Integer + # + # The denominator of the rational number. + # + # 1.5r # denominator 2 + # -- + # : () -> Integer + def denominator: () -> Integer + + # : (untyped other) -> boolish + def ===: (untyped other) -> boolish + end + + # Represents the use of the `redo` keyword. + # + # redo + # ^^^^ + class RedoNode < Node + # Initialize a new RedoNode node. + # -- + # : (Source source, Integer node_id, Location location, Integer flags, ) -> void + def initialize: (Source source, Integer node_id, Location location, Integer flags) -> void + + # See Node.accept. + # -- + # : (_Visitor visitor) -> untyped + def accept: (_Visitor visitor) -> untyped + + # See Node.child_nodes. + # -- + # : () -> Array[node?] + def child_nodes: () -> Array[node?] + + # See Node.each_child_node. + # -- + # : () -> Enumerator[node, void] + # : () { (node) -> void } -> void + def each_child_node: () -> Enumerator[node, void] + | () { (node) -> void } -> void + + # See Node.compact_child_nodes. + # -- + # : () -> Array[node] + def compact_child_nodes: () -> Array[node] + + # See Node.comment_targets. + # -- + # : () -> Array[node | Location] + def comment_targets: () -> Array[node | Location] + + # :call-seq: + # copy(**fields) -> RedoNode + # + # Creates a copy of self with the given fields, using self as the template. + # -- + # : (?node_id: Integer, ?location: Location, ?flags: Integer, ) -> RedoNode + def copy: (?node_id: Integer, ?location: Location, ?flags: Integer) -> RedoNode + + alias deconstruct child_nodes + + # : (Array[Symbol]? keys) -> Hash[Symbol, untyped] + def deconstruct_keys: (Array[Symbol]? keys) -> Hash[Symbol, untyped] + + # See `Node#type`. + # -- + # : () -> :redo_node + def type: () -> :redo_node + + # See `Node.type`. + # -- + # : () -> :redo_node + def self.type: () -> :redo_node + + # : () -> String + def inspect: () -> String + + # : (untyped other) -> boolish + def ===: (untyped other) -> boolish + end + + # Represents a regular expression literal with no interpolation. + # + # /foo/i + # ^^^^^^ + class RegularExpressionNode < Node + @unescaped: String + + @closing_loc: Location + + @content_loc: Location + + @opening_loc: Location + + # Initialize a new RegularExpressionNode node. + # -- + # : (Source source, Integer node_id, Location location, Integer flags, Location opening_loc, Location content_loc, Location closing_loc, String unescaped) -> void + def initialize: (Source source, Integer node_id, Location location, Integer flags, Location opening_loc, Location content_loc, Location closing_loc, String unescaped) -> void + + # See Node.accept. + # -- + # : (_Visitor visitor) -> untyped + def accept: (_Visitor visitor) -> untyped + + # See Node.child_nodes. + # -- + # : () -> Array[node?] + def child_nodes: () -> Array[node?] + + # See Node.each_child_node. + # -- + # : () -> Enumerator[node, void] + # : () { (node) -> void } -> void + def each_child_node: () -> Enumerator[node, void] + | () { (node) -> void } -> void + + # See Node.compact_child_nodes. + # -- + # : () -> Array[node] + def compact_child_nodes: () -> Array[node] + + # See Node.comment_targets. + # -- + # : () -> Array[node | Location] + def comment_targets: () -> Array[node | Location] + + # :call-seq: + # copy(**fields) -> RegularExpressionNode + # + # Creates a copy of self with the given fields, using self as the template. + # -- + # : (?node_id: Integer, ?location: Location, ?flags: Integer, ?opening_loc: Location, ?content_loc: Location, ?closing_loc: Location, ?unescaped: String) -> RegularExpressionNode + def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?opening_loc: Location, ?content_loc: Location, ?closing_loc: Location, ?unescaped: String) -> RegularExpressionNode + + alias deconstruct child_nodes + + # : (Array[Symbol]? keys) -> Hash[Symbol, untyped] + def deconstruct_keys: (Array[Symbol]? keys) -> Hash[Symbol, untyped] + + # See `Node#type`. + # -- + # : () -> :regular_expression_node + def type: () -> :regular_expression_node + + # See `Node.type`. + # -- + # : () -> :regular_expression_node + def self.type: () -> :regular_expression_node + + # : () -> String + def inspect: () -> String + + # :category: Flags + # i - ignores the case of characters when matching + # -- + # : () -> bool + def ignore_case?: () -> bool + + # :category: Flags + # x - ignores whitespace and allows comments in regular expressions + # -- + # : () -> bool + def extended?: () -> bool + + # :category: Flags + # m - allows $ to match the end of lines within strings + # -- + # : () -> bool + def multi_line?: () -> bool + + # :category: Flags + # o - only interpolates values into the regular expression once + # -- + # : () -> bool + def once?: () -> bool + + # :category: Flags + # e - forces the EUC-JP encoding + # -- + # : () -> bool + def euc_jp?: () -> bool + + # :category: Flags + # n - forces the ASCII-8BIT encoding + # -- + # : () -> bool + def ascii_8bit?: () -> bool + + # :category: Flags + # s - forces the Windows-31J encoding + # -- + # : () -> bool + def windows_31j?: () -> bool + + # :category: Flags + # u - forces the UTF-8 encoding + # -- + # : () -> bool + def utf_8?: () -> bool + + # :category: Flags + # internal bytes forced the encoding to UTF-8 + # -- + # : () -> bool + def forced_utf8_encoding?: () -> bool + + # :category: Flags + # internal bytes forced the encoding to binary + # -- + # : () -> bool + def forced_binary_encoding?: () -> bool + + # :category: Flags + # internal bytes forced the encoding to US-ASCII + # -- + # : () -> bool + def forced_us_ascii_encoding?: () -> bool + + # :category: Locations + # :call-seq: + # opening_loc -> Location + # + # Returns the Location represented by `opening_loc`. + # -- + # : () -> Location + def opening_loc: () -> Location + + # :category: Repository + # Save the opening_loc location using the given saved source so that + # it can be retrieved later. + # -- + # : (_Repository repository) -> Relocation::Entry + def save_opening_loc: (_Repository repository) -> Relocation::Entry + + # :category: Locations + # :call-seq: + # content_loc -> Location + # + # Returns the Location represented by `content_loc`. + # -- + # : () -> Location + def content_loc: () -> Location + + # :category: Repository + # Save the content_loc location using the given saved source so that + # it can be retrieved later. + # -- + # : (_Repository repository) -> Relocation::Entry + def save_content_loc: (_Repository repository) -> Relocation::Entry + + # :category: Locations + # :call-seq: + # closing_loc -> Location + # + # Returns the Location represented by `closing_loc`. + # -- + # : () -> Location + def closing_loc: () -> Location + + # :category: Repository + # Save the closing_loc location using the given saved source so that + # it can be retrieved later. + # -- + # : (_Repository repository) -> Relocation::Entry + def save_closing_loc: (_Repository repository) -> Relocation::Entry + + # :call-seq: + # unescaped -> String + # + # Returns the `unescaped` attribute. + # -- + # : () -> String + def unescaped: () -> String + + # :call-seq: + # opening -> String + # + # Slice the location of opening_loc from the source. + # -- + # : () -> String + def opening: () -> String + + # :call-seq: + # content -> String + # + # Slice the location of content_loc from the source. + # -- + # : () -> String + def content: () -> String + + # :call-seq: + # closing -> String + # + # Slice the location of closing_loc from the source. + # -- + # : () -> String + def closing: () -> String + + # : (untyped other) -> boolish + def ===: (untyped other) -> boolish + end + + # Represents a required keyword parameter to a method, block, or lambda definition. + # + # def a(b: ) + # ^^ + # end + class RequiredKeywordParameterNode < Node + @name_loc: Location + + @name: Symbol + + # Initialize a new RequiredKeywordParameterNode node. + # -- + # : (Source source, Integer node_id, Location location, Integer flags, Symbol name, Location name_loc) -> void + def initialize: (Source source, Integer node_id, Location location, Integer flags, Symbol name, Location name_loc) -> void + + # See Node.accept. + # -- + # : (_Visitor visitor) -> untyped + def accept: (_Visitor visitor) -> untyped + + # See Node.child_nodes. + # -- + # : () -> Array[node?] + def child_nodes: () -> Array[node?] + + # See Node.each_child_node. + # -- + # : () -> Enumerator[node, void] + # : () { (node) -> void } -> void + def each_child_node: () -> Enumerator[node, void] + | () { (node) -> void } -> void + + # See Node.compact_child_nodes. + # -- + # : () -> Array[node] + def compact_child_nodes: () -> Array[node] + + # See Node.comment_targets. + # -- + # : () -> Array[node | Location] + def comment_targets: () -> Array[node | Location] + + # :call-seq: + # copy(**fields) -> RequiredKeywordParameterNode + # + # Creates a copy of self with the given fields, using self as the template. + # -- + # : (?node_id: Integer, ?location: Location, ?flags: Integer, ?name: Symbol, ?name_loc: Location) -> RequiredKeywordParameterNode + def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?name: Symbol, ?name_loc: Location) -> RequiredKeywordParameterNode + + alias deconstruct child_nodes + + # : (Array[Symbol]? keys) -> Hash[Symbol, untyped] + def deconstruct_keys: (Array[Symbol]? keys) -> Hash[Symbol, untyped] + + # See `Node#type`. + # -- + # : () -> :required_keyword_parameter_node + def type: () -> :required_keyword_parameter_node + + # See `Node.type`. + # -- + # : () -> :required_keyword_parameter_node + def self.type: () -> :required_keyword_parameter_node + + # : () -> String + def inspect: () -> String + + # :category: Flags + # a parameter name that has been repeated in the method signature + # -- + # : () -> bool + def repeated_parameter?: () -> bool + + # :call-seq: + # name -> Symbol + # + # Returns the `name` attribute. + # -- + # : () -> Symbol + def name: () -> Symbol + + # :category: Locations + # :call-seq: + # name_loc -> Location + # + # Returns the Location represented by `name_loc`. + # -- + # : () -> Location + def name_loc: () -> Location + + # :category: Repository + # Save the name_loc location using the given saved source so that + # it can be retrieved later. + # -- + # : (_Repository repository) -> Relocation::Entry + def save_name_loc: (_Repository repository) -> Relocation::Entry + + # : (untyped other) -> boolish + def ===: (untyped other) -> boolish + end + + # Represents a required parameter to a method, block, or lambda definition. + # + # def a(b) + # ^ + # end + class RequiredParameterNode < Node + @name: Symbol + + # Initialize a new RequiredParameterNode node. + # -- + # : (Source source, Integer node_id, Location location, Integer flags, Symbol name) -> void + def initialize: (Source source, Integer node_id, Location location, Integer flags, Symbol name) -> void + + # See Node.accept. + # -- + # : (_Visitor visitor) -> untyped + def accept: (_Visitor visitor) -> untyped + + # See Node.child_nodes. + # -- + # : () -> Array[node?] + def child_nodes: () -> Array[node?] + + # See Node.each_child_node. + # -- + # : () -> Enumerator[node, void] + # : () { (node) -> void } -> void + def each_child_node: () -> Enumerator[node, void] + | () { (node) -> void } -> void + + # See Node.compact_child_nodes. + # -- + # : () -> Array[node] + def compact_child_nodes: () -> Array[node] + + # See Node.comment_targets. + # -- + # : () -> Array[node | Location] + def comment_targets: () -> Array[node | Location] + + # :call-seq: + # copy(**fields) -> RequiredParameterNode + # + # Creates a copy of self with the given fields, using self as the template. + # -- + # : (?node_id: Integer, ?location: Location, ?flags: Integer, ?name: Symbol) -> RequiredParameterNode + def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?name: Symbol) -> RequiredParameterNode + + alias deconstruct child_nodes + + # : (Array[Symbol]? keys) -> Hash[Symbol, untyped] + def deconstruct_keys: (Array[Symbol]? keys) -> Hash[Symbol, untyped] + + # See `Node#type`. + # -- + # : () -> :required_parameter_node + def type: () -> :required_parameter_node + + # See `Node.type`. + # -- + # : () -> :required_parameter_node + def self.type: () -> :required_parameter_node + + # : () -> String + def inspect: () -> String + + # :category: Flags + # a parameter name that has been repeated in the method signature + # -- + # : () -> bool + def repeated_parameter?: () -> bool + + # :call-seq: + # name -> Symbol + # + # Returns the `name` attribute. + # -- + # : () -> Symbol + def name: () -> Symbol + + # : (untyped other) -> boolish + def ===: (untyped other) -> boolish + end + + # Represents an expression modified with a rescue. + # + # foo rescue nil + # ^^^^^^^^^^^^^^ + class RescueModifierNode < Node + @rescue_expression: Prism::node + + @keyword_loc: Location + + @expression: Prism::node + + # Initialize a new RescueModifierNode node. + # -- + # : (Source source, Integer node_id, Location location, Integer flags, Prism::node expression, Location keyword_loc, Prism::node rescue_expression) -> void + def initialize: (Source source, Integer node_id, Location location, Integer flags, Prism::node expression, Location keyword_loc, Prism::node rescue_expression) -> void + + # See Node.accept. + # -- + # : (_Visitor visitor) -> untyped + def accept: (_Visitor visitor) -> untyped + + # See Node.child_nodes. + # -- + # : () -> Array[node?] + def child_nodes: () -> Array[node?] + + # See Node.each_child_node. + # -- + # : () -> Enumerator[node, void] + # : () { (node) -> void } -> void + def each_child_node: () -> Enumerator[node, void] + | () { (node) -> void } -> void + + # See Node.compact_child_nodes. + # -- + # : () -> Array[node] + def compact_child_nodes: () -> Array[node] + + # See Node.comment_targets. + # -- + # : () -> Array[node | Location] + def comment_targets: () -> Array[node | Location] + + # :call-seq: + # copy(**fields) -> RescueModifierNode + # + # Creates a copy of self with the given fields, using self as the template. + # -- + # : (?node_id: Integer, ?location: Location, ?flags: Integer, ?expression: Prism::node, ?keyword_loc: Location, ?rescue_expression: Prism::node) -> RescueModifierNode + def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?expression: Prism::node, ?keyword_loc: Location, ?rescue_expression: Prism::node) -> RescueModifierNode + + alias deconstruct child_nodes + + # : (Array[Symbol]? keys) -> Hash[Symbol, untyped] + def deconstruct_keys: (Array[Symbol]? keys) -> Hash[Symbol, untyped] + + # See `Node#type`. + # -- + # : () -> :rescue_modifier_node + def type: () -> :rescue_modifier_node + + # See `Node.type`. + # -- + # : () -> :rescue_modifier_node + def self.type: () -> :rescue_modifier_node + + # : () -> String + def inspect: () -> String + + # :call-seq: + # expression -> Node + # + # Returns the `expression` attribute. + # -- + # : () -> Prism::node + def expression: () -> Prism::node + + # :category: Locations + # :call-seq: + # keyword_loc -> Location + # + # Returns the Location represented by `keyword_loc`. + # -- + # : () -> Location + def keyword_loc: () -> Location + + # :category: Repository + # Save the keyword_loc location using the given saved source so that + # it can be retrieved later. + # -- + # : (_Repository repository) -> Relocation::Entry + def save_keyword_loc: (_Repository repository) -> Relocation::Entry + + # :call-seq: + # rescue_expression -> Node + # + # Returns the `rescue_expression` attribute. + # -- + # : () -> Prism::node + def rescue_expression: () -> Prism::node + + # :call-seq: + # keyword -> String + # + # Slice the location of keyword_loc from the source. + # -- + # : () -> String + def keyword: () -> String + + # : (untyped other) -> boolish + def ===: (untyped other) -> boolish + 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 + @subsequent: RescueNode? + + @statements: StatementsNode? + + @then_keyword_loc: Location? + + @reference: (LocalVariableTargetNode | InstanceVariableTargetNode | ClassVariableTargetNode | GlobalVariableTargetNode | ConstantTargetNode | ConstantPathTargetNode | CallTargetNode | IndexTargetNode | BackReferenceReadNode | NumberedReferenceReadNode | MissingNode)? + + @operator_loc: Location? + + @exceptions: Array[Prism::node] + + @keyword_loc: Location + + # Initialize a new RescueNode node. + # -- + # : (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)? reference, Location? then_keyword_loc, StatementsNode? statements, RescueNode? subsequent) -> 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)? reference, Location? then_keyword_loc, StatementsNode? statements, RescueNode? subsequent) -> void + + # See Node.accept. + # -- + # : (_Visitor visitor) -> untyped + def accept: (_Visitor visitor) -> untyped + + # See Node.child_nodes. + # -- + # : () -> Array[node?] + def child_nodes: () -> Array[node?] + + # See Node.each_child_node. + # -- + # : () -> Enumerator[node, void] + # : () { (node) -> void } -> void + def each_child_node: () -> Enumerator[node, void] + | () { (node) -> void } -> void + + # See Node.compact_child_nodes. + # -- + # : () -> Array[node] + def compact_child_nodes: () -> Array[node] + + # See Node.comment_targets. + # -- + # : () -> Array[node | Location] + def comment_targets: () -> Array[node | Location] + + # :call-seq: + # copy(**fields) -> RescueNode + # + # Creates a copy of self with the given fields, using self as the template. + # -- + # : (?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)?, ?then_keyword_loc: Location?, ?statements: StatementsNode?, ?subsequent: RescueNode?) -> RescueNode + 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)?, ?then_keyword_loc: Location?, ?statements: StatementsNode?, ?subsequent: RescueNode?) -> RescueNode + + alias deconstruct child_nodes + + # : (Array[Symbol]? keys) -> Hash[Symbol, untyped] + def deconstruct_keys: (Array[Symbol]? keys) -> Hash[Symbol, untyped] + + # See `Node#type`. + # -- + # : () -> :rescue_node + def type: () -> :rescue_node + + # See `Node.type`. + # -- + # : () -> :rescue_node + def self.type: () -> :rescue_node + + # : () -> String + def inspect: () -> String + + # :category: Locations + # :call-seq: + # keyword_loc -> Location + # + # Returns the Location represented by `keyword_loc`. + # -- + # : () -> Location + def keyword_loc: () -> Location + + # :category: Repository + # Save the keyword_loc location using the given saved source so that + # it can be retrieved later. + # -- + # : (_Repository repository) -> Relocation::Entry + def save_keyword_loc: (_Repository repository) -> Relocation::Entry + + # :call-seq: + # exceptions -> Array[Node] + # + # Returns the `exceptions` attribute. + # -- + # : () -> Array[Prism::node] + def exceptions: () -> Array[Prism::node] + + # :category: Locations + # :call-seq: + # operator_loc -> Location | nil + # + # Returns the Location represented by `operator_loc`. + # -- + # : () -> Location? + def operator_loc: () -> Location? + + # :category: Repository + # Save the operator_loc location using the given saved source so that + # it can be retrieved later. + # -- + # : (_Repository repository) -> Relocation::Entry? + def save_operator_loc: (_Repository repository) -> Relocation::Entry? + + # :call-seq: + # reference -> LocalVariableTargetNode | InstanceVariableTargetNode | ClassVariableTargetNode | GlobalVariableTargetNode | ConstantTargetNode | ConstantPathTargetNode | CallTargetNode | IndexTargetNode | BackReferenceReadNode | NumberedReferenceReadNode | MissingNode | nil + # + # Returns the `reference` attribute. + # -- + # : () -> (LocalVariableTargetNode | InstanceVariableTargetNode | ClassVariableTargetNode | GlobalVariableTargetNode | ConstantTargetNode | ConstantPathTargetNode | CallTargetNode | IndexTargetNode | BackReferenceReadNode | NumberedReferenceReadNode | MissingNode)? + def reference: () -> (LocalVariableTargetNode | InstanceVariableTargetNode | ClassVariableTargetNode | GlobalVariableTargetNode | ConstantTargetNode | ConstantPathTargetNode | CallTargetNode | IndexTargetNode | BackReferenceReadNode | NumberedReferenceReadNode | MissingNode)? + + # :category: Locations + # :call-seq: + # then_keyword_loc -> Location | nil + # + # Returns the Location represented by `then_keyword_loc`. + # -- + # : () -> Location? + def then_keyword_loc: () -> Location? + + # :category: Repository + # Save the then_keyword_loc location using the given saved source so that + # it can be retrieved later. + # -- + # : (_Repository repository) -> Relocation::Entry? + def save_then_keyword_loc: (_Repository repository) -> Relocation::Entry? + + # :call-seq: + # statements -> StatementsNode | nil + # + # Returns the `statements` attribute. + # -- + # : () -> StatementsNode? + def statements: () -> StatementsNode? + + # :call-seq: + # subsequent -> RescueNode | nil + # + # Returns the `subsequent` attribute. + # -- + # : () -> RescueNode? + def subsequent: () -> RescueNode? + + # :call-seq: + # keyword -> String + # + # Slice the location of keyword_loc from the source. + # -- + # : () -> String + def keyword: () -> String + + # :call-seq: + # operator -> String | nil + # + # Slice the location of operator_loc from the source. + # -- + # : () -> String? + def operator: () -> String? + + # :call-seq: + # then_keyword -> String | nil + # + # Slice the location of then_keyword_loc from the source. + # -- + # : () -> String? + def then_keyword: () -> String? + + # : (untyped other) -> boolish + def ===: (untyped other) -> boolish + end + + # Represents a rest parameter to a method, block, or lambda definition. + # + # def a(*b) + # ^^ + # end + class RestParameterNode < Node + @operator_loc: Location + + @name_loc: Location? + + @name: Symbol? + + # Initialize a new RestParameterNode node. + # -- + # : (Source source, Integer node_id, Location location, Integer flags, Symbol? name, Location? name_loc, Location operator_loc) -> void + def initialize: (Source source, Integer node_id, Location location, Integer flags, Symbol? name, Location? name_loc, Location operator_loc) -> void + + # See Node.accept. + # -- + # : (_Visitor visitor) -> untyped + def accept: (_Visitor visitor) -> untyped + + # See Node.child_nodes. + # -- + # : () -> Array[node?] + def child_nodes: () -> Array[node?] + + # See Node.each_child_node. + # -- + # : () -> Enumerator[node, void] + # : () { (node) -> void } -> void + def each_child_node: () -> Enumerator[node, void] + | () { (node) -> void } -> void + + # See Node.compact_child_nodes. + # -- + # : () -> Array[node] + def compact_child_nodes: () -> Array[node] + + # See Node.comment_targets. + # -- + # : () -> Array[node | Location] + def comment_targets: () -> Array[node | Location] + + # :call-seq: + # copy(**fields) -> RestParameterNode + # + # Creates a copy of self with the given fields, using self as the template. + # -- + # : (?node_id: Integer, ?location: Location, ?flags: Integer, ?name: Symbol?, ?name_loc: Location?, ?operator_loc: Location) -> RestParameterNode + def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?name: Symbol?, ?name_loc: Location?, ?operator_loc: Location) -> RestParameterNode + + alias deconstruct child_nodes + + # : (Array[Symbol]? keys) -> Hash[Symbol, untyped] + def deconstruct_keys: (Array[Symbol]? keys) -> Hash[Symbol, untyped] + + # See `Node#type`. + # -- + # : () -> :rest_parameter_node + def type: () -> :rest_parameter_node + + # See `Node.type`. + # -- + # : () -> :rest_parameter_node + def self.type: () -> :rest_parameter_node + + # : () -> String + def inspect: () -> String + + # :category: Flags + # a parameter name that has been repeated in the method signature + # -- + # : () -> bool + def repeated_parameter?: () -> bool + + # :call-seq: + # name -> Symbol | nil + # + # Returns the `name` attribute. + # -- + # : () -> Symbol? + def name: () -> Symbol? + + # :category: Locations + # :call-seq: + # name_loc -> Location | nil + # + # Returns the Location represented by `name_loc`. + # -- + # : () -> Location? + def name_loc: () -> Location? + + # :category: Repository + # Save the name_loc location using the given saved source so that + # it can be retrieved later. + # -- + # : (_Repository repository) -> Relocation::Entry? + def save_name_loc: (_Repository repository) -> Relocation::Entry? + + # :category: Locations + # :call-seq: + # operator_loc -> Location + # + # Returns the Location represented by `operator_loc`. + # -- + # : () -> Location + def operator_loc: () -> Location + + # :category: Repository + # Save the operator_loc location using the given saved source so that + # it can be retrieved later. + # -- + # : (_Repository repository) -> Relocation::Entry + def save_operator_loc: (_Repository repository) -> Relocation::Entry + + # :call-seq: + # operator -> String + # + # Slice the location of operator_loc from the source. + # -- + # : () -> String + def operator: () -> String + + # : (untyped other) -> boolish + def ===: (untyped other) -> boolish + end + + # Represents the use of the `retry` keyword. + # + # retry + # ^^^^^ + class RetryNode < Node + # Initialize a new RetryNode node. + # -- + # : (Source source, Integer node_id, Location location, Integer flags, ) -> void + def initialize: (Source source, Integer node_id, Location location, Integer flags) -> void + + # See Node.accept. + # -- + # : (_Visitor visitor) -> untyped + def accept: (_Visitor visitor) -> untyped + + # See Node.child_nodes. + # -- + # : () -> Array[node?] + def child_nodes: () -> Array[node?] + + # See Node.each_child_node. + # -- + # : () -> Enumerator[node, void] + # : () { (node) -> void } -> void + def each_child_node: () -> Enumerator[node, void] + | () { (node) -> void } -> void + + # See Node.compact_child_nodes. + # -- + # : () -> Array[node] + def compact_child_nodes: () -> Array[node] + + # See Node.comment_targets. + # -- + # : () -> Array[node | Location] + def comment_targets: () -> Array[node | Location] + + # :call-seq: + # copy(**fields) -> RetryNode + # + # Creates a copy of self with the given fields, using self as the template. + # -- + # : (?node_id: Integer, ?location: Location, ?flags: Integer, ) -> RetryNode + def copy: (?node_id: Integer, ?location: Location, ?flags: Integer) -> RetryNode + + alias deconstruct child_nodes + + # : (Array[Symbol]? keys) -> Hash[Symbol, untyped] + def deconstruct_keys: (Array[Symbol]? keys) -> Hash[Symbol, untyped] + + # See `Node#type`. + # -- + # : () -> :retry_node + def type: () -> :retry_node + + # See `Node.type`. + # -- + # : () -> :retry_node + def self.type: () -> :retry_node + + # : () -> String + def inspect: () -> String + + # : (untyped other) -> boolish + def ===: (untyped other) -> boolish + end + + # Represents the use of the `return` keyword. + # + # return 1 + # ^^^^^^^^ + class ReturnNode < Node + @arguments: ArgumentsNode? + + @keyword_loc: Location + + # Initialize a new ReturnNode node. + # -- + # : (Source source, Integer node_id, Location location, Integer flags, Location keyword_loc, ArgumentsNode? arguments) -> void + def initialize: (Source source, Integer node_id, Location location, Integer flags, Location keyword_loc, ArgumentsNode? arguments) -> void + + # See Node.accept. + # -- + # : (_Visitor visitor) -> untyped + def accept: (_Visitor visitor) -> untyped + + # See Node.child_nodes. + # -- + # : () -> Array[node?] + def child_nodes: () -> Array[node?] + + # See Node.each_child_node. + # -- + # : () -> Enumerator[node, void] + # : () { (node) -> void } -> void + def each_child_node: () -> Enumerator[node, void] + | () { (node) -> void } -> void + + # See Node.compact_child_nodes. + # -- + # : () -> Array[node] + def compact_child_nodes: () -> Array[node] + + # See Node.comment_targets. + # -- + # : () -> Array[node | Location] + def comment_targets: () -> Array[node | Location] + + # :call-seq: + # copy(**fields) -> ReturnNode + # + # Creates a copy of self with the given fields, using self as the template. + # -- + # : (?node_id: Integer, ?location: Location, ?flags: Integer, ?keyword_loc: Location, ?arguments: ArgumentsNode?) -> ReturnNode + def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?keyword_loc: Location, ?arguments: ArgumentsNode?) -> ReturnNode + + alias deconstruct child_nodes + + # : (Array[Symbol]? keys) -> Hash[Symbol, untyped] + def deconstruct_keys: (Array[Symbol]? keys) -> Hash[Symbol, untyped] + + # See `Node#type`. + # -- + # : () -> :return_node + def type: () -> :return_node + + # See `Node.type`. + # -- + # : () -> :return_node + def self.type: () -> :return_node + + # : () -> String + def inspect: () -> String + + # :category: Locations + # :call-seq: + # keyword_loc -> Location + # + # Returns the Location represented by `keyword_loc`. + # -- + # : () -> Location + def keyword_loc: () -> Location + + # :category: Repository + # Save the keyword_loc location using the given saved source so that + # it can be retrieved later. + # -- + # : (_Repository repository) -> Relocation::Entry + def save_keyword_loc: (_Repository repository) -> Relocation::Entry + + # :call-seq: + # arguments -> ArgumentsNode | nil + # + # Returns the `arguments` attribute. + # -- + # : () -> ArgumentsNode? + def arguments: () -> ArgumentsNode? + + # :call-seq: + # keyword -> String + # + # Slice the location of keyword_loc from the source. + # -- + # : () -> String + def keyword: () -> String + + # : (untyped other) -> boolish + def ===: (untyped other) -> boolish + end + + # Represents the `self` keyword. + # + # self + # ^^^^ + class SelfNode < Node + # Initialize a new SelfNode node. + # -- + # : (Source source, Integer node_id, Location location, Integer flags, ) -> void + def initialize: (Source source, Integer node_id, Location location, Integer flags) -> void + + # See Node.accept. + # -- + # : (_Visitor visitor) -> untyped + def accept: (_Visitor visitor) -> untyped + + # See Node.child_nodes. + # -- + # : () -> Array[node?] + def child_nodes: () -> Array[node?] + + # See Node.each_child_node. + # -- + # : () -> Enumerator[node, void] + # : () { (node) -> void } -> void + def each_child_node: () -> Enumerator[node, void] + | () { (node) -> void } -> void + + # See Node.compact_child_nodes. + # -- + # : () -> Array[node] + def compact_child_nodes: () -> Array[node] + + # See Node.comment_targets. + # -- + # : () -> Array[node | Location] + def comment_targets: () -> Array[node | Location] + + # :call-seq: + # copy(**fields) -> SelfNode + # + # Creates a copy of self with the given fields, using self as the template. + # -- + # : (?node_id: Integer, ?location: Location, ?flags: Integer, ) -> SelfNode + def copy: (?node_id: Integer, ?location: Location, ?flags: Integer) -> SelfNode + + alias deconstruct child_nodes + + # : (Array[Symbol]? keys) -> Hash[Symbol, untyped] + def deconstruct_keys: (Array[Symbol]? keys) -> Hash[Symbol, untyped] + + # See `Node#type`. + # -- + # : () -> :self_node + def type: () -> :self_node + + # See `Node.type`. + # -- + # : () -> :self_node + def self.type: () -> :self_node + + # : () -> String + def inspect: () -> String + + # : (untyped other) -> boolish + def ===: (untyped other) -> boolish + 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 + @write: ConstantWriteNode | ConstantAndWriteNode | ConstantOrWriteNode | ConstantOperatorWriteNode | ConstantPathWriteNode | ConstantPathAndWriteNode | ConstantPathOrWriteNode | ConstantPathOperatorWriteNode + + # Initialize a new ShareableConstantNode node. + # -- + # : (Source source, Integer node_id, Location location, Integer flags, (ConstantWriteNode | ConstantAndWriteNode | ConstantOrWriteNode | ConstantOperatorWriteNode | ConstantPathWriteNode | ConstantPathAndWriteNode | ConstantPathOrWriteNode | ConstantPathOperatorWriteNode) write) -> void + def initialize: (Source source, Integer node_id, Location location, Integer flags, ConstantWriteNode | ConstantAndWriteNode | ConstantOrWriteNode | ConstantOperatorWriteNode | ConstantPathWriteNode | ConstantPathAndWriteNode | ConstantPathOrWriteNode | ConstantPathOperatorWriteNode write) -> void + + # See Node.accept. + # -- + # : (_Visitor visitor) -> untyped + def accept: (_Visitor visitor) -> untyped + + # See Node.child_nodes. + # -- + # : () -> Array[node?] + def child_nodes: () -> Array[node?] + + # See Node.each_child_node. + # -- + # : () -> Enumerator[node, void] + # : () { (node) -> void } -> void + def each_child_node: () -> Enumerator[node, void] + | () { (node) -> void } -> void + + # See Node.compact_child_nodes. + # -- + # : () -> Array[node] + def compact_child_nodes: () -> Array[node] + + # See Node.comment_targets. + # -- + # : () -> Array[node | Location] + def comment_targets: () -> Array[node | Location] + + # :call-seq: + # copy(**fields) -> ShareableConstantNode + # + # Creates a copy of self with the given fields, using self as the template. + # -- + # : (?node_id: Integer, ?location: Location, ?flags: Integer, ?write: (ConstantWriteNode | ConstantAndWriteNode | ConstantOrWriteNode | ConstantOperatorWriteNode | ConstantPathWriteNode | ConstantPathAndWriteNode | ConstantPathOrWriteNode | ConstantPathOperatorWriteNode)) -> ShareableConstantNode + def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?write: ConstantWriteNode | ConstantAndWriteNode | ConstantOrWriteNode | ConstantOperatorWriteNode | ConstantPathWriteNode | ConstantPathAndWriteNode | ConstantPathOrWriteNode | ConstantPathOperatorWriteNode) -> ShareableConstantNode + + alias deconstruct child_nodes + + # : (Array[Symbol]? keys) -> Hash[Symbol, untyped] + def deconstruct_keys: (Array[Symbol]? keys) -> Hash[Symbol, untyped] + + # See `Node#type`. + # -- + # : () -> :shareable_constant_node + def type: () -> :shareable_constant_node + + # See `Node.type`. + # -- + # : () -> :shareable_constant_node + def self.type: () -> :shareable_constant_node + + # : () -> String + def inspect: () -> String + + # :category: Flags + # constant writes that should be modified with shareable constant value literal + # -- + # : () -> bool + def literal?: () -> bool + + # :category: Flags + # constant writes that should be modified with shareable constant value experimental everything + # -- + # : () -> bool + def experimental_everything?: () -> bool + + # :category: Flags + # constant writes that should be modified with shareable constant value experimental copy + # -- + # : () -> bool + def experimental_copy?: () -> bool + + # :call-seq: + # write -> ConstantWriteNode | ConstantAndWriteNode | ConstantOrWriteNode | ConstantOperatorWriteNode | ConstantPathWriteNode | ConstantPathAndWriteNode | ConstantPathOrWriteNode | ConstantPathOperatorWriteNode + # + # The constant write that should be modified with the shareability state. + # -- + # : () -> (ConstantWriteNode | ConstantAndWriteNode | ConstantOrWriteNode | ConstantOperatorWriteNode | ConstantPathWriteNode | ConstantPathAndWriteNode | ConstantPathOrWriteNode | ConstantPathOperatorWriteNode) + def write: () -> (ConstantWriteNode | ConstantAndWriteNode | ConstantOrWriteNode | ConstantOperatorWriteNode | ConstantPathWriteNode | ConstantPathAndWriteNode | ConstantPathOrWriteNode | ConstantPathOperatorWriteNode) + + # : (untyped other) -> boolish + def ===: (untyped other) -> boolish + end + + # Represents a singleton class declaration involving the `class` keyword. + # + # class << self end + # ^^^^^^^^^^^^^^^^^ + class SingletonClassNode < Node + @end_keyword_loc: Location + + @body: (StatementsNode | BeginNode)? + + @expression: Prism::node + + @operator_loc: Location + + @class_keyword_loc: Location + + @locals: Array[Symbol] + + # Initialize a new SingletonClassNode node. + # -- + # : (Source source, Integer node_id, Location location, Integer flags, Array[Symbol] locals, Location class_keyword_loc, Location operator_loc, Prism::node expression, (StatementsNode | BeginNode)? body, Location end_keyword_loc) -> 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)? body, Location end_keyword_loc) -> void + + # See Node.accept. + # -- + # : (_Visitor visitor) -> untyped + def accept: (_Visitor visitor) -> untyped + + # See Node.child_nodes. + # -- + # : () -> Array[node?] + def child_nodes: () -> Array[node?] + + # See Node.each_child_node. + # -- + # : () -> Enumerator[node, void] + # : () { (node) -> void } -> void + def each_child_node: () -> Enumerator[node, void] + | () { (node) -> void } -> void + + # See Node.compact_child_nodes. + # -- + # : () -> Array[node] + def compact_child_nodes: () -> Array[node] + + # See Node.comment_targets. + # -- + # : () -> Array[node | Location] + def comment_targets: () -> Array[node | Location] + + # :call-seq: + # copy(**fields) -> SingletonClassNode + # + # Creates a copy of self with the given fields, using self as the template. + # -- + # : (?node_id: Integer, ?location: Location, ?flags: Integer, ?locals: Array[Symbol], ?class_keyword_loc: Location, ?operator_loc: Location, ?expression: Prism::node, ?body: (StatementsNode | BeginNode)?, ?end_keyword_loc: Location) -> SingletonClassNode + 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)?, ?end_keyword_loc: Location) -> SingletonClassNode + + alias deconstruct child_nodes + + # : (Array[Symbol]? keys) -> Hash[Symbol, untyped] + def deconstruct_keys: (Array[Symbol]? keys) -> Hash[Symbol, untyped] + + # See `Node#type`. + # -- + # : () -> :singleton_class_node + def type: () -> :singleton_class_node + + # See `Node.type`. + # -- + # : () -> :singleton_class_node + def self.type: () -> :singleton_class_node + + # : () -> String + def inspect: () -> String + + # :call-seq: + # locals -> Array[Symbol] + # + # Returns the `locals` attribute. + # -- + # : () -> Array[Symbol] + def locals: () -> Array[Symbol] + + # :category: Locations + # :call-seq: + # class_keyword_loc -> Location + # + # Returns the Location represented by `class_keyword_loc`. + # -- + # : () -> Location + def class_keyword_loc: () -> Location + + # :category: Repository + # Save the class_keyword_loc location using the given saved source so that + # it can be retrieved later. + # -- + # : (_Repository repository) -> Relocation::Entry + def save_class_keyword_loc: (_Repository repository) -> Relocation::Entry + + # :category: Locations + # :call-seq: + # operator_loc -> Location + # + # Returns the Location represented by `operator_loc`. + # -- + # : () -> Location + def operator_loc: () -> Location + + # :category: Repository + # Save the operator_loc location using the given saved source so that + # it can be retrieved later. + # -- + # : (_Repository repository) -> Relocation::Entry + def save_operator_loc: (_Repository repository) -> Relocation::Entry + + # :call-seq: + # expression -> Node + # + # Returns the `expression` attribute. + # -- + # : () -> Prism::node + def expression: () -> Prism::node + + # :call-seq: + # body -> StatementsNode | BeginNode | nil + # + # Returns the `body` attribute. + # -- + # : () -> (StatementsNode | BeginNode)? + def body: () -> (StatementsNode | BeginNode)? + + # :category: Locations + # :call-seq: + # end_keyword_loc -> Location + # + # Returns the Location represented by `end_keyword_loc`. + # -- + # : () -> Location + def end_keyword_loc: () -> Location + + # :category: Repository + # Save the end_keyword_loc location using the given saved source so that + # it can be retrieved later. + # -- + # : (_Repository repository) -> Relocation::Entry + def save_end_keyword_loc: (_Repository repository) -> Relocation::Entry + + # :call-seq: + # class_keyword -> String + # + # Slice the location of class_keyword_loc from the source. + # -- + # : () -> String + def class_keyword: () -> String + + # :call-seq: + # operator -> String + # + # Slice the location of operator_loc from the source. + # -- + # : () -> String + def operator: () -> String + + # :call-seq: + # end_keyword -> String + # + # Slice the location of end_keyword_loc from the source. + # -- + # : () -> String + def end_keyword: () -> String + + # : (untyped other) -> boolish + def ===: (untyped other) -> boolish + end + + # Represents the use of the `__ENCODING__` keyword. + # + # __ENCODING__ + # ^^^^^^^^^^^^ + class SourceEncodingNode < Node + # Initialize a new SourceEncodingNode node. + # -- + # : (Source source, Integer node_id, Location location, Integer flags, ) -> void + def initialize: (Source source, Integer node_id, Location location, Integer flags) -> void + + # See Node.accept. + # -- + # : (_Visitor visitor) -> untyped + def accept: (_Visitor visitor) -> untyped + + # See Node.child_nodes. + # -- + # : () -> Array[node?] + def child_nodes: () -> Array[node?] + + # See Node.each_child_node. + # -- + # : () -> Enumerator[node, void] + # : () { (node) -> void } -> void + def each_child_node: () -> Enumerator[node, void] + | () { (node) -> void } -> void + + # See Node.compact_child_nodes. + # -- + # : () -> Array[node] + def compact_child_nodes: () -> Array[node] + + # See Node.comment_targets. + # -- + # : () -> Array[node | Location] + def comment_targets: () -> Array[node | Location] + + # :call-seq: + # copy(**fields) -> SourceEncodingNode + # + # Creates a copy of self with the given fields, using self as the template. + # -- + # : (?node_id: Integer, ?location: Location, ?flags: Integer, ) -> SourceEncodingNode + def copy: (?node_id: Integer, ?location: Location, ?flags: Integer) -> SourceEncodingNode + + alias deconstruct child_nodes + + # : (Array[Symbol]? keys) -> Hash[Symbol, untyped] + def deconstruct_keys: (Array[Symbol]? keys) -> Hash[Symbol, untyped] + + # See `Node#type`. + # -- + # : () -> :source_encoding_node + def type: () -> :source_encoding_node + + # See `Node.type`. + # -- + # : () -> :source_encoding_node + def self.type: () -> :source_encoding_node + + # : () -> String + def inspect: () -> String + + # : (untyped other) -> boolish + def ===: (untyped other) -> boolish + end + + # Represents the use of the `__FILE__` keyword. + # + # __FILE__ + # ^^^^^^^^ + class SourceFileNode < Node + @filepath: String + + # Initialize a new SourceFileNode node. + # -- + # : (Source source, Integer node_id, Location location, Integer flags, String filepath) -> void + def initialize: (Source source, Integer node_id, Location location, Integer flags, String filepath) -> void + + # See Node.accept. + # -- + # : (_Visitor visitor) -> untyped + def accept: (_Visitor visitor) -> untyped + + # See Node.child_nodes. + # -- + # : () -> Array[node?] + def child_nodes: () -> Array[node?] + + # See Node.each_child_node. + # -- + # : () -> Enumerator[node, void] + # : () { (node) -> void } -> void + def each_child_node: () -> Enumerator[node, void] + | () { (node) -> void } -> void + + # See Node.compact_child_nodes. + # -- + # : () -> Array[node] + def compact_child_nodes: () -> Array[node] + + # See Node.comment_targets. + # -- + # : () -> Array[node | Location] + def comment_targets: () -> Array[node | Location] + + # :call-seq: + # copy(**fields) -> SourceFileNode + # + # Creates a copy of self with the given fields, using self as the template. + # -- + # : (?node_id: Integer, ?location: Location, ?flags: Integer, ?filepath: String) -> SourceFileNode + def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?filepath: String) -> SourceFileNode + + alias deconstruct child_nodes + + # : (Array[Symbol]? keys) -> Hash[Symbol, untyped] + def deconstruct_keys: (Array[Symbol]? keys) -> Hash[Symbol, untyped] + + # See `Node#type`. + # -- + # : () -> :source_file_node + def type: () -> :source_file_node + + # See `Node.type`. + # -- + # : () -> :source_file_node + def self.type: () -> :source_file_node + + # : () -> String + def inspect: () -> String + + # :category: Flags + # internal bytes forced the encoding to UTF-8 + # -- + # : () -> bool + def forced_utf8_encoding?: () -> bool + + # :category: Flags + # internal bytes forced the encoding to binary + # -- + # : () -> bool + def forced_binary_encoding?: () -> bool + + # :category: Flags + # frozen by virtue of a `frozen_string_literal: true` comment or `--enable-frozen-string-literal` + # -- + # : () -> bool + def frozen?: () -> bool + + # :category: Flags + # mutable by virtue of a `frozen_string_literal: false` comment or `--disable-frozen-string-literal` + # -- + # : () -> bool + def mutable?: () -> bool + + # :call-seq: + # filepath -> String + # + # Represents the file path being parsed. This corresponds directly to the `filepath` option given to the various `Prism.parse*` APIs. + # -- + # : () -> String + def filepath: () -> String + + # : (untyped other) -> boolish + def ===: (untyped other) -> boolish + end + + # Represents the use of the `__LINE__` keyword. + # + # __LINE__ + # ^^^^^^^^ + class SourceLineNode < Node + # Initialize a new SourceLineNode node. + # -- + # : (Source source, Integer node_id, Location location, Integer flags, ) -> void + def initialize: (Source source, Integer node_id, Location location, Integer flags) -> void + + # See Node.accept. + # -- + # : (_Visitor visitor) -> untyped + def accept: (_Visitor visitor) -> untyped + + # See Node.child_nodes. + # -- + # : () -> Array[node?] + def child_nodes: () -> Array[node?] + + # See Node.each_child_node. + # -- + # : () -> Enumerator[node, void] + # : () { (node) -> void } -> void + def each_child_node: () -> Enumerator[node, void] + | () { (node) -> void } -> void + + # See Node.compact_child_nodes. + # -- + # : () -> Array[node] + def compact_child_nodes: () -> Array[node] + + # See Node.comment_targets. + # -- + # : () -> Array[node | Location] + def comment_targets: () -> Array[node | Location] + + # :call-seq: + # copy(**fields) -> SourceLineNode + # + # Creates a copy of self with the given fields, using self as the template. + # -- + # : (?node_id: Integer, ?location: Location, ?flags: Integer, ) -> SourceLineNode + def copy: (?node_id: Integer, ?location: Location, ?flags: Integer) -> SourceLineNode + + alias deconstruct child_nodes + + # : (Array[Symbol]? keys) -> Hash[Symbol, untyped] + def deconstruct_keys: (Array[Symbol]? keys) -> Hash[Symbol, untyped] + + # See `Node#type`. + # -- + # : () -> :source_line_node + def type: () -> :source_line_node + + # See `Node.type`. + # -- + # : () -> :source_line_node + def self.type: () -> :source_line_node + + # : () -> String + def inspect: () -> String + + # : (untyped other) -> boolish + def ===: (untyped other) -> boolish + end + + # Represents the use of the splat operator. + # + # [*a] + # ^^ + class SplatNode < Node + @expression: Prism::node? + + @operator_loc: Location + + # Initialize a new SplatNode node. + # -- + # : (Source source, Integer node_id, Location location, Integer flags, Location operator_loc, Prism::node? expression) -> void + def initialize: (Source source, Integer node_id, Location location, Integer flags, Location operator_loc, Prism::node? expression) -> void + + # See Node.accept. + # -- + # : (_Visitor visitor) -> untyped + def accept: (_Visitor visitor) -> untyped + + # See Node.child_nodes. + # -- + # : () -> Array[node?] + def child_nodes: () -> Array[node?] + + # See Node.each_child_node. + # -- + # : () -> Enumerator[node, void] + # : () { (node) -> void } -> void + def each_child_node: () -> Enumerator[node, void] + | () { (node) -> void } -> void + + # See Node.compact_child_nodes. + # -- + # : () -> Array[node] + def compact_child_nodes: () -> Array[node] + + # See Node.comment_targets. + # -- + # : () -> Array[node | Location] + def comment_targets: () -> Array[node | Location] + + # :call-seq: + # copy(**fields) -> SplatNode + # + # Creates a copy of self with the given fields, using self as the template. + # -- + # : (?node_id: Integer, ?location: Location, ?flags: Integer, ?operator_loc: Location, ?expression: Prism::node?) -> SplatNode + def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?operator_loc: Location, ?expression: Prism::node?) -> SplatNode + + alias deconstruct child_nodes + + # : (Array[Symbol]? keys) -> Hash[Symbol, untyped] + def deconstruct_keys: (Array[Symbol]? keys) -> Hash[Symbol, untyped] + + # See `Node#type`. + # -- + # : () -> :splat_node + def type: () -> :splat_node + + # See `Node.type`. + # -- + # : () -> :splat_node + def self.type: () -> :splat_node + + # : () -> String + def inspect: () -> String + + # :category: Locations + # :call-seq: + # operator_loc -> Location + # + # Returns the Location represented by `operator_loc`. + # -- + # : () -> Location + def operator_loc: () -> Location + + # :category: Repository + # Save the operator_loc location using the given saved source so that + # it can be retrieved later. + # -- + # : (_Repository repository) -> Relocation::Entry + def save_operator_loc: (_Repository repository) -> Relocation::Entry + + # :call-seq: + # expression -> Node | nil + # + # Returns the `expression` attribute. + # -- + # : () -> Prism::node? + def expression: () -> Prism::node? + + # :call-seq: + # operator -> String + # + # Slice the location of operator_loc from the source. + # -- + # : () -> String + def operator: () -> String + + # : (untyped other) -> boolish + def ===: (untyped other) -> boolish + end + + # Represents a set of statements contained within some scope. + # + # foo; bar; baz + # ^^^^^^^^^^^^^ + class StatementsNode < Node + @body: Array[Prism::node] + + # Initialize a new StatementsNode node. + # -- + # : (Source source, Integer node_id, Location location, Integer flags, Array[Prism::node] body) -> void + def initialize: (Source source, Integer node_id, Location location, Integer flags, Array[Prism::node] body) -> void + + # See Node.accept. + # -- + # : (_Visitor visitor) -> untyped + def accept: (_Visitor visitor) -> untyped + + # See Node.child_nodes. + # -- + # : () -> Array[node?] + def child_nodes: () -> Array[node?] + + # See Node.each_child_node. + # -- + # : () -> Enumerator[node, void] + # : () { (node) -> void } -> void + def each_child_node: () -> Enumerator[node, void] + | () { (node) -> void } -> void + + # See Node.compact_child_nodes. + # -- + # : () -> Array[node] + def compact_child_nodes: () -> Array[node] + + # See Node.comment_targets. + # -- + # : () -> Array[node | Location] + def comment_targets: () -> Array[node | Location] + + # :call-seq: + # copy(**fields) -> StatementsNode + # + # Creates a copy of self with the given fields, using self as the template. + # -- + # : (?node_id: Integer, ?location: Location, ?flags: Integer, ?body: Array[Prism::node]) -> StatementsNode + def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?body: Array[Prism::node]) -> StatementsNode + + alias deconstruct child_nodes + + # : (Array[Symbol]? keys) -> Hash[Symbol, untyped] + def deconstruct_keys: (Array[Symbol]? keys) -> Hash[Symbol, untyped] + + # See `Node#type`. + # -- + # : () -> :statements_node + def type: () -> :statements_node + + # See `Node.type`. + # -- + # : () -> :statements_node + def self.type: () -> :statements_node + + # : () -> String + def inspect: () -> String + + # :call-seq: + # body -> Array[Node] + # + # Returns the `body` attribute. + # -- + # : () -> Array[Prism::node] + def body: () -> Array[Prism::node] + + # : (untyped other) -> boolish + def ===: (untyped other) -> boolish + 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 + @unescaped: String + + @closing_loc: Location? + + @content_loc: Location + + @opening_loc: Location? + + # Initialize a new StringNode node. + # -- + # : (Source source, Integer node_id, Location location, Integer flags, Location? opening_loc, Location content_loc, Location? closing_loc, String unescaped) -> void + def initialize: (Source source, Integer node_id, Location location, Integer flags, Location? opening_loc, Location content_loc, Location? closing_loc, String unescaped) -> void + + # See Node.accept. + # -- + # : (_Visitor visitor) -> untyped + def accept: (_Visitor visitor) -> untyped + + # See Node.child_nodes. + # -- + # : () -> Array[node?] + def child_nodes: () -> Array[node?] + + # See Node.each_child_node. + # -- + # : () -> Enumerator[node, void] + # : () { (node) -> void } -> void + def each_child_node: () -> Enumerator[node, void] + | () { (node) -> void } -> void + + # See Node.compact_child_nodes. + # -- + # : () -> Array[node] + def compact_child_nodes: () -> Array[node] + + # See Node.comment_targets. + # -- + # : () -> Array[node | Location] + def comment_targets: () -> Array[node | Location] + + # :call-seq: + # copy(**fields) -> StringNode + # + # Creates a copy of self with the given fields, using self as the template. + # -- + # : (?node_id: Integer, ?location: Location, ?flags: Integer, ?opening_loc: Location?, ?content_loc: Location, ?closing_loc: Location?, ?unescaped: String) -> StringNode + def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?opening_loc: Location?, ?content_loc: Location, ?closing_loc: Location?, ?unescaped: String) -> StringNode + + alias deconstruct child_nodes + + # : (Array[Symbol]? keys) -> Hash[Symbol, untyped] + def deconstruct_keys: (Array[Symbol]? keys) -> Hash[Symbol, untyped] + + # See `Node#type`. + # -- + # : () -> :string_node + def type: () -> :string_node + + # See `Node.type`. + # -- + # : () -> :string_node + def self.type: () -> :string_node + + # : () -> String + def inspect: () -> String + + # :category: Flags + # internal bytes forced the encoding to UTF-8 + # -- + # : () -> bool + def forced_utf8_encoding?: () -> bool + + # :category: Flags + # internal bytes forced the encoding to binary + # -- + # : () -> bool + def forced_binary_encoding?: () -> bool + + # :category: Flags + # frozen by virtue of a `frozen_string_literal: true` comment or `--enable-frozen-string-literal` + # -- + # : () -> bool + def frozen?: () -> bool + + # :category: Flags + # mutable by virtue of a `frozen_string_literal: false` comment or `--disable-frozen-string-literal` + # -- + # : () -> bool + def mutable?: () -> bool + + # :category: Locations + # :call-seq: + # opening_loc -> Location | nil + # + # Returns the Location represented by `opening_loc`. + # -- + # : () -> Location? + def opening_loc: () -> Location? + + # :category: Repository + # Save the opening_loc location using the given saved source so that + # it can be retrieved later. + # -- + # : (_Repository repository) -> Relocation::Entry? + def save_opening_loc: (_Repository repository) -> Relocation::Entry? + + # :category: Locations + # :call-seq: + # content_loc -> Location + # + # Returns the Location represented by `content_loc`. + # -- + # : () -> Location + def content_loc: () -> Location + + # :category: Repository + # Save the content_loc location using the given saved source so that + # it can be retrieved later. + # -- + # : (_Repository repository) -> Relocation::Entry + def save_content_loc: (_Repository repository) -> Relocation::Entry + + # :category: Locations + # :call-seq: + # closing_loc -> Location | nil + # + # Returns the Location represented by `closing_loc`. + # -- + # : () -> Location? + def closing_loc: () -> Location? + + # :category: Repository + # Save the closing_loc location using the given saved source so that + # it can be retrieved later. + # -- + # : (_Repository repository) -> Relocation::Entry? + def save_closing_loc: (_Repository repository) -> Relocation::Entry? + + # :call-seq: + # unescaped -> String + # + # Returns the `unescaped` attribute. + # -- + # : () -> String + def unescaped: () -> String + + # :call-seq: + # opening -> String | nil + # + # Slice the location of opening_loc from the source. + # -- + # : () -> String? + def opening: () -> String? + + # :call-seq: + # content -> String + # + # Slice the location of content_loc from the source. + # -- + # : () -> String + def content: () -> String + + # :call-seq: + # closing -> String | nil + # + # Slice the location of closing_loc from the source. + # -- + # : () -> String? + def closing: () -> String? + + # : (untyped other) -> boolish + def ===: (untyped other) -> boolish + 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 + @block: (BlockNode | BlockArgumentNode)? + + @rparen_loc: Location? + + @arguments: ArgumentsNode? + + @lparen_loc: Location? + + @keyword_loc: Location + + # Initialize a new SuperNode node. + # -- + # : (Source source, Integer node_id, Location location, Integer flags, Location keyword_loc, Location? lparen_loc, ArgumentsNode? arguments, Location? rparen_loc, (BlockNode | BlockArgumentNode)? block) -> 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)? block) -> void + + # See Node.accept. + # -- + # : (_Visitor visitor) -> untyped + def accept: (_Visitor visitor) -> untyped + + # See Node.child_nodes. + # -- + # : () -> Array[node?] + def child_nodes: () -> Array[node?] + + # See Node.each_child_node. + # -- + # : () -> Enumerator[node, void] + # : () { (node) -> void } -> void + def each_child_node: () -> Enumerator[node, void] + | () { (node) -> void } -> void + + # See Node.compact_child_nodes. + # -- + # : () -> Array[node] + def compact_child_nodes: () -> Array[node] + + # See Node.comment_targets. + # -- + # : () -> Array[node | Location] + def comment_targets: () -> Array[node | Location] + + # :call-seq: + # copy(**fields) -> SuperNode + # + # Creates a copy of self with the given fields, using self as the template. + # -- + # : (?node_id: Integer, ?location: Location, ?flags: Integer, ?keyword_loc: Location, ?lparen_loc: Location?, ?arguments: ArgumentsNode?, ?rparen_loc: Location?, ?block: (BlockNode | BlockArgumentNode)?) -> SuperNode + def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?keyword_loc: Location, ?lparen_loc: Location?, ?arguments: ArgumentsNode?, ?rparen_loc: Location?, ?block: (BlockNode | BlockArgumentNode)?) -> SuperNode + + alias deconstruct child_nodes + + # : (Array[Symbol]? keys) -> Hash[Symbol, untyped] + def deconstruct_keys: (Array[Symbol]? keys) -> Hash[Symbol, untyped] + + # See `Node#type`. + # -- + # : () -> :super_node + def type: () -> :super_node + + # See `Node.type`. + # -- + # : () -> :super_node + def self.type: () -> :super_node + + # : () -> String + def inspect: () -> String + + # :category: Locations + # :call-seq: + # keyword_loc -> Location + # + # Returns the Location represented by `keyword_loc`. + # -- + # : () -> Location + def keyword_loc: () -> Location + + # :category: Repository + # Save the keyword_loc location using the given saved source so that + # it can be retrieved later. + # -- + # : (_Repository repository) -> Relocation::Entry + def save_keyword_loc: (_Repository repository) -> Relocation::Entry + + # :category: Locations + # :call-seq: + # lparen_loc -> Location | nil + # + # Returns the Location represented by `lparen_loc`. + # -- + # : () -> Location? + def lparen_loc: () -> Location? + + # :category: Repository + # Save the lparen_loc location using the given saved source so that + # it can be retrieved later. + # -- + # : (_Repository repository) -> Relocation::Entry? + def save_lparen_loc: (_Repository repository) -> Relocation::Entry? + + # :call-seq: + # arguments -> ArgumentsNode | nil + # + # Can be only `nil` when there are empty parentheses, like `super()`. + # -- + # : () -> ArgumentsNode? + def arguments: () -> ArgumentsNode? + + # :category: Locations + # :call-seq: + # rparen_loc -> Location | nil + # + # Returns the Location represented by `rparen_loc`. + # -- + # : () -> Location? + def rparen_loc: () -> Location? + + # :category: Repository + # Save the rparen_loc location using the given saved source so that + # it can be retrieved later. + # -- + # : (_Repository repository) -> Relocation::Entry? + def save_rparen_loc: (_Repository repository) -> Relocation::Entry? + + # :call-seq: + # block -> BlockNode | BlockArgumentNode | nil + # + # Returns the `block` attribute. + # -- + # : () -> (BlockNode | BlockArgumentNode)? + def block: () -> (BlockNode | BlockArgumentNode)? + + # :call-seq: + # keyword -> String + # + # Slice the location of keyword_loc from the source. + # -- + # : () -> String + def keyword: () -> String + + # :call-seq: + # lparen -> String | nil + # + # Slice the location of lparen_loc from the source. + # -- + # : () -> String? + def lparen: () -> String? + + # :call-seq: + # rparen -> String | nil + # + # Slice the location of rparen_loc from the source. + # -- + # : () -> String? + def rparen: () -> String? + + # : (untyped other) -> boolish + def ===: (untyped other) -> boolish + end + + # Represents a symbol literal or a symbol contained within a `%i` list. + # + # :foo + # ^^^^ + # + # %i[foo] + # ^^^ + class SymbolNode < Node + @unescaped: String + + @closing_loc: Location? + + @value_loc: Location? + + @opening_loc: Location? + + # Initialize a new SymbolNode node. + # -- + # : (Source source, Integer node_id, Location location, Integer flags, Location? opening_loc, Location? value_loc, Location? closing_loc, String unescaped) -> void + def initialize: (Source source, Integer node_id, Location location, Integer flags, Location? opening_loc, Location? value_loc, Location? closing_loc, String unescaped) -> void + + # See Node.accept. + # -- + # : (_Visitor visitor) -> untyped + def accept: (_Visitor visitor) -> untyped + + # See Node.child_nodes. + # -- + # : () -> Array[node?] + def child_nodes: () -> Array[node?] + + # See Node.each_child_node. + # -- + # : () -> Enumerator[node, void] + # : () { (node) -> void } -> void + def each_child_node: () -> Enumerator[node, void] + | () { (node) -> void } -> void + + # See Node.compact_child_nodes. + # -- + # : () -> Array[node] + def compact_child_nodes: () -> Array[node] + + # See Node.comment_targets. + # -- + # : () -> Array[node | Location] + def comment_targets: () -> Array[node | Location] + + # :call-seq: + # copy(**fields) -> SymbolNode + # + # Creates a copy of self with the given fields, using self as the template. + # -- + # : (?node_id: Integer, ?location: Location, ?flags: Integer, ?opening_loc: Location?, ?value_loc: Location?, ?closing_loc: Location?, ?unescaped: String) -> SymbolNode + def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?opening_loc: Location?, ?value_loc: Location?, ?closing_loc: Location?, ?unescaped: String) -> SymbolNode + + alias deconstruct child_nodes + + # : (Array[Symbol]? keys) -> Hash[Symbol, untyped] + def deconstruct_keys: (Array[Symbol]? keys) -> Hash[Symbol, untyped] + + # See `Node#type`. + # -- + # : () -> :symbol_node + def type: () -> :symbol_node + + # See `Node.type`. + # -- + # : () -> :symbol_node + def self.type: () -> :symbol_node + + # : () -> String + def inspect: () -> String + + # :category: Flags + # internal bytes forced the encoding to UTF-8 + # -- + # : () -> bool + def forced_utf8_encoding?: () -> bool + + # :category: Flags + # internal bytes forced the encoding to binary + # -- + # : () -> bool + def forced_binary_encoding?: () -> bool + + # :category: Flags + # internal bytes forced the encoding to US-ASCII + # -- + # : () -> bool + def forced_us_ascii_encoding?: () -> bool + + # :category: Locations + # :call-seq: + # opening_loc -> Location | nil + # + # Returns the Location represented by `opening_loc`. + # -- + # : () -> Location? + def opening_loc: () -> Location? + + # :category: Repository + # Save the opening_loc location using the given saved source so that + # it can be retrieved later. + # -- + # : (_Repository repository) -> Relocation::Entry? + def save_opening_loc: (_Repository repository) -> Relocation::Entry? + + # :category: Locations + # :call-seq: + # value_loc -> Location | nil + # + # Returns the Location represented by `value_loc`. + # -- + # : () -> Location? + def value_loc: () -> Location? + + # :category: Repository + # Save the value_loc location using the given saved source so that + # it can be retrieved later. + # -- + # : (_Repository repository) -> Relocation::Entry? + def save_value_loc: (_Repository repository) -> Relocation::Entry? + + # :category: Locations + # :call-seq: + # closing_loc -> Location | nil + # + # Returns the Location represented by `closing_loc`. + # -- + # : () -> Location? + def closing_loc: () -> Location? + + # :category: Repository + # Save the closing_loc location using the given saved source so that + # it can be retrieved later. + # -- + # : (_Repository repository) -> Relocation::Entry? + def save_closing_loc: (_Repository repository) -> Relocation::Entry? + + # :call-seq: + # unescaped -> String + # + # Returns the `unescaped` attribute. + # -- + # : () -> String + def unescaped: () -> String + + # :call-seq: + # opening -> String | nil + # + # Slice the location of opening_loc from the source. + # -- + # : () -> String? + def opening: () -> String? + + # :call-seq: + # value -> String | nil + # + # Slice the location of value_loc from the source. + # -- + # : () -> String? + def value: () -> String? + + # :call-seq: + # closing -> String | nil + # + # Slice the location of closing_loc from the source. + # -- + # : () -> String? + def closing: () -> String? + + # : (untyped other) -> boolish + def ===: (untyped other) -> boolish + end + + # Represents the use of the literal `true` keyword. + # + # true + # ^^^^ + class TrueNode < Node + # Initialize a new TrueNode node. + # -- + # : (Source source, Integer node_id, Location location, Integer flags, ) -> void + def initialize: (Source source, Integer node_id, Location location, Integer flags) -> void + + # See Node.accept. + # -- + # : (_Visitor visitor) -> untyped + def accept: (_Visitor visitor) -> untyped + + # See Node.child_nodes. + # -- + # : () -> Array[node?] + def child_nodes: () -> Array[node?] + + # See Node.each_child_node. + # -- + # : () -> Enumerator[node, void] + # : () { (node) -> void } -> void + def each_child_node: () -> Enumerator[node, void] + | () { (node) -> void } -> void + + # See Node.compact_child_nodes. + # -- + # : () -> Array[node] + def compact_child_nodes: () -> Array[node] + + # See Node.comment_targets. + # -- + # : () -> Array[node | Location] + def comment_targets: () -> Array[node | Location] + + # :call-seq: + # copy(**fields) -> TrueNode + # + # Creates a copy of self with the given fields, using self as the template. + # -- + # : (?node_id: Integer, ?location: Location, ?flags: Integer, ) -> TrueNode + def copy: (?node_id: Integer, ?location: Location, ?flags: Integer) -> TrueNode + + alias deconstruct child_nodes + + # : (Array[Symbol]? keys) -> Hash[Symbol, untyped] + def deconstruct_keys: (Array[Symbol]? keys) -> Hash[Symbol, untyped] + + # See `Node#type`. + # -- + # : () -> :true_node + def type: () -> :true_node + + # See `Node.type`. + # -- + # : () -> :true_node + def self.type: () -> :true_node + + # : () -> String + def inspect: () -> String + + # : (untyped other) -> boolish + def ===: (untyped other) -> boolish + end + + # Represents the use of the `undef` keyword. + # + # undef :foo, :bar, :baz + # ^^^^^^^^^^^^^^^^^^^^^^ + class UndefNode < Node + @keyword_loc: Location + + @names: Array[SymbolNode | InterpolatedSymbolNode] + + # Initialize a new UndefNode node. + # -- + # : (Source source, Integer node_id, Location location, Integer flags, Array[SymbolNode | InterpolatedSymbolNode] names, Location keyword_loc) -> void + def initialize: (Source source, Integer node_id, Location location, Integer flags, Array[SymbolNode | InterpolatedSymbolNode] names, Location keyword_loc) -> void + + # See Node.accept. + # -- + # : (_Visitor visitor) -> untyped + def accept: (_Visitor visitor) -> untyped + + # See Node.child_nodes. + # -- + # : () -> Array[node?] + def child_nodes: () -> Array[node?] + + # See Node.each_child_node. + # -- + # : () -> Enumerator[node, void] + # : () { (node) -> void } -> void + def each_child_node: () -> Enumerator[node, void] + | () { (node) -> void } -> void + + # See Node.compact_child_nodes. + # -- + # : () -> Array[node] + def compact_child_nodes: () -> Array[node] + + # See Node.comment_targets. + # -- + # : () -> Array[node | Location] + def comment_targets: () -> Array[node | Location] + + # :call-seq: + # copy(**fields) -> UndefNode + # + # Creates a copy of self with the given fields, using self as the template. + # -- + # : (?node_id: Integer, ?location: Location, ?flags: Integer, ?names: Array[SymbolNode | InterpolatedSymbolNode], ?keyword_loc: Location) -> UndefNode + def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?names: Array[SymbolNode | InterpolatedSymbolNode], ?keyword_loc: Location) -> UndefNode + + alias deconstruct child_nodes + + # : (Array[Symbol]? keys) -> Hash[Symbol, untyped] + def deconstruct_keys: (Array[Symbol]? keys) -> Hash[Symbol, untyped] + + # See `Node#type`. + # -- + # : () -> :undef_node + def type: () -> :undef_node + + # See `Node.type`. + # -- + # : () -> :undef_node + def self.type: () -> :undef_node + + # : () -> String + def inspect: () -> String + + # :call-seq: + # names -> Array[SymbolNode | InterpolatedSymbolNode] + # + # Returns the `names` attribute. + # -- + # : () -> Array[SymbolNode | InterpolatedSymbolNode] + def names: () -> Array[SymbolNode | InterpolatedSymbolNode] + + # :category: Locations + # :call-seq: + # keyword_loc -> Location + # + # Returns the Location represented by `keyword_loc`. + # -- + # : () -> Location + def keyword_loc: () -> Location + + # :category: Repository + # Save the keyword_loc location using the given saved source so that + # it can be retrieved later. + # -- + # : (_Repository repository) -> Relocation::Entry + def save_keyword_loc: (_Repository repository) -> Relocation::Entry + + # :call-seq: + # keyword -> String + # + # Slice the location of keyword_loc from the source. + # -- + # : () -> String + def keyword: () -> String + + # : (untyped other) -> boolish + def ===: (untyped other) -> boolish + 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 + @end_keyword_loc: Location? + + @else_clause: ElseNode? + + @statements: StatementsNode? + + @then_keyword_loc: Location? + + @predicate: Prism::node + + @keyword_loc: Location + + # Initialize a new UnlessNode node. + # -- + # : (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 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 + + # See Node.accept. + # -- + # : (_Visitor visitor) -> untyped + def accept: (_Visitor visitor) -> untyped + + # See Node.child_nodes. + # -- + # : () -> Array[node?] + def child_nodes: () -> Array[node?] + + # See Node.each_child_node. + # -- + # : () -> Enumerator[node, void] + # : () { (node) -> void } -> void + def each_child_node: () -> Enumerator[node, void] + | () { (node) -> void } -> void + + # See Node.compact_child_nodes. + # -- + # : () -> Array[node] + def compact_child_nodes: () -> Array[node] + + # See Node.comment_targets. + # -- + # : () -> Array[node | Location] + def comment_targets: () -> Array[node | Location] + + # :call-seq: + # copy(**fields) -> UnlessNode + # + # Creates a copy of self with the given fields, using self as the template. + # -- + # : (?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: 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 + + alias deconstruct child_nodes + + # : (Array[Symbol]? keys) -> Hash[Symbol, untyped] + def deconstruct_keys: (Array[Symbol]? keys) -> Hash[Symbol, untyped] + + # See `Node#type`. + # -- + # : () -> :unless_node + def type: () -> :unless_node + + # See `Node.type`. + # -- + # : () -> :unless_node + def self.type: () -> :unless_node + + # : () -> String + def inspect: () -> String + + # :category: Locations + # :call-seq: + # keyword_loc -> Location + # + # The Location of the `unless` keyword. + # + # unless cond then bar end + # ^^^^^^ + # + # bar unless cond + # ^^^^^^ + # -- + # : () -> Location + def keyword_loc: () -> Location + + # :category: Repository + # Save the keyword_loc location using the given saved source so that + # it can be retrieved later. + # -- + # : (_Repository repository) -> Relocation::Entry + def save_keyword_loc: (_Repository repository) -> Relocation::Entry + + # :call-seq: + # predicate -> Node + # + # 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 + # ^^^^ + # -- + # : () -> Prism::node + def predicate: () -> Prism::node + + # :category: Locations + # :call-seq: + # then_keyword_loc -> Location | nil + # + # The Location of the `then` keyword, if present. + # + # unless cond then bar end + # ^^^^ + # -- + # : () -> Location? + def then_keyword_loc: () -> Location? + + # :category: Repository + # Save the then_keyword_loc location using the given saved source so that + # it can be retrieved later. + # -- + # : (_Repository repository) -> Relocation::Entry? + def save_then_keyword_loc: (_Repository repository) -> Relocation::Entry? + + # :call-seq: + # statements -> StatementsNode | nil + # + # 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 + # ^^^ + # -- + # : () -> StatementsNode? + def statements: () -> StatementsNode? + + # :call-seq: + # else_clause -> ElseNode | nil + # + # The else clause of the unless expression, if present. + # + # unless cond then bar else baz end + # ^^^^^^^^^^^^ + # -- + # : () -> ElseNode? + def else_clause: () -> ElseNode? + + # :category: Locations + # :call-seq: + # end_keyword_loc -> Location | nil + # + # The Location of the `end` keyword, if present. + # + # unless cond then bar end + # ^^^ + # -- + # : () -> Location? + def end_keyword_loc: () -> Location? + + # :category: Repository + # Save the end_keyword_loc location using the given saved source so that + # it can be retrieved later. + # -- + # : (_Repository repository) -> Relocation::Entry? + def save_end_keyword_loc: (_Repository repository) -> Relocation::Entry? + + # :call-seq: + # keyword -> String + # + # Slice the location of keyword_loc from the source. + # -- + # : () -> String + def keyword: () -> String + + # :call-seq: + # then_keyword -> String | nil + # + # Slice the location of then_keyword_loc from the source. + # -- + # : () -> String? + def then_keyword: () -> String? + + # :call-seq: + # end_keyword -> String | nil + # + # Slice the location of end_keyword_loc from the source. + # -- + # : () -> String? + def end_keyword: () -> String? + + # : (untyped other) -> boolish + def ===: (untyped other) -> boolish + 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 + @statements: StatementsNode? + + @predicate: Prism::node + + @closing_loc: Location? + + @do_keyword_loc: Location? + + @keyword_loc: Location + + # Initialize a new UntilNode node. + # -- + # : (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 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 + + # See Node.accept. + # -- + # : (_Visitor visitor) -> untyped + def accept: (_Visitor visitor) -> untyped + + # See Node.child_nodes. + # -- + # : () -> Array[node?] + def child_nodes: () -> Array[node?] + + # See Node.each_child_node. + # -- + # : () -> Enumerator[node, void] + # : () { (node) -> void } -> void + def each_child_node: () -> Enumerator[node, void] + | () { (node) -> void } -> void + + # See Node.compact_child_nodes. + # -- + # : () -> Array[node] + def compact_child_nodes: () -> Array[node] + + # See Node.comment_targets. + # -- + # : () -> Array[node | Location] + def comment_targets: () -> Array[node | Location] + + # :call-seq: + # copy(**fields) -> UntilNode + # + # Creates a copy of self with the given fields, using self as the template. + # -- + # : (?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: Integer, ?location: Location, ?flags: Integer, ?keyword_loc: Location, ?do_keyword_loc: Location?, ?closing_loc: Location?, ?predicate: Prism::node, ?statements: StatementsNode?) -> UntilNode + + alias deconstruct child_nodes + + # : (Array[Symbol]? keys) -> Hash[Symbol, untyped] + def deconstruct_keys: (Array[Symbol]? keys) -> Hash[Symbol, untyped] + + # See `Node#type`. + # -- + # : () -> :until_node + def type: () -> :until_node + + # See `Node.type`. + # -- + # : () -> :until_node + def self.type: () -> :until_node + + # : () -> String + def inspect: () -> String + + # :category: Flags + # a loop after a begin statement, so the body is executed first before the condition + # -- + # : () -> bool + def begin_modifier?: () -> bool + + # :category: Locations + # :call-seq: + # keyword_loc -> Location + # + # Returns the Location represented by `keyword_loc`. + # -- + # : () -> Location + def keyword_loc: () -> Location + + # :category: Repository + # Save the keyword_loc location using the given saved source so that + # it can be retrieved later. + # -- + # : (_Repository repository) -> Relocation::Entry + def save_keyword_loc: (_Repository repository) -> Relocation::Entry + + # :category: Locations + # :call-seq: + # do_keyword_loc -> Location | nil + # + # Returns the Location represented by `do_keyword_loc`. + # -- + # : () -> Location? + def do_keyword_loc: () -> Location? + + # :category: Repository + # Save the do_keyword_loc location using the given saved source so that + # it can be retrieved later. + # -- + # : (_Repository repository) -> Relocation::Entry? + def save_do_keyword_loc: (_Repository repository) -> Relocation::Entry? + + # :category: Locations + # :call-seq: + # closing_loc -> Location | nil + # + # Returns the Location represented by `closing_loc`. + # -- + # : () -> Location? + def closing_loc: () -> Location? + + # :category: Repository + # Save the closing_loc location using the given saved source so that + # it can be retrieved later. + # -- + # : (_Repository repository) -> Relocation::Entry? + def save_closing_loc: (_Repository repository) -> Relocation::Entry? + + # :call-seq: + # predicate -> Node + # + # Returns the `predicate` attribute. + # -- + # : () -> Prism::node + def predicate: () -> Prism::node + + # :call-seq: + # statements -> StatementsNode | nil + # + # Returns the `statements` attribute. + # -- + # : () -> StatementsNode? + def statements: () -> StatementsNode? + + # :call-seq: + # keyword -> String + # + # Slice the location of keyword_loc from the source. + # -- + # : () -> String + def keyword: () -> String + + # :call-seq: + # do_keyword -> String | nil + # + # Slice the location of do_keyword_loc from the source. + # -- + # : () -> String? + def do_keyword: () -> String? + + # :call-seq: + # closing -> String | nil + # + # Slice the location of closing_loc from the source. + # -- + # : () -> String? + def closing: () -> String? + + # : (untyped other) -> boolish + def ===: (untyped other) -> boolish + end + + # Represents the use of the `when` keyword within a case statement. + # + # case true + # when true + # ^^^^^^^^^ + # end + class WhenNode < Node + @statements: StatementsNode? + + @then_keyword_loc: Location? + + @conditions: Array[Prism::node] + + @keyword_loc: Location + + # Initialize a new WhenNode node. + # -- + # : (Source source, Integer node_id, Location location, Integer flags, Location keyword_loc, Array[Prism::node] conditions, Location? then_keyword_loc, StatementsNode? statements) -> 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 + + # See Node.accept. + # -- + # : (_Visitor visitor) -> untyped + def accept: (_Visitor visitor) -> untyped + + # See Node.child_nodes. + # -- + # : () -> Array[node?] + def child_nodes: () -> Array[node?] + + # See Node.each_child_node. + # -- + # : () -> Enumerator[node, void] + # : () { (node) -> void } -> void + def each_child_node: () -> Enumerator[node, void] + | () { (node) -> void } -> void + + # See Node.compact_child_nodes. + # -- + # : () -> Array[node] + def compact_child_nodes: () -> Array[node] + + # See Node.comment_targets. + # -- + # : () -> Array[node | Location] + def comment_targets: () -> Array[node | Location] + + # :call-seq: + # copy(**fields) -> WhenNode + # + # Creates a copy of self with the given fields, using self as the template. + # -- + # : (?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: Integer, ?location: Location, ?flags: Integer, ?keyword_loc: Location, ?conditions: Array[Prism::node], ?then_keyword_loc: Location?, ?statements: StatementsNode?) -> WhenNode + + alias deconstruct child_nodes + + # : (Array[Symbol]? keys) -> Hash[Symbol, untyped] + def deconstruct_keys: (Array[Symbol]? keys) -> Hash[Symbol, untyped] + + # See `Node#type`. + # -- + # : () -> :when_node + def type: () -> :when_node + + # See `Node.type`. + # -- + # : () -> :when_node + def self.type: () -> :when_node + + # : () -> String + def inspect: () -> String + + # :category: Locations + # :call-seq: + # keyword_loc -> Location + # + # Returns the Location represented by `keyword_loc`. + # -- + # : () -> Location + def keyword_loc: () -> Location + + # :category: Repository + # Save the keyword_loc location using the given saved source so that + # it can be retrieved later. + # -- + # : (_Repository repository) -> Relocation::Entry + def save_keyword_loc: (_Repository repository) -> Relocation::Entry + + # :call-seq: + # conditions -> Array[Node] + # + # Returns the `conditions` attribute. + # -- + # : () -> Array[Prism::node] + def conditions: () -> Array[Prism::node] + + # :category: Locations + # :call-seq: + # then_keyword_loc -> Location | nil + # + # Returns the Location represented by `then_keyword_loc`. + # -- + # : () -> Location? + def then_keyword_loc: () -> Location? + + # :category: Repository + # Save the then_keyword_loc location using the given saved source so that + # it can be retrieved later. + # -- + # : (_Repository repository) -> Relocation::Entry? + def save_then_keyword_loc: (_Repository repository) -> Relocation::Entry? + + # :call-seq: + # statements -> StatementsNode | nil + # + # Returns the `statements` attribute. + # -- + # : () -> StatementsNode? + def statements: () -> StatementsNode? + + # :call-seq: + # keyword -> String + # + # Slice the location of keyword_loc from the source. + # -- + # : () -> String + def keyword: () -> String + + # :call-seq: + # then_keyword -> String | nil + # + # Slice the location of then_keyword_loc from the source. + # -- + # : () -> String? + def then_keyword: () -> String? + + # : (untyped other) -> boolish + def ===: (untyped other) -> boolish + 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 + @statements: StatementsNode? + + @predicate: Prism::node + + @closing_loc: Location? + + @do_keyword_loc: Location? + + @keyword_loc: Location + + # Initialize a new WhileNode node. + # -- + # : (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 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 + + # See Node.accept. + # -- + # : (_Visitor visitor) -> untyped + def accept: (_Visitor visitor) -> untyped + + # See Node.child_nodes. + # -- + # : () -> Array[node?] + def child_nodes: () -> Array[node?] + + # See Node.each_child_node. + # -- + # : () -> Enumerator[node, void] + # : () { (node) -> void } -> void + def each_child_node: () -> Enumerator[node, void] + | () { (node) -> void } -> void + + # See Node.compact_child_nodes. + # -- + # : () -> Array[node] + def compact_child_nodes: () -> Array[node] + + # See Node.comment_targets. + # -- + # : () -> Array[node | Location] + def comment_targets: () -> Array[node | Location] + + # :call-seq: + # copy(**fields) -> WhileNode + # + # Creates a copy of self with the given fields, using self as the template. + # -- + # : (?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: Integer, ?location: Location, ?flags: Integer, ?keyword_loc: Location, ?do_keyword_loc: Location?, ?closing_loc: Location?, ?predicate: Prism::node, ?statements: StatementsNode?) -> WhileNode + + alias deconstruct child_nodes + + # : (Array[Symbol]? keys) -> Hash[Symbol, untyped] + def deconstruct_keys: (Array[Symbol]? keys) -> Hash[Symbol, untyped] + + # See `Node#type`. + # -- + # : () -> :while_node + def type: () -> :while_node + + # See `Node.type`. + # -- + # : () -> :while_node + def self.type: () -> :while_node + + # : () -> String + def inspect: () -> String + + # :category: Flags + # a loop after a begin statement, so the body is executed first before the condition + # -- + # : () -> bool + def begin_modifier?: () -> bool + + # :category: Locations + # :call-seq: + # keyword_loc -> Location + # + # Returns the Location represented by `keyword_loc`. + # -- + # : () -> Location + def keyword_loc: () -> Location + + # :category: Repository + # Save the keyword_loc location using the given saved source so that + # it can be retrieved later. + # -- + # : (_Repository repository) -> Relocation::Entry + def save_keyword_loc: (_Repository repository) -> Relocation::Entry + + # :category: Locations + # :call-seq: + # do_keyword_loc -> Location | nil + # + # Returns the Location represented by `do_keyword_loc`. + # -- + # : () -> Location? + def do_keyword_loc: () -> Location? + + # :category: Repository + # Save the do_keyword_loc location using the given saved source so that + # it can be retrieved later. + # -- + # : (_Repository repository) -> Relocation::Entry? + def save_do_keyword_loc: (_Repository repository) -> Relocation::Entry? + + # :category: Locations + # :call-seq: + # closing_loc -> Location | nil + # + # Returns the Location represented by `closing_loc`. + # -- + # : () -> Location? + def closing_loc: () -> Location? + + # :category: Repository + # Save the closing_loc location using the given saved source so that + # it can be retrieved later. + # -- + # : (_Repository repository) -> Relocation::Entry? + def save_closing_loc: (_Repository repository) -> Relocation::Entry? + + # :call-seq: + # predicate -> Node + # + # Returns the `predicate` attribute. + # -- + # : () -> Prism::node + def predicate: () -> Prism::node + + # :call-seq: + # statements -> StatementsNode | nil + # + # Returns the `statements` attribute. + # -- + # : () -> StatementsNode? + def statements: () -> StatementsNode? + + # :call-seq: + # keyword -> String + # + # Slice the location of keyword_loc from the source. + # -- + # : () -> String + def keyword: () -> String + + # :call-seq: + # do_keyword -> String | nil + # + # Slice the location of do_keyword_loc from the source. + # -- + # : () -> String? + def do_keyword: () -> String? + + # :call-seq: + # closing -> String | nil + # + # Slice the location of closing_loc from the source. + # -- + # : () -> String? + def closing: () -> String? + + # : (untyped other) -> boolish + def ===: (untyped other) -> boolish + end + + # Represents an xstring literal with no interpolation. + # + # `foo` + # ^^^^^ + class XStringNode < Node + @unescaped: String + + @closing_loc: Location + + @content_loc: Location + + @opening_loc: Location + + # Initialize a new XStringNode node. + # -- + # : (Source source, Integer node_id, Location location, Integer flags, Location opening_loc, Location content_loc, Location closing_loc, String unescaped) -> void + def initialize: (Source source, Integer node_id, Location location, Integer flags, Location opening_loc, Location content_loc, Location closing_loc, String unescaped) -> void + + # See Node.accept. + # -- + # : (_Visitor visitor) -> untyped + def accept: (_Visitor visitor) -> untyped + + # See Node.child_nodes. + # -- + # : () -> Array[node?] + def child_nodes: () -> Array[node?] + + # See Node.each_child_node. + # -- + # : () -> Enumerator[node, void] + # : () { (node) -> void } -> void + def each_child_node: () -> Enumerator[node, void] + | () { (node) -> void } -> void + + # See Node.compact_child_nodes. + # -- + # : () -> Array[node] + def compact_child_nodes: () -> Array[node] + + # See Node.comment_targets. + # -- + # : () -> Array[node | Location] + def comment_targets: () -> Array[node | Location] + + # :call-seq: + # copy(**fields) -> XStringNode + # + # Creates a copy of self with the given fields, using self as the template. + # -- + # : (?node_id: Integer, ?location: Location, ?flags: Integer, ?opening_loc: Location, ?content_loc: Location, ?closing_loc: Location, ?unescaped: String) -> XStringNode + def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?opening_loc: Location, ?content_loc: Location, ?closing_loc: Location, ?unescaped: String) -> XStringNode + + alias deconstruct child_nodes + + # : (Array[Symbol]? keys) -> Hash[Symbol, untyped] + def deconstruct_keys: (Array[Symbol]? keys) -> Hash[Symbol, untyped] + + # See `Node#type`. + # -- + # : () -> :x_string_node + def type: () -> :x_string_node + + # See `Node.type`. + # -- + # : () -> :x_string_node + def self.type: () -> :x_string_node + + # : () -> String + def inspect: () -> String + + # :category: Flags + # internal bytes forced the encoding to UTF-8 + # -- + # : () -> bool + def forced_utf8_encoding?: () -> bool + + # :category: Flags + # internal bytes forced the encoding to binary + # -- + # : () -> bool + def forced_binary_encoding?: () -> bool + + # :category: Locations + # :call-seq: + # opening_loc -> Location + # + # Returns the Location represented by `opening_loc`. + # -- + # : () -> Location + def opening_loc: () -> Location + + # :category: Repository + # Save the opening_loc location using the given saved source so that + # it can be retrieved later. + # -- + # : (_Repository repository) -> Relocation::Entry + def save_opening_loc: (_Repository repository) -> Relocation::Entry + + # :category: Locations + # :call-seq: + # content_loc -> Location + # + # Returns the Location represented by `content_loc`. + # -- + # : () -> Location + def content_loc: () -> Location + + # :category: Repository + # Save the content_loc location using the given saved source so that + # it can be retrieved later. + # -- + # : (_Repository repository) -> Relocation::Entry + def save_content_loc: (_Repository repository) -> Relocation::Entry + + # :category: Locations + # :call-seq: + # closing_loc -> Location + # + # Returns the Location represented by `closing_loc`. + # -- + # : () -> Location + def closing_loc: () -> Location + + # :category: Repository + # Save the closing_loc location using the given saved source so that + # it can be retrieved later. + # -- + # : (_Repository repository) -> Relocation::Entry + def save_closing_loc: (_Repository repository) -> Relocation::Entry + + # :call-seq: + # unescaped -> String + # + # Returns the `unescaped` attribute. + # -- + # : () -> String + def unescaped: () -> String + + # :call-seq: + # opening -> String + # + # Slice the location of opening_loc from the source. + # -- + # : () -> String + def opening: () -> String + + # :call-seq: + # content -> String + # + # Slice the location of content_loc from the source. + # -- + # : () -> String + def content: () -> String + + # :call-seq: + # closing -> String + # + # Slice the location of closing_loc from the source. + # -- + # : () -> String + def closing: () -> String + + # : (untyped other) -> boolish + def ===: (untyped other) -> boolish + end + + # Represents the use of the `yield` keyword. + # + # yield 1 + # ^^^^^^^ + class YieldNode < Node + @rparen_loc: Location? + + @arguments: ArgumentsNode? + + @lparen_loc: Location? + + @keyword_loc: Location + + # Initialize a new YieldNode node. + # -- + # : (Source source, Integer node_id, Location location, Integer flags, Location keyword_loc, Location? lparen_loc, ArgumentsNode? arguments, Location? rparen_loc) -> void + def initialize: (Source source, Integer node_id, Location location, Integer flags, Location keyword_loc, Location? lparen_loc, ArgumentsNode? arguments, Location? rparen_loc) -> void + + # See Node.accept. + # -- + # : (_Visitor visitor) -> untyped + def accept: (_Visitor visitor) -> untyped + + # See Node.child_nodes. + # -- + # : () -> Array[node?] + def child_nodes: () -> Array[node?] + + # See Node.each_child_node. + # -- + # : () -> Enumerator[node, void] + # : () { (node) -> void } -> void + def each_child_node: () -> Enumerator[node, void] + | () { (node) -> void } -> void + + # See Node.compact_child_nodes. + # -- + # : () -> Array[node] + def compact_child_nodes: () -> Array[node] + + # See Node.comment_targets. + # -- + # : () -> Array[node | Location] + def comment_targets: () -> Array[node | Location] + + # :call-seq: + # copy(**fields) -> YieldNode + # + # Creates a copy of self with the given fields, using self as the template. + # -- + # : (?node_id: Integer, ?location: Location, ?flags: Integer, ?keyword_loc: Location, ?lparen_loc: Location?, ?arguments: ArgumentsNode?, ?rparen_loc: Location?) -> YieldNode + def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?keyword_loc: Location, ?lparen_loc: Location?, ?arguments: ArgumentsNode?, ?rparen_loc: Location?) -> YieldNode + + alias deconstruct child_nodes + + # : (Array[Symbol]? keys) -> Hash[Symbol, untyped] + def deconstruct_keys: (Array[Symbol]? keys) -> Hash[Symbol, untyped] + + # See `Node#type`. + # -- + # : () -> :yield_node + def type: () -> :yield_node + + # See `Node.type`. + # -- + # : () -> :yield_node + def self.type: () -> :yield_node + + # : () -> String + def inspect: () -> String + + # :category: Locations + # :call-seq: + # keyword_loc -> Location + # + # Returns the Location represented by `keyword_loc`. + # -- + # : () -> Location + def keyword_loc: () -> Location + + # :category: Repository + # Save the keyword_loc location using the given saved source so that + # it can be retrieved later. + # -- + # : (_Repository repository) -> Relocation::Entry + def save_keyword_loc: (_Repository repository) -> Relocation::Entry + + # :category: Locations + # :call-seq: + # lparen_loc -> Location | nil + # + # Returns the Location represented by `lparen_loc`. + # -- + # : () -> Location? + def lparen_loc: () -> Location? + + # :category: Repository + # Save the lparen_loc location using the given saved source so that + # it can be retrieved later. + # -- + # : (_Repository repository) -> Relocation::Entry? + def save_lparen_loc: (_Repository repository) -> Relocation::Entry? + + # :call-seq: + # arguments -> ArgumentsNode | nil + # + # Returns the `arguments` attribute. + # -- + # : () -> ArgumentsNode? + def arguments: () -> ArgumentsNode? + + # :category: Locations + # :call-seq: + # rparen_loc -> Location | nil + # + # Returns the Location represented by `rparen_loc`. + # -- + # : () -> Location? + def rparen_loc: () -> Location? + + # :category: Repository + # Save the rparen_loc location using the given saved source so that + # it can be retrieved later. + # -- + # : (_Repository repository) -> Relocation::Entry? + def save_rparen_loc: (_Repository repository) -> Relocation::Entry? + + # :call-seq: + # keyword -> String + # + # Slice the location of keyword_loc from the source. + # -- + # : () -> String + def keyword: () -> String + + # :call-seq: + # lparen -> String | nil + # + # Slice the location of lparen_loc from the source. + # -- + # : () -> String? + def lparen: () -> String? + + # :call-seq: + # rparen -> String | nil + # + # Slice the location of rparen_loc from the source. + # -- + # : () -> String? + def rparen: () -> String? + + # : (untyped other) -> boolish + def ===: (untyped other) -> boolish + end + + # Flags for arguments nodes. + module ArgumentsNodeFlags + # if the arguments contain forwarding + CONTAINS_FORWARDING: untyped + + # if the arguments contain keywords + CONTAINS_KEYWORDS: untyped + + # if the arguments contain a keyword splat + CONTAINS_KEYWORD_SPLAT: untyped + + # if the arguments contain a splat + CONTAINS_SPLAT: untyped + + # if the arguments contain multiple splats + CONTAINS_MULTIPLE_SPLATS: untyped + end + + # Flags for array nodes. + module ArrayNodeFlags + # if array contains splat nodes + CONTAINS_SPLAT: untyped + end + + # Flags for call nodes. + module CallNodeFlags + # &. operator + SAFE_NAVIGATION: untyped + + # a call that could have been a local variable + VARIABLE_CALL: untyped + + # a call that is an attribute write, so the value being written should be returned + ATTRIBUTE_WRITE: untyped + + # a call that ignores method visibility + IGNORE_VISIBILITY: untyped + end + + # Flags for nodes that have unescaped content. + module EncodingFlags + # internal bytes forced the encoding to UTF-8 + FORCED_UTF8_ENCODING: untyped + + # internal bytes forced the encoding to binary + FORCED_BINARY_ENCODING: untyped + end + + # Flags for integer nodes that correspond to the base of the integer. + module IntegerBaseFlags + # 0b prefix + BINARY: untyped + + # 0d or no prefix + DECIMAL: untyped + + # 0o or 0 prefix + OCTAL: untyped + + # 0x prefix + HEXADECIMAL: untyped + 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: untyped + + # mutable by virtue of a `frozen_string_literal: false` comment or `--disable-frozen-string-literal`; only for adjacent string literals like `'a' 'b'` + MUTABLE: untyped + 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: untyped + 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: untyped + end + + # Flags for parameter nodes. + module ParameterFlags + # a parameter name that has been repeated in the method signature + REPEATED_PARAMETER: untyped + end + + # Flags for parentheses nodes. + module ParenthesesNodeFlags + # parentheses that contain multiple potentially void statements + MULTIPLE_STATEMENTS: untyped + end + + # Flags for range and flip-flop nodes. + module RangeFlags + # ... operator + EXCLUDE_END: untyped + end + + # Flags for regular expression and match last line nodes. + module RegularExpressionFlags + # i - ignores the case of characters when matching + IGNORE_CASE: untyped + + # x - ignores whitespace and allows comments in regular expressions + EXTENDED: untyped + + # m - allows $ to match the end of lines within strings + MULTI_LINE: untyped + + # o - only interpolates values into the regular expression once + ONCE: untyped + + # e - forces the EUC-JP encoding + EUC_JP: untyped + + # n - forces the ASCII-8BIT encoding + ASCII_8BIT: untyped + + # s - forces the Windows-31J encoding + WINDOWS_31J: untyped + + # u - forces the UTF-8 encoding + UTF_8: untyped + + # internal bytes forced the encoding to UTF-8 + FORCED_UTF8_ENCODING: untyped + + # internal bytes forced the encoding to binary + FORCED_BINARY_ENCODING: untyped + + # internal bytes forced the encoding to US-ASCII + FORCED_US_ASCII_ENCODING: untyped + end + + # Flags for shareable constant nodes. + module ShareableConstantNodeFlags + # constant writes that should be modified with shareable constant value literal + LITERAL: untyped + + # constant writes that should be modified with shareable constant value experimental everything + EXPERIMENTAL_EVERYTHING: untyped + + # constant writes that should be modified with shareable constant value experimental copy + EXPERIMENTAL_COPY: untyped + end + + # Flags for string nodes. + module StringFlags + # internal bytes forced the encoding to UTF-8 + FORCED_UTF8_ENCODING: untyped + + # internal bytes forced the encoding to binary + FORCED_BINARY_ENCODING: untyped + + # frozen by virtue of a `frozen_string_literal: true` comment or `--enable-frozen-string-literal` + FROZEN: untyped + + # mutable by virtue of a `frozen_string_literal: false` comment or `--disable-frozen-string-literal` + MUTABLE: untyped + end + + # Flags for symbol nodes. + module SymbolFlags + # internal bytes forced the encoding to UTF-8 + FORCED_UTF8_ENCODING: untyped + + # internal bytes forced the encoding to binary + FORCED_BINARY_ENCODING: untyped + + # internal bytes forced the encoding to US-ASCII + FORCED_US_ASCII_ENCODING: untyped + 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: ::Integer + + # A flag to indicate that the value that the node represents is a value that + # can be determined at parse-time. + STATIC_LITERAL: ::Integer + end +end diff --git a/sig/generated/prism/node_ext.rbs b/sig/generated/prism/node_ext.rbs new file mode 100644 index 0000000000..db2be11684 --- /dev/null +++ b/sig/generated/prism/node_ext.rbs @@ -0,0 +1,215 @@ +# Generated from lib/prism/node_ext.rb with RBS::Inline + +# -- +# 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 + # : (*String replacements) -> void + def deprecated: (*String replacements) -> void + end + + module RegularExpressionOptions + # Returns a numeric value that represents the flags that were used to create + # the regular expression. + # -- + # : (Integer flags) -> Integer + def self.options: (Integer flags) -> Integer + end + + class InterpolatedMatchLastLineNode < Node + # Returns a numeric value that represents the flags that were used to create + # the regular expression. + # -- + # : () -> Integer + def options: () -> Integer + end + + class InterpolatedRegularExpressionNode < Node + # Returns a numeric value that represents the flags that were used to create + # the regular expression. + # -- + # : () -> Integer + def options: () -> Integer + end + + class MatchLastLineNode < Node + # Returns a numeric value that represents the flags that were used to create + # the regular expression. + # -- + # : () -> Integer + def options: () -> Integer + end + + class RegularExpressionNode < Node + # Returns a numeric value that represents the flags that were used to create + # the regular expression. + # -- + # : () -> Integer + def options: () -> Integer + end + + module HeredocQuery + # Returns true if this node was represented as a heredoc in the source code. + # -- + # : (String? opening) -> bool? + def self.heredoc?: (String? opening) -> bool? + end + + class InterpolatedStringNode < Node + # Returns true if this node was represented as a heredoc in the source code. + # -- + # : () -> bool? + def heredoc?: () -> bool? + end + + class InterpolatedXStringNode < Node + # Returns true if this node was represented as a heredoc in the source code. + # -- + # : () -> bool? + def heredoc?: () -> bool? + end + + class StringNode < Node + # Returns true if this node was represented as a heredoc in the source code. + # -- + # : () -> bool? + def heredoc?: () -> bool? + + # Occasionally it's helpful to treat a string as if it were interpolated so + # that there's a consistent interface for working with strings. + # -- + # : () -> InterpolatedStringNode + def to_interpolated: () -> InterpolatedStringNode + end + + class XStringNode < Node + # Returns true if this node was represented as a heredoc in the source code. + # -- + # : () -> bool? + def heredoc?: () -> bool? + + # Occasionally it's helpful to treat a string as if it were interpolated so + # that there's a consistent interface for working with strings. + # -- + # : () -> InterpolatedXStringNode + def to_interpolated: () -> InterpolatedXStringNode + end + + class ImaginaryNode < Node + # Returns the value of the node as a Ruby Complex. + # -- + # : () -> Complex + def value: () -> Complex + end + + class RationalNode < Node + # Returns the value of the node as a Ruby Rational. + # -- + # : () -> Rational + def value: () -> Rational + end + + class ConstantReadNode < Node + # Returns the list of parts for the full name of this constant. + # For example: [:Foo] + # -- + # : () -> Array[Symbol] + def full_name_parts: () -> Array[Symbol] + + # Returns the full name of this constant. For example: "Foo" + # -- + # : () -> String + def full_name: () -> String + end + + class ConstantWriteNode < Node + # Returns the list of parts for the full name of this constant. + # For example: [:Foo] + # -- + # : () -> Array[Symbol] + def full_name_parts: () -> Array[Symbol] + + # Returns the full name of this constant. For example: "Foo" + # -- + # : () -> String + def full_name: () -> String + 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] + # -- + # : () -> Array[Symbol] + def full_name_parts: () -> Array[Symbol] + + # Returns the full name of this constant path. For example: "Foo::Bar" + # -- + # : () -> String + def full_name: () -> String + end + + class ConstantPathTargetNode < Node + # Returns the list of parts for the full name of this constant path. + # For example: [:Foo, :Bar] + # -- + # : () -> Array[Symbol] + def full_name_parts: () -> Array[Symbol] + + # Returns the full name of this constant path. For example: "Foo::Bar" + # -- + # : () -> String + def full_name: () -> String + end + + class ConstantTargetNode < Node + # Returns the list of parts for the full name of this constant. + # For example: [:Foo] + # -- + # : () -> Array[Symbol] + def full_name_parts: () -> Array[Symbol] + + # Returns the full name of this constant. For example: "Foo" + # -- + # : () -> String + def full_name: () -> String + end + + class ParametersNode < Node + # Mirrors the Method#parameters method. + # -- + # : () -> Array[[Symbol, Symbol] | [Symbol]] + def signature: () -> Array[[ Symbol, Symbol ] | [ Symbol ]] + 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. + # -- + # : () -> Location? + def full_message_loc: () -> Location? + end +end diff --git a/sig/generated/prism/node_find.rbs b/sig/generated/prism/node_find.rbs new file mode 100644 index 0000000000..9924ff1452 --- /dev/null +++ b/sig/generated/prism/node_find.rbs @@ -0,0 +1,81 @@ +# Generated from lib/prism/node_find.rb with RBS::Inline + +module Prism + # Finds the Prism AST node corresponding to a given Method, UnboundMethod, + # Proc, or Thread::Backtrace::Location. On CRuby, uses node_id from the + # instruction sequence for an exact match. On other implementations, falls + # back to best-effort matching by source location line number. + # + # This module is autoloaded so that programs that don't use Prism.find don't + # pay for its definition. + module NodeFind + # Find the node for the given callable or backtrace location. + # -- + # : (Method | UnboundMethod | Proc | Thread::Backtrace::Location callable, bool rubyvm) -> Node? + def self.find: (Method | UnboundMethod | Proc | Thread::Backtrace::Location callable, bool rubyvm) -> Node? + + # Base class that handles parsing a file. + class Find + private + + # Parse the given file path, returning a ParseResult or nil. + # -- + # : (String? file) -> ParseResult? + def parse_file: (String? file) -> ParseResult? + end + + # Finds the AST node for a Method, UnboundMethod, or Proc using the node_id + # from the instruction sequence. + class RubyVMCallableFind < Find + # Find the node for the given callable using the ISeq node_id. + # -- + # : (Method | UnboundMethod | Proc callable) -> Node? + def find: (Method | UnboundMethod | Proc callable) -> Node? + end + + # Finds the AST node for a Thread::Backtrace::Location using the node_id + # from the backtrace location. + class RubyVMBacktraceLocationFind < Find + # Find the node for the given backtrace location using node_id. + # -- + # : (Thread::Backtrace::Location location) -> Node? + def find: (Thread::Backtrace::Location location) -> Node? + end + + # Finds the AST node for a Method or UnboundMethod using best-effort line + # matching. Used on non-CRuby implementations. + class LineMethodFind < Find + # Find the node for the given method by matching on name and line. + # -- + # : (Method | UnboundMethod callable) -> Node? + def find: (Method | UnboundMethod callable) -> Node? + end + + # Finds the AST node for a lambda using best-effort line matching. Used + # on non-CRuby implementations. + class LineLambdaFind < Find + # Find the node for the given lambda by matching on line. + # -- + # : (Proc callable) -> Node? + def find: (Proc callable) -> Node? + end + + # Finds the AST node for a non-lambda Proc using best-effort line + # matching. Used on non-CRuby implementations. + class LineProcFind < Find + # Find the node for the given proc by matching on line. + # -- + # : (Proc callable) -> Node? + def find: (Proc callable) -> Node? + end + + # Finds the AST node for a Thread::Backtrace::Location using best-effort + # line matching. Used on non-CRuby implementations. + class LineBacktraceLocationFind < Find + # Find the node for the given backtrace location by matching on line. + # -- + # : (Thread::Backtrace::Location location) -> Node? + def find: (Thread::Backtrace::Location location) -> Node? + end + end +end diff --git a/sig/generated/prism/parse_result.rbs b/sig/generated/prism/parse_result.rbs new file mode 100644 index 0000000000..f005f17375 --- /dev/null +++ b/sig/generated/prism/parse_result.rbs @@ -0,0 +1,850 @@ +# Generated from lib/prism/parse_result.rb with RBS::Inline + +module Prism + # An internal interface for a cache that can be used to compute code + # units from byte offsets. + interface _CodeUnitsCache + def []: (Integer byte_offset) -> Integer + end + + # 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. + # + # Note that if you are calling this method manually, you will need to supply + # the start_line and offsets parameters. start_line is the line number that + # the source starts on, which is typically 1 but can be different if this + # source is a subset of a larger source or if this is an eval. offsets is an + # array of byte offsets for the start of each line in the source code, which + # can be calculated by iterating through the source code and recording the + # byte offset whenever a newline character is encountered. The first + # element is always 0 to mark the first line. + # -- + # : (String source, Integer start_line, Array[Integer] offsets) -> Source + def self.for: (String source, Integer start_line, Array[Integer] offsets) -> Source + + # The source code that this source object represents. + attr_reader source: String + + # The line number where this source starts. + attr_reader start_line: Integer + + # The list of newline byte offsets in the source code. When initialized from + # the C extension, this may be a packed binary string of uint32_t values + # that is lazily unpacked on first access. + # -- + # : () -> Array[Integer] + def offsets: () -> Array[Integer] + + # Create a new source object with the given source code. The offsets + # parameter can be either an Array of Integer byte offsets or a packed + # binary string of uint32_t values (from the C extension). + # -- + # : (String source, Integer start_line, Array[Integer] | String offsets) -> void + def initialize: (String source, Integer start_line, Array[Integer] | String offsets) -> void + + # Replace the value of start_line with the given value. + # -- + # : (Integer start_line) -> void + def replace_start_line: (Integer start_line) -> void + + # Replace the value of offsets with the given value. + # -- + # : (Array[Integer] offsets) -> void + def replace_offsets: (Array[Integer] offsets) -> void + + # Returns the encoding of the source code, which is set by parameters to the + # parser or by the encoding magic comment. + # -- + # : () -> Encoding + def encoding: () -> Encoding + + # Returns the lines of the source code as an array of strings. + # -- + # : () -> Array[String] + def lines: () -> Array[String] + + # Perform a byteslice on the source code using the given byte offset and + # byte length. + # -- + # : (Integer byte_offset, Integer length) -> String + def slice: (Integer byte_offset, Integer length) -> String + + # Converts the line number and column in bytes to a byte offset. + # -- + # : (Integer line, Integer column) -> Integer + def byte_offset: (Integer line, Integer column) -> Integer + + # Binary search through the offsets to find the line number for the given + # byte offset. + # -- + # : (Integer byte_offset) -> Integer + def line: (Integer byte_offset) -> Integer + + # Return the byte offset of the start of the line corresponding to the given + # byte offset. + # -- + # : (Integer byte_offset) -> Integer + def line_start: (Integer byte_offset) -> Integer + + # Returns the byte offset of the end of the line corresponding to the given + # byte offset. + # -- + # : (Integer byte_offset) -> Integer + def line_end: (Integer byte_offset) -> Integer + + # Return the column in bytes for the given byte offset. + # -- + # : (Integer byte_offset) -> Integer + def column: (Integer byte_offset) -> Integer + + # Return the character offset for the given byte offset. + # -- + # : (Integer byte_offset) -> Integer + def character_offset: (Integer byte_offset) -> Integer + + # Return the column in characters for the given byte offset. + # -- + # : (Integer byte_offset) -> Integer + def character_column: (Integer byte_offset) -> Integer + + # 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. + # -- + # : (Integer byte_offset, Encoding encoding) -> Integer + def code_units_offset: (Integer byte_offset, Encoding encoding) -> Integer + + # Generate a cache that targets a specific encoding for calculating code + # unit offsets. + # -- + # : (Encoding encoding) -> CodeUnitsCache + def code_units_cache: (Encoding encoding) -> CodeUnitsCache + + # Returns the column in code units for the given encoding for the + # given byte offset. + # -- + # : (Integer byte_offset, Encoding encoding) -> Integer + def code_units_column: (Integer byte_offset, Encoding encoding) -> Integer + + # Freeze this object and the objects it contains. + # -- + # : () -> void + def deep_freeze: () -> void + + private + + # Binary search through the offsets to find the line number for the given + # byte offset. + # -- + # : (Integer byte_offset) -> Integer + def find_line: (Integer byte_offset) -> Integer + 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 + @source: String + + @encoding: Encoding + + # : (String source, Encoding encoding) -> void + def initialize: (String source, Encoding encoding) -> void + + # : (Integer byte_offset, Integer byte_length) -> Integer + def count: (Integer byte_offset, Integer byte_length) -> Integer + end + + class LengthCounter + @source: String + + @encoding: Encoding + + # : (String source, Encoding encoding) -> void + def initialize: (String source, Encoding encoding) -> void + + # : (Integer byte_offset, Integer byte_length) -> Integer + def count: (Integer byte_offset, Integer byte_length) -> Integer + end + + @source: String + + @counter: UTF16Counter | LengthCounter + + @cache: Hash[Integer, Integer] + + @offsets: Array[Integer] + + # Initialize a new cache with the given source and encoding. + # -- + # : (String source, Encoding encoding) -> void + def initialize: (String source, Encoding encoding) -> void + + # Retrieve the code units offset from the given byte offset. + # -- + # : (Integer byte_offset) -> Integer + def []: (Integer byte_offset) -> Integer + 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. + # -- + # : (Integer byte_offset) -> Integer + def character_offset: (Integer byte_offset) -> Integer + + # Return the column in characters for the given byte offset. + # -- + # : (Integer byte_offset) -> Integer + def character_column: (Integer byte_offset) -> Integer + + # 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. + # -- + # : (Integer byte_offset, Encoding encoding) -> Integer + def code_units_offset: (Integer byte_offset, Encoding encoding) -> Integer + + # 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. + # -- + # : (Encoding encoding) -> _CodeUnitsCache + def code_units_cache: (Encoding encoding) -> _CodeUnitsCache + + # 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`. + # -- + # : (Integer byte_offset, Encoding encoding) -> Integer + def code_units_column: (Integer byte_offset, Encoding encoding) -> Integer + 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: Source + + # The byte offset from the beginning of the source where this location + # starts. + attr_reader start_offset: Integer + + # The length of this location in bytes. + attr_reader length: Integer + + @trailing_comments: Array[Comment]? + + @leading_comments: Array[Comment]? + + # Create a new location object with the given source, start byte offset, and + # byte length. + # -- + # : (Source source, Integer start_offset, Integer length) -> void + def initialize: (Source source, Integer start_offset, Integer length) -> void + + # These are the comments that are associated with this location that exist + # before the start of this location. + # -- + # : () -> Array[Comment] + def leading_comments: () -> Array[Comment] + + # Attach a comment to the leading comments of this location. + # -- + # : (Comment comment) -> void + def leading_comment: (Comment comment) -> void + + # These are the comments that are associated with this location that exist + # after the end of this location. + # -- + # : () -> Array[Comment] + def trailing_comments: () -> Array[Comment] + + # Attach a comment to the trailing comments of this location. + # -- + # : (Comment comment) -> void + def trailing_comment: (Comment comment) -> void + + # Returns all comments that are associated with this location (both leading + # and trailing comments). + # -- + # : () -> Array[Comment] + def comments: () -> Array[Comment] + + # Create a new location object with the given options. + # -- + # : (?source: Source, ?start_offset: Integer, ?length: Integer) -> Location + def copy: (?source: Source, ?start_offset: Integer, ?length: Integer) -> Location + + # Returns a new location that is the result of chopping off the last byte. + # -- + # : () -> Location + def chop: () -> Location + + # Returns a string representation of this location. + # -- + # : () -> String + def inspect: () -> String + + # Returns all of the lines of the source code associated with this location. + # -- + # : () -> Array[String] + def source_lines: () -> Array[String] + + # The source code that this location represents. + # -- + # : () -> String + def slice: () -> String + + # 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. + # -- + # : () -> String + def slice_lines: () -> String + + # The character offset from the beginning of the source where this location + # starts. + # -- + # : () -> Integer + def start_character_offset: () -> Integer + + # The offset from the start of the file in code units of the given encoding. + # -- + # : (Encoding encoding) -> Integer + def start_code_units_offset: (Encoding encoding) -> Integer + + # The start offset from the start of the file in code units using the given + # cache to fetch or calculate the value. + # -- + # : (_CodeUnitsCache cache) -> Integer + def cached_start_code_units_offset: (_CodeUnitsCache cache) -> Integer + + # The byte offset from the beginning of the source where this location ends. + # -- + # : () -> Integer + def end_offset: () -> Integer + + # The character offset from the beginning of the source where this location + # ends. + # -- + # : () -> Integer + def end_character_offset: () -> Integer + + # The offset from the start of the file in code units of the given encoding. + # -- + # : (Encoding encoding) -> Integer + def end_code_units_offset: (Encoding encoding) -> Integer + + # The end offset from the start of the file in code units using the given + # cache to fetch or calculate the value. + # -- + # : (_CodeUnitsCache cache) -> Integer + def cached_end_code_units_offset: (_CodeUnitsCache cache) -> Integer + + # The line number where this location starts. + # -- + # : () -> Integer + def start_line: () -> Integer + + # The content of the line where this location starts before this location. + # -- + # : () -> String + def start_line_slice: () -> String + + # The line number where this location ends. + # -- + # : () -> Integer + def end_line: () -> Integer + + # The column in bytes where this location starts from the start of + # the line. + # -- + # : () -> Integer + def start_column: () -> Integer + + # The column in characters where this location ends from the start of + # the line. + # -- + # : () -> Integer + def start_character_column: () -> Integer + + # The column in code units of the given encoding where this location + # starts from the start of the line. + # -- + # : (?Encoding encoding) -> Integer + def start_code_units_column: (?Encoding encoding) -> Integer + + # The start column in code units using the given cache to fetch or calculate + # the value. + # -- + # : (_CodeUnitsCache cache) -> Integer + def cached_start_code_units_column: (_CodeUnitsCache cache) -> Integer + + # The column in bytes where this location ends from the start of the + # line. + # -- + # : () -> Integer + def end_column: () -> Integer + + # The column in characters where this location ends from the start of + # the line. + # -- + # : () -> Integer + def end_character_column: () -> Integer + + # The column in code units of the given encoding where this location + # ends from the start of the line. + # -- + # : (?Encoding encoding) -> Integer + def end_code_units_column: (?Encoding encoding) -> Integer + + # The end column in code units using the given cache to fetch or calculate + # the value. + # -- + # : (_CodeUnitsCache cache) -> Integer + def cached_end_code_units_column: (_CodeUnitsCache cache) -> Integer + + # Implement the hash pattern matching interface for Location. + # -- + # : (Array[Symbol]? keys) -> Hash[Symbol, untyped] + def deconstruct_keys: (Array[Symbol]? keys) -> Hash[Symbol, untyped] + + # Implement the pretty print interface for Location. + # -- + # : (PP q) -> void + def pretty_print: (PP q) -> void + + # Returns true if the given other location is equal to this location. + # -- + # : (untyped other) -> bool + def ==: (untyped other) -> bool + + # 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. + # -- + # : (Location other) -> Location + def join: (Location other) -> Location + + # 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. + # -- + # : (String string) -> Location + def adjoin: (String string) -> Location + 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: Location + + # Create a new comment object with the given location. + # -- + # : (Location location) -> void + def initialize: (Location location) -> void + + # Implement the hash pattern matching interface for Comment. + # -- + # : (Array[Symbol]? keys) -> Hash[Symbol, untyped] + def deconstruct_keys: (Array[Symbol]? keys) -> Hash[Symbol, untyped] + + # Returns the content of the comment by slicing it from the source code. + # -- + # : () -> String + def slice: () -> String + + # Returns true if this comment happens on the same line as other code and + # false if the comment is by itself. This can only be true for inline + # comments and should be false for block comments. + # -- + # : () -> bool + def trailing?: () -> bool + 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. + # -- + # : () -> bool + def trailing?: () -> bool + + # Returns a string representation of this comment. + # -- + # : () -> String + def inspect: () -> String + end + + # EmbDocComment objects correspond to comments that are surrounded by =begin + # and =end. + class EmbDocComment < Comment + # Returns false. This can only be true for inline comments. + # -- + # : () -> bool + def trailing?: () -> bool + + # Returns a string representation of this comment. + # -- + # : () -> String + def inspect: () -> String + 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: Location + + # A Location object representing the location of the value in the source. + attr_reader value_loc: Location + + # Create a new magic comment object with the given key and value locations. + # -- + # : (Location key_loc, Location value_loc) -> void + def initialize: (Location key_loc, Location value_loc) -> void + + # Returns the key of the magic comment by slicing it from the source code. + # -- + # : () -> String + def key: () -> String + + # Returns the value of the magic comment by slicing it from the source code. + # -- + # : () -> String + def value: () -> String + + # Implement the hash pattern matching interface for MagicComment. + # -- + # : (Array[Symbol]? keys) -> Hash[Symbol, untyped] + def deconstruct_keys: (Array[Symbol]? keys) -> Hash[Symbol, untyped] + + # Returns a string representation of this magic comment. + # -- + # : () -> String + def inspect: () -> String + 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: Symbol + + # The message associated with this error. + attr_reader message: String + + # A Location object representing the location of this error in the source. + attr_reader location: Location + + # The level of this error. + attr_reader level: Symbol + + # Create a new error object with the given message and location. + # -- + # : (Symbol type, String message, Location location, Symbol level) -> void + def initialize: (Symbol type, String message, Location location, Symbol level) -> void + + # Implement the hash pattern matching interface for ParseError. + # -- + # : (Array[Symbol]? keys) -> Hash[Symbol, untyped] + def deconstruct_keys: (Array[Symbol]? keys) -> Hash[Symbol, untyped] + + # Returns a string representation of this error. + # -- + # : () -> String + def inspect: () -> String + 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: Symbol + + # The message associated with this warning. + attr_reader message: String + + # A Location object representing the location of this warning in the source. + attr_reader location: Location + + # The level of this warning. + attr_reader level: Symbol + + # Create a new warning object with the given message and location. + # -- + # : (Symbol type, String message, Location location, Symbol level) -> void + def initialize: (Symbol type, String message, Location location, Symbol level) -> void + + # Implement the hash pattern matching interface for ParseWarning. + # -- + # : (Array[Symbol]? keys) -> Hash[Symbol, untyped] + def deconstruct_keys: (Array[Symbol]? keys) -> Hash[Symbol, untyped] + + # Returns a string representation of this warning. + # -- + # : () -> String + def inspect: () -> String + end + + # This represents the result of a call to Prism.parse or Prism.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: Array[Comment] + + # The list of magic comments that were encountered during parsing. + attr_reader magic_comments: Array[MagicComment] + + # 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: Location? + + # The list of errors that were generated during parsing. + attr_reader errors: Array[ParseError] + + # The list of warnings that were generated during parsing. + attr_reader warnings: Array[ParseWarning] + + # A Source instance that represents the source code that was parsed. + attr_reader source: Source + + # Create a new result object with the given values. + # -- + # : (Array[Comment] comments, Array[MagicComment] magic_comments, Location? data_loc, Array[ParseError] errors, Array[ParseWarning] warnings, bool continuable, Source source) -> void + def initialize: (Array[Comment] comments, Array[MagicComment] magic_comments, Location? data_loc, Array[ParseError] errors, Array[ParseWarning] warnings, bool continuable, Source source) -> void + + # Implement the hash pattern matching interface for Result. + # -- + # : (Array[Symbol]? keys) -> Hash[Symbol, untyped] + def deconstruct_keys: (Array[Symbol]? keys) -> Hash[Symbol, untyped] + + # Returns the encoding of the source code that was parsed. + # -- + # : () -> Encoding + def encoding: () -> Encoding + + # Returns true if there were no errors during parsing and false if there + # were. + # -- + # : () -> bool + def success?: () -> bool + + # Returns true if there were errors during parsing and false if there were + # not. + # -- + # : () -> bool + def failure?: () -> bool + + # Returns true if the parsed source is an incomplete expression that could + # become valid with additional input. This is useful for REPL contexts (such + # as IRB) where the user may be entering a multi-line expression one line at + # a time and the implementation needs to determine whether to wait for more + # input or to evaluate what has been entered so far. + # + # Concretely, this returns true when every error present is caused by the + # parser reaching the end of the input before a construct was closed (e.g. + # an unclosed string, array, block, or keyword), and returns false when any + # error is caused by a token that makes the input structurally invalid + # regardless of what might follow (e.g. a stray `end`, `]`, or `)` with no + # matching opener). + # + # Examples: + # + # Prism.parse("1 + [").continuable? #=> true (unclosed array) + # Prism.parse("1 + ]").continuable? #=> false (stray ]) + # Prism.parse("tap do").continuable? #=> true (unclosed block) + # Prism.parse("end.tap do").continuable? #=> false (stray end) + # + # -- + # : () -> bool + def continuable?: () -> bool + + # Create a code units cache for the given encoding. + # -- + # : (Encoding encoding) -> _CodeUnitsCache + def code_units_cache: (Encoding encoding) -> _CodeUnitsCache + end + + # This is a result specific to the `parse` and `parse_file` methods. + class ParseResult < Result + # The syntax tree that was parsed from the source code. + attr_reader value: ProgramNode + + # Create a new parse result object with the given values. + # -- + # : (ProgramNode value, Array[Comment] comments, Array[MagicComment] magic_comments, Location? data_loc, Array[ParseError] errors, Array[ParseWarning] warnings, bool continuable, Source source) -> void + def initialize: (ProgramNode value, Array[Comment] comments, Array[MagicComment] magic_comments, Location? data_loc, Array[ParseError] errors, Array[ParseWarning] warnings, bool continuable, Source source) -> void + + # Implement the hash pattern matching interface for ParseResult. + # -- + # : (Array[Symbol]? keys) -> Hash[Symbol, untyped] + def deconstruct_keys: (Array[Symbol]? keys) -> Hash[Symbol, untyped] + + # Attach the list of comments to their respective locations in the tree. + # -- + # : () -> void + def attach_comments!: () -> void + + # Walk the tree and mark nodes that are on a new line, loosely emulating + # the behavior of CRuby's `:line` tracepoint event. + # -- + # : () -> void + def mark_newlines!: () -> void + + # Returns a string representation of the syntax tree with the errors + # displayed inline. + # -- + # : () -> String + def errors_format: () -> String + 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: Array[[ Token, Integer ]] + + # Create a new lex result object with the given values. + # -- + # : (Array[[Token, Integer]] value, Array[Comment] comments, Array[MagicComment] magic_comments, Location? data_loc, Array[ParseError] errors, Array[ParseWarning] warnings, bool continuable, Source source) -> void + def initialize: (Array[[ Token, Integer ]] value, Array[Comment] comments, Array[MagicComment] magic_comments, Location? data_loc, Array[ParseError] errors, Array[ParseWarning] warnings, bool continuable, Source source) -> void + + # Implement the hash pattern matching interface for LexResult. + # -- + # : (Array[Symbol]? keys) -> Hash[Symbol, untyped] + def deconstruct_keys: (Array[Symbol]? keys) -> Hash[Symbol, untyped] + 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: [ ProgramNode, Array[[ Token, Integer ]] ] + + # Create a new parse lex result object with the given values. + # -- + # : ([ProgramNode, Array[[Token, Integer]]] value, Array[Comment] comments, Array[MagicComment] magic_comments, Location? data_loc, Array[ParseError] errors, Array[ParseWarning] warnings, bool continuable, Source source) -> void + def initialize: ([ ProgramNode, Array[[ Token, Integer ]] ] value, Array[Comment] comments, Array[MagicComment] magic_comments, Location? data_loc, Array[ParseError] errors, Array[ParseWarning] warnings, bool continuable, Source source) -> void + + # Implement the hash pattern matching interface for ParseLexResult. + # -- + # : (Array[Symbol]? keys) -> Hash[Symbol, untyped] + def deconstruct_keys: (Array[Symbol]? keys) -> Hash[Symbol, untyped] + 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: Source + + # The type of token that this token is. + attr_reader type: Symbol + + # A byteslice of the source that this token represents. + attr_reader value: String + + @location: Location | Integer + + # Create a new token object with the given type, value, and location. + # -- + # : (Source source, Symbol type, String value, Location | Integer location) -> void + def initialize: (Source source, Symbol type, String value, Location | Integer location) -> void + + # Implement the hash pattern matching interface for Token. + # -- + # : (Array[Symbol]? keys) -> Hash[Symbol, untyped] + def deconstruct_keys: (Array[Symbol]? keys) -> Hash[Symbol, untyped] + + # A Location object representing the location of this token in the source. + # -- + # : () -> Location + def location: () -> Location + + # Implement the pretty print interface for Token. + # -- + # : (PP q) -> void + def pretty_print: (PP q) -> void + + # Returns true if the given other token is equal to this token. + # -- + # : (untyped other) -> bool + def ==: (untyped other) -> bool + + # Returns a string representation of this token. + # -- + # : () -> String + def inspect: () -> String + + # Freeze this object and the objects it contains. + # -- + # : () -> void + def deep_freeze: () -> void + 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: Array[Symbol] + + # 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: Array[Symbol] + + # Create a new scope object with the given locals and forwarding. + # -- + # : (Array[Symbol] locals, Array[Symbol] forwarding) -> void + def initialize: (Array[Symbol] locals, Array[Symbol] forwarding) -> void + 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. + # -- + # : (?locals: Array[Symbol], ?forwarding: Array[Symbol]) -> Scope + def self.scope: (?locals: Array[Symbol], ?forwarding: Array[Symbol]) -> Scope +end diff --git a/sig/generated/prism/parse_result/comments.rbs b/sig/generated/prism/parse_result/comments.rbs new file mode 100644 index 0000000000..1dc7ae974b --- /dev/null +++ b/sig/generated/prism/parse_result/comments.rbs @@ -0,0 +1,108 @@ +# Generated from lib/prism/parse_result/comments.rb with RBS::Inline + +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 + # An internal interface for a target that comments can be attached + # to. This is either going to be a NodeTarget or a CommentTarget. + interface _CommentTarget + def start_offset: () -> Integer + + def end_offset: () -> Integer + + def encloses?: (Comment) -> bool + + def leading_comment: (Comment) -> void + + def trailing_comment: (Comment) -> void + end + + # A target for attaching comments that is based on a specific node's + # location. + class NodeTarget + # :nodoc: + attr_reader node: node + + # : (node node) -> void + def initialize: (node node) -> void + + # : () -> Integer + def start_offset: () -> Integer + + # : () -> Integer + def end_offset: () -> Integer + + # : (Comment comment) -> bool + def encloses?: (Comment comment) -> bool + + # : (Comment comment) -> void + def leading_comment: (Comment comment) -> void + + # : (Comment comment) -> void + def trailing_comment: (Comment comment) -> void + 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: Location + + # : (Location location) -> void + def initialize: (Location location) -> void + + # : () -> Integer + def start_offset: () -> Integer + + # : () -> Integer + def end_offset: () -> Integer + + # : (Comment comment) -> bool + def encloses?: (Comment comment) -> bool + + # : (Comment comment) -> void + def leading_comment: (Comment comment) -> void + + # : (Comment comment) -> void + def trailing_comment: (Comment comment) -> void + end + + # The parse result that we are attaching comments to. + attr_reader parse_result: ParseResult + + # Create a new Comments object that will attach comments to the given + # parse result. + # -- + # : (ParseResult parse_result) -> void + def initialize: (ParseResult parse_result) -> void + + # Attach the comments to their respective locations in the tree by + # mutating the parse result. + # -- + # : () -> void + def attach!: () -> void + + private + + # Responsible for finding the nearest targets to the given comment within + # the context of the given encapsulating node. + # -- + # : (node node, Comment comment) -> [_CommentTarget?, _CommentTarget?, _CommentTarget?] + def nearest_targets: (node node, Comment comment) -> [ _CommentTarget?, _CommentTarget?, _CommentTarget? ] + end + end +end diff --git a/sig/generated/prism/parse_result/errors.rbs b/sig/generated/prism/parse_result/errors.rbs new file mode 100644 index 0000000000..1b97d8a763 --- /dev/null +++ b/sig/generated/prism/parse_result/errors.rbs @@ -0,0 +1,22 @@ +# Generated from lib/prism/parse_result/errors.rb with RBS::Inline + +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: ParseResult + + # Initialize a new set of errors from the given parse result. + # -- + # : (ParseResult parse_result) -> void + def initialize: (ParseResult parse_result) -> void + + # Formats the errors in a human-readable way and return them as a string. + # -- + # : () -> String + def format: () -> String + end + end +end diff --git a/sig/generated/prism/parse_result/newlines.rbs b/sig/generated/prism/parse_result/newlines.rbs new file mode 100644 index 0000000000..45c04783d0 --- /dev/null +++ b/sig/generated/prism/parse_result/newlines.rbs @@ -0,0 +1,129 @@ +# Generated from lib/prism/parse_result/newlines.rb with RBS::Inline + +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 + @lines: Array[bool] + + # Create a new Newlines visitor with the given newline offsets. + # -- + # : (Integer lines) -> void + def initialize: (Integer lines) -> void + + # Permit block nodes to mark newlines within themselves. + # -- + # : (BlockNode node) -> void + def visit_block_node: (BlockNode node) -> void + + # Permit lambda nodes to mark newlines within themselves. + # -- + # : (LambdaNode node) -> void + def visit_lambda_node: (LambdaNode node) -> void + + # Mark if nodes as newlines. + # -- + # : (IfNode node) -> void + def visit_if_node: (IfNode node) -> void + + # Mark unless nodes as newlines. + # -- + # : (UnlessNode node) -> void + def visit_unless_node: (UnlessNode node) -> void + + # Permit statements lists to mark newlines within themselves. + # -- + # : (StatementsNode node) -> void + def visit_statements_node: (StatementsNode node) -> void + end + end + + class Node + @newline_flag: bool + + # : () -> bool + def newline_flag?: () -> bool + + # : (Array[bool] lines) -> void + def newline_flag!: (Array[bool] lines) -> void + end + + class BeginNode < Node + # : (Array[bool] lines) -> void + def newline_flag!: (Array[bool] lines) -> void + end + + class ParenthesesNode < Node + # : (Array[bool] lines) -> void + def newline_flag!: (Array[bool] lines) -> void + end + + class IfNode < Node + # : (Array[bool] lines) -> void + def newline_flag!: (Array[bool] lines) -> void + end + + class UnlessNode < Node + # : (Array[bool] lines) -> void + def newline_flag!: (Array[bool] lines) -> void + end + + class UntilNode < Node + # : (Array[bool] lines) -> void + def newline_flag!: (Array[bool] lines) -> void + end + + class WhileNode < Node + # : (Array[bool] lines) -> void + def newline_flag!: (Array[bool] lines) -> void + end + + class RescueModifierNode < Node + # : (Array[bool] lines) -> void + def newline_flag!: (Array[bool] lines) -> void + end + + class InterpolatedMatchLastLineNode < Node + # : (Array[bool] lines) -> void + def newline_flag!: (Array[bool] lines) -> void + end + + class InterpolatedRegularExpressionNode < Node + # : (Array[bool] lines) -> void + def newline_flag!: (Array[bool] lines) -> void + end + + class InterpolatedStringNode < Node + # : (Array[bool] lines) -> void + def newline_flag!: (Array[bool] lines) -> void + end + + class InterpolatedSymbolNode < Node + # : (Array[bool] lines) -> void + def newline_flag!: (Array[bool] lines) -> void + end + + class InterpolatedXStringNode < Node + # : (Array[bool] lines) -> void + def newline_flag!: (Array[bool] lines) -> void + end +end diff --git a/sig/generated/prism/pattern.rbs b/sig/generated/prism/pattern.rbs new file mode 100644 index 0000000000..7ea2808da4 --- /dev/null +++ b/sig/generated/prism/pattern.rbs @@ -0,0 +1,156 @@ +# Generated from lib/prism/pattern.rb with RBS::Inline + +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. + # -- + # : (String repr) -> void + def initialize: (String repr) -> void + end + + # The query that this pattern was initialized with. + attr_reader query: String + + @compiled: Proc? + + # Create a new pattern with the given query. The query should be a string + # containing a Ruby pattern matching expression. + # -- + # : (String query) -> void + def initialize: (String query) -> void + + # Compile the query into a callable object that can be used to match against + # nodes. + # -- + # : () -> Proc + def compile: () -> Proc + + # 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. + # -- + # : (node root) -> Enumerator[node, void] + # : (node root) { (node) -> void } -> void + def scan: (node root) -> Enumerator[node, void] + | (node root) { (node) -> void } -> void + + private + + # Shortcut for combining two procs into one that returns true if both return + # true. + # -- + # : (Proc left, Proc right) -> Proc + def combine_and: (Proc left, Proc right) -> Proc + + # Shortcut for combining two procs into one that returns true if either + # returns true. + # -- + # : (Proc left, Proc right) -> Proc + def combine_or: (Proc left, Proc right) -> Proc + + # Raise an error because the given node is not supported. Note purposefully + # not typing this method since it is a no return method that Steep does not + # understand. + # -- + # : (node node) -> bot + def compile_error: (node node) -> bot + + # in [foo, bar, baz] + # -- + # : (ArrayPatternNode node) -> Proc + def compile_array_pattern_node: (ArrayPatternNode node) -> Proc + + # in foo | bar + # -- + # : (AlternationPatternNode node) -> Proc + def compile_alternation_pattern_node: (AlternationPatternNode node) -> Proc + + # in Prism::ConstantReadNode + # -- + # : (ConstantPathNode node) -> Proc + def compile_constant_path_node: (ConstantPathNode node) -> Proc + + # in ConstantReadNode + # in String + # -- + # : (ConstantReadNode node) -> Proc + def compile_constant_read_node: (ConstantReadNode node) -> Proc + + # Compile a name associated with a constant. + # -- + # : ((ConstantPathNode | ConstantReadNode) node, Symbol name) -> Proc + def compile_constant_name: (ConstantPathNode | ConstantReadNode node, Symbol name) -> Proc + + # in InstanceVariableReadNode[name: Symbol] + # in { name: Symbol } + # -- + # : (HashPatternNode node) -> Proc + def compile_hash_pattern_node: (HashPatternNode node) -> Proc + + # in nil + # -- + # : (NilNode node) -> Proc + def compile_nil_node: (NilNode node) -> Proc + + # in /foo/ + # -- + # : (RegularExpressionNode node) -> Proc + def compile_regular_expression_node: (RegularExpressionNode node) -> Proc + + # in "" + # in "foo" + # -- + # : (StringNode node) -> Proc + def compile_string_node: (StringNode node) -> Proc + + # in :+ + # in :foo + # -- + # : (SymbolNode node) -> Proc + def compile_symbol_node: (SymbolNode node) -> Proc + + # Compile any kind of node. Dispatch out to the individual compilation + # methods based on the type of node. + # -- + # : (node node) -> Proc + def compile_node: (node node) -> Proc + end +end diff --git a/sig/generated/prism/reflection.rbs b/sig/generated/prism/reflection.rbs new file mode 100644 index 0000000000..8237da3a2e --- /dev/null +++ b/sig/generated/prism/reflection.rbs @@ -0,0 +1,101 @@ +# Generated from lib/prism/reflection.rb with RBS::Inline + +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: Symbol + + # Initializes the field with the given name. + # -- + # : (Symbol name) -> void + def initialize: (Symbol name) -> void + 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: Array[Symbol] + + # Initializes the flags field with the given name and flags. + # -- + # : (Symbol name, Array[Symbol] flags) -> void + def initialize: (Symbol name, Array[Symbol] flags) -> void + end + + # Returns the fields for the given node. + # -- + # : (singleton(Node) node) -> Array[Field] + def self.fields_for: (singleton(Node) node) -> Array[Field] + end +end diff --git a/sig/generated/prism/relocation.rbs b/sig/generated/prism/relocation.rbs new file mode 100644 index 0000000000..35238609ca --- /dev/null +++ b/sig/generated/prism/relocation.rbs @@ -0,0 +1,502 @@ +# Generated from lib/prism/relocation.rb with RBS::Inline + +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 + type entry_value = untyped + + type entry_values = Hash[Symbol, entry_value] + + 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 + + # 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 + + @values: Hash[Symbol, untyped]? + + @repository: Repository? + + # Initialize a new entry with the given repository. + # -- + # : (Repository repository) -> void + def initialize: (Repository repository) -> void + + # Fetch the filepath of the value. + # -- + # : () -> String + def filepath: () -> String + + # Fetch the start line of the value. + # -- + # : () -> Integer + def start_line: () -> Integer + + # Fetch the end line of the value. + # -- + # : () -> Integer + def end_line: () -> Integer + + # Fetch the start byte offset of the value. + # -- + # : () -> Integer + def start_offset: () -> Integer + + # Fetch the end byte offset of the value. + # -- + # : () -> Integer + def end_offset: () -> Integer + + # Fetch the start character offset of the value. + # -- + # : () -> Integer + def start_character_offset: () -> Integer + + # Fetch the end character offset of the value. + # -- + # : () -> Integer + def end_character_offset: () -> Integer + + # Fetch the start code units offset of the value, for the encoding that + # was configured on the repository. + # -- + # : () -> Integer + def start_code_units_offset: () -> Integer + + # Fetch the end code units offset of the value, for the encoding that was + # configured on the repository. + # -- + # : () -> Integer + def end_code_units_offset: () -> Integer + + # Fetch the start byte column of the value. + # -- + # : () -> Integer + def start_column: () -> Integer + + # Fetch the end byte column of the value. + # -- + # : () -> Integer + def end_column: () -> Integer + + # Fetch the start character column of the value. + # -- + # : () -> Integer + def start_character_column: () -> Integer + + # Fetch the end character column of the value. + # -- + # : () -> Integer + def end_character_column: () -> Integer + + # Fetch the start code units column of the value, for the encoding that + # was configured on the repository. + # -- + # : () -> Integer + def start_code_units_column: () -> Integer + + # Fetch the end code units column of the value, for the encoding that was + # configured on the repository. + # -- + # : () -> Integer + def end_code_units_column: () -> Integer + + # Fetch the leading comments of the value. + # -- + # : () -> Array[CommentsField::Comment] + def leading_comments: () -> Array[CommentsField::Comment] + + # Fetch the trailing comments of the value. + # -- + # : () -> Array[CommentsField::Comment] + def trailing_comments: () -> Array[CommentsField::Comment] + + # Fetch the leading and trailing comments of the value. + # -- + # : () -> Array[CommentsField::Comment] + def comments: () -> Array[CommentsField::Comment] + + # 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. + # -- + # : (entry_values values) -> void + def reify!: (entry_values values) -> void + + private + + # Fetch a value from the entry, raising an error if it is missing. + # -- + # : (Symbol name) -> entry_value + def fetch_value: (Symbol name) -> entry_value + + # Return the values from the repository, reifying them if necessary. + # -- + # : () -> entry_values + def values: () -> entry_values + end + + # Represents the source of a repository that will be reparsed. + class Source + # The value that will need to be reparsed. + attr_reader value: untyped + + # Initialize the source with the given value. + # -- + # : (untyped value) -> void + def initialize: (untyped value) -> void + + # Reparse the value and return the parse result. + # -- + # : () -> ParseResult + def result: () -> ParseResult + + # Create a code units cache for the given encoding. + # -- + # : (Encoding encoding) -> _CodeUnitsCache + def code_units_cache: (Encoding encoding) -> _CodeUnitsCache + end + + # A source that is represented by a file path. + class SourceFilepath < Source + # Reparse the file and return the parse result. + # -- + # : () -> ParseResult + def result: () -> ParseResult + end + + # A source that is represented by a string. + class SourceString < Source + # Reparse the string and return the parse result. + # -- + # : () -> ParseResult + def result: () -> ParseResult + end + + # A field that represents the file path. + class FilepathField + # The file path that this field represents. + attr_reader value: String + + # Initialize a new field with the given file path. + # -- + # : (String value) -> void + def initialize: (String value) -> void + + # Fetch the file path. + # -- + # : (_Value _value) -> entry_values + def fields: (_Value _value) -> entry_values + end + + # A field representing the start and end lines. + class LinesField + # Fetches the start and end line of a value. + # -- + # : (_Value value) -> entry_values + def fields: (_Value value) -> entry_values + end + + # A field representing the start and end byte offsets. + class OffsetsField + # Fetches the start and end byte offset of a value. + # -- + # : (_Value value) -> entry_values + def fields: (_Value value) -> entry_values + end + + # A field representing the start and end character offsets. + class CharacterOffsetsField + # Fetches the start and end character offset of a value. + # -- + # : (_Value value) -> entry_values + def fields: (_Value value) -> entry_values + 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: Repository + + # The associated encoding for the code units. + attr_reader encoding: Encoding + + @cache: _CodeUnitsCache? + + # Initialize a new field with the associated repository and encoding. + # -- + # : (Repository repository, Encoding encoding) -> void + def initialize: (Repository repository, Encoding encoding) -> void + + # Fetches the start and end code units offset of a value for a particular + # encoding. + # -- + # : (_Value value) -> entry_values + def fields: (_Value value) -> entry_values + + private + + # Lazily create a code units cache for the associated encoding. + # -- + # : () -> _CodeUnitsCache + def cache: () -> _CodeUnitsCache + end + + # A field representing the start and end byte columns. + class ColumnsField + # Fetches the start and end byte column of a value. + # -- + # : (_Value value) -> entry_values + def fields: (_Value value) -> entry_values + end + + # A field representing the start and end character columns. + class CharacterColumnsField + # Fetches the start and end character column of a value. + # -- + # : (_Value value) -> entry_values + def fields: (_Value value) -> entry_values + 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: Repository + + # The associated encoding for the code units. + attr_reader encoding: Encoding + + @cache: _CodeUnitsCache? + + # Initialize a new field with the associated repository and encoding. + # -- + # : (Repository repository, Encoding encoding) -> void + def initialize: (Repository repository, Encoding encoding) -> void + + # Fetches the start and end code units column of a value for a particular + # encoding. + # -- + # : (_Value value) -> entry_values + def fields: (_Value value) -> entry_values + + private + + # Lazily create a code units cache for the associated encoding. + # -- + # : () -> _CodeUnitsCache + def cache: () -> _CodeUnitsCache + 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: String + + # Initialize a new comment with the given slice. + # + # : (String slice) -> void + def initialize: (String slice) -> void + end + + private + + # Create comment objects from the given values. + # -- + # : (entry_value values) -> Array[Comment] + def comments: (entry_value values) -> Array[Comment] + end + + # A field representing the leading comments. + class LeadingCommentsField < CommentsField + # Fetches the leading comments of a value. + # -- + # : (_Value value) -> entry_values + def fields: (_Value value) -> entry_values + end + + # A field representing the trailing comments. + class TrailingCommentsField < CommentsField + # Fetches the trailing comments of a value. + # -- + # : (_Value value) -> entry_values + def fields: (_Value value) -> entry_values + 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: Source + + # The fields that have been configured on this repository. + attr_reader fields: Hash[Symbol, _Field] + + # The entries that have been saved on this repository. + attr_reader entries: Hash[Integer, Hash[Symbol, Entry]] + + # Initialize a new repository with the given source. + # -- + # : (Source source) -> void + def initialize: (Source source) -> void + + # Create a code units cache for the given encoding from the source. + # -- + # : (Encoding encoding) -> _CodeUnitsCache + def code_units_cache: (Encoding encoding) -> _CodeUnitsCache + + # Configure the filepath field for this repository and return self. + # -- + # : () -> self + def filepath: () -> self + + # Configure the lines field for this repository and return self. + # -- + # : () -> self + def lines: () -> self + + # Configure the offsets field for this repository and return self. + # -- + # : () -> self + def offsets: () -> self + + # Configure the character offsets field for this repository and return + # self. + # -- + # : () -> self + def character_offsets: () -> self + + # Configure the code unit offsets field for this repository for a specific + # encoding and return self. + # -- + # : (Encoding encoding) -> self + def code_unit_offsets: (Encoding encoding) -> self + + # Configure the columns field for this repository and return self. + # -- + # : () -> self + def columns: () -> self + + # Configure the character columns field for this repository and return + # self. + # -- + # : () -> self + def character_columns: () -> self + + # Configure the code unit columns field for this repository for a specific + # encoding and return self. + # -- + # : (Encoding encoding) -> self + def code_unit_columns: (Encoding encoding) -> self + + # Configure the leading comments field for this repository and return + # self. + # -- + # : () -> self + def leading_comments: () -> self + + # Configure the trailing comments field for this repository and return + # self. + # -- + # : () -> self + def trailing_comments: () -> self + + # Configure both the leading and trailing comment fields for this + # repository and return self. + # -- + # : () -> self + def comments: () -> self + + # 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. + # -- + # : (Integer node_id, Symbol field_name) -> Entry + def enter: (Integer node_id, Symbol field_name) -> Entry + + # 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. + # -- + # : () -> void + def reify!: () -> void + + private + + # Append the given field to the repository and return the repository so + # that these calls can be chained. + # -- + # : (Symbol name, _Field) -> self + def field: (Symbol name, _Field) -> self + end + + # Create a new repository for the given filepath. + # -- + # : (String value) -> Repository + def self.filepath: (String value) -> Repository + + # Create a new repository for the given string. + # -- + # : (String value) -> Repository + def self.string: (String value) -> Repository + end +end diff --git a/sig/generated/prism/serialize.rbs b/sig/generated/prism/serialize.rbs new file mode 100644 index 0000000000..a83dae70d8 --- /dev/null +++ b/sig/generated/prism/serialize.rbs @@ -0,0 +1,184 @@ +# Generated from lib/prism/serialize.rb with RBS::Inline + +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: ::Integer + + # The minor version of prism that we are expecting to find in the serialized + # strings. + MINOR_VERSION: ::Integer + + # The patch version of prism that we are expecting to find in the serialized + # strings. + PATCH_VERSION: ::Integer + + # 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. + # -- + # : (String input, String serialized, bool freeze) -> ParseResult + def self.load_parse: (String input, String serialized, bool freeze) -> ParseResult + + # 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. + # -- + # : (String input, String serialized, bool freeze) -> LexResult + def self.load_lex: (String input, String serialized, bool freeze) -> LexResult + + # 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. + # -- + # : (String input, String serialized, bool freeze) -> Array[Comment] + def self.load_parse_comments: (String input, String serialized, bool freeze) -> Array[Comment] + + # 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. + # -- + # : (String input, String serialized, bool freeze) -> ParseLexResult + def self.load_parse_lex: (String input, String serialized, bool freeze) -> ParseLexResult + + class ConstantPool + # :nodoc: + attr_reader size: Integer + + @serialized: String + + @base: Integer + + @pool: Array[Symbol?] + + # : (String serialized, Integer base, Integer size) -> void + def initialize: (String serialized, Integer base, Integer size) -> void + + # : (Integer index, Encoding encoding) -> Symbol + def get: (Integer index, Encoding encoding) -> Symbol + end + + FastStringIO: untyped + + class Loader + # :nodoc: + attr_reader input: String + + attr_reader io: StringIO + + attr_reader source: Source + + # : (Source source, String serialized) -> void + def initialize: (Source source, String serialized) -> void + + # : () -> bool + def eof?: () -> bool + + # : (ConstantPool constant_pool) -> void + def load_constant_pool: (ConstantPool constant_pool) -> void + + # : () -> void + def load_header: () -> void + + # : () -> Encoding + def load_encoding: () -> Encoding + + # : (bool freeze) -> Array[Integer] + def load_line_offsets: (bool freeze) -> Array[Integer] + + # : (bool freeze) -> Array[Comment] + def load_comments: (bool freeze) -> Array[Comment] + + # : (bool freeze) -> Array[MagicComment] + def load_magic_comments: (bool freeze) -> Array[MagicComment] + + DIAGNOSTIC_TYPES: Array[Symbol] + + # : () -> Symbol + def load_error_level: () -> Symbol + + # : (Encoding encoding, bool freeze) -> Array[ParseError] + def load_errors: (Encoding encoding, bool freeze) -> Array[ParseError] + + # : () -> Symbol + def load_warning_level: () -> Symbol + + # : (Encoding encoding, bool freeze) -> Array[ParseWarning] + def load_warnings: (Encoding encoding, bool freeze) -> Array[ParseWarning] + + # : () -> Array[[Token, Integer]] + def load_tokens: () -> Array[[ Token, Integer ]] + + # variable-length integer using https://en.wikipedia.org/wiki/LEB128 + # This is also what protobuf uses: https://protobuf.dev/programming-guides/encoding/#varints + # -- + # : () -> Integer + def load_varuint: () -> Integer + + # : () -> Integer + def load_varsint: () -> Integer + + # : () -> Integer + def load_integer: () -> Integer + + # : () -> Float + def load_double: () -> Float + + # : () -> bool + def load_bool: () -> bool + + # : () -> Integer + def load_uint32: () -> Integer + + # : (ConstantPool constant_pool, Encoding encoding, bool freeze) -> node? + def load_optional_node: (ConstantPool constant_pool, Encoding encoding, bool freeze) -> node? + + # : (Encoding encoding) -> String + def load_string: (Encoding encoding) -> String + + # : (bool freeze) -> Location + def load_location_object: (bool freeze) -> Location + + # Load a location object from the serialized data. Note that we are lying + # about the signature a bit here, because we sometimes load it as a packed + # integer instead of an object. + # -- + # : (bool freeze) -> Location + def load_location: (bool freeze) -> Location + + # Load an optional location object from the serialized data if it is + # present. Note that we are lying about the signature a bit here, because + # we sometimes load it as a packed integer instead of an object. + # -- + # : (bool freeze) -> Location? + def load_optional_location: (bool freeze) -> Location? + + # : (bool freeze) -> Location? + def load_optional_location_object: (bool freeze) -> Location? + + # : (ConstantPool constant_pool, Encoding encoding) -> Symbol + def load_constant: (ConstantPool constant_pool, Encoding encoding) -> Symbol + + # : (ConstantPool constant_pool, Encoding encoding) -> Symbol? + def load_optional_constant: (ConstantPool constant_pool, Encoding encoding) -> Symbol? + + # : (ConstantPool constant_pool, Encoding encoding, bool freeze) -> node + def load_node: (ConstantPool constant_pool, Encoding encoding, bool freeze) -> node + + @load_node_lambdas: Array[Proc] + + def define_load_node_lambdas: () -> void + end + + # The token types that can be indexed by their enum values. + TOKEN_TYPES: Array[Symbol?] + end +end diff --git a/sig/generated/prism/string_query.rbs b/sig/generated/prism/string_query.rbs new file mode 100644 index 0000000000..de2183a731 --- /dev/null +++ b/sig/generated/prism/string_query.rbs @@ -0,0 +1,36 @@ +# Generated from lib/prism/string_query.rb with RBS::Inline + +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 + def self.local?: (String string) -> bool + + def self.constant?: (String string) -> bool + + def self.method_name?: (String string) -> bool + + # The string that this query is wrapping. + attr_reader string: String + + # Initialize a new query with the given string. + # -- + # : (String string) -> void + def initialize: (String string) -> void + + # Whether or not this string is a valid local variable name. + # -- + # : () -> bool + def local?: () -> bool + + # Whether or not this string is a valid constant name. + # -- + # : () -> bool + def constant?: () -> bool + + # Whether or not this string is a valid method name. + # -- + # : () -> bool + def method_name?: () -> bool + end +end diff --git a/sig/generated/prism/translation.rbs b/sig/generated/prism/translation.rbs new file mode 100644 index 0000000000..09971a2971 --- /dev/null +++ b/sig/generated/prism/translation.rbs @@ -0,0 +1,8 @@ +# Generated from lib/prism/translation.rb with RBS::Inline + +module Prism + # This module is responsible for converting the prism syntax tree into other + # syntax trees. + module Translation + end +end diff --git a/sig/generated/prism/visitor.rbs b/sig/generated/prism/visitor.rbs new file mode 100644 index 0000000000..ec65adbb29 --- /dev/null +++ b/sig/generated/prism/visitor.rbs @@ -0,0 +1,1111 @@ +# Generated from lib/prism/visitor.rb with RBS::Inline + +module Prism + 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_block_parameter_node: (NoBlockParameterNode) -> 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 + + # 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. + # -- + # : (node? node) -> void + def visit: (node? node) -> void + + # Visits each node in `nodes` by calling `accept` on each one. + # -- + # : (Array[node?] nodes) -> void + def visit_all: (Array[node?] nodes) -> void + + # Visits the child nodes of `node` by calling `accept` on each one. + # -- + # : (node node) -> void + def visit_child_nodes: (node node) -> void + 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 + # -- + # : (AliasGlobalVariableNode node) -> void + def visit_alias_global_variable_node: (AliasGlobalVariableNode node) -> void + + # Visit a AliasMethodNode node + # -- + # : (AliasMethodNode node) -> void + def visit_alias_method_node: (AliasMethodNode node) -> void + + # Visit a AlternationPatternNode node + # -- + # : (AlternationPatternNode node) -> void + def visit_alternation_pattern_node: (AlternationPatternNode node) -> void + + # Visit a AndNode node + # -- + # : (AndNode node) -> void + def visit_and_node: (AndNode node) -> void + + # Visit a ArgumentsNode node + # -- + # : (ArgumentsNode node) -> void + def visit_arguments_node: (ArgumentsNode node) -> void + + # Visit a ArrayNode node + # -- + # : (ArrayNode node) -> void + def visit_array_node: (ArrayNode node) -> void + + # Visit a ArrayPatternNode node + # -- + # : (ArrayPatternNode node) -> void + def visit_array_pattern_node: (ArrayPatternNode node) -> void + + # Visit a AssocNode node + # -- + # : (AssocNode node) -> void + def visit_assoc_node: (AssocNode node) -> void + + # Visit a AssocSplatNode node + # -- + # : (AssocSplatNode node) -> void + def visit_assoc_splat_node: (AssocSplatNode node) -> void + + # Visit a BackReferenceReadNode node + # -- + # : (BackReferenceReadNode node) -> void + def visit_back_reference_read_node: (BackReferenceReadNode node) -> void + + # Visit a BeginNode node + # -- + # : (BeginNode node) -> void + def visit_begin_node: (BeginNode node) -> void + + # Visit a BlockArgumentNode node + # -- + # : (BlockArgumentNode node) -> void + def visit_block_argument_node: (BlockArgumentNode node) -> void + + # Visit a BlockLocalVariableNode node + # -- + # : (BlockLocalVariableNode node) -> void + def visit_block_local_variable_node: (BlockLocalVariableNode node) -> void + + # Visit a BlockNode node + # -- + # : (BlockNode node) -> void + def visit_block_node: (BlockNode node) -> void + + # Visit a BlockParameterNode node + # -- + # : (BlockParameterNode node) -> void + def visit_block_parameter_node: (BlockParameterNode node) -> void + + # Visit a BlockParametersNode node + # -- + # : (BlockParametersNode node) -> void + def visit_block_parameters_node: (BlockParametersNode node) -> void + + # Visit a BreakNode node + # -- + # : (BreakNode node) -> void + def visit_break_node: (BreakNode node) -> void + + # Visit a CallAndWriteNode node + # -- + # : (CallAndWriteNode node) -> void + def visit_call_and_write_node: (CallAndWriteNode node) -> void + + # Visit a CallNode node + # -- + # : (CallNode node) -> void + def visit_call_node: (CallNode node) -> void + + # Visit a CallOperatorWriteNode node + # -- + # : (CallOperatorWriteNode node) -> void + def visit_call_operator_write_node: (CallOperatorWriteNode node) -> void + + # Visit a CallOrWriteNode node + # -- + # : (CallOrWriteNode node) -> void + def visit_call_or_write_node: (CallOrWriteNode node) -> void + + # Visit a CallTargetNode node + # -- + # : (CallTargetNode node) -> void + def visit_call_target_node: (CallTargetNode node) -> void + + # Visit a CapturePatternNode node + # -- + # : (CapturePatternNode node) -> void + def visit_capture_pattern_node: (CapturePatternNode node) -> void + + # Visit a CaseMatchNode node + # -- + # : (CaseMatchNode node) -> void + def visit_case_match_node: (CaseMatchNode node) -> void + + # Visit a CaseNode node + # -- + # : (CaseNode node) -> void + def visit_case_node: (CaseNode node) -> void + + # Visit a ClassNode node + # -- + # : (ClassNode node) -> void + def visit_class_node: (ClassNode node) -> void + + # Visit a ClassVariableAndWriteNode node + # -- + # : (ClassVariableAndWriteNode node) -> void + def visit_class_variable_and_write_node: (ClassVariableAndWriteNode node) -> void + + # Visit a ClassVariableOperatorWriteNode node + # -- + # : (ClassVariableOperatorWriteNode node) -> void + def visit_class_variable_operator_write_node: (ClassVariableOperatorWriteNode node) -> void + + # Visit a ClassVariableOrWriteNode node + # -- + # : (ClassVariableOrWriteNode node) -> void + def visit_class_variable_or_write_node: (ClassVariableOrWriteNode node) -> void + + # Visit a ClassVariableReadNode node + # -- + # : (ClassVariableReadNode node) -> void + def visit_class_variable_read_node: (ClassVariableReadNode node) -> void + + # Visit a ClassVariableTargetNode node + # -- + # : (ClassVariableTargetNode node) -> void + def visit_class_variable_target_node: (ClassVariableTargetNode node) -> void + + # Visit a ClassVariableWriteNode node + # -- + # : (ClassVariableWriteNode node) -> void + def visit_class_variable_write_node: (ClassVariableWriteNode node) -> void + + # Visit a ConstantAndWriteNode node + # -- + # : (ConstantAndWriteNode node) -> void + def visit_constant_and_write_node: (ConstantAndWriteNode node) -> void + + # Visit a ConstantOperatorWriteNode node + # -- + # : (ConstantOperatorWriteNode node) -> void + def visit_constant_operator_write_node: (ConstantOperatorWriteNode node) -> void + + # Visit a ConstantOrWriteNode node + # -- + # : (ConstantOrWriteNode node) -> void + def visit_constant_or_write_node: (ConstantOrWriteNode node) -> void + + # Visit a ConstantPathAndWriteNode node + # -- + # : (ConstantPathAndWriteNode node) -> void + def visit_constant_path_and_write_node: (ConstantPathAndWriteNode node) -> void + + # Visit a ConstantPathNode node + # -- + # : (ConstantPathNode node) -> void + def visit_constant_path_node: (ConstantPathNode node) -> void + + # Visit a ConstantPathOperatorWriteNode node + # -- + # : (ConstantPathOperatorWriteNode node) -> void + def visit_constant_path_operator_write_node: (ConstantPathOperatorWriteNode node) -> void + + # Visit a ConstantPathOrWriteNode node + # -- + # : (ConstantPathOrWriteNode node) -> void + def visit_constant_path_or_write_node: (ConstantPathOrWriteNode node) -> void + + # Visit a ConstantPathTargetNode node + # -- + # : (ConstantPathTargetNode node) -> void + def visit_constant_path_target_node: (ConstantPathTargetNode node) -> void + + # Visit a ConstantPathWriteNode node + # -- + # : (ConstantPathWriteNode node) -> void + def visit_constant_path_write_node: (ConstantPathWriteNode node) -> void + + # Visit a ConstantReadNode node + # -- + # : (ConstantReadNode node) -> void + def visit_constant_read_node: (ConstantReadNode node) -> void + + # Visit a ConstantTargetNode node + # -- + # : (ConstantTargetNode node) -> void + def visit_constant_target_node: (ConstantTargetNode node) -> void + + # Visit a ConstantWriteNode node + # -- + # : (ConstantWriteNode node) -> void + def visit_constant_write_node: (ConstantWriteNode node) -> void + + # Visit a DefNode node + # -- + # : (DefNode node) -> void + def visit_def_node: (DefNode node) -> void + + # Visit a DefinedNode node + # -- + # : (DefinedNode node) -> void + def visit_defined_node: (DefinedNode node) -> void + + # Visit a ElseNode node + # -- + # : (ElseNode node) -> void + def visit_else_node: (ElseNode node) -> void + + # Visit a EmbeddedStatementsNode node + # -- + # : (EmbeddedStatementsNode node) -> void + def visit_embedded_statements_node: (EmbeddedStatementsNode node) -> void + + # Visit a EmbeddedVariableNode node + # -- + # : (EmbeddedVariableNode node) -> void + def visit_embedded_variable_node: (EmbeddedVariableNode node) -> void + + # Visit a EnsureNode node + # -- + # : (EnsureNode node) -> void + def visit_ensure_node: (EnsureNode node) -> void + + # Visit a FalseNode node + # -- + # : (FalseNode node) -> void + def visit_false_node: (FalseNode node) -> void + + # Visit a FindPatternNode node + # -- + # : (FindPatternNode node) -> void + def visit_find_pattern_node: (FindPatternNode node) -> void + + # Visit a FlipFlopNode node + # -- + # : (FlipFlopNode node) -> void + def visit_flip_flop_node: (FlipFlopNode node) -> void + + # Visit a FloatNode node + # -- + # : (FloatNode node) -> void + def visit_float_node: (FloatNode node) -> void + + # Visit a ForNode node + # -- + # : (ForNode node) -> void + def visit_for_node: (ForNode node) -> void + + # Visit a ForwardingArgumentsNode node + # -- + # : (ForwardingArgumentsNode node) -> void + def visit_forwarding_arguments_node: (ForwardingArgumentsNode node) -> void + + # Visit a ForwardingParameterNode node + # -- + # : (ForwardingParameterNode node) -> void + def visit_forwarding_parameter_node: (ForwardingParameterNode node) -> void + + # Visit a ForwardingSuperNode node + # -- + # : (ForwardingSuperNode node) -> void + def visit_forwarding_super_node: (ForwardingSuperNode node) -> void + + # Visit a GlobalVariableAndWriteNode node + # -- + # : (GlobalVariableAndWriteNode node) -> void + def visit_global_variable_and_write_node: (GlobalVariableAndWriteNode node) -> void + + # Visit a GlobalVariableOperatorWriteNode node + # -- + # : (GlobalVariableOperatorWriteNode node) -> void + def visit_global_variable_operator_write_node: (GlobalVariableOperatorWriteNode node) -> void + + # Visit a GlobalVariableOrWriteNode node + # -- + # : (GlobalVariableOrWriteNode node) -> void + def visit_global_variable_or_write_node: (GlobalVariableOrWriteNode node) -> void + + # Visit a GlobalVariableReadNode node + # -- + # : (GlobalVariableReadNode node) -> void + def visit_global_variable_read_node: (GlobalVariableReadNode node) -> void + + # Visit a GlobalVariableTargetNode node + # -- + # : (GlobalVariableTargetNode node) -> void + def visit_global_variable_target_node: (GlobalVariableTargetNode node) -> void + + # Visit a GlobalVariableWriteNode node + # -- + # : (GlobalVariableWriteNode node) -> void + def visit_global_variable_write_node: (GlobalVariableWriteNode node) -> void + + # Visit a HashNode node + # -- + # : (HashNode node) -> void + def visit_hash_node: (HashNode node) -> void + + # Visit a HashPatternNode node + # -- + # : (HashPatternNode node) -> void + def visit_hash_pattern_node: (HashPatternNode node) -> void + + # Visit a IfNode node + # -- + # : (IfNode node) -> void + def visit_if_node: (IfNode node) -> void + + # Visit a ImaginaryNode node + # -- + # : (ImaginaryNode node) -> void + def visit_imaginary_node: (ImaginaryNode node) -> void + + # Visit a ImplicitNode node + # -- + # : (ImplicitNode node) -> void + def visit_implicit_node: (ImplicitNode node) -> void + + # Visit a ImplicitRestNode node + # -- + # : (ImplicitRestNode node) -> void + def visit_implicit_rest_node: (ImplicitRestNode node) -> void + + # Visit a InNode node + # -- + # : (InNode node) -> void + def visit_in_node: (InNode node) -> void + + # Visit a IndexAndWriteNode node + # -- + # : (IndexAndWriteNode node) -> void + def visit_index_and_write_node: (IndexAndWriteNode node) -> void + + # Visit a IndexOperatorWriteNode node + # -- + # : (IndexOperatorWriteNode node) -> void + def visit_index_operator_write_node: (IndexOperatorWriteNode node) -> void + + # Visit a IndexOrWriteNode node + # -- + # : (IndexOrWriteNode node) -> void + def visit_index_or_write_node: (IndexOrWriteNode node) -> void + + # Visit a IndexTargetNode node + # -- + # : (IndexTargetNode node) -> void + def visit_index_target_node: (IndexTargetNode node) -> void + + # Visit a InstanceVariableAndWriteNode node + # -- + # : (InstanceVariableAndWriteNode node) -> void + def visit_instance_variable_and_write_node: (InstanceVariableAndWriteNode node) -> void + + # Visit a InstanceVariableOperatorWriteNode node + # -- + # : (InstanceVariableOperatorWriteNode node) -> void + def visit_instance_variable_operator_write_node: (InstanceVariableOperatorWriteNode node) -> void + + # Visit a InstanceVariableOrWriteNode node + # -- + # : (InstanceVariableOrWriteNode node) -> void + def visit_instance_variable_or_write_node: (InstanceVariableOrWriteNode node) -> void + + # Visit a InstanceVariableReadNode node + # -- + # : (InstanceVariableReadNode node) -> void + def visit_instance_variable_read_node: (InstanceVariableReadNode node) -> void + + # Visit a InstanceVariableTargetNode node + # -- + # : (InstanceVariableTargetNode node) -> void + def visit_instance_variable_target_node: (InstanceVariableTargetNode node) -> void + + # Visit a InstanceVariableWriteNode node + # -- + # : (InstanceVariableWriteNode node) -> void + def visit_instance_variable_write_node: (InstanceVariableWriteNode node) -> void + + # Visit a IntegerNode node + # -- + # : (IntegerNode node) -> void + def visit_integer_node: (IntegerNode node) -> void + + # Visit a InterpolatedMatchLastLineNode node + # -- + # : (InterpolatedMatchLastLineNode node) -> void + def visit_interpolated_match_last_line_node: (InterpolatedMatchLastLineNode node) -> void + + # Visit a InterpolatedRegularExpressionNode node + # -- + # : (InterpolatedRegularExpressionNode node) -> void + def visit_interpolated_regular_expression_node: (InterpolatedRegularExpressionNode node) -> void + + # Visit a InterpolatedStringNode node + # -- + # : (InterpolatedStringNode node) -> void + def visit_interpolated_string_node: (InterpolatedStringNode node) -> void + + # Visit a InterpolatedSymbolNode node + # -- + # : (InterpolatedSymbolNode node) -> void + def visit_interpolated_symbol_node: (InterpolatedSymbolNode node) -> void + + # Visit a InterpolatedXStringNode node + # -- + # : (InterpolatedXStringNode node) -> void + def visit_interpolated_x_string_node: (InterpolatedXStringNode node) -> void + + # Visit a ItLocalVariableReadNode node + # -- + # : (ItLocalVariableReadNode node) -> void + def visit_it_local_variable_read_node: (ItLocalVariableReadNode node) -> void + + # Visit a ItParametersNode node + # -- + # : (ItParametersNode node) -> void + def visit_it_parameters_node: (ItParametersNode node) -> void + + # Visit a KeywordHashNode node + # -- + # : (KeywordHashNode node) -> void + def visit_keyword_hash_node: (KeywordHashNode node) -> void + + # Visit a KeywordRestParameterNode node + # -- + # : (KeywordRestParameterNode node) -> void + def visit_keyword_rest_parameter_node: (KeywordRestParameterNode node) -> void + + # Visit a LambdaNode node + # -- + # : (LambdaNode node) -> void + def visit_lambda_node: (LambdaNode node) -> void + + # Visit a LocalVariableAndWriteNode node + # -- + # : (LocalVariableAndWriteNode node) -> void + def visit_local_variable_and_write_node: (LocalVariableAndWriteNode node) -> void + + # Visit a LocalVariableOperatorWriteNode node + # -- + # : (LocalVariableOperatorWriteNode node) -> void + def visit_local_variable_operator_write_node: (LocalVariableOperatorWriteNode node) -> void + + # Visit a LocalVariableOrWriteNode node + # -- + # : (LocalVariableOrWriteNode node) -> void + def visit_local_variable_or_write_node: (LocalVariableOrWriteNode node) -> void + + # Visit a LocalVariableReadNode node + # -- + # : (LocalVariableReadNode node) -> void + def visit_local_variable_read_node: (LocalVariableReadNode node) -> void + + # Visit a LocalVariableTargetNode node + # -- + # : (LocalVariableTargetNode node) -> void + def visit_local_variable_target_node: (LocalVariableTargetNode node) -> void + + # Visit a LocalVariableWriteNode node + # -- + # : (LocalVariableWriteNode node) -> void + def visit_local_variable_write_node: (LocalVariableWriteNode node) -> void + + # Visit a MatchLastLineNode node + # -- + # : (MatchLastLineNode node) -> void + def visit_match_last_line_node: (MatchLastLineNode node) -> void + + # Visit a MatchPredicateNode node + # -- + # : (MatchPredicateNode node) -> void + def visit_match_predicate_node: (MatchPredicateNode node) -> void + + # Visit a MatchRequiredNode node + # -- + # : (MatchRequiredNode node) -> void + def visit_match_required_node: (MatchRequiredNode node) -> void + + # Visit a MatchWriteNode node + # -- + # : (MatchWriteNode node) -> void + def visit_match_write_node: (MatchWriteNode node) -> void + + # Visit a MissingNode node + # -- + # : (MissingNode node) -> void + def visit_missing_node: (MissingNode node) -> void + + # Visit a ModuleNode node + # -- + # : (ModuleNode node) -> void + def visit_module_node: (ModuleNode node) -> void + + # Visit a MultiTargetNode node + # -- + # : (MultiTargetNode node) -> void + def visit_multi_target_node: (MultiTargetNode node) -> void + + # Visit a MultiWriteNode node + # -- + # : (MultiWriteNode node) -> void + def visit_multi_write_node: (MultiWriteNode node) -> void + + # Visit a NextNode node + # -- + # : (NextNode node) -> void + def visit_next_node: (NextNode node) -> void + + # Visit a NilNode node + # -- + # : (NilNode node) -> void + def visit_nil_node: (NilNode node) -> void + + # Visit a NoBlockParameterNode node + # -- + # : (NoBlockParameterNode node) -> void + def visit_no_block_parameter_node: (NoBlockParameterNode node) -> void + + # Visit a NoKeywordsParameterNode node + # -- + # : (NoKeywordsParameterNode node) -> void + def visit_no_keywords_parameter_node: (NoKeywordsParameterNode node) -> void + + # Visit a NumberedParametersNode node + # -- + # : (NumberedParametersNode node) -> void + def visit_numbered_parameters_node: (NumberedParametersNode node) -> void + + # Visit a NumberedReferenceReadNode node + # -- + # : (NumberedReferenceReadNode node) -> void + def visit_numbered_reference_read_node: (NumberedReferenceReadNode node) -> void + + # Visit a OptionalKeywordParameterNode node + # -- + # : (OptionalKeywordParameterNode node) -> void + def visit_optional_keyword_parameter_node: (OptionalKeywordParameterNode node) -> void + + # Visit a OptionalParameterNode node + # -- + # : (OptionalParameterNode node) -> void + def visit_optional_parameter_node: (OptionalParameterNode node) -> void + + # Visit a OrNode node + # -- + # : (OrNode node) -> void + def visit_or_node: (OrNode node) -> void + + # Visit a ParametersNode node + # -- + # : (ParametersNode node) -> void + def visit_parameters_node: (ParametersNode node) -> void + + # Visit a ParenthesesNode node + # -- + # : (ParenthesesNode node) -> void + def visit_parentheses_node: (ParenthesesNode node) -> void + + # Visit a PinnedExpressionNode node + # -- + # : (PinnedExpressionNode node) -> void + def visit_pinned_expression_node: (PinnedExpressionNode node) -> void + + # Visit a PinnedVariableNode node + # -- + # : (PinnedVariableNode node) -> void + def visit_pinned_variable_node: (PinnedVariableNode node) -> void + + # Visit a PostExecutionNode node + # -- + # : (PostExecutionNode node) -> void + def visit_post_execution_node: (PostExecutionNode node) -> void + + # Visit a PreExecutionNode node + # -- + # : (PreExecutionNode node) -> void + def visit_pre_execution_node: (PreExecutionNode node) -> void + + # Visit a ProgramNode node + # -- + # : (ProgramNode node) -> void + def visit_program_node: (ProgramNode node) -> void + + # Visit a RangeNode node + # -- + # : (RangeNode node) -> void + def visit_range_node: (RangeNode node) -> void + + # Visit a RationalNode node + # -- + # : (RationalNode node) -> void + def visit_rational_node: (RationalNode node) -> void + + # Visit a RedoNode node + # -- + # : (RedoNode node) -> void + def visit_redo_node: (RedoNode node) -> void + + # Visit a RegularExpressionNode node + # -- + # : (RegularExpressionNode node) -> void + def visit_regular_expression_node: (RegularExpressionNode node) -> void + + # Visit a RequiredKeywordParameterNode node + # -- + # : (RequiredKeywordParameterNode node) -> void + def visit_required_keyword_parameter_node: (RequiredKeywordParameterNode node) -> void + + # Visit a RequiredParameterNode node + # -- + # : (RequiredParameterNode node) -> void + def visit_required_parameter_node: (RequiredParameterNode node) -> void + + # Visit a RescueModifierNode node + # -- + # : (RescueModifierNode node) -> void + def visit_rescue_modifier_node: (RescueModifierNode node) -> void + + # Visit a RescueNode node + # -- + # : (RescueNode node) -> void + def visit_rescue_node: (RescueNode node) -> void + + # Visit a RestParameterNode node + # -- + # : (RestParameterNode node) -> void + def visit_rest_parameter_node: (RestParameterNode node) -> void + + # Visit a RetryNode node + # -- + # : (RetryNode node) -> void + def visit_retry_node: (RetryNode node) -> void + + # Visit a ReturnNode node + # -- + # : (ReturnNode node) -> void + def visit_return_node: (ReturnNode node) -> void + + # Visit a SelfNode node + # -- + # : (SelfNode node) -> void + def visit_self_node: (SelfNode node) -> void + + # Visit a ShareableConstantNode node + # -- + # : (ShareableConstantNode node) -> void + def visit_shareable_constant_node: (ShareableConstantNode node) -> void + + # Visit a SingletonClassNode node + # -- + # : (SingletonClassNode node) -> void + def visit_singleton_class_node: (SingletonClassNode node) -> void + + # Visit a SourceEncodingNode node + # -- + # : (SourceEncodingNode node) -> void + def visit_source_encoding_node: (SourceEncodingNode node) -> void + + # Visit a SourceFileNode node + # -- + # : (SourceFileNode node) -> void + def visit_source_file_node: (SourceFileNode node) -> void + + # Visit a SourceLineNode node + # -- + # : (SourceLineNode node) -> void + def visit_source_line_node: (SourceLineNode node) -> void + + # Visit a SplatNode node + # -- + # : (SplatNode node) -> void + def visit_splat_node: (SplatNode node) -> void + + # Visit a StatementsNode node + # -- + # : (StatementsNode node) -> void + def visit_statements_node: (StatementsNode node) -> void + + # Visit a StringNode node + # -- + # : (StringNode node) -> void + def visit_string_node: (StringNode node) -> void + + # Visit a SuperNode node + # -- + # : (SuperNode node) -> void + def visit_super_node: (SuperNode node) -> void + + # Visit a SymbolNode node + # -- + # : (SymbolNode node) -> void + def visit_symbol_node: (SymbolNode node) -> void + + # Visit a TrueNode node + # -- + # : (TrueNode node) -> void + def visit_true_node: (TrueNode node) -> void + + # Visit a UndefNode node + # -- + # : (UndefNode node) -> void + def visit_undef_node: (UndefNode node) -> void + + # Visit a UnlessNode node + # -- + # : (UnlessNode node) -> void + def visit_unless_node: (UnlessNode node) -> void + + # Visit a UntilNode node + # -- + # : (UntilNode node) -> void + def visit_until_node: (UntilNode node) -> void + + # Visit a WhenNode node + # -- + # : (WhenNode node) -> void + def visit_when_node: (WhenNode node) -> void + + # Visit a WhileNode node + # -- + # : (WhileNode node) -> void + def visit_while_node: (WhileNode node) -> void + + # Visit a XStringNode node + # -- + # : (XStringNode node) -> void + def visit_x_string_node: (XStringNode node) -> void + + # Visit a YieldNode node + # -- + # : (YieldNode node) -> void + def visit_yield_node: (YieldNode node) -> void + end +end diff --git a/sig/prism/_private/node.rbs b/sig/prism/_private/node.rbs deleted file mode 100644 index b5918a7133..0000000000 --- a/sig/prism/_private/node.rbs +++ /dev/null @@ -1,8 +0,0 @@ -module Prism - class Node - @newline_flag: bool - - def newline_flag?: () -> bool - def newline_flag!: (Array[bool] lines) -> void - end -end diff --git a/sig/prism/_private/node_ext.rbs b/sig/prism/_private/node_ext.rbs deleted file mode 100644 index 82e08f4263..0000000000 --- a/sig/prism/_private/node_ext.rbs +++ /dev/null @@ -1,9 +0,0 @@ -module Prism - module RegularExpressionOptions: InterpolatedMatchLastLineNode, InterpolatedRegularExpressionNode, MatchLastLineNode, RegularExpressionNode - def options: () -> Integer - end - - module HeredocQuery: InterpolatedStringNode, InterpolatedXStringNode, StringNode, XStringNode - def heredoc?: () -> bool? - end -end diff --git a/sig/prism/_private/pack.rbs b/sig/prism/_private/pack.rbs deleted file mode 100644 index ef1eb43d04..0000000000 --- a/sig/prism/_private/pack.rbs +++ /dev/null @@ -1,69 +0,0 @@ -module Prism - module Pack - SPACE: :SPACE - COMMENT: :COMMENT - INTEGER: :INTEGER - UTF8: :UTF8 - BER: :BER - FLOAT: :FLOAT - STRING_SPACE_PADDED: :STRING_SPACE_PADDED - STRING_NULL_PADDED: :STRING_NULL_PADDED - STRING_NULL_TERMINATED: :STRING_NULL_TERMINATED - STRING_MSB: :STRING_MSB - STRING_LSB: :STRING_LSB - STRING_HEX_HIGH: :STRING_HEX_HIGH - STRING_HEX_LOW: :STRING_HEX_LOW - STRING_UU: :STRING_UU - STRING_MIME: :STRING_MIME - STRING_BASE64: :STRING_BASE64 - STRING_FIXED: :STRING_FIXED - STRING_POINTER: :STRING_POINTER - MOVE: :MOVE - BACK: :BACK - NULL: :NULL - UNSIGNED: :UNSIGNED - SIGNED: :SIGNED - SIGNED_NA: :SIGNED_NA - AGNOSTIC_ENDIAN: :AGNOSTIC_ENDIAN - LITTLE_ENDIAN: :LITTLE_ENDIAN - BIG_ENDIAN: :BIG_ENDIAN - NATIVE_ENDIAN: :NATIVE_ENDIAN - ENDIAN_NA: :ENDIAN_NA - SIZE_SHORT: :SIZE_SHORT - SIZE_INT: :SIZE_INT - SIZE_LONG: :SIZE_LONG - SIZE_LONG_LONG: :SIZE_LONG_LONG - SIZE_8: :SIZE_8 - SIZE_16: :SIZE_16 - SIZE_32: :SIZE_32 - SIZE_64: :SIZE_64 - SIZE_P: :SIZE_P - SIZE_NA: :SIZE_NA - LENGTH_FIXED: :LENGTH_FIXED - LENGTH_MAX: :LENGTH_MAX - LENGTH_RELATIVE: :LENGTH_RELATIVE - LENGTH_NA: :LENGTH_NA - - class Directive - ENDIAN_DESCRIPTIONS: Hash[endianness, String] - SIGNED_DESCRIPTIONS: Hash[signness, String] - SIZE_DESCRIPTIONS: Hash[size, String] - - def initialize: ( - Symbol version, - variant variant, - String source, - directive_type type, - signness signed, - endianness endian, - size size, - length_type length_type, - Integer length - ) -> void - end - - class Format - def initialize: (Array[Directive] directives, Encoding encoding) -> void - end - end -end diff --git a/sig/prism/_private/parse_result.rbs b/sig/prism/_private/parse_result.rbs deleted file mode 100644 index 380b6f9dd6..0000000000 --- a/sig/prism/_private/parse_result.rbs +++ /dev/null @@ -1,50 +0,0 @@ -module Prism - class Source - private - - def find_line: (Integer) -> Integer - end - - class CodeUnitsCache - class UTF16Counter - def initialize: (String source, Encoding encoding) -> void - def count: (Integer byte_offset, Integer byte_length) -> Integer - end - - class LengthCounter - def initialize: (String source, Encoding encoding) -> void - def count: (Integer byte_offset, Integer byte_length) -> Integer - end - end - - class Location - private - - def source!: () -> Source - end - - class ParseResult - def attach_comments!: () -> void - def mark_newlines!: () -> untyped - - class Errors - attr_reader parse_result: ParseResult - - def initialize: (ParseResult parse_result) -> void - def format: () -> String - end - - class Newlines < Visitor - @newline_marked: Array[bool] - - # Create a new Newlines visitor with the given newline offsets. - def initialize: (Array[bool] newline_marked) -> void - - def visit_block_node: (BlockNode node) -> void - def visit_lambda_node: (LambdaNode node) -> void - def visit_if_node: (IfNode node) -> void - def visit_unless_node: (UnlessNode node) -> void - def visit_statements_node: (StatementsNode node) -> void - end - end -end diff --git a/sig/prism/_private/pattern.rbs b/sig/prism/_private/pattern.rbs deleted file mode 100644 index 244206d15d..0000000000 --- a/sig/prism/_private/pattern.rbs +++ /dev/null @@ -1,26 +0,0 @@ -module Prism - class Pattern - class CompilationError < StandardError - def initialize: (String) -> void - end - - @compiled: Proc? - - private - - def combine_and: (Proc, Proc) -> Proc - def combine_or: (Proc, Proc) -> Proc - def compile_error: (Prism::node?) -> Proc - def compile_array_pattern_node: (ArrayPatternNode) -> Proc - def compile_alternation_pattern_node: (AlternationPatternNode) -> Proc - def compile_constant_path_node: (ConstantPathNode) -> Proc - def compile_constant_read_node: (ConstantReadNode) -> Proc - def compile_constant_name: (Prism::node, Symbol) -> Proc - def compile_hash_pattern_node: (HashPatternNode) -> Proc - def compile_nil_node: (NilNode) -> Proc - def compile_regular_expression_node: (RegularExpressionNode) -> Proc - def compile_string_node: (StringNode) -> Proc - def compile_symbol_node: (SymbolNode) -> Proc - def compile_node: (Prism::node?) -> Proc - end -end diff --git a/sig/prism/_private/relocation.rbs b/sig/prism/_private/relocation.rbs deleted file mode 100644 index fda123bb37..0000000000 --- a/sig/prism/_private/relocation.rbs +++ /dev/null @@ -1,12 +0,0 @@ -module Prism - module Relocation - class Entry - def reify!: (entry_values values) -> void - end - - class Repository - def enter: (Integer node_id, Symbol field_name) -> Entry - def reify!: () -> void - end - end -end diff --git a/sig/prism/_private/string_query.rbs b/sig/prism/_private/string_query.rbs deleted file mode 100644 index 47ea2ca1a0..0000000000 --- a/sig/prism/_private/string_query.rbs +++ /dev/null @@ -1,7 +0,0 @@ -module Prism - class StringQuery - def self.local?: (String string) -> bool - def self.constant?: (String string) -> bool - def self.method_name?: (String string) -> bool - end -end diff --git a/sig/prism/compiler.rbs b/sig/prism/compiler.rbs deleted file mode 100644 index 1afa7db74d..0000000000 --- a/sig/prism/compiler.rbs +++ /dev/null @@ -1,9 +0,0 @@ -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/sig/prism/dispatcher.rbs b/sig/prism/dispatcher.rbs deleted file mode 100644 index f58b8661f3..0000000000 --- a/sig/prism/dispatcher.rbs +++ /dev/null @@ -1,19 +0,0 @@ -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/sig/prism/dot_visitor.rbs b/sig/prism/dot_visitor.rbs deleted file mode 100644 index 46b3dbf419..0000000000 --- a/sig/prism/dot_visitor.rbs +++ /dev/null @@ -1,6 +0,0 @@ -module Prism - class DotVisitor < Visitor - def initialize: () -> void - def to_dot: () -> String - end -end diff --git a/sig/prism/inspect_visitor.rbs b/sig/prism/inspect_visitor.rbs deleted file mode 100644 index 70fa878ac3..0000000000 --- a/sig/prism/inspect_visitor.rbs +++ /dev/null @@ -1,22 +0,0 @@ -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/sig/prism/lex_compat.rbs b/sig/prism/lex_compat.rbs deleted file mode 100644 index 5dc1f8764e..0000000000 --- a/sig/prism/lex_compat.rbs +++ /dev/null @@ -1,10 +0,0 @@ -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/sig/prism/node_ext.rbs b/sig/prism/node_ext.rbs deleted file mode 100644 index a187c1d246..0000000000 --- a/sig/prism/node_ext.rbs +++ /dev/null @@ -1,149 +0,0 @@ -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/sig/prism/pack.rbs b/sig/prism/pack.rbs deleted file mode 100644 index acbc92b69e..0000000000 --- a/sig/prism/pack.rbs +++ /dev/null @@ -1,43 +0,0 @@ -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/sig/prism/parse_result.rbs b/sig/prism/parse_result.rbs deleted file mode 100644 index cbcf3fc2f8..0000000000 --- a/sig/prism/parse_result.rbs +++ /dev/null @@ -1,197 +0,0 @@ -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/sig/prism/parse_result/comments.rbs b/sig/prism/parse_result/comments.rbs deleted file mode 100644 index 5b9e315063..0000000000 --- a/sig/prism/parse_result/comments.rbs +++ /dev/null @@ -1,38 +0,0 @@ -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/sig/prism/pattern.rbs b/sig/prism/pattern.rbs deleted file mode 100644 index a70293e18b..0000000000 --- a/sig/prism/pattern.rbs +++ /dev/null @@ -1,13 +0,0 @@ -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/sig/prism/reflection.rbs b/sig/prism/reflection.rbs deleted file mode 100644 index 047ea32a50..0000000000 --- a/sig/prism/reflection.rbs +++ /dev/null @@ -1,50 +0,0 @@ -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/sig/prism/relocation.rbs b/sig/prism/relocation.rbs deleted file mode 100644 index 7f5637d5fa..0000000000 --- a/sig/prism/relocation.rbs +++ /dev/null @@ -1,185 +0,0 @@ -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/sig/prism/serialize.rbs b/sig/prism/serialize.rbs deleted file mode 100644 index 71a7c5c1c9..0000000000 --- a/sig/prism/serialize.rbs +++ /dev/null @@ -1,8 +0,0 @@ -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/sig/prism/string_query.rbs b/sig/prism/string_query.rbs deleted file mode 100644 index 098746e552..0000000000 --- a/sig/prism/string_query.rbs +++ /dev/null @@ -1,11 +0,0 @@ -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/snapshots/4.0/endless_methods_command_call.txt b/snapshots/4.0/endless_methods_command_call.txt index d2c58ec8b0..cb819358ef 100644 --- a/snapshots/4.0/endless_methods_command_call.txt +++ b/snapshots/4.0/endless_methods_command_call.txt @@ -1,10 +1,10 @@ -@ ProgramNode (location: (1,0)-(8,31)) +@ ProgramNode (location: (1,0)-(11,37)) ├── flags: ∅ ├── locals: [] └── statements: - @ StatementsNode (location: (1,0)-(8,31)) + @ StatementsNode (location: (1,0)-(11,37)) ├── flags: ∅ - └── body: (length: 8) + └── body: (length: 10) ├── @ CallNode (location: (1,0)-(1,30)) │ ├── flags: newline, ignore_visibility │ ├── receiver: ∅ @@ -421,75 +421,208 @@ │ ├── closing_loc: ∅ │ ├── equal_loc: ∅ │ └── block: ∅ - └── @ CallNode (location: (8,0)-(8,31)) + ├── @ CallNode (location: (8,0)-(8,31)) + │ ├── flags: newline, ignore_visibility + │ ├── receiver: ∅ + │ ├── call_operator_loc: ∅ + │ ├── name: :private + │ ├── message_loc: (8,0)-(8,7) = "private" + │ ├── opening_loc: ∅ + │ ├── arguments: + │ │ @ ArgumentsNode (location: (8,8)-(8,31)) + │ │ ├── flags: ∅ + │ │ └── arguments: (length: 1) + │ │ └── @ DefNode (location: (8,8)-(8,31)) + │ │ ├── flags: ∅ + │ │ ├── name: :foo + │ │ ├── name_loc: (8,16)-(8,19) = "foo" + │ │ ├── receiver: + │ │ │ @ CallNode (location: (8,12)-(8,15)) + │ │ │ ├── flags: variable_call, ignore_visibility + │ │ │ ├── receiver: ∅ + │ │ │ ├── call_operator_loc: ∅ + │ │ │ ├── name: :obj + │ │ │ ├── message_loc: (8,12)-(8,15) = "obj" + │ │ │ ├── opening_loc: ∅ + │ │ │ ├── arguments: ∅ + │ │ │ ├── closing_loc: ∅ + │ │ │ ├── equal_loc: ∅ + │ │ │ └── block: ∅ + │ │ ├── parameters: + │ │ │ @ ParametersNode (location: (8,20)-(8,21)) + │ │ │ ├── flags: ∅ + │ │ │ ├── requireds: (length: 1) + │ │ │ │ └── @ RequiredParameterNode (location: (8,20)-(8,21)) + │ │ │ │ ├── flags: ∅ + │ │ │ │ └── name: :x + │ │ │ ├── optionals: (length: 0) + │ │ │ ├── rest: ∅ + │ │ │ ├── posts: (length: 0) + │ │ │ ├── keywords: (length: 0) + │ │ │ ├── keyword_rest: ∅ + │ │ │ └── block: ∅ + │ │ ├── body: + │ │ │ @ StatementsNode (location: (8,25)-(8,31)) + │ │ │ ├── flags: ∅ + │ │ │ └── body: (length: 1) + │ │ │ └── @ CallNode (location: (8,25)-(8,31)) + │ │ │ ├── flags: ignore_visibility + │ │ │ ├── receiver: ∅ + │ │ │ ├── call_operator_loc: ∅ + │ │ │ ├── name: :puts + │ │ │ ├── message_loc: (8,25)-(8,29) = "puts" + │ │ │ ├── opening_loc: ∅ + │ │ │ ├── arguments: + │ │ │ │ @ ArgumentsNode (location: (8,30)-(8,31)) + │ │ │ │ ├── flags: ∅ + │ │ │ │ └── arguments: (length: 1) + │ │ │ │ └── @ LocalVariableReadNode (location: (8,30)-(8,31)) + │ │ │ │ ├── flags: ∅ + │ │ │ │ ├── name: :x + │ │ │ │ └── depth: 0 + │ │ │ ├── closing_loc: ∅ + │ │ │ ├── equal_loc: ∅ + │ │ │ └── block: ∅ + │ │ ├── locals: [:x] + │ │ ├── def_keyword_loc: (8,8)-(8,11) = "def" + │ │ ├── operator_loc: (8,15)-(8,16) = "." + │ │ ├── lparen_loc: (8,19)-(8,20) = "(" + │ │ ├── rparen_loc: (8,21)-(8,22) = ")" + │ │ ├── equal_loc: (8,23)-(8,24) = "=" + │ │ └── end_keyword_loc: ∅ + │ ├── closing_loc: ∅ + │ ├── equal_loc: ∅ + │ └── block: ∅ + ├── @ CallNode (location: (10,0)-(10,25)) + │ ├── flags: newline, ignore_visibility + │ ├── receiver: ∅ + │ ├── call_operator_loc: ∅ + │ ├── name: :private + │ ├── message_loc: (10,0)-(10,7) = "private" + │ ├── opening_loc: ∅ + │ ├── arguments: + │ │ @ ArgumentsNode (location: (10,8)-(10,25)) + │ │ ├── flags: ∅ + │ │ └── arguments: (length: 1) + │ │ └── @ DefNode (location: (10,8)-(10,25)) + │ │ ├── flags: ∅ + │ │ ├── name: :foo + │ │ ├── name_loc: (10,12)-(10,15) = "foo" + │ │ ├── receiver: ∅ + │ │ ├── parameters: ∅ + │ │ ├── body: + │ │ │ @ StatementsNode (location: (10,18)-(10,25)) + │ │ │ ├── flags: ∅ + │ │ │ └── body: (length: 1) + │ │ │ └── @ CallNode (location: (10,18)-(10,25)) + │ │ │ ├── flags: ignore_visibility + │ │ │ ├── receiver: ∅ + │ │ │ ├── call_operator_loc: ∅ + │ │ │ ├── name: :bar + │ │ │ ├── message_loc: (10,18)-(10,21) = "bar" + │ │ │ ├── opening_loc: ∅ + │ │ │ ├── arguments: + │ │ │ │ @ ArgumentsNode (location: (10,22)-(10,25)) + │ │ │ │ ├── flags: ∅ + │ │ │ │ └── arguments: (length: 1) + │ │ │ │ └── @ CallNode (location: (10,22)-(10,25)) + │ │ │ │ ├── flags: variable_call, ignore_visibility + │ │ │ │ ├── receiver: ∅ + │ │ │ │ ├── call_operator_loc: ∅ + │ │ │ │ ├── name: :baz + │ │ │ │ ├── message_loc: (10,22)-(10,25) = "baz" + │ │ │ │ ├── opening_loc: ∅ + │ │ │ │ ├── arguments: ∅ + │ │ │ │ ├── closing_loc: ∅ + │ │ │ │ ├── equal_loc: ∅ + │ │ │ │ └── block: ∅ + │ │ │ ├── closing_loc: ∅ + │ │ │ ├── equal_loc: ∅ + │ │ │ └── block: ∅ + │ │ ├── locals: [] + │ │ ├── def_keyword_loc: (10,8)-(10,11) = "def" + │ │ ├── operator_loc: ∅ + │ │ ├── lparen_loc: ∅ + │ │ ├── rparen_loc: ∅ + │ │ ├── equal_loc: (10,16)-(10,17) = "=" + │ │ └── end_keyword_loc: ∅ + │ ├── closing_loc: ∅ + │ ├── equal_loc: ∅ + │ └── block: ∅ + └── @ CallNode (location: (11,0)-(11,37)) ├── flags: newline, ignore_visibility ├── receiver: ∅ ├── call_operator_loc: ∅ ├── name: :private - ├── message_loc: (8,0)-(8,7) = "private" + ├── message_loc: (11,0)-(11,7) = "private" ├── opening_loc: ∅ ├── arguments: - │ @ ArgumentsNode (location: (8,8)-(8,31)) + │ @ ArgumentsNode (location: (11,8)-(11,25)) │ ├── flags: ∅ │ └── arguments: (length: 1) - │ └── @ DefNode (location: (8,8)-(8,31)) + │ └── @ DefNode (location: (11,8)-(11,25)) │ ├── flags: ∅ │ ├── name: :foo - │ ├── name_loc: (8,16)-(8,19) = "foo" - │ ├── receiver: - │ │ @ CallNode (location: (8,12)-(8,15)) - │ │ ├── flags: variable_call, ignore_visibility - │ │ ├── receiver: ∅ - │ │ ├── call_operator_loc: ∅ - │ │ ├── name: :obj - │ │ ├── message_loc: (8,12)-(8,15) = "obj" - │ │ ├── opening_loc: ∅ - │ │ ├── arguments: ∅ - │ │ ├── closing_loc: ∅ - │ │ ├── equal_loc: ∅ - │ │ └── block: ∅ - │ ├── parameters: - │ │ @ ParametersNode (location: (8,20)-(8,21)) - │ │ ├── flags: ∅ - │ │ ├── requireds: (length: 1) - │ │ │ └── @ RequiredParameterNode (location: (8,20)-(8,21)) - │ │ │ ├── flags: ∅ - │ │ │ └── name: :x - │ │ ├── optionals: (length: 0) - │ │ ├── rest: ∅ - │ │ ├── posts: (length: 0) - │ │ ├── keywords: (length: 0) - │ │ ├── keyword_rest: ∅ - │ │ └── block: ∅ + │ ├── name_loc: (11,12)-(11,15) = "foo" + │ ├── receiver: ∅ + │ ├── parameters: ∅ │ ├── body: - │ │ @ StatementsNode (location: (8,25)-(8,31)) + │ │ @ StatementsNode (location: (11,18)-(11,25)) │ │ ├── flags: ∅ │ │ └── body: (length: 1) - │ │ └── @ CallNode (location: (8,25)-(8,31)) + │ │ └── @ CallNode (location: (11,18)-(11,25)) │ │ ├── flags: ignore_visibility │ │ ├── receiver: ∅ │ │ ├── call_operator_loc: ∅ - │ │ ├── name: :puts - │ │ ├── message_loc: (8,25)-(8,29) = "puts" + │ │ ├── name: :bar + │ │ ├── message_loc: (11,18)-(11,21) = "bar" │ │ ├── opening_loc: ∅ │ │ ├── arguments: - │ │ │ @ ArgumentsNode (location: (8,30)-(8,31)) + │ │ │ @ ArgumentsNode (location: (11,22)-(11,25)) │ │ │ ├── flags: ∅ │ │ │ └── arguments: (length: 1) - │ │ │ └── @ LocalVariableReadNode (location: (8,30)-(8,31)) - │ │ │ ├── flags: ∅ - │ │ │ ├── name: :x - │ │ │ └── depth: 0 + │ │ │ └── @ CallNode (location: (11,22)-(11,25)) + │ │ │ ├── flags: variable_call, ignore_visibility + │ │ │ ├── receiver: ∅ + │ │ │ ├── call_operator_loc: ∅ + │ │ │ ├── name: :baz + │ │ │ ├── message_loc: (11,22)-(11,25) = "baz" + │ │ │ ├── opening_loc: ∅ + │ │ │ ├── arguments: ∅ + │ │ │ ├── closing_loc: ∅ + │ │ │ ├── equal_loc: ∅ + │ │ │ └── block: ∅ │ │ ├── closing_loc: ∅ │ │ ├── equal_loc: ∅ │ │ └── block: ∅ - │ ├── locals: [:x] - │ ├── def_keyword_loc: (8,8)-(8,11) = "def" - │ ├── operator_loc: (8,15)-(8,16) = "." - │ ├── lparen_loc: (8,19)-(8,20) = "(" - │ ├── rparen_loc: (8,21)-(8,22) = ")" - │ ├── equal_loc: (8,23)-(8,24) = "=" + │ ├── locals: [] + │ ├── def_keyword_loc: (11,8)-(11,11) = "def" + │ ├── operator_loc: ∅ + │ ├── lparen_loc: ∅ + │ ├── rparen_loc: ∅ + │ ├── equal_loc: (11,16)-(11,17) = "=" │ └── end_keyword_loc: ∅ ├── closing_loc: ∅ ├── equal_loc: ∅ - └── block: ∅ + └── block: + @ BlockNode (location: (11,26)-(11,37)) + ├── flags: ∅ + ├── locals: [] + ├── parameters: ∅ + ├── body: + │ @ StatementsNode (location: (11,29)-(11,33)) + │ ├── flags: ∅ + │ └── body: (length: 1) + │ └── @ CallNode (location: (11,29)-(11,33)) + │ ├── flags: newline, variable_call, ignore_visibility + │ ├── receiver: ∅ + │ ├── call_operator_loc: ∅ + │ ├── name: :expr + │ ├── message_loc: (11,29)-(11,33) = "expr" + │ ├── opening_loc: ∅ + │ ├── arguments: ∅ + │ ├── closing_loc: ∅ + │ ├── equal_loc: ∅ + │ └── block: ∅ + ├── opening_loc: (11,26)-(11,28) = "do" + └── closing_loc: (11,34)-(11,37) = "end" diff --git a/snapshots/4.0/leading_logical.txt b/snapshots/4.0/leading_logical.txt index 9e043a88ce..c8ecaa40ba 100644 --- a/snapshots/4.0/leading_logical.txt +++ b/snapshots/4.0/leading_logical.txt @@ -1,10 +1,10 @@ -@ ProgramNode (location: (1,0)-(21,5)) +@ ProgramNode (location: (1,0)-(15,4)) ├── flags: ∅ ├── locals: [] └── statements: - @ StatementsNode (location: (1,0)-(21,5)) + @ StatementsNode (location: (1,0)-(15,4)) ├── flags: ∅ - └── body: (length: 8) + └── body: (length: 4) ├── @ AndNode (location: (1,0)-(3,4)) │ ├── flags: newline │ ├── left: @@ -62,50 +62,22 @@ │ │ ├── flags: static_literal, decimal │ │ └── value: 3 │ └── operator_loc: (11,0)-(11,3) = "and" - ├── @ OrNode (location: (13,0)-(15,4)) - │ ├── flags: newline - │ ├── left: - │ │ @ OrNode (location: (13,0)-(14,4)) - │ │ ├── flags: ∅ - │ │ ├── left: - │ │ │ @ IntegerNode (location: (13,0)-(13,1)) - │ │ │ ├── flags: static_literal, decimal - │ │ │ └── value: 1 - │ │ ├── right: - │ │ │ @ IntegerNode (location: (14,3)-(14,4)) - │ │ │ ├── flags: static_literal, decimal - │ │ │ └── value: 2 - │ │ └── operator_loc: (14,0)-(14,2) = "or" - │ ├── right: - │ │ @ IntegerNode (location: (15,3)-(15,4)) - │ │ ├── flags: static_literal, decimal - │ │ └── value: 3 - │ └── operator_loc: (15,0)-(15,2) = "or" - ├── @ IntegerNode (location: (17,0)-(17,1)) - │ ├── flags: newline, static_literal, decimal - │ └── value: 1 - ├── @ CallNode (location: (18,0)-(18,6)) - │ ├── flags: newline, variable_call, ignore_visibility - │ ├── receiver: ∅ - │ ├── call_operator_loc: ∅ - │ ├── name: :andfoo - │ ├── message_loc: (18,0)-(18,6) = "andfoo" - │ ├── opening_loc: ∅ - │ ├── arguments: ∅ - │ ├── closing_loc: ∅ - │ ├── equal_loc: ∅ - │ └── block: ∅ - ├── @ IntegerNode (location: (20,0)-(20,1)) - │ ├── flags: newline, static_literal, decimal - │ └── value: 2 - └── @ CallNode (location: (21,0)-(21,5)) - ├── flags: newline, variable_call, ignore_visibility - ├── receiver: ∅ - ├── call_operator_loc: ∅ - ├── name: :orfoo - ├── message_loc: (21,0)-(21,5) = "orfoo" - ├── opening_loc: ∅ - ├── arguments: ∅ - ├── closing_loc: ∅ - ├── equal_loc: ∅ - └── block: ∅ + └── @ OrNode (location: (13,0)-(15,4)) + ├── flags: newline + ├── left: + │ @ OrNode (location: (13,0)-(14,4)) + │ ├── flags: ∅ + │ ├── left: + │ │ @ IntegerNode (location: (13,0)-(13,1)) + │ │ ├── flags: static_literal, decimal + │ │ └── value: 1 + │ ├── right: + │ │ @ IntegerNode (location: (14,3)-(14,4)) + │ │ ├── flags: static_literal, decimal + │ │ └── value: 2 + │ └── operator_loc: (14,0)-(14,2) = "or" + ├── right: + │ @ IntegerNode (location: (15,3)-(15,4)) + │ ├── flags: static_literal, decimal + │ └── value: 3 + └── operator_loc: (15,0)-(15,2) = "or" diff --git a/snapshots/4.1/noblock.txt b/snapshots/4.1/noblock.txt new file mode 100644 index 0000000000..ce2827c24f --- /dev/null +++ b/snapshots/4.1/noblock.txt @@ -0,0 +1,61 @@ +@ ProgramNode (location: (1,0)-(4,12)) +├── flags: ∅ +├── locals: [] +└── statements: + @ StatementsNode (location: (1,0)-(4,12)) + ├── flags: ∅ + └── body: (length: 2) + ├── @ DefNode (location: (1,0)-(2,3)) + │ ├── flags: newline + │ ├── name: :foo + │ ├── name_loc: (1,4)-(1,7) = "foo" + │ ├── receiver: ∅ + │ ├── parameters: + │ │ @ ParametersNode (location: (1,8)-(1,12)) + │ │ ├── flags: ∅ + │ │ ├── requireds: (length: 0) + │ │ ├── optionals: (length: 0) + │ │ ├── rest: ∅ + │ │ ├── posts: (length: 0) + │ │ ├── keywords: (length: 0) + │ │ ├── keyword_rest: ∅ + │ │ └── block: + │ │ @ NoBlockParameterNode (location: (1,8)-(1,12)) + │ │ ├── flags: ∅ + │ │ ├── operator_loc: (1,8)-(1,9) = "&" + │ │ └── keyword_loc: (1,9)-(1,12) = "nil" + │ ├── body: ∅ + │ ├── locals: [] + │ ├── def_keyword_loc: (1,0)-(1,3) = "def" + │ ├── operator_loc: ∅ + │ ├── lparen_loc: (1,7)-(1,8) = "(" + │ ├── rparen_loc: (1,12)-(1,13) = ")" + │ ├── equal_loc: ∅ + │ └── end_keyword_loc: (2,0)-(2,3) = "end" + └── @ LambdaNode (location: (4,0)-(4,12)) + ├── flags: newline + ├── locals: [] + ├── operator_loc: (4,0)-(4,2) = "->" + ├── opening_loc: (4,10)-(4,11) = "{" + ├── closing_loc: (4,11)-(4,12) = "}" + ├── parameters: + │ @ BlockParametersNode (location: (4,3)-(4,9)) + │ ├── flags: ∅ + │ ├── parameters: + │ │ @ ParametersNode (location: (4,4)-(4,8)) + │ │ ├── flags: ∅ + │ │ ├── requireds: (length: 0) + │ │ ├── optionals: (length: 0) + │ │ ├── rest: ∅ + │ │ ├── posts: (length: 0) + │ │ ├── keywords: (length: 0) + │ │ ├── keyword_rest: ∅ + │ │ └── block: + │ │ @ NoBlockParameterNode (location: (4,4)-(4,8)) + │ │ ├── flags: ∅ + │ │ ├── operator_loc: (4,4)-(4,5) = "&" + │ │ └── keyword_loc: (4,5)-(4,8) = "nil" + │ ├── locals: (length: 0) + │ ├── opening_loc: (4,3)-(4,4) = "(" + │ └── closing_loc: (4,8)-(4,9) = ")" + └── body: ∅ diff --git a/snapshots/4.1/trailing_comma_after_method_arguments.txt b/snapshots/4.1/trailing_comma_after_method_arguments.txt new file mode 100644 index 0000000000..b71459ff54 --- /dev/null +++ b/snapshots/4.1/trailing_comma_after_method_arguments.txt @@ -0,0 +1,207 @@ +@ ProgramNode (location: (1,0)-(15,5)) +├── flags: ∅ +├── locals: [] +└── statements: + @ StatementsNode (location: (1,0)-(15,5)) + ├── flags: ∅ + └── body: (length: 6) + ├── @ DefNode (location: (1,0)-(1,19)) + │ ├── flags: newline + │ ├── name: :foo + │ ├── name_loc: (1,4)-(1,7) = "foo" + │ ├── receiver: ∅ + │ ├── parameters: + │ │ @ ParametersNode (location: (1,8)-(1,13)) + │ │ ├── flags: ∅ + │ │ ├── requireds: (length: 3) + │ │ │ ├── @ RequiredParameterNode (location: (1,8)-(1,9)) + │ │ │ │ ├── flags: ∅ + │ │ │ │ └── name: :a + │ │ │ ├── @ RequiredParameterNode (location: (1,10)-(1,11)) + │ │ │ │ ├── flags: ∅ + │ │ │ │ └── name: :b + │ │ │ └── @ RequiredParameterNode (location: (1,12)-(1,13)) + │ │ │ ├── flags: ∅ + │ │ │ └── name: :c + │ │ ├── optionals: (length: 0) + │ │ ├── rest: ∅ + │ │ ├── posts: (length: 0) + │ │ ├── keywords: (length: 0) + │ │ ├── keyword_rest: ∅ + │ │ └── block: ∅ + │ ├── body: ∅ + │ ├── locals: [:a, :b, :c] + │ ├── def_keyword_loc: (1,0)-(1,3) = "def" + │ ├── operator_loc: ∅ + │ ├── lparen_loc: (1,7)-(1,8) = "(" + │ ├── rparen_loc: (1,14)-(1,15) = ")" + │ ├── equal_loc: ∅ + │ └── end_keyword_loc: (1,16)-(1,19) = "end" + ├── @ DefNode (location: (3,0)-(3,20)) + │ ├── flags: newline + │ ├── name: :foo + │ ├── name_loc: (3,4)-(3,7) = "foo" + │ ├── receiver: ∅ + │ ├── parameters: + │ │ @ ParametersNode (location: (3,8)-(3,14)) + │ │ ├── flags: ∅ + │ │ ├── requireds: (length: 2) + │ │ │ ├── @ RequiredParameterNode (location: (3,8)-(3,9)) + │ │ │ │ ├── flags: ∅ + │ │ │ │ └── name: :a + │ │ │ └── @ RequiredParameterNode (location: (3,10)-(3,11)) + │ │ │ ├── flags: ∅ + │ │ │ └── name: :b + │ │ ├── optionals: (length: 0) + │ │ ├── rest: + │ │ │ @ RestParameterNode (location: (3,12)-(3,14)) + │ │ │ ├── flags: ∅ + │ │ │ ├── name: :c + │ │ │ ├── name_loc: (3,13)-(3,14) = "c" + │ │ │ └── operator_loc: (3,12)-(3,13) = "*" + │ │ ├── posts: (length: 0) + │ │ ├── keywords: (length: 0) + │ │ ├── keyword_rest: ∅ + │ │ └── block: ∅ + │ ├── body: ∅ + │ ├── locals: [:a, :b, :c] + │ ├── def_keyword_loc: (3,0)-(3,3) = "def" + │ ├── operator_loc: ∅ + │ ├── lparen_loc: (3,7)-(3,8) = "(" + │ ├── rparen_loc: (3,15)-(3,16) = ")" + │ ├── equal_loc: ∅ + │ └── end_keyword_loc: (3,17)-(3,20) = "end" + ├── @ DefNode (location: (5,0)-(5,19)) + │ ├── flags: newline + │ ├── name: :foo + │ ├── name_loc: (5,4)-(5,7) = "foo" + │ ├── receiver: ∅ + │ ├── parameters: + │ │ @ ParametersNode (location: (5,8)-(5,13)) + │ │ ├── flags: ∅ + │ │ ├── requireds: (length: 2) + │ │ │ ├── @ RequiredParameterNode (location: (5,8)-(5,9)) + │ │ │ │ ├── flags: ∅ + │ │ │ │ └── name: :a + │ │ │ └── @ RequiredParameterNode (location: (5,10)-(5,11)) + │ │ │ ├── flags: ∅ + │ │ │ └── name: :b + │ │ ├── optionals: (length: 0) + │ │ ├── rest: + │ │ │ @ RestParameterNode (location: (5,12)-(5,13)) + │ │ │ ├── flags: ∅ + │ │ │ ├── name: ∅ + │ │ │ ├── name_loc: ∅ + │ │ │ └── operator_loc: (5,12)-(5,13) = "*" + │ │ ├── posts: (length: 0) + │ │ ├── keywords: (length: 0) + │ │ ├── keyword_rest: ∅ + │ │ └── block: ∅ + │ ├── body: ∅ + │ ├── locals: [:a, :b] + │ ├── def_keyword_loc: (5,0)-(5,3) = "def" + │ ├── operator_loc: ∅ + │ ├── lparen_loc: (5,7)-(5,8) = "(" + │ ├── rparen_loc: (5,14)-(5,15) = ")" + │ ├── equal_loc: ∅ + │ └── end_keyword_loc: (5,16)-(5,19) = "end" + ├── @ DefNode (location: (7,0)-(7,21)) + │ ├── flags: newline + │ ├── name: :foo + │ ├── name_loc: (7,4)-(7,7) = "foo" + │ ├── receiver: ∅ + │ ├── parameters: + │ │ @ ParametersNode (location: (7,8)-(7,15)) + │ │ ├── flags: ∅ + │ │ ├── requireds: (length: 2) + │ │ │ ├── @ RequiredParameterNode (location: (7,8)-(7,9)) + │ │ │ │ ├── flags: ∅ + │ │ │ │ └── name: :a + │ │ │ └── @ RequiredParameterNode (location: (7,10)-(7,11)) + │ │ │ ├── flags: ∅ + │ │ │ └── name: :b + │ │ ├── optionals: (length: 0) + │ │ ├── rest: ∅ + │ │ ├── posts: (length: 0) + │ │ ├── keywords: (length: 0) + │ │ ├── keyword_rest: + │ │ │ @ KeywordRestParameterNode (location: (7,12)-(7,15)) + │ │ │ ├── flags: ∅ + │ │ │ ├── name: :c + │ │ │ ├── name_loc: (7,14)-(7,15) = "c" + │ │ │ └── operator_loc: (7,12)-(7,14) = "**" + │ │ └── block: ∅ + │ ├── body: ∅ + │ ├── locals: [:a, :b, :c] + │ ├── def_keyword_loc: (7,0)-(7,3) = "def" + │ ├── operator_loc: ∅ + │ ├── lparen_loc: (7,7)-(7,8) = "(" + │ ├── rparen_loc: (7,16)-(7,17) = ")" + │ ├── equal_loc: ∅ + │ └── end_keyword_loc: (7,18)-(7,21) = "end" + ├── @ DefNode (location: (9,0)-(9,20)) + │ ├── flags: newline + │ ├── name: :foo + │ ├── name_loc: (9,4)-(9,7) = "foo" + │ ├── receiver: ∅ + │ ├── parameters: + │ │ @ ParametersNode (location: (9,8)-(9,14)) + │ │ ├── flags: ∅ + │ │ ├── requireds: (length: 2) + │ │ │ ├── @ RequiredParameterNode (location: (9,8)-(9,9)) + │ │ │ │ ├── flags: ∅ + │ │ │ │ └── name: :a + │ │ │ └── @ RequiredParameterNode (location: (9,10)-(9,11)) + │ │ │ ├── flags: ∅ + │ │ │ └── name: :b + │ │ ├── optionals: (length: 0) + │ │ ├── rest: ∅ + │ │ ├── posts: (length: 0) + │ │ ├── keywords: (length: 0) + │ │ ├── keyword_rest: + │ │ │ @ KeywordRestParameterNode (location: (9,12)-(9,14)) + │ │ │ ├── flags: ∅ + │ │ │ ├── name: ∅ + │ │ │ ├── name_loc: ∅ + │ │ │ └── operator_loc: (9,12)-(9,14) = "**" + │ │ └── block: ∅ + │ ├── body: ∅ + │ ├── locals: [:a, :b] + │ ├── def_keyword_loc: (9,0)-(9,3) = "def" + │ ├── operator_loc: ∅ + │ ├── lparen_loc: (9,7)-(9,8) = "(" + │ ├── rparen_loc: (9,15)-(9,16) = ")" + │ ├── equal_loc: ∅ + │ └── end_keyword_loc: (9,17)-(9,20) = "end" + └── @ DefNode (location: (11,0)-(15,5)) + ├── flags: newline + ├── name: :foo + ├── name_loc: (11,4)-(11,7) = "foo" + ├── receiver: ∅ + ├── parameters: + │ @ ParametersNode (location: (12,2)-(14,3)) + │ ├── flags: ∅ + │ ├── requireds: (length: 3) + │ │ ├── @ RequiredParameterNode (location: (12,2)-(12,3)) + │ │ │ ├── flags: ∅ + │ │ │ └── name: :a + │ │ ├── @ RequiredParameterNode (location: (13,2)-(13,3)) + │ │ │ ├── flags: ∅ + │ │ │ └── name: :b + │ │ └── @ RequiredParameterNode (location: (14,2)-(14,3)) + │ │ ├── flags: ∅ + │ │ └── name: :c + │ ├── optionals: (length: 0) + │ ├── rest: ∅ + │ ├── posts: (length: 0) + │ ├── keywords: (length: 0) + │ ├── keyword_rest: ∅ + │ └── block: ∅ + ├── body: ∅ + ├── locals: [:a, :b, :c] + ├── def_keyword_loc: (11,0)-(11,3) = "def" + ├── operator_loc: ∅ + ├── lparen_loc: (11,7)-(11,8) = "(" + ├── rparen_loc: (15,0)-(15,1) = ")" + ├── equal_loc: ∅ + └── end_keyword_loc: (15,2)-(15,5) = "end" diff --git a/snapshots/and_or_with_suffix.txt b/snapshots/and_or_with_suffix.txt new file mode 100644 index 0000000000..593dee1766 --- /dev/null +++ b/snapshots/and_or_with_suffix.txt @@ -0,0 +1,139 @@ +@ ProgramNode (location: (1,0)-(17,5)) +├── flags: ∅ +├── locals: [] +└── statements: + @ StatementsNode (location: (1,0)-(17,5)) + ├── flags: ∅ + └── body: (length: 12) + ├── @ CallNode (location: (1,0)-(1,3)) + │ ├── flags: newline, variable_call, ignore_visibility + │ ├── receiver: ∅ + │ ├── call_operator_loc: ∅ + │ ├── name: :foo + │ ├── message_loc: (1,0)-(1,3) = "foo" + │ ├── opening_loc: ∅ + │ ├── arguments: ∅ + │ ├── closing_loc: ∅ + │ ├── equal_loc: ∅ + │ └── block: ∅ + ├── @ CallNode (location: (2,0)-(2,4)) + │ ├── flags: newline, ignore_visibility + │ ├── receiver: ∅ + │ ├── call_operator_loc: ∅ + │ ├── name: :and? + │ ├── message_loc: (2,0)-(2,4) = "and?" + │ ├── opening_loc: ∅ + │ ├── arguments: ∅ + │ ├── closing_loc: ∅ + │ ├── equal_loc: ∅ + │ └── block: ∅ + ├── @ CallNode (location: (4,0)-(4,3)) + │ ├── flags: newline, variable_call, ignore_visibility + │ ├── receiver: ∅ + │ ├── call_operator_loc: ∅ + │ ├── name: :foo + │ ├── message_loc: (4,0)-(4,3) = "foo" + │ ├── opening_loc: ∅ + │ ├── arguments: ∅ + │ ├── closing_loc: ∅ + │ ├── equal_loc: ∅ + │ └── block: ∅ + ├── @ CallNode (location: (5,0)-(5,3)) + │ ├── flags: newline, ignore_visibility + │ ├── receiver: ∅ + │ ├── call_operator_loc: ∅ + │ ├── name: :or? + │ ├── message_loc: (5,0)-(5,3) = "or?" + │ ├── opening_loc: ∅ + │ ├── arguments: ∅ + │ ├── closing_loc: ∅ + │ ├── equal_loc: ∅ + │ └── block: ∅ + ├── @ CallNode (location: (7,0)-(7,3)) + │ ├── flags: newline, variable_call, ignore_visibility + │ ├── receiver: ∅ + │ ├── call_operator_loc: ∅ + │ ├── name: :foo + │ ├── message_loc: (7,0)-(7,3) = "foo" + │ ├── opening_loc: ∅ + │ ├── arguments: ∅ + │ ├── closing_loc: ∅ + │ ├── equal_loc: ∅ + │ └── block: ∅ + ├── @ CallNode (location: (8,0)-(8,4)) + │ ├── flags: newline, ignore_visibility + │ ├── receiver: ∅ + │ ├── call_operator_loc: ∅ + │ ├── name: :and! + │ ├── message_loc: (8,0)-(8,4) = "and!" + │ ├── opening_loc: ∅ + │ ├── arguments: ∅ + │ ├── closing_loc: ∅ + │ ├── equal_loc: ∅ + │ └── block: ∅ + ├── @ CallNode (location: (10,0)-(10,3)) + │ ├── flags: newline, variable_call, ignore_visibility + │ ├── receiver: ∅ + │ ├── call_operator_loc: ∅ + │ ├── name: :foo + │ ├── message_loc: (10,0)-(10,3) = "foo" + │ ├── opening_loc: ∅ + │ ├── arguments: ∅ + │ ├── closing_loc: ∅ + │ ├── equal_loc: ∅ + │ └── block: ∅ + ├── @ CallNode (location: (11,0)-(11,3)) + │ ├── flags: newline, ignore_visibility + │ ├── receiver: ∅ + │ ├── call_operator_loc: ∅ + │ ├── name: :or! + │ ├── message_loc: (11,0)-(11,3) = "or!" + │ ├── opening_loc: ∅ + │ ├── arguments: ∅ + │ ├── closing_loc: ∅ + │ ├── equal_loc: ∅ + │ └── block: ∅ + ├── @ CallNode (location: (13,0)-(13,3)) + │ ├── flags: newline, variable_call, ignore_visibility + │ ├── receiver: ∅ + │ ├── call_operator_loc: ∅ + │ ├── name: :foo + │ ├── message_loc: (13,0)-(13,3) = "foo" + │ ├── opening_loc: ∅ + │ ├── arguments: ∅ + │ ├── closing_loc: ∅ + │ ├── equal_loc: ∅ + │ └── block: ∅ + ├── @ CallNode (location: (14,0)-(14,6)) + │ ├── flags: newline, variable_call, ignore_visibility + │ ├── receiver: ∅ + │ ├── call_operator_loc: ∅ + │ ├── name: :andbar + │ ├── message_loc: (14,0)-(14,6) = "andbar" + │ ├── opening_loc: ∅ + │ ├── arguments: ∅ + │ ├── closing_loc: ∅ + │ ├── equal_loc: ∅ + │ └── block: ∅ + ├── @ CallNode (location: (16,0)-(16,3)) + │ ├── flags: newline, variable_call, ignore_visibility + │ ├── receiver: ∅ + │ ├── call_operator_loc: ∅ + │ ├── name: :foo + │ ├── message_loc: (16,0)-(16,3) = "foo" + │ ├── opening_loc: ∅ + │ ├── arguments: ∅ + │ ├── closing_loc: ∅ + │ ├── equal_loc: ∅ + │ └── block: ∅ + └── @ CallNode (location: (17,0)-(17,5)) + ├── flags: newline, variable_call, ignore_visibility + ├── receiver: ∅ + ├── call_operator_loc: ∅ + ├── name: :orbar + ├── message_loc: (17,0)-(17,5) = "orbar" + ├── opening_loc: ∅ + ├── arguments: ∅ + ├── closing_loc: ∅ + ├── equal_loc: ∅ + └── block: ∅ diff --git a/snapshots/blocks.txt b/snapshots/blocks.txt index 62943535e1..03148044f9 100644 --- a/snapshots/blocks.txt +++ b/snapshots/blocks.txt @@ -1,10 +1,10 @@ -@ ProgramNode (location: (1,0)-(54,17)) +@ ProgramNode (location: (1,0)-(62,25)) ├── flags: ∅ ├── locals: [:fork] └── statements: - @ StatementsNode (location: (1,0)-(54,17)) + @ StatementsNode (location: (1,0)-(62,25)) ├── flags: ∅ - └── body: (length: 20) + └── body: (length: 24) ├── @ CallNode (location: (1,0)-(1,16)) │ ├── flags: newline │ ├── receiver: @@ -829,41 +829,284 @@ │ ├── closing_loc: ∅ │ ├── equal_loc: ∅ │ └── block: ∅ - └── @ CallNode (location: (54,0)-(54,17)) + ├── @ CallNode (location: (54,0)-(54,17)) + │ ├── flags: newline, ignore_visibility + │ ├── receiver: ∅ + │ ├── call_operator_loc: ∅ + │ ├── name: :foo + │ ├── message_loc: (54,0)-(54,3) = "foo" + │ ├── opening_loc: ∅ + │ ├── arguments: ∅ + │ ├── closing_loc: ∅ + │ ├── equal_loc: ∅ + │ └── block: + │ @ BlockNode (location: (54,4)-(54,17)) + │ ├── flags: ∅ + │ ├── locals: [:bar] + │ ├── parameters: + │ │ @ BlockParametersNode (location: (54,7)-(54,13)) + │ │ ├── flags: ∅ + │ │ ├── parameters: + │ │ │ @ ParametersNode (location: (54,8)-(54,12)) + │ │ │ ├── flags: ∅ + │ │ │ ├── requireds: (length: 1) + │ │ │ │ └── @ RequiredParameterNode (location: (54,8)-(54,11)) + │ │ │ │ ├── flags: ∅ + │ │ │ │ └── name: :bar + │ │ │ ├── optionals: (length: 0) + │ │ │ ├── rest: + │ │ │ │ @ ImplicitRestNode (location: (54,11)-(54,12)) + │ │ │ │ └── flags: ∅ + │ │ │ ├── posts: (length: 0) + │ │ │ ├── keywords: (length: 0) + │ │ │ ├── keyword_rest: ∅ + │ │ │ └── block: ∅ + │ │ ├── locals: (length: 0) + │ │ ├── opening_loc: (54,7)-(54,8) = "|" + │ │ └── closing_loc: (54,12)-(54,13) = "|" + │ ├── body: ∅ + │ ├── opening_loc: (54,4)-(54,6) = "do" + │ └── closing_loc: (54,14)-(54,17) = "end" + ├── @ CallNode (location: (56,0)-(56,23)) + │ ├── flags: newline, ignore_visibility + │ ├── receiver: ∅ + │ ├── call_operator_loc: ∅ + │ ├── name: :foo + │ ├── message_loc: (56,0)-(56,3) = "foo" + │ ├── opening_loc: ∅ + │ ├── arguments: + │ │ @ ArgumentsNode (location: (56,4)-(56,16)) + │ │ ├── flags: ∅ + │ │ └── arguments: (length: 1) + │ │ └── @ CallNode (location: (56,4)-(56,16)) + │ │ ├── flags: ignore_visibility + │ │ ├── receiver: ∅ + │ │ ├── call_operator_loc: ∅ + │ │ ├── name: :bar + │ │ ├── message_loc: (56,4)-(56,7) = "bar" + │ │ ├── opening_loc: ∅ + │ │ ├── arguments: + │ │ │ @ ArgumentsNode (location: (56,8)-(56,16)) + │ │ │ ├── flags: ∅ + │ │ │ └── arguments: (length: 2) + │ │ │ ├── @ CallNode (location: (56,8)-(56,11)) + │ │ │ │ ├── flags: variable_call, ignore_visibility + │ │ │ │ ├── receiver: ∅ + │ │ │ │ ├── call_operator_loc: ∅ + │ │ │ │ ├── name: :baz + │ │ │ │ ├── message_loc: (56,8)-(56,11) = "baz" + │ │ │ │ ├── opening_loc: ∅ + │ │ │ │ ├── arguments: ∅ + │ │ │ │ ├── closing_loc: ∅ + │ │ │ │ ├── equal_loc: ∅ + │ │ │ │ └── block: ∅ + │ │ │ └── @ CallNode (location: (56,13)-(56,16)) + │ │ │ ├── flags: variable_call, ignore_visibility + │ │ │ ├── receiver: ∅ + │ │ │ ├── call_operator_loc: ∅ + │ │ │ ├── name: :qux + │ │ │ ├── message_loc: (56,13)-(56,16) = "qux" + │ │ │ ├── opening_loc: ∅ + │ │ │ ├── arguments: ∅ + │ │ │ ├── closing_loc: ∅ + │ │ │ ├── equal_loc: ∅ + │ │ │ └── block: ∅ + │ │ ├── closing_loc: ∅ + │ │ ├── equal_loc: ∅ + │ │ └── block: ∅ + │ ├── closing_loc: ∅ + │ ├── equal_loc: ∅ + │ └── block: + │ @ BlockNode (location: (56,17)-(56,23)) + │ ├── flags: ∅ + │ ├── locals: [] + │ ├── parameters: ∅ + │ ├── body: ∅ + │ ├── opening_loc: (56,17)-(56,19) = "do" + │ └── closing_loc: (56,20)-(56,23) = "end" + ├── @ CallNode (location: (58,0)-(58,18)) + │ ├── flags: newline + │ ├── receiver: + │ │ @ CallNode (location: (58,0)-(58,3)) + │ │ ├── flags: variable_call, ignore_visibility + │ │ ├── receiver: ∅ + │ │ ├── call_operator_loc: ∅ + │ │ ├── name: :foo + │ │ ├── message_loc: (58,0)-(58,3) = "foo" + │ │ ├── opening_loc: ∅ + │ │ ├── arguments: ∅ + │ │ ├── closing_loc: ∅ + │ │ ├── equal_loc: ∅ + │ │ └── block: ∅ + │ ├── call_operator_loc: (58,3)-(58,4) = "." + │ ├── name: :bar + │ ├── message_loc: (58,4)-(58,7) = "bar" + │ ├── opening_loc: ∅ + │ ├── arguments: + │ │ @ ArgumentsNode (location: (58,8)-(58,11)) + │ │ ├── flags: ∅ + │ │ └── arguments: (length: 1) + │ │ └── @ CallNode (location: (58,8)-(58,11)) + │ │ ├── flags: variable_call, ignore_visibility + │ │ ├── receiver: ∅ + │ │ ├── call_operator_loc: ∅ + │ │ ├── name: :baz + │ │ ├── message_loc: (58,8)-(58,11) = "baz" + │ │ ├── opening_loc: ∅ + │ │ ├── arguments: ∅ + │ │ ├── closing_loc: ∅ + │ │ ├── equal_loc: ∅ + │ │ └── block: ∅ + │ ├── closing_loc: ∅ + │ ├── equal_loc: ∅ + │ └── block: + │ @ BlockNode (location: (58,12)-(58,18)) + │ ├── flags: ∅ + │ ├── locals: [] + │ ├── parameters: ∅ + │ ├── body: ∅ + │ ├── opening_loc: (58,12)-(58,14) = "do" + │ └── closing_loc: (58,15)-(58,18) = "end" + ├── @ CallNode (location: (60,0)-(60,34)) + │ ├── flags: newline + │ ├── receiver: + │ │ @ CallNode (location: (60,0)-(60,18)) + │ │ ├── flags: ∅ + │ │ ├── receiver: + │ │ │ @ CallNode (location: (60,0)-(60,3)) + │ │ │ ├── flags: variable_call, ignore_visibility + │ │ │ ├── receiver: ∅ + │ │ │ ├── call_operator_loc: ∅ + │ │ │ ├── name: :foo + │ │ │ ├── message_loc: (60,0)-(60,3) = "foo" + │ │ │ ├── opening_loc: ∅ + │ │ │ ├── arguments: ∅ + │ │ │ ├── closing_loc: ∅ + │ │ │ ├── equal_loc: ∅ + │ │ │ └── block: ∅ + │ │ ├── call_operator_loc: (60,3)-(60,4) = "." + │ │ ├── name: :bar + │ │ ├── message_loc: (60,4)-(60,7) = "bar" + │ │ ├── opening_loc: ∅ + │ │ ├── arguments: + │ │ │ @ ArgumentsNode (location: (60,8)-(60,11)) + │ │ │ ├── flags: ∅ + │ │ │ └── arguments: (length: 1) + │ │ │ └── @ CallNode (location: (60,8)-(60,11)) + │ │ │ ├── flags: variable_call, ignore_visibility + │ │ │ ├── receiver: ∅ + │ │ │ ├── call_operator_loc: ∅ + │ │ │ ├── name: :baz + │ │ │ ├── message_loc: (60,8)-(60,11) = "baz" + │ │ │ ├── opening_loc: ∅ + │ │ │ ├── arguments: ∅ + │ │ │ ├── closing_loc: ∅ + │ │ │ ├── equal_loc: ∅ + │ │ │ └── block: ∅ + │ │ ├── closing_loc: ∅ + │ │ ├── equal_loc: ∅ + │ │ └── block: + │ │ @ BlockNode (location: (60,12)-(60,18)) + │ │ ├── flags: ∅ + │ │ ├── locals: [] + │ │ ├── parameters: ∅ + │ │ ├── body: ∅ + │ │ ├── opening_loc: (60,12)-(60,14) = "do" + │ │ └── closing_loc: (60,15)-(60,18) = "end" + │ ├── call_operator_loc: (60,18)-(60,19) = "." + │ ├── name: :qux + │ ├── message_loc: (60,19)-(60,22) = "qux" + │ ├── opening_loc: ∅ + │ ├── arguments: + │ │ @ ArgumentsNode (location: (60,23)-(60,27)) + │ │ ├── flags: ∅ + │ │ └── arguments: (length: 1) + │ │ └── @ CallNode (location: (60,23)-(60,27)) + │ │ ├── flags: variable_call, ignore_visibility + │ │ ├── receiver: ∅ + │ │ ├── call_operator_loc: ∅ + │ │ ├── name: :quux + │ │ ├── message_loc: (60,23)-(60,27) = "quux" + │ │ ├── opening_loc: ∅ + │ │ ├── arguments: ∅ + │ │ ├── closing_loc: ∅ + │ │ ├── equal_loc: ∅ + │ │ └── block: ∅ + │ ├── closing_loc: ∅ + │ ├── equal_loc: ∅ + │ └── block: + │ @ BlockNode (location: (60,28)-(60,34)) + │ ├── flags: ∅ + │ ├── locals: [] + │ ├── parameters: ∅ + │ ├── body: ∅ + │ ├── opening_loc: (60,28)-(60,30) = "do" + │ └── closing_loc: (60,31)-(60,34) = "end" + └── @ CallNode (location: (62,0)-(62,25)) ├── flags: newline, ignore_visibility ├── receiver: ∅ ├── call_operator_loc: ∅ ├── name: :foo - ├── message_loc: (54,0)-(54,3) = "foo" + ├── message_loc: (62,0)-(62,3) = "foo" ├── opening_loc: ∅ - ├── arguments: ∅ + ├── arguments: + │ @ ArgumentsNode (location: (62,4)-(62,12)) + │ ├── flags: ∅ + │ └── arguments: (length: 2) + │ ├── @ CallNode (location: (62,4)-(62,7)) + │ │ ├── flags: variable_call, ignore_visibility + │ │ ├── receiver: ∅ + │ │ ├── call_operator_loc: ∅ + │ │ ├── name: :bar + │ │ ├── message_loc: (62,4)-(62,7) = "bar" + │ │ ├── opening_loc: ∅ + │ │ ├── arguments: ∅ + │ │ ├── closing_loc: ∅ + │ │ ├── equal_loc: ∅ + │ │ └── block: ∅ + │ └── @ CallNode (location: (62,9)-(62,12)) + │ ├── flags: variable_call, ignore_visibility + │ ├── receiver: ∅ + │ ├── call_operator_loc: ∅ + │ ├── name: :baz + │ ├── message_loc: (62,9)-(62,12) = "baz" + │ ├── opening_loc: ∅ + │ ├── arguments: ∅ + │ ├── closing_loc: ∅ + │ ├── equal_loc: ∅ + │ └── block: ∅ ├── closing_loc: ∅ ├── equal_loc: ∅ └── block: - @ BlockNode (location: (54,4)-(54,17)) + @ BlockNode (location: (62,13)-(62,25)) ├── flags: ∅ - ├── locals: [:bar] + ├── locals: [:x] ├── parameters: - │ @ BlockParametersNode (location: (54,7)-(54,13)) + │ @ BlockParametersNode (location: (62,16)-(62,19)) │ ├── flags: ∅ │ ├── parameters: - │ │ @ ParametersNode (location: (54,8)-(54,12)) + │ │ @ ParametersNode (location: (62,17)-(62,18)) │ │ ├── flags: ∅ │ │ ├── requireds: (length: 1) - │ │ │ └── @ RequiredParameterNode (location: (54,8)-(54,11)) + │ │ │ └── @ RequiredParameterNode (location: (62,17)-(62,18)) │ │ │ ├── flags: ∅ - │ │ │ └── name: :bar + │ │ │ └── name: :x │ │ ├── optionals: (length: 0) - │ │ ├── rest: - │ │ │ @ ImplicitRestNode (location: (54,11)-(54,12)) - │ │ │ └── flags: ∅ + │ │ ├── rest: ∅ │ │ ├── posts: (length: 0) │ │ ├── keywords: (length: 0) │ │ ├── keyword_rest: ∅ │ │ └── block: ∅ │ ├── locals: (length: 0) - │ ├── opening_loc: (54,7)-(54,8) = "|" - │ └── closing_loc: (54,12)-(54,13) = "|" - ├── body: ∅ - ├── opening_loc: (54,4)-(54,6) = "do" - └── closing_loc: (54,14)-(54,17) = "end" + │ ├── opening_loc: (62,16)-(62,17) = "|" + │ └── closing_loc: (62,18)-(62,19) = "|" + ├── body: + │ @ StatementsNode (location: (62,20)-(62,21)) + │ ├── flags: ∅ + │ └── body: (length: 1) + │ └── @ LocalVariableReadNode (location: (62,20)-(62,21)) + │ ├── flags: newline + │ ├── name: :x + │ └── depth: 0 + ├── opening_loc: (62,13)-(62,15) = "do" + └── closing_loc: (62,22)-(62,25) = "end" diff --git a/snapshots/case_in_in.txt b/snapshots/case_in_in.txt new file mode 100644 index 0000000000..0fcedb0e89 --- /dev/null +++ b/snapshots/case_in_in.txt @@ -0,0 +1,81 @@ +@ ProgramNode (location: (1,0)-(4,3)) +├── flags: ∅ +├── locals: [:event] +└── statements: + @ StatementsNode (location: (1,0)-(4,3)) + ├── flags: ∅ + └── body: (length: 1) + └── @ CaseMatchNode (location: (1,0)-(4,3)) + ├── flags: newline + ├── predicate: + │ @ CallNode (location: (1,5)-(1,9)) + │ ├── flags: variable_call, ignore_visibility + │ ├── receiver: ∅ + │ ├── call_operator_loc: ∅ + │ ├── name: :args + │ ├── message_loc: (1,5)-(1,9) = "args" + │ ├── opening_loc: ∅ + │ ├── arguments: ∅ + │ ├── closing_loc: ∅ + │ ├── equal_loc: ∅ + │ └── block: ∅ + ├── conditions: (length: 1) + │ └── @ InNode (location: (2,0)-(3,25)) + │ ├── flags: ∅ + │ ├── pattern: + │ │ @ ArrayPatternNode (location: (2,3)-(2,10)) + │ │ ├── flags: ∅ + │ │ ├── constant: ∅ + │ │ ├── requireds: (length: 1) + │ │ │ └── @ LocalVariableTargetNode (location: (2,4)-(2,9)) + │ │ │ ├── flags: ∅ + │ │ │ ├── name: :event + │ │ │ └── depth: 0 + │ │ ├── rest: ∅ + │ │ ├── posts: (length: 0) + │ │ ├── opening_loc: (2,3)-(2,4) = "[" + │ │ └── closing_loc: (2,9)-(2,10) = "]" + │ ├── statements: + │ │ @ StatementsNode (location: (3,2)-(3,25)) + │ │ ├── flags: ∅ + │ │ └── body: (length: 1) + │ │ └── @ MatchPredicateNode (location: (3,2)-(3,25)) + │ │ ├── flags: newline + │ │ ├── value: + │ │ │ @ CallNode (location: (3,2)-(3,15)) + │ │ │ ├── flags: ∅ + │ │ │ ├── receiver: + │ │ │ │ @ CallNode (location: (3,2)-(3,9)) + │ │ │ │ ├── flags: variable_call, ignore_visibility + │ │ │ │ ├── receiver: ∅ + │ │ │ │ ├── call_operator_loc: ∅ + │ │ │ │ ├── name: :context + │ │ │ │ ├── message_loc: (3,2)-(3,9) = "context" + │ │ │ │ ├── opening_loc: ∅ + │ │ │ │ ├── arguments: ∅ + │ │ │ │ ├── closing_loc: ∅ + │ │ │ │ ├── equal_loc: ∅ + │ │ │ │ └── block: ∅ + │ │ │ ├── call_operator_loc: (3,9)-(3,10) = "." + │ │ │ ├── name: :event + │ │ │ ├── message_loc: (3,10)-(3,15) = "event" + │ │ │ ├── opening_loc: ∅ + │ │ │ ├── arguments: ∅ + │ │ │ ├── closing_loc: ∅ + │ │ │ ├── equal_loc: ∅ + │ │ │ └── block: ∅ + │ │ ├── pattern: + │ │ │ @ PinnedVariableNode (location: (3,19)-(3,25)) + │ │ │ ├── flags: ∅ + │ │ │ ├── variable: + │ │ │ │ @ LocalVariableReadNode (location: (3,20)-(3,25)) + │ │ │ │ ├── flags: ∅ + │ │ │ │ ├── name: :event + │ │ │ │ └── depth: 0 + │ │ │ └── operator_loc: (3,19)-(3,20) = "^" + │ │ └── operator_loc: (3,16)-(3,18) = "in" + │ ├── in_loc: (2,0)-(2,2) = "in" + │ └── then_loc: ∅ + ├── else_clause: ∅ + ├── case_keyword_loc: (1,0)-(1,4) = "case" + └── end_keyword_loc: (4,0)-(4,3) = "end" diff --git a/snapshots/command_method_call_2.txt b/snapshots/command_method_call_2.txt index ef3e73201c..8b52d4b368 100644 --- a/snapshots/command_method_call_2.txt +++ b/snapshots/command_method_call_2.txt @@ -1,95 +1,49 @@ -@ ProgramNode (location: (1,0)-(3,17)) +@ ProgramNode (location: (1,0)-(1,17)) ├── flags: ∅ ├── locals: [] └── statements: - @ StatementsNode (location: (1,0)-(3,17)) + @ StatementsNode (location: (1,0)-(1,17)) ├── flags: ∅ - └── body: (length: 2) - ├── @ CallNode (location: (1,0)-(1,19)) - │ ├── flags: newline, ignore_visibility - │ ├── receiver: ∅ - │ ├── call_operator_loc: ∅ - │ ├── name: :foo - │ ├── message_loc: (1,0)-(1,3) = "foo" - │ ├── opening_loc: (1,3)-(1,4) = "(" - │ ├── arguments: - │ │ @ ArgumentsNode (location: (1,4)-(1,18)) - │ │ ├── flags: ∅ - │ │ └── arguments: (length: 1) - │ │ └── @ CallNode (location: (1,4)-(1,18)) - │ │ ├── flags: ignore_visibility - │ │ ├── receiver: ∅ - │ │ ├── call_operator_loc: ∅ - │ │ ├── name: :bar - │ │ ├── message_loc: (1,4)-(1,7) = "bar" - │ │ ├── opening_loc: ∅ - │ │ ├── arguments: - │ │ │ @ ArgumentsNode (location: (1,8)-(1,11)) - │ │ │ ├── flags: ∅ - │ │ │ └── arguments: (length: 1) - │ │ │ └── @ CallNode (location: (1,8)-(1,11)) - │ │ │ ├── flags: variable_call, ignore_visibility - │ │ │ ├── receiver: ∅ - │ │ │ ├── call_operator_loc: ∅ - │ │ │ ├── name: :baz - │ │ │ ├── message_loc: (1,8)-(1,11) = "baz" - │ │ │ ├── opening_loc: ∅ - │ │ │ ├── arguments: ∅ - │ │ │ ├── closing_loc: ∅ - │ │ │ ├── equal_loc: ∅ - │ │ │ └── block: ∅ - │ │ ├── closing_loc: ∅ - │ │ ├── equal_loc: ∅ - │ │ └── block: - │ │ @ BlockNode (location: (1,12)-(1,18)) - │ │ ├── flags: ∅ - │ │ ├── locals: [] - │ │ ├── parameters: ∅ - │ │ ├── body: ∅ - │ │ ├── opening_loc: (1,12)-(1,14) = "do" - │ │ └── closing_loc: (1,15)-(1,18) = "end" - │ ├── closing_loc: (1,18)-(1,19) = ")" - │ ├── equal_loc: ∅ - │ └── block: ∅ - └── @ CallNode (location: (3,0)-(3,17)) + └── body: (length: 1) + └── @ CallNode (location: (1,0)-(1,17)) ├── flags: newline, ignore_visibility ├── receiver: ∅ ├── call_operator_loc: ∅ ├── name: :foo - ├── message_loc: (3,0)-(3,3) = "foo" - ├── opening_loc: (3,3)-(3,4) = "(" + ├── message_loc: (1,0)-(1,3) = "foo" + ├── opening_loc: (1,3)-(1,4) = "(" ├── arguments: - │ @ ArgumentsNode (location: (3,4)-(3,16)) + │ @ ArgumentsNode (location: (1,4)-(1,16)) │ ├── flags: ∅ │ └── arguments: (length: 1) - │ └── @ CallNode (location: (3,4)-(3,16)) + │ └── @ CallNode (location: (1,4)-(1,16)) │ ├── flags: ignore_visibility │ ├── receiver: ∅ │ ├── call_operator_loc: ∅ │ ├── name: :bar - │ ├── message_loc: (3,4)-(3,7) = "bar" + │ ├── message_loc: (1,4)-(1,7) = "bar" │ ├── opening_loc: ∅ │ ├── arguments: - │ │ @ ArgumentsNode (location: (3,8)-(3,16)) + │ │ @ ArgumentsNode (location: (1,8)-(1,16)) │ │ ├── flags: ∅ │ │ └── arguments: (length: 2) - │ │ ├── @ CallNode (location: (3,8)-(3,11)) + │ │ ├── @ CallNode (location: (1,8)-(1,11)) │ │ │ ├── flags: variable_call, ignore_visibility │ │ │ ├── receiver: ∅ │ │ │ ├── call_operator_loc: ∅ │ │ │ ├── name: :baz - │ │ │ ├── message_loc: (3,8)-(3,11) = "baz" + │ │ │ ├── message_loc: (1,8)-(1,11) = "baz" │ │ │ ├── opening_loc: ∅ │ │ │ ├── arguments: ∅ │ │ │ ├── closing_loc: ∅ │ │ │ ├── equal_loc: ∅ │ │ │ └── block: ∅ - │ │ └── @ CallNode (location: (3,13)-(3,16)) + │ │ └── @ CallNode (location: (1,13)-(1,16)) │ │ ├── flags: variable_call, ignore_visibility │ │ ├── receiver: ∅ │ │ ├── call_operator_loc: ∅ │ │ ├── name: :bat - │ │ ├── message_loc: (3,13)-(3,16) = "bat" + │ │ ├── message_loc: (1,13)-(1,16) = "bat" │ │ ├── opening_loc: ∅ │ │ ├── arguments: ∅ │ │ ├── closing_loc: ∅ @@ -98,6 +52,6 @@ │ ├── closing_loc: ∅ │ ├── equal_loc: ∅ │ └── block: ∅ - ├── closing_loc: (3,16)-(3,17) = ")" + ├── closing_loc: (1,16)-(1,17) = ")" ├── equal_loc: ∅ └── block: ∅ diff --git a/snapshots/endless_methods.txt b/snapshots/endless_methods.txt index 938ec25a56..9334abec63 100644 --- a/snapshots/endless_methods.txt +++ b/snapshots/endless_methods.txt @@ -1,10 +1,10 @@ -@ ProgramNode (location: (1,0)-(7,15)) +@ ProgramNode (location: (1,0)-(11,18)) ├── flags: ∅ ├── locals: [:x] └── statements: - @ StatementsNode (location: (1,0)-(7,15)) + @ StatementsNode (location: (1,0)-(11,18)) ├── flags: ∅ - └── body: (length: 4) + └── body: (length: 6) ├── @ DefNode (location: (1,0)-(1,11)) │ ├── flags: newline │ ├── name: :foo @@ -116,44 +116,128 @@ │ ├── rparen_loc: ∅ │ ├── equal_loc: (5,11)-(5,12) = "=" │ └── end_keyword_loc: ∅ - └── @ LocalVariableWriteNode (location: (7,0)-(7,15)) + ├── @ LocalVariableWriteNode (location: (7,0)-(7,15)) + │ ├── flags: newline + │ ├── name: :x + │ ├── depth: 0 + │ ├── name_loc: (7,0)-(7,1) = "x" + │ ├── value: + │ │ @ DefNode (location: (7,4)-(7,15)) + │ │ ├── flags: ∅ + │ │ ├── name: :f + │ │ ├── name_loc: (7,8)-(7,9) = "f" + │ │ ├── receiver: ∅ + │ │ ├── parameters: ∅ + │ │ ├── body: + │ │ │ @ StatementsNode (location: (7,12)-(7,15)) + │ │ │ ├── flags: ∅ + │ │ │ └── body: (length: 1) + │ │ │ └── @ CallNode (location: (7,12)-(7,15)) + │ │ │ ├── flags: ignore_visibility + │ │ │ ├── receiver: ∅ + │ │ │ ├── call_operator_loc: ∅ + │ │ │ ├── name: :p + │ │ │ ├── message_loc: (7,12)-(7,13) = "p" + │ │ │ ├── opening_loc: ∅ + │ │ │ ├── arguments: + │ │ │ │ @ ArgumentsNode (location: (7,14)-(7,15)) + │ │ │ │ ├── flags: ∅ + │ │ │ │ └── arguments: (length: 1) + │ │ │ │ └── @ IntegerNode (location: (7,14)-(7,15)) + │ │ │ │ ├── flags: static_literal, decimal + │ │ │ │ └── value: 1 + │ │ │ ├── closing_loc: ∅ + │ │ │ ├── equal_loc: ∅ + │ │ │ └── block: ∅ + │ │ ├── locals: [] + │ │ ├── def_keyword_loc: (7,4)-(7,7) = "def" + │ │ ├── operator_loc: ∅ + │ │ ├── lparen_loc: ∅ + │ │ ├── rparen_loc: ∅ + │ │ ├── equal_loc: (7,10)-(7,11) = "=" + │ │ └── end_keyword_loc: ∅ + │ └── operator_loc: (7,2)-(7,3) = "=" + ├── @ DefNode (location: (9,0)-(9,17)) + │ ├── flags: newline + │ ├── name: :foo + │ ├── name_loc: (9,4)-(9,7) = "foo" + │ ├── receiver: ∅ + │ ├── parameters: ∅ + │ ├── body: + │ │ @ StatementsNode (location: (9,10)-(9,17)) + │ │ ├── flags: ∅ + │ │ └── body: (length: 1) + │ │ └── @ CallNode (location: (9,10)-(9,17)) + │ │ ├── flags: ignore_visibility + │ │ ├── receiver: ∅ + │ │ ├── call_operator_loc: ∅ + │ │ ├── name: :bar + │ │ ├── message_loc: (9,10)-(9,13) = "bar" + │ │ ├── opening_loc: ∅ + │ │ ├── arguments: + │ │ │ @ ArgumentsNode (location: (9,14)-(9,17)) + │ │ │ ├── flags: ∅ + │ │ │ └── arguments: (length: 1) + │ │ │ └── @ CallNode (location: (9,14)-(9,17)) + │ │ │ ├── flags: variable_call, ignore_visibility + │ │ │ ├── receiver: ∅ + │ │ │ ├── call_operator_loc: ∅ + │ │ │ ├── name: :baz + │ │ │ ├── message_loc: (9,14)-(9,17) = "baz" + │ │ │ ├── opening_loc: ∅ + │ │ │ ├── arguments: ∅ + │ │ │ ├── closing_loc: ∅ + │ │ │ ├── equal_loc: ∅ + │ │ │ └── block: ∅ + │ │ ├── closing_loc: ∅ + │ │ ├── equal_loc: ∅ + │ │ └── block: ∅ + │ ├── locals: [] + │ ├── def_keyword_loc: (9,0)-(9,3) = "def" + │ ├── operator_loc: ∅ + │ ├── lparen_loc: ∅ + │ ├── rparen_loc: ∅ + │ ├── equal_loc: (9,8)-(9,9) = "=" + │ └── end_keyword_loc: ∅ + └── @ DefNode (location: (11,0)-(11,18)) ├── flags: newline - ├── name: :x - ├── depth: 0 - ├── name_loc: (7,0)-(7,1) = "x" - ├── value: - │ @ DefNode (location: (7,4)-(7,15)) + ├── name: :foo + ├── name_loc: (11,4)-(11,7) = "foo" + ├── receiver: ∅ + ├── parameters: ∅ + ├── body: + │ @ StatementsNode (location: (11,10)-(11,18)) │ ├── flags: ∅ - │ ├── name: :f - │ ├── name_loc: (7,8)-(7,9) = "f" - │ ├── receiver: ∅ - │ ├── parameters: ∅ - │ ├── body: - │ │ @ StatementsNode (location: (7,12)-(7,15)) - │ │ ├── flags: ∅ - │ │ └── body: (length: 1) - │ │ └── @ CallNode (location: (7,12)-(7,15)) - │ │ ├── flags: ignore_visibility - │ │ ├── receiver: ∅ - │ │ ├── call_operator_loc: ∅ - │ │ ├── name: :p - │ │ ├── message_loc: (7,12)-(7,13) = "p" - │ │ ├── opening_loc: ∅ - │ │ ├── arguments: - │ │ │ @ ArgumentsNode (location: (7,14)-(7,15)) - │ │ │ ├── flags: ∅ - │ │ │ └── arguments: (length: 1) - │ │ │ └── @ IntegerNode (location: (7,14)-(7,15)) - │ │ │ ├── flags: static_literal, decimal - │ │ │ └── value: 1 - │ │ ├── closing_loc: ∅ - │ │ ├── equal_loc: ∅ - │ │ └── block: ∅ - │ ├── locals: [] - │ ├── def_keyword_loc: (7,4)-(7,7) = "def" - │ ├── operator_loc: ∅ - │ ├── lparen_loc: ∅ - │ ├── rparen_loc: ∅ - │ ├── equal_loc: (7,10)-(7,11) = "=" - │ └── end_keyword_loc: ∅ - └── operator_loc: (7,2)-(7,3) = "=" + │ └── body: (length: 1) + │ └── @ CallNode (location: (11,10)-(11,18)) + │ ├── flags: ignore_visibility + │ ├── receiver: ∅ + │ ├── call_operator_loc: ∅ + │ ├── name: :bar + │ ├── message_loc: (11,10)-(11,13) = "bar" + │ ├── opening_loc: (11,13)-(11,14) = "(" + │ ├── arguments: + │ │ @ ArgumentsNode (location: (11,14)-(11,17)) + │ │ ├── flags: ∅ + │ │ └── arguments: (length: 1) + │ │ └── @ CallNode (location: (11,14)-(11,17)) + │ │ ├── flags: variable_call, ignore_visibility + │ │ ├── receiver: ∅ + │ │ ├── call_operator_loc: ∅ + │ │ ├── name: :baz + │ │ ├── message_loc: (11,14)-(11,17) = "baz" + │ │ ├── opening_loc: ∅ + │ │ ├── arguments: ∅ + │ │ ├── closing_loc: ∅ + │ │ ├── equal_loc: ∅ + │ │ └── block: ∅ + │ ├── closing_loc: (11,17)-(11,18) = ")" + │ ├── equal_loc: ∅ + │ └── block: ∅ + ├── locals: [] + ├── def_keyword_loc: (11,0)-(11,3) = "def" + ├── operator_loc: ∅ + ├── lparen_loc: ∅ + ├── rparen_loc: ∅ + ├── equal_loc: (11,8)-(11,9) = "=" + └── end_keyword_loc: ∅ diff --git a/snapshots/heredoc_dedent_line_continuation.txt b/snapshots/heredoc_dedent_line_continuation.txt new file mode 100644 index 0000000000..d0c29ef2a2 --- /dev/null +++ b/snapshots/heredoc_dedent_line_continuation.txt @@ -0,0 +1,30 @@ +@ ProgramNode (location: (1,0)-(1,6)) +├── flags: ∅ +├── locals: [] +└── statements: + @ StatementsNode (location: (1,0)-(1,6)) + ├── flags: ∅ + └── body: (length: 1) + └── @ InterpolatedStringNode (location: (1,0)-(1,6)) + ├── flags: newline + ├── opening_loc: (1,0)-(1,6) = "<<~FOO" + ├── parts: (length: 3) + │ ├── @ StringNode (location: (2,0)-(3,0)) + │ │ ├── flags: static_literal, frozen + │ │ ├── opening_loc: ∅ + │ │ ├── content_loc: (2,0)-(3,0) = " foo\\\n" + │ │ ├── closing_loc: ∅ + │ │ └── unescaped: "foo" + │ ├── @ StringNode (location: (3,0)-(4,0)) + │ │ ├── flags: static_literal, frozen + │ │ ├── opening_loc: ∅ + │ │ ├── content_loc: (3,0)-(4,0) = " \\\n" + │ │ ├── closing_loc: ∅ + │ │ └── unescaped: "" + │ └── @ StringNode (location: (4,0)-(5,0)) + │ ├── flags: static_literal, frozen + │ ├── opening_loc: ∅ + │ ├── content_loc: (4,0)-(5,0) = " bar\n" + │ ├── closing_loc: ∅ + │ └── unescaped: "bar\n" + └── closing_loc: (5,0)-(6,0) = "FOO\n" diff --git a/snapshots/seattlerb/regexp_escape_extended.txt b/snapshots/seattlerb/regexp_escape_extended.txt index 732899a20d..a2e4d5eb96 100644 --- a/snapshots/seattlerb/regexp_escape_extended.txt +++ b/snapshots/seattlerb/regexp_escape_extended.txt @@ -6,7 +6,7 @@ ├── flags: ∅ └── body: (length: 1) └── @ RegularExpressionNode (location: (1,0)-(1,6)) - ├── flags: newline, static_literal + ├── flags: newline, static_literal, forced_us_ascii_encoding ├── opening_loc: (1,0)-(1,1) = "/" ├── content_loc: (1,1)-(1,5) = "\\“" ├── closing_loc: (1,5)-(1,6) = "/" diff --git a/snapshots/write_command_operator.txt b/snapshots/write_command_operator.txt new file mode 100644 index 0000000000..f1d92729ed --- /dev/null +++ b/snapshots/write_command_operator.txt @@ -0,0 +1,84 @@ +@ ProgramNode (location: (1,0)-(3,24)) +├── flags: ∅ +├── locals: [:foo] +└── statements: + @ StatementsNode (location: (1,0)-(3,24)) + ├── flags: ∅ + └── body: (length: 2) + ├── @ OrNode (location: (1,0)-(1,27)) + │ ├── flags: newline + │ ├── left: + │ │ @ LocalVariableWriteNode (location: (1,0)-(1,17)) + │ │ ├── flags: ∅ + │ │ ├── name: :foo + │ │ ├── depth: 0 + │ │ ├── name_loc: (1,0)-(1,3) = "foo" + │ │ ├── value: + │ │ │ @ CallNode (location: (1,6)-(1,17)) + │ │ │ ├── flags: ∅ + │ │ │ ├── receiver: + │ │ │ │ @ IntegerNode (location: (1,6)-(1,9)) + │ │ │ │ ├── flags: static_literal, decimal + │ │ │ │ └── value: 123 + │ │ │ ├── call_operator_loc: ∅ + │ │ │ ├── name: :| + │ │ │ ├── message_loc: (1,10)-(1,11) = "|" + │ │ │ ├── opening_loc: ∅ + │ │ │ ├── arguments: + │ │ │ │ @ ArgumentsNode (location: (1,12)-(1,17)) + │ │ │ │ ├── flags: ∅ + │ │ │ │ └── arguments: (length: 1) + │ │ │ │ └── @ StringNode (location: (1,12)-(1,17)) + │ │ │ │ ├── flags: ∅ + │ │ │ │ ├── opening_loc: (1,12)-(1,13) = "'" + │ │ │ │ ├── content_loc: (1,13)-(1,16) = "456" + │ │ │ │ ├── closing_loc: (1,16)-(1,17) = "'" + │ │ │ │ └── unescaped: "456" + │ │ │ ├── closing_loc: ∅ + │ │ │ ├── equal_loc: ∅ + │ │ │ └── block: ∅ + │ │ └── operator_loc: (1,4)-(1,5) = "=" + │ ├── right: + │ │ @ ReturnNode (location: (1,21)-(1,27)) + │ │ ├── flags: ∅ + │ │ ├── keyword_loc: (1,21)-(1,27) = "return" + │ │ └── arguments: ∅ + │ └── operator_loc: (1,18)-(1,20) = "or" + └── @ MatchPredicateNode (location: (3,0)-(3,24)) + ├── flags: newline + ├── value: + │ @ LocalVariableWriteNode (location: (3,0)-(3,17)) + │ ├── flags: ∅ + │ ├── name: :foo + │ ├── depth: 0 + │ ├── name_loc: (3,0)-(3,3) = "foo" + │ ├── value: + │ │ @ CallNode (location: (3,6)-(3,17)) + │ │ ├── flags: ∅ + │ │ ├── receiver: + │ │ │ @ IntegerNode (location: (3,6)-(3,9)) + │ │ │ ├── flags: static_literal, decimal + │ │ │ └── value: 123 + │ │ ├── call_operator_loc: ∅ + │ │ ├── name: :| + │ │ ├── message_loc: (3,10)-(3,11) = "|" + │ │ ├── opening_loc: ∅ + │ │ ├── arguments: + │ │ │ @ ArgumentsNode (location: (3,12)-(3,17)) + │ │ │ ├── flags: ∅ + │ │ │ └── arguments: (length: 1) + │ │ │ └── @ StringNode (location: (3,12)-(3,17)) + │ │ │ ├── flags: ∅ + │ │ │ ├── opening_loc: (3,12)-(3,13) = "'" + │ │ │ ├── content_loc: (3,13)-(3,16) = "456" + │ │ │ ├── closing_loc: (3,16)-(3,17) = "'" + │ │ │ └── unescaped: "456" + │ │ ├── closing_loc: ∅ + │ │ ├── equal_loc: ∅ + │ │ └── block: ∅ + │ └── operator_loc: (3,4)-(3,5) = "=" + ├── pattern: + │ @ ConstantReadNode (location: (3,21)-(3,24)) + │ ├── flags: ∅ + │ └── name: :BAR + └── operator_loc: (3,18)-(3,20) = "in" diff --git a/src/arena.c b/src/arena.c new file mode 100644 index 0000000000..64a731649d --- /dev/null +++ b/src/arena.c @@ -0,0 +1,117 @@ +#include "prism/internal/arena.h" + +#include "prism/internal/allocator.h" + +#include +#include +#include + +/** + * Compute the block allocation size using offsetof so it is correct regardless + * of PM_FLEX_ARRAY_LENGTH. + */ +#define PM_ARENA_BLOCK_SIZE(data_size) (offsetof(pm_arena_block_t, data) + (data_size)) + +/** Initial block data size: 8 KB. */ +#define PM_ARENA_INITIAL_SIZE 8192 + +/** Double the block size every this many blocks. */ +#define PM_ARENA_GROWTH_INTERVAL 8 + +/** Maximum block data size: 1 MB. */ +#define PM_ARENA_MAX_SIZE (1024 * 1024) + +/** + * Compute the data size for the next block. + */ +static size_t +pm_arena_next_block_size(const pm_arena_t *arena, size_t min_size) { + size_t size = PM_ARENA_INITIAL_SIZE; + + for (size_t exp = PM_ARENA_GROWTH_INTERVAL; exp <= arena->block_count; exp += PM_ARENA_GROWTH_INTERVAL) { + if (size < PM_ARENA_MAX_SIZE) size *= 2; + } + + return size > min_size ? size : min_size; +} + +/** + * Allocate a new block with the given data capacity and initial usage, link it + * into the arena, and return it. Aborts on allocation failure. + */ +static pm_arena_block_t * +pm_arena_block_new(pm_arena_t *arena, size_t data_size, size_t initial_used) { + assert(initial_used <= data_size); + pm_arena_block_t *block = (pm_arena_block_t *) xmalloc(PM_ARENA_BLOCK_SIZE(data_size)); + + if (block == NULL) { + fprintf(stderr, "prism: out of memory; aborting\n"); + abort(); + } + + block->capacity = data_size; + block->used = initial_used; + block->prev = arena->current; + arena->current = block; + arena->block_count++; + + return block; +} + +/** + * Ensure the arena has at least `capacity` bytes available in its current + * block, allocating a new block if necessary. This allows callers to + * pre-size the arena to avoid repeated small block allocations. + */ +void +pm_arena_reserve(pm_arena_t *arena, size_t capacity) { + if (capacity <= PM_ARENA_INITIAL_SIZE) return; + if (arena->current != NULL && (arena->current->capacity - arena->current->used) >= capacity) return; + pm_arena_block_new(arena, capacity, 0); +} + +/** + * Slow path for pm_arena_alloc: allocate a new block and return a pointer to + * the first `size` bytes. Called when the current block has insufficient space. + */ +void * +pm_arena_alloc_slow(pm_arena_t *arena, size_t size) { + size_t block_data_size = pm_arena_next_block_size(arena, size); + pm_arena_block_t *block = pm_arena_block_new(arena, block_data_size, size); + return block->data; +} + +/** + * Returns a newly allocated and initialized arena. + */ +pm_arena_t * +pm_arena_new(void) { + pm_arena_t *arena = (pm_arena_t *) xcalloc(1, sizeof(pm_arena_t)); + if (arena == NULL) abort(); + return arena; +} + +/** + * Free all blocks in the arena. + */ +void +pm_arena_cleanup(pm_arena_t *arena) { + pm_arena_block_t *block = arena->current; + + while (block != NULL) { + pm_arena_block_t *prev = block->prev; + xfree_sized(block, PM_ARENA_BLOCK_SIZE(block->capacity)); + block = prev; + } + + *arena = (pm_arena_t) { 0 }; +} + +/** + * Frees both the held memory and the arena itself. + */ +void +pm_arena_free(pm_arena_t *arena) { + pm_arena_cleanup(arena); + xfree_sized(arena, sizeof(pm_arena_t)); +} diff --git a/src/util/pm_buffer.c b/src/buffer.c similarity index 90% rename from src/util/pm_buffer.c rename to src/buffer.c index 2136a7c43e..cb3b9a4fe8 100644 --- a/src/util/pm_buffer.c +++ b/src/buffer.c @@ -1,31 +1,38 @@ -#include "prism/util/pm_buffer.h" +#include "prism/internal/buffer.h" -/** - * Return the size of the pm_buffer_t struct. - */ -size_t -pm_buffer_sizeof(void) { - return sizeof(pm_buffer_t); -} +#include "prism/compiler/inline.h" + +#include "prism/internal/char.h" +#include "prism/internal/allocator.h" + +#include +#include +#include +#include +#include /** * Initialize a pm_buffer_t with the given capacity. */ -bool -pm_buffer_init_capacity(pm_buffer_t *buffer, size_t capacity) { +void +pm_buffer_init(pm_buffer_t *buffer, size_t capacity) { buffer->length = 0; buffer->capacity = capacity; buffer->value = (char *) xmalloc(capacity); - return buffer->value != NULL; + if (buffer->value == NULL) abort(); } /** - * Initialize a pm_buffer_t with its default values. + * Allocate and initialize a new buffer. */ -bool -pm_buffer_init(pm_buffer_t *buffer) { - return pm_buffer_init_capacity(buffer, 1024); +pm_buffer_t * +pm_buffer_new(void) { + pm_buffer_t *buffer = (pm_buffer_t *) xmalloc(sizeof(pm_buffer_t)); + if (buffer == NULL) abort(); + + pm_buffer_init(buffer, 1024); + return buffer; } /** @@ -47,9 +54,10 @@ pm_buffer_length(const pm_buffer_t *buffer) { /** * Append the given amount of space to the buffer. */ -static inline bool +static PRISM_INLINE bool pm_buffer_append_length(pm_buffer_t *buffer, size_t length) { size_t next_length = buffer->length + length; + const size_t original_capacity = buffer->capacity; if (next_length > buffer->capacity) { if (buffer->capacity == 0) { @@ -60,7 +68,7 @@ pm_buffer_append_length(pm_buffer_t *buffer, size_t length) { buffer->capacity *= 2; } - buffer->value = xrealloc(buffer->value, buffer->capacity); + buffer->value = xrealloc_sized(buffer->value, buffer->capacity, original_capacity); if (buffer->value == NULL) return false; } @@ -71,7 +79,7 @@ pm_buffer_append_length(pm_buffer_t *buffer, size_t length) { /** * Append a generic pointer to memory to the buffer. */ -static inline void +static PRISM_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)) { @@ -349,9 +357,18 @@ pm_buffer_insert(pm_buffer_t *buffer, size_t index, const char *value, size_t le } /** - * Free the memory associated with the buffer. + * Free the memory held by the buffer. + */ +void +pm_buffer_cleanup(pm_buffer_t *buffer) { + xfree_sized(buffer->value, buffer->capacity); +} + +/** + * Free both the memory held by the buffer and the buffer itself. */ void pm_buffer_free(pm_buffer_t *buffer) { - xfree(buffer->value); + pm_buffer_cleanup(buffer); + xfree_sized(buffer, sizeof(pm_buffer_t)); } diff --git a/src/util/pm_char.c b/src/char.c similarity index 85% rename from src/util/pm_char.c rename to src/char.c index 748582b7fe..08e457aa1f 100644 --- a/src/util/pm_char.c +++ b/src/char.c @@ -1,7 +1,8 @@ -#include "prism/util/pm_char.h" +#include "prism/internal/char.h" + +#include "prism/compiler/inline.h" +#include "prism/internal/line_offset_list.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) @@ -13,7 +14,7 @@ #define PRISM_NUMBER_BIT_HEXADECIMAL_DIGIT (1 << 6) #define PRISM_NUMBER_BIT_HEXADECIMAL_NUMBER (1 << 7) -static const uint8_t pm_byte_table[256] = { +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 @@ -57,7 +58,7 @@ static const uint8_t pm_number_table[256] = { * 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 +static PRISM_INLINE size_t pm_strspn_char_kind(const uint8_t *string, ptrdiff_t length, uint8_t kind) { if (length <= 0) return 0; @@ -83,7 +84,7 @@ pm_strspn_whitespace(const uint8_t *string, ptrdiff_t length) { * 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, uint32_t start_offset) { +pm_strspn_whitespace_newlines(const uint8_t *string, ptrdiff_t length, pm_arena_t *arena, pm_line_offset_list_t *line_offsets, uint32_t start_offset) { if (length <= 0) return 0; uint32_t size = 0; @@ -91,7 +92,7 @@ pm_strspn_whitespace_newlines(const uint8_t *string, ptrdiff_t length, pm_newlin while (size < maximum && (pm_byte_table[string[size]] & PRISM_CHAR_BIT_WHITESPACE)) { if (string[size] == '\n') { - pm_newline_list_append(newline_list, start_offset + size + 1); + pm_line_offset_list_append(arena, line_offsets, start_offset + size + 1); } size++; @@ -100,15 +101,6 @@ pm_strspn_whitespace_newlines(const uint8_t *string, ptrdiff_t length, pm_newlin 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. @@ -118,36 +110,13 @@ 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 +static PRISM_INLINE size_t pm_strspn_number_kind(const uint8_t *string, ptrdiff_t length, uint8_t kind) { if (length <= 0) return 0; @@ -166,7 +135,7 @@ pm_strspn_number_kind(const uint8_t *string, ptrdiff_t length, uint8_t kind) { * Additionally, report the location of the last invalid underscore character * found in the string through the out invalid parameter. */ -static inline size_t +static PRISM_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; @@ -267,7 +236,7 @@ pm_strspn_hexadecimal_number(const uint8_t *string, ptrdiff_t length, const uint /** * Returns true if the given character matches the given kind. */ -static inline bool +static PRISM_INLINE bool pm_char_is_number_kind(const uint8_t b, uint8_t kind) { return (pm_number_table[b] & kind) != 0; } @@ -303,16 +272,3 @@ 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/src/util/pm_constant_pool.c b/src/constant_pool.c similarity index 53% rename from src/util/pm_constant_pool.c rename to src/constant_pool.c index 922ce6a18c..90201ebb8e 100644 --- a/src/util/pm_constant_pool.c +++ b/src/constant_pool.c @@ -1,4 +1,11 @@ -#include "prism/util/pm_constant_pool.h" +#include "prism/internal/constant_pool.h" + +#include "prism/compiler/align.h" +#include "prism/compiler/inline.h" +#include "prism/internal/arena.h" + +#include +#include /** * Initialize a list of constant ids. @@ -14,10 +21,9 @@ pm_constant_id_list_init(pm_constant_id_list_t *list) { * 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) { +pm_constant_id_list_init_capacity(pm_arena_t *arena, 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(); + list->ids = (pm_constant_id_t *) pm_arena_zalloc(arena, capacity * sizeof(pm_constant_id_t), PRISM_ALIGNOF(pm_constant_id_t)); } else { list->ids = NULL; } @@ -27,19 +33,23 @@ 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. + * Append a constant id to a list of constant ids. */ -bool -pm_constant_id_list_append(pm_constant_id_list_t *list, pm_constant_id_t id) { +void +pm_constant_id_list_append(pm_arena_t *arena, 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; + size_t new_capacity = list->capacity == 0 ? 8 : list->capacity * 2; + pm_constant_id_t *new_ids = (pm_constant_id_t *) pm_arena_alloc(arena, sizeof(pm_constant_id_t) * new_capacity, PRISM_ALIGNOF(pm_constant_id_t)); + + if (list->size > 0) { + memcpy(new_ids, list->ids, list->size * sizeof(pm_constant_id_t)); + } + + list->ids = new_ids; + list->capacity = new_capacity; } list->ids[list->size++] = id; - return true; } /** @@ -66,29 +76,66 @@ 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. + * A multiply-xorshift hash that processes input a word at a time. This is + * significantly faster than the byte-at-a-time djb2 hash for the short strings + * typical in Ruby source (~15 bytes average). Each word is mixed into the hash + * by XOR followed by multiplication by a large odd constant, which spreads + * entropy across all bits. A final xorshift fold produces the 32-bit result. */ -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 +static PRISM_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; + // This constant is borrowed from wyhash. It is a 64-bit odd integer with + // roughly equal 0/1 bits, chosen for good avalanche behavior when used in + // multiply-xorshift sequences. + static const uint64_t secret = 0x517cc1b727220a95ULL; + uint64_t hash = (uint64_t) length; + + if (length <= 8) { + // Short strings: read first and last 4 bytes (overlapping for len < 8). + // This covers the majority of Ruby identifiers with a single multiply. + if (length >= 4) { + uint32_t a, b; + memcpy(&a, start, 4); + memcpy(&b, start + length - 4, 4); + hash ^= (uint64_t) a | ((uint64_t) b << 32); + } else if (length > 0) { + hash ^= (uint64_t) start[0] | ((uint64_t) start[length >> 1] << 8) | ((uint64_t) start[length - 1] << 16); + } + hash *= secret; + } else if (length <= 16) { + // Medium strings: read first and last 8 bytes (overlapping). + // Two multiplies instead of the three the loop-based approach needs. + uint64_t word; + memcpy(&word, start, 8); + hash ^= word; + hash *= secret; + memcpy(&word, start + length - 8, 8); + hash ^= word; + hash *= secret; + } else { + const uint8_t *ptr = start; + size_t remaining = length; + + while (remaining >= 8) { + uint64_t word; + memcpy(&word, ptr, 8); + hash ^= word; + hash *= secret; + ptr += 8; + remaining -= 8; + } - for (size_t index = 0; index < length; index++) { - value = ((value << 5) + value) + start[index]; + if (remaining > 0) { + // Read the last 8 bytes (overlapping with already-processed data). + uint64_t word; + memcpy(&word, start + length - 8, 8); + hash ^= word; + hash *= secret; + } } - return value; + hash ^= hash >> 32; + return (uint32_t) hash; } /** @@ -121,21 +168,15 @@ is_power_of_two(uint32_t size) { /** * Resize a constant pool to a given capacity. */ -static inline bool -pm_constant_pool_resize(pm_constant_pool_t *pool) { +static PRISM_INLINE void +pm_constant_pool_resize(pm_arena_t *arena, 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)); + pm_constant_pool_bucket_t *next_buckets = (pm_constant_pool_bucket_t *) pm_arena_zalloc(arena, next_capacity * sizeof(pm_constant_pool_bucket_t), PRISM_ALIGNOF(pm_constant_pool_bucket_t)); + pm_constant_t *next_constants = (pm_constant_t *) pm_arena_alloc(arena, next_capacity * sizeof(pm_constant_t), PRISM_ALIGNOF(pm_constant_t)); // For each bucket in the current constant pool, find the index in the // next constant pool, and insert it. @@ -163,33 +204,22 @@ pm_constant_pool_resize(pm_constant_pool_t *pool) { // 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; - +void +pm_constant_pool_init(pm_arena_t *arena, pm_constant_pool_t *pool, uint32_t capacity) { 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->buckets = (pm_constant_pool_bucket_t *) pm_arena_zalloc(arena, capacity * sizeof(pm_constant_pool_bucket_t), PRISM_ALIGNOF(pm_constant_pool_bucket_t)); + pool->constants = (pm_constant_t *) pm_arena_alloc(arena, capacity * sizeof(pm_constant_t), PRISM_ALIGNOF(pm_constant_t)); pool->size = 0; pool->capacity = capacity; - return true; } /** @@ -215,8 +245,7 @@ pm_constant_pool_find(const pm_constant_pool_t *pool, const uint8_t *start, size 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) { + if ((bucket->length == length) && memcmp(bucket->start, start, length) == 0) { return bucket->id; } @@ -229,10 +258,10 @@ pm_constant_pool_find(const pm_constant_pool_t *pool, const uint8_t *start, size /** * 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) { +static PRISM_INLINE pm_constant_id_t +pm_constant_pool_insert(pm_arena_t *arena, 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; + pm_constant_pool_resize(arena, pool); } assert(is_power_of_two(pool->capacity)); @@ -246,25 +275,17 @@ pm_constant_pool_insert(pm_constant_pool_t *pool, const uint8_t *start, size_t l // 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) { + if ((bucket->length == length) && memcmp(bucket->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 (type != PM_CONSTANT_POOL_BUCKET_OWNED && 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; + // existing constant is owned, then we can replace it with the + // shared constant to prefer non-owned references. + bucket->start = start; bucket->type = (unsigned int) (type & 0x3); + pool->constants[bucket->id - 1].start = start; } return bucket->id; @@ -281,7 +302,9 @@ pm_constant_pool_insert(pm_constant_pool_t *pool, const uint8_t *start, size_t l *bucket = (pm_constant_pool_bucket_t) { .id = (unsigned int) (id & 0x3fffffff), .type = (unsigned int) (type & 0x3), - .hash = hash + .hash = hash, + .start = start, + .length = length }; pool->constants[id - 1] = (pm_constant_t) { @@ -297,8 +320,8 @@ pm_constant_pool_insert(pm_constant_pool_t *pool, const uint8_t *start, size_t l * 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); +pm_constant_pool_insert_shared(pm_arena_t *arena, pm_constant_pool_t *pool, const uint8_t *start, size_t length) { + return pm_constant_pool_insert(arena, pool, start, length, PM_CONSTANT_POOL_BUCKET_DEFAULT); } /** @@ -307,8 +330,8 @@ pm_constant_pool_insert_shared(pm_constant_pool_t *pool, const uint8_t *start, s * 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); +pm_constant_pool_insert_owned(pm_arena_t *arena, pm_constant_pool_t *pool, uint8_t *start, size_t length) { + return pm_constant_pool_insert(arena, pool, start, length, PM_CONSTANT_POOL_BUCKET_OWNED); } /** @@ -317,26 +340,21 @@ pm_constant_pool_insert_owned(pm_constant_pool_t *pool, uint8_t *start, size_t l * 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); +pm_constant_pool_insert_constant(pm_arena_t *arena, pm_constant_pool_t *pool, const uint8_t *start, size_t length) { + return pm_constant_pool_insert(arena, pool, start, length, PM_CONSTANT_POOL_BUCKET_CONSTANT); } /** - * Free the memory associated with a constant pool. + * Return a raw pointer to the start of a constant. */ -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); - } - } +const uint8_t * +pm_constant_start(const pm_constant_t *constant) { + return constant->start; +} - xfree(pool->buckets); +/** + * Return the length of a constant. + */ +size_t pm_constant_length(const pm_constant_t *constant) { + return constant->length; } diff --git a/src/encoding.c b/src/encoding.c index d7e5616840..c9c2e13056 100644 --- a/src/encoding.c +++ b/src/encoding.c @@ -1,4 +1,9 @@ -#include "prism/encoding.h" +#include "prism/internal/encoding.h" + +#include "prism/compiler/unused.h" +#include "prism/internal/strncasecmp.h" + +#include typedef uint32_t pm_unicode_codepoint_t; @@ -4089,7 +4094,7 @@ pm_encoding_ascii_isupper_char(const uint8_t *b, ptrdiff_t n) { * 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) { +pm_encoding_single_char_width(PRISM_UNUSED const uint8_t *b, PRISM_UNUSED ptrdiff_t n) { return 1; } diff --git a/src/util/pm_integer.c b/src/integer.c similarity index 96% rename from src/util/pm_integer.c rename to src/integer.c index 4170ecc58d..1b69dbdceb 100644 --- a/src/util/pm_integer.c +++ b/src/integer.c @@ -1,4 +1,25 @@ -#include "prism/util/pm_integer.h" +#include "prism/internal/integer.h" + +#include "prism/internal/allocator.h" +#include "prism/internal/buffer.h" + +#include +#include +#include +#include +#include +#include + +/** + * Free the internal memory of an integer. This memory will only be allocated if + * the integer exceeds the size of a single uint32_t. + */ +static void +pm_integer_free(pm_integer_t *integer) { + if (integer->values) { + xfree(integer->values); + } +} /** * Pull out the length and values from the integer, regardless of the form in @@ -374,7 +395,7 @@ pm_integer_convert_base(pm_integer_t *destination, const pm_integer_t *source, u } } - xfree(bigints); + xfree_sized(bigints, bigints_length * sizeof(pm_integer_t)); bigints = next_bigints; bigints_length = next_length; } @@ -383,7 +404,7 @@ pm_integer_convert_base(pm_integer_t *destination, const pm_integer_t *source, u destination->negative = source->negative; pm_integer_normalize(destination); - xfree(bigints); + xfree_sized(bigints, bigints_length * sizeof(pm_integer_t)); pm_integer_free(&base); } @@ -422,7 +443,7 @@ pm_integer_parse_powof2(pm_integer_t *integer, uint32_t base, const uint8_t *dig 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; + const size_t length = (digits_length + batch - 1) / batch; uint32_t *values = (uint32_t *) xcalloc(length, sizeof(uint32_t)); uint32_t value = 0; @@ -439,7 +460,7 @@ pm_integer_parse_decimal(pm_integer_t *integer, const uint8_t *digits, size_t di // 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); + xfree_sized(values, length * sizeof(uint32_t)); } /** @@ -448,7 +469,8 @@ pm_integer_parse_decimal(pm_integer_t *integer, const uint8_t *digits, size_t di 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)); + const size_t digits_capa = sizeof(uint8_t) * (size_t) (end - start); + uint8_t *digits = xmalloc(digits_capa); size_t digits_length = 0; for (; start < end; start++) { @@ -463,7 +485,7 @@ pm_integer_parse_big(pm_integer_t *integer, uint32_t multiplier, const uint8_t * pm_integer_parse_powof2(integer, multiplier, digits, digits_length); } - xfree(digits); + xfree_sized(digits, digits_capa); } /** @@ -603,7 +625,7 @@ void pm_integers_reduce(pm_integer_t *numerator, pm_integer_t *denominator) { /** * Convert an integer to a decimal string. */ -PRISM_EXPORTED_FUNCTION void +void pm_integer_string(pm_buffer_t *buffer, const pm_integer_t *integer) { if (integer->negative) { pm_buffer_append_byte(buffer, '-'); @@ -635,7 +657,7 @@ pm_integer_string(pm_buffer_t *buffer, const pm_integer_t *integer) { } // Allocate a buffer that we'll copy the decimal digits into. - size_t digits_length = converted.length * 9; + const size_t digits_length = converted.length * 9; char *digits = xcalloc(digits_length, sizeof(char)); if (digits == NULL) return; @@ -654,17 +676,6 @@ pm_integer_string(pm_buffer_t *buffer, const pm_integer_t *integer) { // 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); + xfree_sized(digits, sizeof(char) * digits_length); 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/src/util/pm_newline_list.c b/src/line_offset_list.c similarity index 51% rename from src/util/pm_newline_list.c rename to src/line_offset_list.c index 89c294a6d7..ce217ebd3f 100644 --- a/src/util/pm_newline_list.c +++ b/src/line_offset_list.c @@ -1,51 +1,46 @@ -#include "prism/util/pm_newline_list.h" +#include "prism/compiler/align.h" +#include "prism/internal/line_offset_list.h" +#include "prism/internal/arena.h" + +#include +#include /** - * Initialize a new newline list with the given capacity. Returns true if the - * allocation of the offsets succeeds, otherwise returns false. + * Initialize a new line offset list with the given capacity. */ -bool -pm_newline_list_init(pm_newline_list_t *list, size_t capacity) { - list->offsets = (uint32_t *) xcalloc(capacity, sizeof(uint32_t)); - if (list->offsets == NULL) return false; +void +pm_line_offset_list_init(pm_arena_t *arena, pm_line_offset_list_t *list, size_t capacity) { + list->offsets = (uint32_t *) pm_arena_alloc(arena, capacity * sizeof(uint32_t), PRISM_ALIGNOF(uint32_t)); - // 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. + // The first line always has offset 0. + list->offsets[0] = 0; 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) { +pm_line_offset_list_clear(pm_line_offset_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. + * Append a new offset to the newline list (slow path: resize and store). */ -bool -pm_newline_list_append(pm_newline_list_t *list, uint32_t cursor) { - if (list->size == list->capacity) { - uint32_t *original_offsets = list->offsets; +void +pm_line_offset_list_append_slow(pm_arena_t *arena, pm_line_offset_list_t *list, uint32_t cursor) { + size_t new_capacity = (list->capacity * 3) / 2; + uint32_t *new_offsets = (uint32_t *) pm_arena_alloc(arena, new_capacity * sizeof(uint32_t), PRISM_ALIGNOF(uint32_t)); - list->capacity = (list->capacity * 3) / 2; - list->offsets = (uint32_t *) xcalloc(list->capacity, sizeof(uint32_t)); - if (list->offsets == NULL) return false; + memcpy(new_offsets, list->offsets, list->size * sizeof(uint32_t)); - memcpy(list->offsets, original_offsets, list->size * sizeof(uint32_t)); - xfree(original_offsets); - } + list->offsets = new_offsets; + list->capacity = new_capacity; assert(list->size == 0 || cursor > list->offsets[list->size - 1]); list->offsets[list->size++] = cursor; - - return true; } /** @@ -53,7 +48,7 @@ pm_newline_list_append(pm_newline_list_t *list, uint32_t cursor) { * line of the closest offset less than the given offset is returned. */ int32_t -pm_newline_list_line(const pm_newline_list_t *list, uint32_t cursor, int32_t start_line) { +pm_line_offset_list_line(const pm_line_offset_list_t *list, uint32_t cursor, int32_t start_line) { size_t left = 0; size_t right = list->size - 1; @@ -80,7 +75,7 @@ pm_newline_list_line(const pm_newline_list_t *list, uint32_t cursor, int32_t sta * are returned. */ pm_line_column_t -pm_newline_list_line_column(const pm_newline_list_t *list, uint32_t cursor, int32_t start_line) { +pm_line_offset_list_line_column(const pm_line_offset_list_t *list, uint32_t cursor, int32_t start_line) { size_t left = 0; size_t right = list->size - 1; @@ -103,11 +98,3 @@ pm_newline_list_line_column(const pm_newline_list_t *list, uint32_t cursor, int3 .column = cursor - 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/src/list.c b/src/list.c new file mode 100644 index 0000000000..8d4cd1be94 --- /dev/null +++ b/src/list.c @@ -0,0 +1,24 @@ +#include "prism/internal/list.h" + +/** + * Returns the size of the list. + */ +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++; +} diff --git a/src/util/pm_memchr.c b/src/memchr.c similarity index 85% rename from src/util/pm_memchr.c rename to src/memchr.c index 7ea20ace6d..6266d4ca7a 100644 --- a/src/util/pm_memchr.c +++ b/src/memchr.c @@ -1,6 +1,10 @@ -#include "prism/util/pm_memchr.h" +#include "prism/internal/memchr.h" -#define PRISM_MEMCHR_TRAILING_BYTE_MINIMUM 0x40 +#include +#include +#include + +#define TRAILING_BYTE_MINIMUM 0x40 /** * We need to roll our own memchr to handle cases where the encoding changes and @@ -9,7 +13,7 @@ */ 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) { + if (encoding_changed && encoding->multibyte && character >= TRAILING_BYTE_MINIMUM) { const uint8_t *source = (const uint8_t *) memory; size_t index = 0; @@ -31,5 +35,3 @@ pm_memchr(const void *memory, int character, size_t number, bool encoding_change return memchr(memory, character, number); } } - -#undef PRISM_MEMCHR_TRAILING_BYTE_MINIMUM diff --git a/src/options.c b/src/options.c index 09d2a65a6c..b589865a2a 100644 --- a/src/options.c +++ b/src/options.c @@ -1,18 +1,78 @@ -#include "prism/options.h" +#include "prism/internal/options.h" + +#include "prism/compiler/inline.h" + +#include "prism/internal/allocator.h" +#include "prism/internal/char.h" +#include "prism/internal/stringy.h" + +#include +#include + +/** + * Allocate a new options struct. If the options struct cannot be allocated, + * this function aborts the process. + */ +pm_options_t * +pm_options_new(void) { + pm_options_t *options = xcalloc(1, sizeof(pm_options_t)); + if (options == NULL) abort(); + return options; +} + +/** + * Free the internal memory associated with the options. + */ +void +pm_options_cleanup(pm_options_t *options) { + pm_string_cleanup(&options->filepath); + pm_string_cleanup(&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_cleanup(&scope->locals[local_index]); + } + + xfree_sized(scope->locals, scope->locals_count * sizeof(pm_string_t)); + } + + xfree_sized(options->scopes, options->scopes_count * sizeof(pm_options_scope_t)); +} + +/** + * Free both the held memory of the given options struct and the struct itself. + * + * @param options The options struct to free. + */ +void +pm_options_free(pm_options_t *options) { + pm_options_cleanup(options); + xfree_sized(options, sizeof(pm_options_t)); +} /** * Set the shebang callback option on the given options struct. */ -PRISM_EXPORTED_FUNCTION void +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; } +/** + * Get the filepath option on the given options struct. + */ +const pm_string_t * +pm_options_filepath(const pm_options_t *options) { + return &options->filepath; +} + /** * Set the filepath option on the given options struct. */ -PRISM_EXPORTED_FUNCTION void +void pm_options_filepath_set(pm_options_t *options, const char *filepath) { pm_string_constant_init(&options->filepath, filepath, strlen(filepath)); } @@ -20,7 +80,7 @@ pm_options_filepath_set(pm_options_t *options, const char *filepath) { /** * Set the encoding option on the given options struct. */ -PRISM_EXPORTED_FUNCTION void +void pm_options_encoding_set(pm_options_t *options, const char *encoding) { pm_string_constant_init(&options->encoding, encoding, strlen(encoding)); } @@ -28,7 +88,7 @@ pm_options_encoding_set(pm_options_t *options, const char *encoding) { /** * Set the encoding_locked option on the given options struct. */ -PRISM_EXPORTED_FUNCTION void +void pm_options_encoding_locked_set(pm_options_t *options, bool encoding_locked) { options->encoding_locked = encoding_locked; } @@ -36,7 +96,7 @@ pm_options_encoding_locked_set(pm_options_t *options, bool encoding_locked) { /** * Set the line option on the given options struct. */ -PRISM_EXPORTED_FUNCTION void +void pm_options_line_set(pm_options_t *options, int32_t line) { options->line = line; } @@ -44,7 +104,7 @@ pm_options_line_set(pm_options_t *options, int32_t line) { /** * Set the frozen string literal option on the given options struct. */ -PRISM_EXPORTED_FUNCTION void +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; } @@ -52,7 +112,7 @@ pm_options_frozen_string_literal_set(pm_options_t *options, bool frozen_string_l /** * Sets the command line option on the given options struct. */ -PRISM_EXPORTED_FUNCTION void +void pm_options_command_line_set(pm_options_t *options, uint8_t command_line) { options->command_line = command_line; } @@ -60,7 +120,7 @@ pm_options_command_line_set(pm_options_t *options, uint8_t command_line) { /** * Checks if the given slice represents a number. */ -static inline bool +static PRISM_INLINE bool is_number(const char *string, size_t length) { return pm_strspn_decimal_digit((const uint8_t *) string, (ptrdiff_t) length) == length; } @@ -70,7 +130,7 @@ is_number(const char *string, size_t length) { * string. If the string contains an invalid option, this returns false. * Otherwise, it returns true. */ -PRISM_EXPORTED_FUNCTION bool +bool pm_options_version_set(pm_options_t *options, const char *version, size_t length) { if (version == NULL) { options->version = PM_OPTIONS_VERSION_LATEST; @@ -123,8 +183,8 @@ pm_options_version_set(pm_options_t *options, const char *version, size_t length } } - if (length >= 6) { - if (strncmp(version, "latest", 7) == 0) { // 7 to compare the \0 as well + if (length == 6) { + if (strncmp(version, "latest", 6) == 0) { options->version = PM_OPTIONS_VERSION_LATEST; return true; } @@ -133,10 +193,28 @@ pm_options_version_set(pm_options_t *options, const char *version, size_t length return false; } +/** + * Set the version option on the given options struct to the lowest version of + * Ruby that prism supports. + */ +void +pm_options_version_set_lowest(pm_options_t *options) { + options->version = PM_OPTIONS_VERSION_CRUBY_3_3; +} + +/** + * Set the version option on the given options struct to the highest version of + * Ruby that prism supports. + */ +void +pm_options_version_set_highest(pm_options_t *options) { + options->version = PM_OPTIONS_VERSION_LATEST; +} + /** * Set the main script option on the given options struct. */ -PRISM_EXPORTED_FUNCTION void +void pm_options_main_script_set(pm_options_t *options, bool main_script) { options->main_script = main_script; } @@ -144,15 +222,23 @@ pm_options_main_script_set(pm_options_t *options, bool main_script) { /** * Set the partial script option on the given options struct. */ -PRISM_EXPORTED_FUNCTION void +void pm_options_partial_script_set(pm_options_t *options, bool partial_script) { options->partial_script = partial_script; } +/** + * Get the freeze option on the given options struct. + */ +bool +pm_options_freeze(const pm_options_t *options) { + return options->freeze; +} + /** * Set the freeze option on the given options struct. */ -PRISM_EXPORTED_FUNCTION void +void pm_options_freeze_set(pm_options_t *options, bool freeze) { options->freeze = freeze; } @@ -168,7 +254,7 @@ pm_options_freeze_set(pm_options_t *options, bool freeze) { /** * Allocate and zero out the scopes array on the given options struct. */ -PRISM_EXPORTED_FUNCTION bool +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)); @@ -176,10 +262,20 @@ 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. + * Return a constant pointer to the scope at the given index within the given + * options. + */ +const pm_options_scope_t * +pm_options_scope(const pm_options_t *options, size_t index) { + return &options->scopes[index]; +} + +/** + * Return a mutable 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) { +pm_options_scope_t * +pm_options_scope_mut(pm_options_t *options, size_t index) { return &options->scopes[index]; } @@ -187,49 +283,38 @@ 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. */ -PRISM_EXPORTED_FUNCTION bool +void 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; + if (scope->locals == NULL) abort(); } /** - * Return a pointer to the local at the given index within the given scope. + * Return a constant 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) { +const pm_string_t * +pm_options_scope_local(const pm_options_scope_t *scope, size_t index) { return &scope->locals[index]; } /** - * Set the forwarding option on the given scope struct. + * Return a mutable pointer to the local at the given index within the given + * scope. */ -PRISM_EXPORTED_FUNCTION void -pm_options_scope_forwarding_set(pm_options_scope_t *scope, uint8_t forwarding) { - scope->forwarding = forwarding; +pm_string_t * +pm_options_scope_local_mut(pm_options_scope_t *scope, size_t index) { + return &scope->locals[index]; } /** - * Free the internal memory associated with the options. + * Set the forwarding option on the given scope struct. */ -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); +void +pm_options_scope_forwarding_set(pm_options_scope_t *scope, uint8_t forwarding) { + scope->forwarding = forwarding; } /** @@ -314,10 +399,7 @@ pm_options_read(pm_options_t *options, const char *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; - } + pm_options_scope_init(scope, locals_count); uint8_t forwarding = (uint8_t) *data++; pm_options_scope_forwarding_set(&options->scopes[scope_index], forwarding); diff --git a/src/pack.c b/src/pack.c deleted file mode 100644 index 1388ca8a3b..0000000000 --- a/src/pack.c +++ /dev/null @@ -1,509 +0,0 @@ -#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/src/parser.c b/src/parser.c new file mode 100644 index 0000000000..415cd31984 --- /dev/null +++ b/src/parser.c @@ -0,0 +1,302 @@ +#include "prism/internal/parser.h" + +#include "prism/internal/allocator.h" +#include "prism/internal/comments.h" +#include "prism/internal/diagnostic.h" +#include "prism/internal/encoding.h" +#include "prism/internal/magic_comments.h" + +#include + +/** + * Register a callback that will be called whenever prism changes the encoding + * it is using to parse based on the magic comment. + */ +void +pm_parser_encoding_changed_callback_set(pm_parser_t *parser, pm_encoding_changed_callback_t callback) { + parser->encoding_changed_callback = callback; +} + +/** + * Register a callback that will be called whenever a token is lexed. + */ +void +pm_parser_lex_callback_set(pm_parser_t *parser, pm_lex_callback_t callback, void *data) { + parser->lex_callback.callback = callback; + parser->lex_callback.data = data; +} + +/** + * Returns the opaque data that is passed to the lex callback when it is called. + */ +void * +pm_parser_lex_callback_data(const pm_parser_t *parser) { + return parser->lex_callback.data; +} + +/** + * Returns the raw pointer to the start of the source that is being parsed. + */ +const uint8_t * +pm_parser_start(const pm_parser_t *parser) { + return parser->start; +} + +/** + * Returns the raw pointer to the end of the source that is being parsed. + */ +const uint8_t * +pm_parser_end(const pm_parser_t *parser) { + return parser->end; +} + +/** + * Returns the line that the parser was considered to have started on. + * + * @param parser the parser whose start line we want to get + * @return the line that the parser was considered to have started on + */ +int32_t +pm_parser_start_line(const pm_parser_t *parser) { + return parser->start_line; +} + +/** + * Returns the name of the encoding that is being used to parse the source. + */ +const char * +pm_parser_encoding_name(const pm_parser_t *parser) { + return parser->encoding->name; +} + +/** + * Returns the width of the character at the given pointer in the encoding that + * is being used to parse the source. + */ +size_t +pm_parser_encoding_char_width(const pm_parser_t *parser, const uint8_t *start, ptrdiff_t remaining) { + return parser->encoding->char_width(start, remaining); +} + +/** + * Returns whether or not the parser is using the US-ASCII encoding. + */ +bool +pm_parser_encoding_us_ascii(const pm_parser_t *parser) { + return parser->encoding == PM_ENCODING_US_ASCII_ENTRY; +} + +/** + * Returns the filepath that is being used to parse the source. + */ +const pm_string_t * +pm_parser_filepath(const pm_parser_t *parser) { + return &parser->filepath; +} + +/** + * Find a constant in the parser's constant pool. Returns the id of the + * constant, or 0 if the constant is not found. + */ +pm_constant_id_t +pm_parser_constant_find(const pm_parser_t *parser, const uint8_t *start, size_t length) { + return pm_constant_pool_find(&parser->constant_pool, start, length); +} + +/** + * Returns the frozen string literal value of the parser. + */ +int8_t +pm_parser_frozen_string_literal(const pm_parser_t *parser) { + return parser->frozen_string_literal; +} + +/** + * Returns the line offsets that are associated with the given parser. + * + * @param parser the parser whose line offsets we want to get + * @return the line offsets that are associated with the given parser + */ +const pm_line_offset_list_t * +pm_parser_line_offsets(const pm_parser_t *parser) { + return &parser->line_offsets; +} + +/** + * Returns the location of the __DATA__ section that is associated with the + * given parser, if it exists. + */ +const pm_location_t * +pm_parser_data_loc(const pm_parser_t *parser) { + return &parser->data_loc; +} + +/** + * Returns whether the given parser is continuable, meaning that it could become + * valid if more input were appended, as opposed to being definitively invalid. + */ +bool +pm_parser_continuable(const pm_parser_t *parser) { + return parser->continuable; +} + +/** + * Returns the lex state of the parser. Note that this is an internal detail, + * and we are purposefully not returning an instance of the internal enum that + * we use to track this. This is only exposed because we need it for some very + * niche use cases. Most consumers should avoid this function. + */ +int +pm_parser_lex_state(const pm_parser_t *parser) { + return (int) parser->lex_state; +} + +/** + * Returns the location associated with the given comment. + */ +pm_location_t +pm_comment_location(const pm_comment_t *comment) { + return comment->location; +} + +/** + * Returns the type associated with the given comment. + */ +pm_comment_type_t +pm_comment_type(const pm_comment_t *comment) { + return comment->type; +} + +/** + * Returns the number of comments associated with the given parser. + */ +size_t +pm_parser_comments_size(const pm_parser_t *parser) { + return parser->comment_list.size; +} + +/** + * Iterates over the comments associated with the given parser and calls the + * given callback for each comment. + */ +void +pm_parser_comments_each(const pm_parser_t *parser, pm_comment_callback_t callback, void *data) { + const pm_list_node_t *current = parser->comment_list.head; + while (current != NULL) { + const pm_comment_t *comment = (const pm_comment_t *) current; + callback(comment, data); + current = current->next; + } +} + +/** + * Returns the location associated with the given magic comment key. + */ +pm_location_t +pm_magic_comment_key(const pm_magic_comment_t *magic_comment) { + return magic_comment->key; +} + +/** + * Returns the location associated with the given magic comment value. + */ +pm_location_t +pm_magic_comment_value(const pm_magic_comment_t *magic_comment) { + return magic_comment->value; +} + +/** + * Returns the number of magic comments associated with the given parser. + */ +size_t +pm_parser_magic_comments_size(const pm_parser_t *parser) { + return parser->magic_comment_list.size; +} + +/** + * Iterates over the magic comments associated with the given parser and calls + * the given callback for each magic comment. + */ +void +pm_parser_magic_comments_each(const pm_parser_t *parser, pm_magic_comment_callback_t callback, void *data) { + const pm_list_node_t *current = parser->magic_comment_list.head; + while (current != NULL) { + const pm_magic_comment_t *magic_comment = (const pm_magic_comment_t *) current; + callback(magic_comment, data); + current = current->next; + } +} + +/** + * Returns the number of errors associated with the given parser. + */ +size_t +pm_parser_errors_size(const pm_parser_t *parser) { + return parser->error_list.size; +} + +/** + * Returns the number of warnings associated with the given parser. + */ +size_t +pm_parser_warnings_size(const pm_parser_t *parser) { + return parser->warning_list.size; +} + +static inline void +pm_parser_diagnostics_each(const pm_list_t *list, pm_diagnostic_callback_t callback, void *data) { + const pm_list_node_t *current = list->head; + while (current != NULL) { + const pm_diagnostic_t *diagnostic = (const pm_diagnostic_t *) current; + callback(diagnostic, data); + current = current->next; + } +} + +/** + * Iterates over the errors associated with the given parser and calls the + * given callback for each error. + */ +void +pm_parser_errors_each(const pm_parser_t *parser, pm_diagnostic_callback_t callback, void *data) { + pm_parser_diagnostics_each(&parser->error_list, callback, data); +} + +/** + * Iterates over the warnings associated with the given parser and calls the + * given callback for each warning. + */ +void +pm_parser_warnings_each(const pm_parser_t *parser, pm_diagnostic_callback_t callback, void *data) { + pm_parser_diagnostics_each(&parser->warning_list, callback, data); +} + +/** + * Returns the number of constants in the constant pool associated with the + * given parser. + */ +size_t +pm_parser_constants_size(const pm_parser_t *parser) { + return parser->constant_pool.size; +} + +/** + * Iterates over the constants in the constant pool associated with the given + * parser and calls the given callback for each constant. + */ +void +pm_parser_constants_each(const pm_parser_t *parser, pm_constant_callback_t callback, void *data) { + for (uint32_t index = 0; index < parser->constant_pool.size; index++) { + const pm_constant_t *constant = &parser->constant_pool.constants[index]; + callback(constant, data); + } +} + +/** + * Returns a pointer to the constant at the given id in the constant pool + * associated with the given parser. + */ +const pm_constant_t * +pm_parser_constant(const pm_parser_t *parser, pm_constant_id_t constant_id) { + return pm_constant_pool_id_to_constant(&parser->constant_pool, constant_id); +} diff --git a/src/prism.c b/src/prism.c index 8f3617adce..1fa4a46ed8 100644 --- a/src/prism.c +++ b/src/prism.c @@ -1,4 +1,90 @@ -#include "prism.h" +#include "prism/compiler/accel.h" +#include "prism/compiler/fallthrough.h" +#include "prism/compiler/unused.h" + +#include "prism/internal/allocator.h" +#include "prism/internal/arena.h" +#include "prism/internal/bit.h" +#include "prism/internal/buffer.h" +#include "prism/internal/char.h" +#include "prism/internal/comments.h" +#include "prism/internal/constant_pool.h" +#include "prism/internal/diagnostic.h" +#include "prism/internal/encoding.h" +#include "prism/internal/integer.h" +#include "prism/internal/isinf.h" +#include "prism/internal/line_offset_list.h" +#include "prism/internal/list.h" +#include "prism/internal/magic_comments.h" +#include "prism/internal/memchr.h" +#include "prism/internal/node.h" +#include "prism/internal/options.h" +#include "prism/internal/parser.h" +#include "prism/internal/regexp.h" +#include "prism/internal/serialize.h" +#include "prism/internal/source.h" +#include "prism/internal/static_literals.h" +#include "prism/internal/stringy.h" +#include "prism/internal/strncasecmp.h" +#include "prism/internal/strpbrk.h" +#include "prism/internal/tokens.h" + +#include "prism/excludes.h" +#include "prism/serialize.h" +#include "prism/stream.h" +#include "prism/version.h" + +#include +#include +#include +#include +#include +#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 + +/** + * 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 + +/** + * 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 /** * The prism version and the serialization format. @@ -71,7 +157,7 @@ pm_version(void) { * Returns the incrementor character that should be used to increment the * nesting count if one is possible. */ -static inline uint8_t +static PRISM_INLINE uint8_t lex_mode_incrementor(const uint8_t start) { switch (start) { case '(': @@ -88,7 +174,7 @@ lex_mode_incrementor(const uint8_t start) { * Returns the matching character that should be used to terminate a list * beginning with the given character. */ -static inline uint8_t +static PRISM_INLINE uint8_t lex_mode_terminator(const uint8_t start) { switch (start) { case '(': @@ -130,7 +216,7 @@ lex_mode_push(pm_parser_t *parser, pm_lex_mode_t lex_mode) { /** * Push on a new list lex mode. */ -static inline bool +static PRISM_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); @@ -148,7 +234,8 @@ lex_mode_push_list(pm_parser_t *parser, bool interpolation, uint8_t delimiter) { // 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)); + memset(breakpoints, 0, PM_STRPBRK_CACHE_SIZE); + memcpy(breakpoints, "\\ \t\f\r\v\n", sizeof("\\ \t\f\r\v\n") - 1); size_t index = 7; // Now we'll add the terminator to the list of breakpoints. If the @@ -177,7 +264,7 @@ lex_mode_push_list(pm_parser_t *parser, bool interpolation, uint8_t delimiter) { * 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 +static PRISM_INLINE bool lex_mode_push_list_eof(pm_parser_t *parser) { return lex_mode_push_list(parser, false, '\0'); } @@ -185,7 +272,7 @@ lex_mode_push_list_eof(pm_parser_t *parser) { /** * Push on a new regexp lex mode. */ -static inline bool +static PRISM_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, @@ -200,7 +287,8 @@ lex_mode_push_regexp(pm_parser_t *parser, uint8_t incrementor, uint8_t terminato // 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)); + memset(breakpoints, 0, PM_STRPBRK_CACHE_SIZE); + memcpy(breakpoints, "\r\n\\#", sizeof("\r\n\\#") - 1); size_t index = 4; // First we'll add the terminator. @@ -220,7 +308,7 @@ lex_mode_push_regexp(pm_parser_t *parser, uint8_t incrementor, uint8_t terminato /** * Push on a new string lex mode. */ -static inline bool +static PRISM_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, @@ -236,7 +324,8 @@ lex_mode_push_string(pm_parser_t *parser, bool interpolation, bool label_allowed // 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)); + memset(breakpoints, 0, PM_STRPBRK_CACHE_SIZE); + memcpy(breakpoints, "\r\n\\", sizeof("\r\n\\") - 1); size_t index = 3; // Now add in the terminator. If the terminator is not already a NULL byte, @@ -266,7 +355,7 @@ lex_mode_push_string(pm_parser_t *parser, bool interpolation, bool label_allowed * 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 +static PRISM_INLINE bool lex_mode_push_string_eof(pm_parser_t *parser) { return lex_mode_push_string(parser, false, false, '\0', '\0'); } @@ -286,7 +375,7 @@ lex_mode_pop(pm_parser_t *parser) { } else { parser->lex_modes.index--; pm_lex_mode_t *prev = parser->lex_modes.current->prev; - xfree(parser->lex_modes.current); + xfree_sized(parser->lex_modes.current, sizeof(pm_lex_mode_t)); parser->lex_modes.current = prev; } } @@ -294,7 +383,7 @@ lex_mode_pop(pm_parser_t *parser) { /** * This is the equivalent of IS_lex_state is CRuby. */ -static inline bool +static PRISM_INLINE bool lex_state_p(const pm_parser_t *parser, pm_lex_state_t state) { return parser->lex_state & state; } @@ -305,7 +394,7 @@ typedef enum { PM_IGNORED_NEWLINE_PATTERN } pm_ignored_newline_type_t; -static inline pm_ignored_newline_type_t +static PRISM_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); @@ -318,17 +407,17 @@ lex_state_ignored_p(pm_parser_t *parser) { } } -static inline bool +static PRISM_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 +static PRISM_INLINE bool lex_state_arg_p(pm_parser_t *parser) { return lex_state_p(parser, PM_LEX_STATE_ARG_ANY); } -static inline bool +static PRISM_INLINE bool lex_state_spcarg_p(pm_parser_t *parser, bool space_seen) { if (parser->current.end >= parser->end) { return false; @@ -336,7 +425,7 @@ lex_state_spcarg_p(pm_parser_t *parser, bool space_seen) { return lex_state_arg_p(parser) && space_seen && !pm_char_is_whitespace(*parser->current.end); } -static inline bool +static PRISM_INLINE bool lex_state_end_p(pm_parser_t *parser) { return lex_state_p(parser, PM_LEX_STATE_END_ANY); } @@ -344,7 +433,7 @@ lex_state_end_p(pm_parser_t *parser) { /** * This is the equivalent of IS_AFTER_OPERATOR in CRuby. */ -static inline bool +static PRISM_INLINE bool lex_state_operator_p(pm_parser_t *parser) { return lex_state_p(parser, PM_LEX_STATE_FNAME | PM_LEX_STATE_DOT); } @@ -353,7 +442,7 @@ lex_state_operator_p(pm_parser_t *parser) { * Set the state of the lexer. This is defined as a function to be able to put a * breakpoint in it. */ -static inline void +static PRISM_INLINE void lex_state_set(pm_parser_t *parser, pm_lex_state_t state) { parser->lex_state = state; } @@ -367,7 +456,7 @@ lex_state_set(pm_parser_t *parser, pm_lex_state_t state) { #endif #if PM_DEBUG_LOGGING -PRISM_ATTRIBUTE_UNUSED static void +PRISM_UNUSED static void debug_state(pm_parser_t *parser) { fprintf(stderr, "STATE: "); bool first = true; @@ -448,16 +537,16 @@ debug_lex_state_set(pm_parser_t *parser, pm_lex_state_t state, char const * call /** * Append an error to the list of errors on the parser. */ -static inline void +static PRISM_INLINE void pm_parser_err(pm_parser_t *parser, uint32_t start, uint32_t length, pm_diagnostic_id_t diag_id) { - pm_diagnostic_list_append(&parser->error_list, start, length, diag_id); + pm_diagnostic_list_append(&parser->metadata_arena, &parser->error_list, start, length, diag_id); } /** * Append an error to the list of errors on the parser using the location of the * given token. */ -static inline void +static PRISM_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, PM_TOKEN_START(parser, token), PM_TOKEN_LENGTH(token), diag_id); } @@ -466,7 +555,7 @@ pm_parser_err_token(pm_parser_t *parser, const pm_token_t *token, pm_diagnostic_ * Append an error to the list of errors on the parser using the location of the * current token. */ -static inline void +static PRISM_INLINE void pm_parser_err_current(pm_parser_t *parser, pm_diagnostic_id_t diag_id) { pm_parser_err_token(parser, &parser->current, diag_id); } @@ -475,7 +564,7 @@ pm_parser_err_current(pm_parser_t *parser, pm_diagnostic_id_t diag_id) { * Append an error to the list of errors on the parser using the location of the * previous token. */ -static inline void +static PRISM_INLINE void pm_parser_err_previous(pm_parser_t *parser, pm_diagnostic_id_t diag_id) { pm_parser_err_token(parser, &parser->previous, diag_id); } @@ -484,7 +573,7 @@ pm_parser_err_previous(pm_parser_t *parser, pm_diagnostic_id_t diag_id) { * Append an error to the list of errors on the parser using the location of the * given node. */ -static inline void +static PRISM_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, PM_NODE_START(node), PM_NODE_LENGTH(node), diag_id); } @@ -493,7 +582,7 @@ pm_parser_err_node(pm_parser_t *parser, const pm_node_t *node, pm_diagnostic_id_ * Append an error to the list of errors on the parser using a format string. */ #define PM_PARSER_ERR_FORMAT(parser_, start_, length_, diag_id_, ...) \ - pm_diagnostic_list_append_format(&(parser_)->error_list, start_, length_, diag_id_, __VA_ARGS__) + pm_diagnostic_list_append_format(&(parser_)->metadata_arena, &(parser_)->error_list, start_, length_, diag_id_, __VA_ARGS__) /** * Append an error to the list of errors on the parser using the location of the @@ -526,16 +615,16 @@ pm_parser_err_node(pm_parser_t *parser, const pm_node_t *node, pm_diagnostic_id_ /** * Append a warning to the list of warnings on the parser. */ -static inline void +static PRISM_INLINE void pm_parser_warn(pm_parser_t *parser, uint32_t start, uint32_t length, pm_diagnostic_id_t diag_id) { - pm_diagnostic_list_append(&parser->warning_list, start, length, diag_id); + pm_diagnostic_list_append(&parser->metadata_arena, &parser->warning_list, start, length, diag_id); } /** * Append a warning to the list of warnings on the parser using the location of * the given token. */ -static inline void +static PRISM_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, PM_TOKEN_START(parser, token), PM_TOKEN_LENGTH(token), diag_id); } @@ -544,7 +633,7 @@ pm_parser_warn_token(pm_parser_t *parser, const pm_token_t *token, pm_diagnostic * Append a warning to the list of warnings on the parser using the location of * the given node. */ -static inline void +static PRISM_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, PM_NODE_START(node), PM_NODE_LENGTH(node), diag_id); } @@ -554,7 +643,7 @@ pm_parser_warn_node(pm_parser_t *parser, const pm_node_t *node, pm_diagnostic_id * and the given location. */ #define PM_PARSER_WARN_FORMAT(parser_, start_, length_, diag_id_, ...) \ - pm_diagnostic_list_append_format(&(parser_)->warning_list, start_, length_, diag_id_, __VA_ARGS__) + pm_diagnostic_list_append_format(&(parser_)->metadata_arena, &(parser_)->warning_list, start_, length_, diag_id_, __VA_ARGS__) /** * Append a warning to the list of warnings on the parser using the location of @@ -747,7 +836,7 @@ pm_parser_scope_forwarding_keywords_check(pm_parser_t *parser, const pm_token_t /** * Get the current state of constant shareability. */ -static inline pm_shareable_constant_value_t +static PRISM_INLINE pm_shareable_constant_value_t pm_parser_scope_shareable_constant_get(pm_parser_t *parser) { return parser->current_scope->shareable_constant; } @@ -772,12 +861,12 @@ pm_parser_scope_shareable_constant_set(pm_parser_t *parser, pm_shareable_constan /** * The point at which the set of locals switches from being a list to a hash. */ -#define PM_LOCALS_HASH_THRESHOLD 9 +#define PM_LOCALS_HASH_THRESHOLD 5 static void pm_locals_free(pm_locals_t *locals) { if (locals->capacity > 0) { - xfree(locals->locals); + xfree_sized(locals->locals, locals->capacity * sizeof(pm_local_t)); } } @@ -854,6 +943,8 @@ pm_locals_write(pm_locals_t *locals, pm_constant_id_t name, uint32_t start, uint pm_locals_resize(locals); } + locals->bloom |= (1u << (name & 31)); + if (locals->capacity < PM_LOCALS_HASH_THRESHOLD) { for (uint32_t index = 0; index < locals->capacity; index++) { pm_local_t *local = &locals->locals[index]; @@ -906,6 +997,8 @@ pm_locals_write(pm_locals_t *locals, pm_constant_id_t name, uint32_t start, uint */ static uint32_t pm_locals_find(pm_locals_t *locals, pm_constant_id_t name) { + if (!(locals->bloom & (1u << (name & 31)))) return UINT32_MAX; + if (locals->capacity < PM_LOCALS_HASH_THRESHOLD) { for (uint32_t index = 0; index < locals->size; index++) { pm_local_t *local = &locals->locals[index]; @@ -982,8 +1075,8 @@ pm_locals_reads(pm_locals_t *locals, pm_constant_id_t name) { * 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); +pm_locals_order(pm_parser_t *parser, pm_locals_t *locals, pm_constant_id_list_t *list, bool toplevel) { + pm_constant_id_list_init_capacity(parser->arena, 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 @@ -1000,7 +1093,7 @@ pm_locals_order(PRISM_ATTRIBUTE_UNUSED pm_parser_t *parser, pm_locals_t *locals, 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))) { + if (warn_unused && local->reads == 0 && ((parser->start_line >= 0) || (pm_line_offset_list_line(&parser->line_offsets, 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 != '_') { @@ -1025,31 +1118,43 @@ pm_locals_order(PRISM_ATTRIBUTE_UNUSED pm_parser_t *parser, pm_locals_t *locals, /** * Retrieve the constant pool id for the given location. */ -static inline pm_constant_id_t +static PRISM_INLINE pm_constant_id_t pm_parser_constant_id_raw(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)); + /* Fast path: if this is the same token as the last lookup (same pointer + * range), return the cached result. */ + if (start == parser->constant_cache.start && end == parser->constant_cache.end) { + return parser->constant_cache.id; + } + + pm_constant_id_t id = pm_constant_pool_insert_shared(&parser->metadata_arena, &parser->constant_pool, start, (size_t) (end - start)); + + parser->constant_cache.start = start; + parser->constant_cache.end = end; + parser->constant_cache.id = id; + + return id; } /** * Retrieve the constant pool id for the given string. */ -static inline pm_constant_id_t +static PRISM_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); + return pm_constant_pool_insert_owned(&parser->metadata_arena, &parser->constant_pool, start, length); } /** * Retrieve the constant pool id for the given static literal C string. */ -static inline pm_constant_id_t +static PRISM_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); + return pm_constant_pool_insert_constant(&parser->metadata_arena, &parser->constant_pool, (const uint8_t *) start, length); } /** * Retrieve the constant pool id for the given token. */ -static inline pm_constant_id_t +static PRISM_INLINE pm_constant_id_t pm_parser_constant_id_token(pm_parser_t *parser, const pm_token_t *token) { return pm_parser_constant_id_raw(parser, token->start, token->end); } @@ -1268,7 +1373,7 @@ pm_check_value_expression(pm_parser_t *parser, pm_node_t *node) { return NULL; } -static inline void +static PRISM_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) { @@ -1509,7 +1614,7 @@ pm_conditional_predicate_warn_write_literal_p(const pm_node_t *node) { * 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 +static PRISM_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); @@ -1675,7 +1780,7 @@ typedef struct { /** * Retrieve the end location of a `pm_arguments_t` object. */ -static inline const pm_location_t * +static PRISM_INLINE const pm_location_t * pm_arguments_end(pm_arguments_t *arguments) { if (arguments->block != NULL) { uint32_t end = PM_NODE_END(arguments->block); @@ -1738,7 +1843,7 @@ pm_arguments_validate_block(pm_parser_t *parser, pm_arguments_t *arguments, pm_b * 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 +static PRISM_INLINE size_t char_is_identifier_start(const pm_parser_t *parser, const uint8_t *b, ptrdiff_t n) { if (n <= 0) return 0; @@ -1765,7 +1870,7 @@ char_is_identifier_start(const pm_parser_t *parser, const uint8_t *b, ptrdiff_t * Similar to char_is_identifier but this function assumes that the encoding * has not been changed. */ -static inline size_t +static PRISM_INLINE size_t char_is_identifier_utf8(const uint8_t *b, ptrdiff_t n) { if (n <= 0) { return 0; @@ -1776,12 +1881,190 @@ char_is_identifier_utf8(const uint8_t *b, ptrdiff_t n) { } } +/** + * Scan forward through ASCII identifier characters (a-z, A-Z, 0-9, _) using + * wide operations. Returns the number of leading ASCII identifier bytes. + * Callers must handle any remaining bytes (short tail or non-ASCII/UTF-8) + * with a byte-at-a-time loop. + * + * Up to three optimized implementations are selected at compile time, with a + * no-op fallback for unsupported platforms: + * 1. NEON — processes 16 bytes per iteration on aarch64. + * 2. SSSE3 — processes 16 bytes per iteration on x86-64. + * 3. SWAR — little-endian fallback, processes 8 bytes per iteration. + */ + +#if defined(PRISM_HAS_NEON) +#include + +static PRISM_INLINE size_t +scan_identifier_ascii(const uint8_t *start, const uint8_t *end) { + const uint8_t *cursor = start; + + // Nibble-based lookup tables for classifying [a-zA-Z0-9_]. + // Each high nibble is assigned a unique bit; the low nibble table + // contains the OR of bits for all high nibbles that have an + // identifier character at that low nibble position. A byte is an + // identifier character iff (low_lut[lo] & high_lut[hi]) != 0. + static const uint8_t low_lut_data[16] = { + 0x15, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, + 0x1F, 0x1F, 0x1E, 0x0A, 0x0A, 0x0A, 0x0A, 0x0E + }; + static const uint8_t high_lut_data[16] = { + 0x00, 0x00, 0x00, 0x01, 0x02, 0x04, 0x08, 0x10, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }; + const uint8x16_t low_lut = vld1q_u8(low_lut_data); + const uint8x16_t high_lut = vld1q_u8(high_lut_data); + const uint8x16_t mask_0f = vdupq_n_u8(0x0F); + + while (cursor + 16 <= end) { + uint8x16_t v = vld1q_u8(cursor); + + uint8x16_t lo_class = vqtbl1q_u8(low_lut, vandq_u8(v, mask_0f)); + uint8x16_t hi_class = vqtbl1q_u8(high_lut, vshrq_n_u8(v, 4)); + uint8x16_t ident = vandq_u8(lo_class, hi_class); + + // Fast check: if the per-byte minimum is nonzero, every byte matched. + if (vminvq_u8(ident) != 0) { + cursor += 16; + continue; + } + + // Find the first non-identifier byte (zero in ident). + uint8x16_t is_zero = vceqq_u8(ident, vdupq_n_u8(0)); + uint64_t lo = vgetq_lane_u64(vreinterpretq_u64_u8(is_zero), 0); + + if (lo != 0) { + cursor += pm_ctzll(lo) / 8; + } else { + uint64_t hi = vgetq_lane_u64(vreinterpretq_u64_u8(is_zero), 1); + cursor += 8 + pm_ctzll(hi) / 8; + } + + return (size_t) (cursor - start); + } + + return (size_t) (cursor - start); +} + +#elif defined(PRISM_HAS_SSSE3) +#include + +static PRISM_INLINE size_t +scan_identifier_ascii(const uint8_t *start, const uint8_t *end) { + const uint8_t *cursor = start; + + while (cursor + 16 <= end) { + __m128i v = _mm_loadu_si128((const __m128i *) cursor); + __m128i zero = _mm_setzero_si128(); + + // Unsigned range check via saturating subtraction: + // byte >= lo ⟺ saturate(lo - byte) == 0 + // byte <= hi ⟺ saturate(byte - hi) == 0 + + // Fold case: OR with 0x20 maps A-Z to a-z. + __m128i lowered = _mm_or_si128(v, _mm_set1_epi8(0x20)); + __m128i letter = _mm_and_si128( + _mm_cmpeq_epi8(_mm_subs_epu8(_mm_set1_epi8(0x61), lowered), zero), + _mm_cmpeq_epi8(_mm_subs_epu8(lowered, _mm_set1_epi8(0x7A)), zero)); + + __m128i digit = _mm_and_si128( + _mm_cmpeq_epi8(_mm_subs_epu8(_mm_set1_epi8(0x30), v), zero), + _mm_cmpeq_epi8(_mm_subs_epu8(v, _mm_set1_epi8(0x39)), zero)); + + __m128i underscore = _mm_cmpeq_epi8(v, _mm_set1_epi8(0x5F)); + + __m128i ident = _mm_or_si128(_mm_or_si128(letter, digit), underscore); + int mask = _mm_movemask_epi8(ident); + + if (mask == 0xFFFF) { + cursor += 16; + continue; + } + + cursor += pm_ctzll((uint64_t) (~mask & 0xFFFF)); + return (size_t) (cursor - start); + } + + return (size_t) (cursor - start); +} + +// The SWAR path uses pm_ctzll to find the first non-matching byte within a +// word, which only yields the correct byte index on little-endian targets. +// We gate on a positive little-endian check so that unknown-endianness +// platforms safely fall through to the no-op fallback. +#elif defined(PRISM_HAS_SWAR) + +/** + * Portable SWAR fallback — processes 8 bytes per iteration. + * + * The byte-wise range checks avoid cross-byte borrows by pre-setting the high + * bit of each byte before subtraction: (byte | 0x80) - lo has a minimum value + * of 0x80 - 0x7F = 1, so underflow (and thus a borrow into the next byte) is + * impossible. The result has bit 7 set if and only if byte >= lo. The same + * reasoning applies to the upper-bound direction. + */ +static PRISM_INLINE size_t +scan_identifier_ascii(const uint8_t *start, const uint8_t *end) { + static const uint64_t ones = 0x0101010101010101ULL; + static const uint64_t highs = 0x8080808080808080ULL; + const uint8_t *cursor = start; + + while (cursor + 8 <= end) { + uint64_t word; + memcpy(&word, cursor, 8); + + // Bail on any non-ASCII byte. + if (word & highs) break; + + uint64_t digit = ((word | highs) - ones * 0x30) & ((ones * 0x39 | highs) - word) & highs; + + // Fold upper- and lowercase together by forcing bit 5 (OR 0x20), + // then check the lowercase range once. A-Z maps to a-z; the + // only non-letter byte that could alias into [0x61,0x7A] is one + // whose original value was in [0x41,0x5A] — which is exactly + // the uppercase letters we want to match. + uint64_t lowered = word | (ones * 0x20); + uint64_t letter = ((lowered | highs) - ones * 0x61) & ((ones * 0x7A | highs) - lowered) & highs; + + // Standard SWAR "has zero byte" idiom on (word XOR 0x5F) to find + // bytes equal to underscore. Safe from cross-byte borrows because + // the ASCII guard above ensures all bytes are < 0x80. + uint64_t xor_us = word ^ (ones * 0x5F); + uint64_t underscore = (xor_us - ones) & ~xor_us & highs; + + uint64_t ident = digit | letter | underscore; + + if (ident == highs) { + cursor += 8; + continue; + } + + // Find the first non-identifier byte. On little-endian the first + // byte sits in the least-significant position. + uint64_t not_ident = ~ident & highs; + cursor += pm_ctzll(not_ident) / 8; + return (size_t) (cursor - start); + } + + return (size_t) (cursor - start); +} + +#else + +// No-op fallback for big-endian or other unsupported platforms. +// The caller's byte-at-a-time loop handles everything. +#define scan_identifier_ascii(start, end) ((size_t) 0) + +#endif + /** * 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 +static PRISM_INLINE size_t char_is_identifier(const pm_parser_t *parser, const uint8_t *b, ptrdiff_t n) { if (n <= 0) { return 0; @@ -1819,7 +2102,7 @@ const unsigned int pm_global_name_punctuation_hash[(0x7e - 0x20 + 31) / 32] = { #undef BIT #undef PUNCT -static inline bool +static PRISM_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; @@ -1827,7 +2110,7 @@ char_is_global_name_punctuation(const uint8_t b) { return (pm_global_name_punctuation_hash[(i - 0x20) / 32] >> (i % 32)) & 1; } -static inline bool +static PRISM_INLINE bool token_is_setter_name(pm_token_t *token) { return ( (token->type == PM_TOKEN_BRACKET_LEFT_RIGHT_EQUAL) || @@ -1915,7 +2198,7 @@ pm_local_is_keyword(const char *source, size_t length) { /** * Set the given flag on the given node. */ -static inline void +static PRISM_INLINE void pm_node_flag_set(pm_node_t *node, pm_node_flags_t flag) { node->flags |= flag; } @@ -1923,7 +2206,7 @@ pm_node_flag_set(pm_node_t *node, pm_node_flags_t flag) { /** * Remove the given flag from the given node. */ -static inline void +static PRISM_INLINE void pm_node_flag_unset(pm_node_t *node, pm_node_flags_t flag) { node->flags &= (pm_node_flags_t) ~flag; } @@ -1931,7 +2214,7 @@ pm_node_flag_unset(pm_node_t *node, pm_node_flags_t flag) { /** * Set the repeated parameter flag on the given node. */ -static inline void +static PRISM_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 || @@ -1959,7 +2242,7 @@ pm_node_flag_set_repeated_parameter(pm_node_t *node) { /** * Parse out the options for a regular expression. */ -static inline pm_node_flags_t +static PRISM_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; @@ -1987,7 +2270,7 @@ pm_regular_expression_flags_create(pm_parser_t *parser, const pm_token_t *closin 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); + pm_buffer_cleanup(&unknown_flags); } return flags; @@ -2005,25 +2288,17 @@ 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. + * Move an integer's values array into the arena. If the integer has heap- + * allocated values, copy them to the arena and free the original. */ -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(); +static PRISM_INLINE void +pm_integer_arena_move(pm_arena_t *arena, pm_integer_t *integer) { + if (integer->values != NULL) { + size_t byte_size = integer->length * sizeof(uint32_t); + uint32_t *old_values = integer->values; + integer->values = (uint32_t *) pm_arena_memdup(arena, old_values, byte_size, PRISM_ALIGNOF(uint32_t)); + xfree(old_values); } - return memory; -} - -#define PM_NODE_ALLOC(parser_, type_) (type_ *) pm_node_alloc(parser_, sizeof(type_)) -#define PM_NODE_INIT(parser_, type_, flags_, location_) (pm_node_t) { \ - .type = (type_), \ - .flags = (flags_), \ - .node_id = ++(parser_)->node_id, \ - .location = location_ \ } /** @@ -2031,13 +2306,12 @@ pm_node_alloc(PRISM_ATTRIBUTE_UNUSED pm_parser_t *parser, size_t size) { */ static pm_missing_node_t * pm_missing_node_create(pm_parser_t *parser, uint32_t start, uint32_t length) { - 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, ((pm_location_t) { .start = start, .length = length })) - }; - - return node; + return pm_missing_node_new( + parser->arena, + ++parser->node_id, + 0, + ((pm_location_t) { .start = start, .length = length }) + ); } /** @@ -2046,16 +2320,16 @@ pm_missing_node_create(pm_parser_t *parser, uint32_t start, uint32_t length) { 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(parser, PM_ALIAS_GLOBAL_VARIABLE_NODE, 0, PM_LOCATION_INIT_TOKEN_NODE(parser, keyword, old_name)), - .new_name = new_name, - .old_name = old_name, - .keyword_loc = TOK2LOC(parser, keyword) - }; - return node; + return pm_alias_global_variable_node_new( + parser->arena, + ++parser->node_id, + 0, + PM_LOCATION_INIT_TOKEN_NODE(parser, keyword, old_name), + new_name, + old_name, + TOK2LOC(parser, keyword) + ); } /** @@ -2064,16 +2338,16 @@ pm_alias_global_variable_node_create(pm_parser_t *parser, const pm_token_t *keyw 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(parser, PM_ALIAS_METHOD_NODE, 0, PM_LOCATION_INIT_TOKEN_NODE(parser, keyword, old_name)), - .new_name = new_name, - .old_name = old_name, - .keyword_loc = TOK2LOC(parser, keyword) - }; - return node; + return pm_alias_method_node_new( + parser->arena, + ++parser->node_id, + 0, + PM_LOCATION_INIT_TOKEN_NODE(parser, keyword, old_name), + new_name, + old_name, + TOK2LOC(parser, keyword) + ); } /** @@ -2081,16 +2355,15 @@ pm_alias_method_node_create(pm_parser_t *parser, const pm_token_t *keyword, pm_n */ 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(parser, PM_ALTERNATION_PATTERN_NODE, 0, PM_LOCATION_INIT_NODES(left, right)), - .left = left, - .right = right, - .operator_loc = TOK2LOC(parser, operator) - }; - - return node; + return pm_alternation_pattern_node_new( + parser->arena, + ++parser->node_id, + 0, + PM_LOCATION_INIT_NODES(left, right), + left, + right, + TOK2LOC(parser, operator) + ); } /** @@ -2100,16 +2373,15 @@ 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(parser, PM_AND_NODE, 0, PM_LOCATION_INIT_NODES(left, right)), - .left = left, - .operator_loc = TOK2LOC(parser, operator), - .right = right - }; - - return node; + return pm_and_node_new( + parser->arena, + ++parser->node_id, + 0, + PM_LOCATION_INIT_NODES(left, right), + left, + right, + TOK2LOC(parser, operator) + ); } /** @@ -2117,14 +2389,13 @@ pm_and_node_create(pm_parser_t *parser, pm_node_t *left, const pm_token_t *opera */ 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(parser, PM_ARGUMENTS_NODE, 0, PM_LOCATION_INIT_UNSET), - .arguments = { 0 } - }; - - return node; + return pm_arguments_node_new( + parser->arena, + ++parser->node_id, + 0, + PM_LOCATION_INIT_UNSET, + ((pm_node_list_t) { 0 }) + ); } /** @@ -2139,7 +2410,7 @@ pm_arguments_node_size(pm_arguments_node_t *node) { * Append an argument to an arguments node. */ static void -pm_arguments_node_arguments_append(pm_arguments_node_t *node, pm_node_t *argument) { +pm_arguments_node_arguments_append(pm_arena_t *arena, pm_arguments_node_t *node, pm_node_t *argument) { if (pm_arguments_node_size(node) == 0) { PM_NODE_START_SET_NODE(node, argument); } @@ -2148,7 +2419,7 @@ pm_arguments_node_arguments_append(pm_arguments_node_t *node, pm_node_t *argumen PM_NODE_LENGTH_SET_NODE(node, argument); } - pm_node_list_append(&node->arguments, argument); + pm_node_list_append(arena, &node->arguments, argument); if (PM_NODE_TYPE_P(argument, PM_SPLAT_NODE)) { if (PM_NODE_FLAG_P(node, PM_ARGUMENTS_NODE_FLAGS_CONTAINS_SPLAT)) { @@ -2164,37 +2435,39 @@ pm_arguments_node_arguments_append(pm_arguments_node_t *node, pm_node_t *argumen */ 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); - if (opening == NULL) { - *node = (pm_array_node_t) { - .base = PM_NODE_INIT(parser, PM_ARRAY_NODE, PM_NODE_FLAG_STATIC_LITERAL, PM_LOCATION_INIT_UNSET), - .opening_loc = { 0 }, - .closing_loc = { 0 }, - .elements = { 0 } - }; + return pm_array_node_new( + parser->arena, + ++parser->node_id, + PM_NODE_FLAG_STATIC_LITERAL, + PM_LOCATION_INIT_UNSET, + ((pm_node_list_t) { 0 }), + ((pm_location_t) { 0 }), + ((pm_location_t) { 0 }) + ); } else { - *node = (pm_array_node_t) { - .base = PM_NODE_INIT(parser, PM_ARRAY_NODE, PM_NODE_FLAG_STATIC_LITERAL, PM_LOCATION_INIT_TOKEN(parser, opening)), - .opening_loc = TOK2LOC(parser, opening), - .closing_loc = TOK2LOC(parser, opening), - .elements = { 0 } - }; + return pm_array_node_new( + parser->arena, + ++parser->node_id, + PM_NODE_FLAG_STATIC_LITERAL, + PM_LOCATION_INIT_TOKEN(parser, opening), + ((pm_node_list_t) { 0 }), + TOK2LOC(parser, opening), + TOK2LOC(parser, opening) + ); } - - 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) { +static PRISM_INLINE void +pm_array_node_elements_append(pm_arena_t *arena, pm_array_node_t *node, pm_node_t *element) { if (!node->elements.size && !node->opening_loc.length) { PM_NODE_START_SET_NODE(node, element); } - pm_node_list_append(&node->elements, element); + pm_node_list_append(arena, &node->elements, element); PM_NODE_LENGTH_SET_NODE(node, element); // If the element is not a static literal, then the array is not a static @@ -2224,17 +2497,18 @@ pm_array_node_close_set(const pm_parser_t *parser, pm_array_node_t *node, const */ 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(parser, PM_ARRAY_PATTERN_NODE, 0, PM_LOCATION_INIT_NODES(nodes->nodes[0], nodes->nodes[nodes->size - 1])), - .constant = NULL, - .rest = NULL, - .requireds = { 0 }, - .posts = { 0 }, - .opening_loc = { 0 }, - .closing_loc = { 0 } - }; + pm_array_pattern_node_t *node = pm_array_pattern_node_new( + parser->arena, + ++parser->node_id, + 0, + PM_LOCATION_INIT_NODES(nodes->nodes[0], nodes->nodes[nodes->size - 1]), + NULL, + ((pm_node_list_t) { 0 }), + NULL, + ((pm_node_list_t) { 0 }), + ((pm_location_t) { 0 }), + ((pm_location_t) { 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. @@ -2246,9 +2520,9 @@ pm_array_pattern_node_node_list_create(pm_parser_t *parser, pm_node_list_t *node node->rest = child; found_rest = true; } else if (found_rest) { - pm_node_list_append(&node->posts, child); + pm_node_list_append(parser->arena, &node->posts, child); } else { - pm_node_list_append(&node->requireds, child); + pm_node_list_append(parser->arena, &node->requireds, child); } } @@ -2260,19 +2534,18 @@ pm_array_pattern_node_node_list_create(pm_parser_t *parser, pm_node_list_t *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(parser, PM_ARRAY_PATTERN_NODE, 0, PM_LOCATION_INIT_NODE(rest)), - .constant = NULL, - .rest = rest, - .requireds = { 0 }, - .posts = { 0 }, - .opening_loc = { 0 }, - .closing_loc = { 0 } - }; - - return node; + return pm_array_pattern_node_new( + parser->arena, + ++parser->node_id, + 0, + PM_LOCATION_INIT_NODE(rest), + NULL, + ((pm_node_list_t) { 0 }), + rest, + ((pm_node_list_t) { 0 }), + ((pm_location_t) { 0 }), + ((pm_location_t) { 0 }) + ); } /** @@ -2281,19 +2554,18 @@ pm_array_pattern_node_rest_create(pm_parser_t *parser, pm_node_t *rest) { */ 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(parser, PM_ARRAY_PATTERN_NODE, 0, PM_LOCATION_INIT_NODE_TOKEN(parser, constant, closing)), - .constant = constant, - .rest = NULL, - .opening_loc = TOK2LOC(parser, opening), - .closing_loc = TOK2LOC(parser, closing), - .requireds = { 0 }, - .posts = { 0 } - }; - - return node; + return pm_array_pattern_node_new( + parser->arena, + ++parser->node_id, + 0, + PM_LOCATION_INIT_NODE_TOKEN(parser, constant, closing), + constant, + ((pm_node_list_t) { 0 }), + NULL, + ((pm_node_list_t) { 0 }), + TOK2LOC(parser, opening), + TOK2LOC(parser, closing) + ); } /** @@ -2302,24 +2574,23 @@ pm_array_pattern_node_constant_create(pm_parser_t *parser, pm_node_t *constant, */ 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(parser, PM_ARRAY_PATTERN_NODE, 0, PM_LOCATION_INIT_TOKENS(parser, opening, closing)), - .constant = NULL, - .rest = NULL, - .opening_loc = TOK2LOC(parser, opening), - .closing_loc = TOK2LOC(parser, closing), - .requireds = { 0 }, - .posts = { 0 } - }; - - return node; + return pm_array_pattern_node_new( + parser->arena, + ++parser->node_id, + 0, + PM_LOCATION_INIT_TOKENS(parser, opening, closing), + NULL, + ((pm_node_list_t) { 0 }), + NULL, + ((pm_node_list_t) { 0 }), + TOK2LOC(parser, opening), + TOK2LOC(parser, closing) + ); } -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); +static PRISM_INLINE void +pm_array_pattern_node_requireds_append(pm_arena_t *arena, pm_array_pattern_node_t *node, pm_node_t *inner) { + pm_node_list_append(arena, &node->requireds, inner); } /** @@ -2327,7 +2598,6 @@ pm_array_pattern_node_requireds_append(pm_array_pattern_node_t *node, pm_node_t */ 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); uint32_t end; if (value != NULL && PM_NODE_END(value) > PM_NODE_END(key)) { @@ -2355,14 +2625,15 @@ pm_assoc_node_create(pm_parser_t *parser, pm_node_t *key, const pm_token_t *oper flags = key->flags & value->flags & PM_NODE_FLAG_STATIC_LITERAL; } - *node = (pm_assoc_node_t) { - .base = PM_NODE_INIT(parser, PM_ASSOC_NODE, flags, ((pm_location_t) { .start = PM_NODE_START(key), .length = U32(end - PM_NODE_START(key)) })), - .key = key, - .operator_loc = NTOK2LOC(parser, operator), - .value = value - }; - - return node; + return pm_assoc_node_new( + parser->arena, + ++parser->node_id, + flags, + ((pm_location_t) { .start = PM_NODE_START(key), .length = U32(end - PM_NODE_START(key)) }), + key, + value, + NTOK2LOC(parser, operator) + ); } /** @@ -2371,15 +2642,15 @@ pm_assoc_node_create(pm_parser_t *parser, pm_node_t *key, const pm_token_t *oper 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 = PM_NODE_INIT(parser, PM_ASSOC_SPLAT_NODE, 0, (value == NULL) ? PM_LOCATION_INIT_TOKEN(parser, operator) : PM_LOCATION_INIT_TOKEN_NODE(parser, operator, value)), - .value = value, - .operator_loc = TOK2LOC(parser, operator) - }; - - return node; + return pm_assoc_splat_node_new( + parser->arena, + ++parser->node_id, + 0, + (value == NULL) ? PM_LOCATION_INIT_TOKEN(parser, operator) : PM_LOCATION_INIT_TOKEN_NODE(parser, operator, value), + value, + TOK2LOC(parser, operator) + ); } /** @@ -2388,14 +2659,14 @@ pm_assoc_splat_node_create(pm_parser_t *parser, pm_node_t *value, const pm_token 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(parser, PM_BACK_REFERENCE_READ_NODE, 0, PM_LOCATION_INIT_TOKEN(parser, name)), - .name = pm_parser_constant_id_token(parser, name) - }; - - return node; + return pm_back_reference_read_node_new( + parser->arena, + ++parser->node_id, + 0, + PM_LOCATION_INIT_TOKEN(parser, name), + pm_parser_constant_id_token(parser, name) + ); } /** @@ -2403,19 +2674,21 @@ pm_back_reference_read_node_create(pm_parser_t *parser, const pm_token_t *name) */ 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); - uint32_t start = begin_keyword == NULL ? 0 : PM_TOKEN_START(parser, begin_keyword); uint32_t end = statements == NULL ? (begin_keyword == NULL ? 0 : PM_TOKEN_END(parser, begin_keyword)) : PM_NODE_END(statements); - *node = (pm_begin_node_t) { - .base = PM_NODE_INIT(parser, PM_BEGIN_NODE, 0, ((pm_location_t) { .start = start, .length = U32(end - start) })), - .begin_keyword_loc = NTOK2LOC(parser, begin_keyword), - .statements = statements, - .end_keyword_loc = { 0 } - }; - - return node; + return pm_begin_node_new( + parser->arena, + ++parser->node_id, + 0, + ((pm_location_t) { .start = start, .length = U32(end - start) }), + NTOK2LOC(parser, begin_keyword), + statements, + NULL, + NULL, + NULL, + ((pm_location_t) { 0 }) + ); } /** @@ -2470,15 +2743,15 @@ pm_begin_node_end_keyword_set(const pm_parser_t *parser, pm_begin_node_t *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) { assert(operator->type == PM_TOKEN_UAMPERSAND); - pm_block_argument_node_t *node = PM_NODE_ALLOC(parser, pm_block_argument_node_t); - - *node = (pm_block_argument_node_t) { - .base = PM_NODE_INIT(parser, PM_BLOCK_ARGUMENT_NODE, 0, (expression == NULL) ? PM_LOCATION_INIT_TOKEN(parser, operator) : PM_LOCATION_INIT_TOKEN_NODE(parser, operator, expression)), - .expression = expression, - .operator_loc = TOK2LOC(parser, operator) - }; - return node; + return pm_block_argument_node_new( + parser->arena, + ++parser->node_id, + 0, + (expression == NULL) ? PM_LOCATION_INIT_TOKEN(parser, operator) : PM_LOCATION_INIT_TOKEN_NODE(parser, operator, expression), + expression, + TOK2LOC(parser, operator) + ); } /** @@ -2486,18 +2759,17 @@ pm_block_argument_node_create(pm_parser_t *parser, const pm_token_t *operator, p */ 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(parser, PM_BLOCK_NODE, 0, PM_LOCATION_INIT_TOKENS(parser, opening, closing)), - .locals = *locals, - .parameters = parameters, - .body = body, - .opening_loc = TOK2LOC(parser, opening), - .closing_loc = TOK2LOC(parser, closing) - }; - - return node; + return pm_block_node_new( + parser->arena, + ++parser->node_id, + 0, + PM_LOCATION_INIT_TOKENS(parser, opening, closing), + *locals, + parameters, + body, + TOK2LOC(parser, opening), + TOK2LOC(parser, closing) + ); } /** @@ -2506,16 +2778,16 @@ pm_block_node_create(pm_parser_t *parser, pm_constant_id_list_t *locals, const p 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_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 = PM_NODE_INIT(parser, PM_BLOCK_PARAMETER_NODE, 0, (name == NULL) ? PM_LOCATION_INIT_TOKEN(parser, operator) : PM_LOCATION_INIT_TOKENS(parser, operator, name)), - .name = name == NULL ? 0 : pm_parser_constant_id_token(parser, name), - .name_loc = NTOK2LOC(parser, name), - .operator_loc = TOK2LOC(parser, operator) - }; - return node; + return pm_block_parameter_node_new( + parser->arena, + ++parser->node_id, + 0, + (name == NULL) ? PM_LOCATION_INIT_TOKEN(parser, operator) : PM_LOCATION_INIT_TOKENS(parser, operator, name), + name == NULL ? 0 : pm_parser_constant_id_token(parser, name), + NTOK2LOC(parser, name), + TOK2LOC(parser, operator) + ); } /** @@ -2523,8 +2795,6 @@ pm_block_parameter_node_create(pm_parser_t *parser, const pm_token_t *name, cons */ 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); - uint32_t start; if (opening != NULL) { start = PM_TOKEN_START(parser, opening); @@ -2543,15 +2813,16 @@ pm_block_parameters_node_create(pm_parser_t *parser, pm_parameters_node_t *param end = 0; } - *node = (pm_block_parameters_node_t) { - .base = PM_NODE_INIT(parser, PM_BLOCK_PARAMETERS_NODE, 0, ((pm_location_t) { .start = start, .length = U32(end - start) })), - .parameters = parameters, - .opening_loc = NTOK2LOC(parser, opening), - .closing_loc = { 0 }, - .locals = { 0 } - }; - - return node; + return pm_block_parameters_node_new( + parser->arena, + ++parser->node_id, + 0, + ((pm_location_t) { .start = start, .length = U32(end - start) }), + parameters, + ((pm_node_list_t) { 0 }), + NTOK2LOC(parser, opening), + ((pm_location_t) { 0 }) + ); } /** @@ -2569,22 +2840,21 @@ pm_block_parameters_node_closing_set(const pm_parser_t *parser, pm_block_paramet */ 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(parser, PM_BLOCK_LOCAL_VARIABLE_NODE, 0, PM_LOCATION_INIT_TOKEN(parser, name)), - .name = pm_parser_constant_id_token(parser, name) - }; - - return node; + return pm_block_local_variable_node_new( + parser->arena, + ++parser->node_id, + 0, + PM_LOCATION_INIT_TOKEN(parser, name), + pm_parser_constant_id_token(parser, name) + ); } /** * 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)); +pm_block_parameters_node_append_local(pm_arena_t *arena, pm_block_parameters_node_t *node, const pm_block_local_variable_node_t *local) { + pm_node_list_append(arena, &node->locals, UP(local)); if (PM_NODE_LENGTH(node) == 0) { PM_NODE_START_SET_NODE(node, local); @@ -2599,15 +2869,15 @@ pm_block_parameters_node_append_local(pm_block_parameters_node_t *node, const pm 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 = PM_NODE_INIT(parser, PM_BREAK_NODE, 0, (arguments == NULL) ? PM_LOCATION_INIT_TOKEN(parser, keyword) : PM_LOCATION_INIT_TOKEN_NODE(parser, keyword, arguments)), - .arguments = arguments, - .keyword_loc = TOK2LOC(parser, keyword) - }; - return node; + return pm_break_node_new( + parser->arena, + ++parser->node_id, + 0, + (arguments == NULL) ? PM_LOCATION_INIT_TOKEN(parser, keyword) : PM_LOCATION_INIT_TOKEN_NODE(parser, keyword, arguments), + arguments, + TOK2LOC(parser, keyword) + ); } // There are certain flags that we want to use internally but don't want to @@ -2626,29 +2896,28 @@ static const pm_node_flags_t PM_CALL_NODE_FLAGS_INDEX = ((PM_CALL_NODE_FLAGS_LAS */ 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(parser, PM_CALL_NODE, flags, PM_LOCATION_INIT_UNSET), - .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; + return pm_call_node_new( + parser->arena, + ++parser->node_id, + flags, + PM_LOCATION_INIT_UNSET, + NULL, + ((pm_location_t) { 0 }), + 0, + ((pm_location_t) { 0 }), + ((pm_location_t) { 0 }), + NULL, + ((pm_location_t) { 0 }), + ((pm_location_t) { 0 }), + NULL + ); } /** * Returns the value that the ignore visibility flag should be set to for the * given receiver. */ -static inline pm_node_flags_t +static PRISM_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; } @@ -2704,7 +2973,7 @@ pm_call_node_binary_create(pm_parser_t *parser, pm_node_t *receiver, pm_token_t node->message_loc = TOK2LOC(parser, operator); pm_arguments_node_t *arguments = pm_arguments_node_create(parser); - pm_arguments_node_arguments_append(arguments, argument); + pm_arguments_node_arguments_append(parser->arena, arguments, argument); node->arguments = arguments; node->name = pm_parser_constant_id_token(parser, operator); @@ -2898,7 +3167,7 @@ pm_call_node_variable_call_create(pm_parser_t *parser, pm_token_t *message) { * Returns whether or not this call can be used on the left-hand side of an * operator assignment. */ -static inline bool +static PRISM_INLINE bool pm_call_node_writable_p(const pm_parser_t *parser, const pm_call_node_t *node) { return ( (node->message_loc.length > 0) && @@ -2921,10 +3190,10 @@ pm_call_write_read_name_init(pm_parser_t *parser, pm_constant_id_t *read_name, p if (write_constant->length > 0) { size_t length = write_constant->length - 1; - void *memory = xmalloc(length); + uint8_t *memory = (uint8_t *) pm_arena_alloc(parser->arena, length, 1); memcpy(memory, write_constant->start, length); - *read_name = pm_constant_pool_insert_owned(&parser->constant_pool, (uint8_t *) memory, length); + *read_name = pm_constant_pool_insert_owned(&parser->metadata_arena, &parser->constant_pool, 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); @@ -2938,25 +3207,25 @@ 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(parser, PM_CALL_AND_WRITE_NODE, FL(target), PM_LOCATION_INIT_NODES(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 = TOK2LOC(parser, operator), - .value = value - }; + + pm_call_and_write_node_t *node = pm_call_and_write_node_new( + parser->arena, + ++parser->node_id, + FL(target), + PM_LOCATION_INIT_NODES(target, value), + target->receiver, + target->call_operator_loc, + target->message_loc, + 0, + target->name, + TOK2LOC(parser, operator), + 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); + // The target is no longer necessary because we've reused its children. + // It is arena-allocated so no explicit free is needed. return node; } @@ -2990,27 +3259,28 @@ pm_index_arguments_check(pm_parser_t *parser, const pm_arguments_node_t *argumen 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(parser, PM_INDEX_AND_WRITE_NODE, FL(target), PM_LOCATION_INIT_NODES(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 = TOK2LOC(parser, 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); + pm_index_and_write_node_t *node = pm_index_and_write_node_new( + parser->arena, + ++parser->node_id, + FL(target), + PM_LOCATION_INIT_NODES(target, value), + target->receiver, + target->call_operator_loc, + target->opening_loc, + target->arguments, + target->closing_loc, + (pm_block_argument_node_t *) target->block, + TOK2LOC(parser, operator), + value + ); + + // The target is no longer necessary because we've reused its children. + // It is arena-allocated so no explicit free is needed. return node; } @@ -3021,26 +3291,26 @@ pm_index_and_write_node_create(pm_parser_t *parser, pm_call_node_t *target, cons 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(parser, PM_CALL_OPERATOR_WRITE_NODE, FL(target), PM_LOCATION_INIT_NODES(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_raw(parser, operator->start, operator->end - 1), - .binary_operator_loc = TOK2LOC(parser, operator), - .value = value - }; + + pm_call_operator_write_node_t *node = pm_call_operator_write_node_new( + parser->arena, + ++parser->node_id, + FL(target), + PM_LOCATION_INIT_NODES(target, value), + target->receiver, + target->call_operator_loc, + target->message_loc, + 0, + target->name, + pm_parser_constant_id_raw(parser, operator->start, operator->end - 1), + TOK2LOC(parser, operator), + 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); + // The target is no longer necessary because we've reused its children. + // It is arena-allocated so no explicit free is needed. return node; } @@ -3050,28 +3320,28 @@ pm_call_operator_write_node_create(pm_parser_t *parser, pm_call_node_t *target, */ 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(parser, PM_INDEX_OPERATOR_WRITE_NODE, FL(target), PM_LOCATION_INIT_NODES(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_raw(parser, operator->start, operator->end - 1), - .binary_operator_loc = TOK2LOC(parser, 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); + pm_index_operator_write_node_t *node = pm_index_operator_write_node_new( + parser->arena, + ++parser->node_id, + FL(target), + PM_LOCATION_INIT_NODES(target, value), + target->receiver, + target->call_operator_loc, + target->opening_loc, + target->arguments, + target->closing_loc, + (pm_block_argument_node_t *) target->block, + pm_parser_constant_id_raw(parser, operator->start, operator->end - 1), + TOK2LOC(parser, operator), + value + ); + + // The target is no longer necessary because we've reused its children. + // It is arena-allocated so no explicit free is needed. return node; } @@ -3083,25 +3353,25 @@ 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(parser, PM_CALL_OR_WRITE_NODE, FL(target), PM_LOCATION_INIT_NODES(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 = TOK2LOC(parser, operator), - .value = value - }; + + pm_call_or_write_node_t *node = pm_call_or_write_node_new( + parser->arena, + ++parser->node_id, + FL(target), + PM_LOCATION_INIT_NODES(target, value), + target->receiver, + target->call_operator_loc, + target->message_loc, + 0, + target->name, + TOK2LOC(parser, operator), + 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); + // The target is no longer necessary because we've reused its children. + // It is arena-allocated so no explicit free is needed. return node; } @@ -3112,27 +3382,28 @@ pm_call_or_write_node_create(pm_parser_t *parser, pm_call_node_t *target, const 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(parser, PM_INDEX_OR_WRITE_NODE, FL(target), PM_LOCATION_INIT_NODES(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 = TOK2LOC(parser, 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); + pm_index_or_write_node_t *node = pm_index_or_write_node_new( + parser->arena, + ++parser->node_id, + FL(target), + PM_LOCATION_INIT_NODES(target, value), + target->receiver, + target->call_operator_loc, + target->opening_loc, + target->arguments, + target->closing_loc, + (pm_block_argument_node_t *) target->block, + TOK2LOC(parser, operator), + value + ); + + // The target is no longer necessary because we've reused its children. + // It is arena-allocated so no explicit free is needed. return node; } @@ -3143,15 +3414,16 @@ pm_index_or_write_node_create(pm_parser_t *parser, pm_call_node_t *target, const */ 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(parser, PM_CALL_TARGET_NODE, FL(target), PM_LOCATION_INIT_NODE(target)), - .receiver = target->receiver, - .call_operator_loc = target->call_operator_loc, - .name = target->name, - .message_loc = target->message_loc - }; + pm_call_target_node_t *node = pm_call_target_node_new( + parser->arena, + ++parser->node_id, + FL(target), + PM_LOCATION_INIT_NODE(target), + target->receiver, + target->call_operator_loc, + target->name, + 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 @@ -3161,10 +3433,8 @@ pm_call_target_node_create(pm_parser_t *parser, pm_call_node_t *target) { node->call_operator_loc = target->base.location; } - // 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); + // The target is no longer necessary because we've reused its children. + // It is arena-allocated so no explicit free is needed. return node; } @@ -3175,24 +3445,23 @@ pm_call_target_node_create(pm_parser_t *parser, pm_call_node_t *target) { */ 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(parser, PM_INDEX_TARGET_NODE, FL(target) | PM_CALL_NODE_FLAGS_ATTRIBUTE_WRITE, PM_LOCATION_INIT_NODE(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, - }; + pm_index_target_node_t *node = pm_index_target_node_new( + parser->arena, + ++parser->node_id, + FL(target) | PM_CALL_NODE_FLAGS_ATTRIBUTE_WRITE, + PM_LOCATION_INIT_NODE(target), + target->receiver, + target->opening_loc, + target->arguments, + target->closing_loc, + (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); + // The target is no longer necessary because we've reused its children. + // It is arena-allocated so no explicit free is needed. return node; } @@ -3202,16 +3471,15 @@ pm_index_target_node_create(pm_parser_t *parser, pm_call_node_t *target) { */ 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(parser, PM_CAPTURE_PATTERN_NODE, 0, PM_LOCATION_INIT_NODES(value, target)), - .value = value, - .target = target, - .operator_loc = TOK2LOC(parser, operator) - }; - - return node; + return pm_capture_pattern_node_new( + parser->arena, + ++parser->node_id, + 0, + PM_LOCATION_INIT_NODES(value, target), + value, + target, + TOK2LOC(parser, operator) + ); } /** @@ -3219,28 +3487,27 @@ pm_capture_pattern_node_create(pm_parser_t *parser, pm_node_t *value, pm_local_v */ 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(parser, PM_CASE_NODE, 0, PM_LOCATION_INIT_TOKENS(parser, case_keyword, end_keyword == NULL ? case_keyword : end_keyword)), - .predicate = predicate, - .else_clause = NULL, - .case_keyword_loc = TOK2LOC(parser, case_keyword), - .end_keyword_loc = NTOK2LOC(parser, end_keyword), - .conditions = { 0 } - }; - - return node; + return pm_case_node_new( + parser->arena, + ++parser->node_id, + 0, + PM_LOCATION_INIT_TOKENS(parser, case_keyword, end_keyword == NULL ? case_keyword : end_keyword), + predicate, + ((pm_node_list_t) { 0 }), + NULL, + TOK2LOC(parser, case_keyword), + NTOK2LOC(parser, end_keyword) + ); } /** * Append a new condition to a CaseNode node. */ static void -pm_case_node_condition_append(pm_case_node_t *node, pm_node_t *condition) { +pm_case_node_condition_append(pm_arena_t *arena, 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); + pm_node_list_append(arena, &node->conditions, condition); PM_NODE_LENGTH_SET_NODE(node, condition); } @@ -3267,28 +3534,27 @@ pm_case_node_end_keyword_loc_set(const pm_parser_t *parser, pm_case_node_t *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) { - 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(parser, PM_CASE_MATCH_NODE, 0, PM_LOCATION_INIT_TOKEN(parser, case_keyword)), - .predicate = predicate, - .else_clause = NULL, - .case_keyword_loc = TOK2LOC(parser, case_keyword), - .end_keyword_loc = { 0 }, - .conditions = { 0 } - }; - - return node; + return pm_case_match_node_new( + parser->arena, + ++parser->node_id, + 0, + PM_LOCATION_INIT_TOKEN(parser, case_keyword), + predicate, + ((pm_node_list_t) { 0 }), + NULL, + TOK2LOC(parser, case_keyword), + ((pm_location_t) { 0 }) + ); } /** * 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) { +pm_case_match_node_condition_append(pm_arena_t *arena, 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); + pm_node_list_append(arena, &node->conditions, condition); PM_NODE_LENGTH_SET_NODE(node, condition); } @@ -3315,21 +3581,20 @@ pm_case_match_node_end_keyword_loc_set(const pm_parser_t *parser, pm_case_match_ */ 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(parser, PM_CLASS_NODE, 0, PM_LOCATION_INIT_TOKENS(parser, class_keyword, end_keyword)), - .locals = *locals, - .class_keyword_loc = TOK2LOC(parser, class_keyword), - .constant_path = constant_path, - .inheritance_operator_loc = NTOK2LOC(parser, inheritance_operator), - .superclass = superclass, - .body = body, - .end_keyword_loc = TOK2LOC(parser, end_keyword), - .name = pm_parser_constant_id_token(parser, name) - }; - - return node; + return pm_class_node_new( + parser->arena, + ++parser->node_id, + 0, + PM_LOCATION_INIT_TOKENS(parser, class_keyword, end_keyword), + *locals, + TOK2LOC(parser, class_keyword), + constant_path, + NTOK2LOC(parser, inheritance_operator), + superclass, + body, + TOK2LOC(parser, end_keyword), + pm_parser_constant_id_token(parser, name) + ); } /** @@ -3338,17 +3603,17 @@ pm_class_node_create(pm_parser_t *parser, pm_constant_id_list_t *locals, const p 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(parser, PM_CLASS_VARIABLE_AND_WRITE_NODE, 0, PM_LOCATION_INIT_NODES(target, value)), - .name = target->name, - .name_loc = target->base.location, - .operator_loc = TOK2LOC(parser, operator), - .value = value - }; - return node; + return pm_class_variable_and_write_node_new( + parser->arena, + ++parser->node_id, + 0, + PM_LOCATION_INIT_NODES(target, value), + target->name, + target->base.location, + TOK2LOC(parser, operator), + value + ); } /** @@ -3356,18 +3621,17 @@ pm_class_variable_and_write_node_create(pm_parser_t *parser, pm_class_variable_r */ 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(parser, PM_CLASS_VARIABLE_OPERATOR_WRITE_NODE, 0, PM_LOCATION_INIT_NODES(target, value)), - .name = target->name, - .name_loc = target->base.location, - .binary_operator_loc = TOK2LOC(parser, operator), - .value = value, - .binary_operator = pm_parser_constant_id_raw(parser, operator->start, operator->end - 1) - }; - - return node; + return pm_class_variable_operator_write_node_new( + parser->arena, + ++parser->node_id, + 0, + PM_LOCATION_INIT_NODES(target, value), + target->name, + target->base.location, + TOK2LOC(parser, operator), + value, + pm_parser_constant_id_raw(parser, operator->start, operator->end - 1) + ); } /** @@ -3376,17 +3640,17 @@ pm_class_variable_operator_write_node_create(pm_parser_t *parser, pm_class_varia 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(parser, PM_CLASS_VARIABLE_OR_WRITE_NODE, 0, PM_LOCATION_INIT_NODES(target, value)), - .name = target->name, - .name_loc = target->base.location, - .operator_loc = TOK2LOC(parser, operator), - .value = value - }; - return node; + return pm_class_variable_or_write_node_new( + parser->arena, + ++parser->node_id, + 0, + PM_LOCATION_INIT_NODES(target, value), + target->name, + target->base.location, + TOK2LOC(parser, operator), + value + ); } /** @@ -3395,14 +3659,14 @@ pm_class_variable_or_write_node_create(pm_parser_t *parser, pm_class_variable_re 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(parser, PM_CLASS_VARIABLE_READ_NODE, 0, PM_LOCATION_INIT_TOKEN(parser, token)), - .name = pm_parser_constant_id_token(parser, token) - }; - return node; + return pm_class_variable_read_node_new( + parser->arena, + ++parser->node_id, + 0, + PM_LOCATION_INIT_TOKEN(parser, token), + pm_parser_constant_id_token(parser, token) + ); } /** @@ -3411,7 +3675,7 @@ pm_class_variable_read_node_create(pm_parser_t *parser, const pm_token_t *token) * a = *b * a = 1, 2, 3 */ -static inline pm_node_flags_t +static PRISM_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.length == 0) { return flags; @@ -3424,18 +3688,16 @@ pm_implicit_array_write_flags(const pm_node_t *node, pm_node_flags_t flags) { */ 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(parser, PM_CLASS_VARIABLE_WRITE_NODE, flags, PM_LOCATION_INIT_NODES(read_node, value)), - .name = read_node->name, - .name_loc = read_node->base.location, - .operator_loc = TOK2LOC(parser, operator), - .value = value - }; - - return node; + return pm_class_variable_write_node_new( + parser->arena, + ++parser->node_id, + pm_implicit_array_write_flags(value, PM_WRITE_NODE_FLAGS_IMPLICIT_ARRAY), + PM_LOCATION_INIT_NODES(read_node, value), + read_node->name, + read_node->base.location, + value, + TOK2LOC(parser, operator) + ); } /** @@ -3444,16 +3706,16 @@ pm_class_variable_write_node_create(pm_parser_t *parser, pm_class_variable_read_ 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(parser, PM_CONSTANT_PATH_AND_WRITE_NODE, 0, PM_LOCATION_INIT_NODES(target, value)), - .target = target, - .operator_loc = TOK2LOC(parser, operator), - .value = value - }; - return node; + return pm_constant_path_and_write_node_new( + parser->arena, + ++parser->node_id, + 0, + PM_LOCATION_INIT_NODES(target, value), + target, + TOK2LOC(parser, operator), + value + ); } /** @@ -3461,17 +3723,16 @@ pm_constant_path_and_write_node_create(pm_parser_t *parser, pm_constant_path_nod */ 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(parser, PM_CONSTANT_PATH_OPERATOR_WRITE_NODE, 0, PM_LOCATION_INIT_NODES(target, value)), - .target = target, - .binary_operator_loc = TOK2LOC(parser, operator), - .value = value, - .binary_operator = pm_parser_constant_id_raw(parser, operator->start, operator->end - 1) - }; - - return node; + return pm_constant_path_operator_write_node_new( + parser->arena, + ++parser->node_id, + 0, + PM_LOCATION_INIT_NODES(target, value), + target, + TOK2LOC(parser, operator), + value, + pm_parser_constant_id_raw(parser, operator->start, operator->end - 1) + ); } /** @@ -3480,16 +3741,16 @@ pm_constant_path_operator_write_node_create(pm_parser_t *parser, pm_constant_pat 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(parser, PM_CONSTANT_PATH_OR_WRITE_NODE, 0, PM_LOCATION_INIT_NODES(target, value)), - .target = target, - .operator_loc = TOK2LOC(parser, operator), - .value = value - }; - return node; + return pm_constant_path_or_write_node_new( + parser->arena, + ++parser->node_id, + 0, + PM_LOCATION_INIT_NODES(target, value), + target, + TOK2LOC(parser, operator), + value + ); } /** @@ -3498,22 +3759,22 @@ pm_constant_path_or_write_node_create(pm_parser_t *parser, pm_constant_path_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); } - *node = (pm_constant_path_node_t) { - .base = PM_NODE_INIT(parser, PM_CONSTANT_PATH_NODE, 0, (parent == NULL) ? PM_LOCATION_INIT_TOKENS(parser, delimiter, name_token) : PM_LOCATION_INIT_NODE_TOKEN(parser, parent, name_token)), - .parent = parent, - .name = name, - .delimiter_loc = TOK2LOC(parser, delimiter), - .name_loc = TOK2LOC(parser, name_token) - }; - - return node; + return pm_constant_path_node_new( + parser->arena, + ++parser->node_id, + 0, + (parent == NULL) ? PM_LOCATION_INIT_TOKENS(parser, delimiter, name_token) : PM_LOCATION_INIT_NODE_TOKEN(parser, parent, name_token), + parent, + name, + TOK2LOC(parser, delimiter), + TOK2LOC(parser, name_token) + ); } /** @@ -3521,17 +3782,15 @@ pm_constant_path_node_create(pm_parser_t *parser, pm_node_t *parent, const pm_to */ 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(parser, PM_CONSTANT_PATH_WRITE_NODE, flags, PM_LOCATION_INIT_NODES(target, value)), - .target = target, - .operator_loc = TOK2LOC(parser, operator), - .value = value - }; - - return node; + return pm_constant_path_write_node_new( + parser->arena, + ++parser->node_id, + pm_implicit_array_write_flags(value, PM_WRITE_NODE_FLAGS_IMPLICIT_ARRAY), + PM_LOCATION_INIT_NODES(target, value), + target, + TOK2LOC(parser, operator), + value + ); } /** @@ -3540,17 +3799,17 @@ pm_constant_path_write_node_create(pm_parser_t *parser, pm_constant_path_node_t 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(parser, PM_CONSTANT_AND_WRITE_NODE, 0, PM_LOCATION_INIT_NODES(target, value)), - .name = target->name, - .name_loc = target->base.location, - .operator_loc = TOK2LOC(parser, operator), - .value = value - }; - return node; + return pm_constant_and_write_node_new( + parser->arena, + ++parser->node_id, + 0, + PM_LOCATION_INIT_NODES(target, value), + target->name, + target->base.location, + TOK2LOC(parser, operator), + value + ); } /** @@ -3558,18 +3817,17 @@ pm_constant_and_write_node_create(pm_parser_t *parser, pm_constant_read_node_t * */ 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(parser, PM_CONSTANT_OPERATOR_WRITE_NODE, 0, PM_LOCATION_INIT_NODES(target, value)), - .name = target->name, - .name_loc = target->base.location, - .binary_operator_loc = TOK2LOC(parser, operator), - .value = value, - .binary_operator = pm_parser_constant_id_raw(parser, operator->start, operator->end - 1) - }; - - return node; + return pm_constant_operator_write_node_new( + parser->arena, + ++parser->node_id, + 0, + PM_LOCATION_INIT_NODES(target, value), + target->name, + target->base.location, + TOK2LOC(parser, operator), + value, + pm_parser_constant_id_raw(parser, operator->start, operator->end - 1) + ); } /** @@ -3578,17 +3836,17 @@ pm_constant_operator_write_node_create(pm_parser_t *parser, pm_constant_read_nod 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(parser, PM_CONSTANT_OR_WRITE_NODE, 0, PM_LOCATION_INIT_NODES(target, value)), - .name = target->name, - .name_loc = target->base.location, - .operator_loc = TOK2LOC(parser, operator), - .value = value - }; - return node; + return pm_constant_or_write_node_new( + parser->arena, + ++parser->node_id, + 0, + PM_LOCATION_INIT_NODES(target, value), + target->name, + target->base.location, + TOK2LOC(parser, operator), + value + ); } /** @@ -3597,14 +3855,14 @@ pm_constant_or_write_node_create(pm_parser_t *parser, pm_constant_read_node_t *t 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 == 0); - 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(parser, PM_CONSTANT_READ_NODE, 0, PM_LOCATION_INIT_TOKEN(parser, name)), - .name = pm_parser_constant_id_token(parser, name) - }; - - return node; + return pm_constant_read_node_new( + parser->arena, + ++parser->node_id, + 0, + PM_LOCATION_INIT_TOKEN(parser, name), + pm_parser_constant_id_token(parser, name) + ); } /** @@ -3612,18 +3870,16 @@ pm_constant_read_node_create(pm_parser_t *parser, const pm_token_t *name) { */ 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(parser, PM_CONSTANT_WRITE_NODE, flags, PM_LOCATION_INIT_NODES(target, value)), - .name = target->name, - .name_loc = target->base.location, - .operator_loc = TOK2LOC(parser, operator), - .value = value - }; - - return node; + return pm_constant_write_node_new( + parser->arena, + ++parser->node_id, + pm_implicit_array_write_flags(value, PM_WRITE_NODE_FLAGS_IMPLICIT_ARRAY), + PM_LOCATION_INIT_NODES(target, value), + target->name, + target->base.location, + value, + TOK2LOC(parser, operator) + ); } /** @@ -3689,29 +3945,28 @@ pm_def_node_create( 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 = PM_NODE_INIT(parser, PM_DEF_NODE, 0, (end_keyword == NULL) ? PM_LOCATION_INIT_TOKEN_NODE(parser, def_keyword, body) : PM_LOCATION_INIT_TOKENS(parser, def_keyword, end_keyword)), - .name = name, - .name_loc = TOK2LOC(parser, name_loc), - .receiver = receiver, - .parameters = parameters, - .body = body, - .locals = *locals, - .def_keyword_loc = TOK2LOC(parser, def_keyword), - .operator_loc = NTOK2LOC(parser, operator), - .lparen_loc = NTOK2LOC(parser, lparen), - .rparen_loc = NTOK2LOC(parser, rparen), - .equal_loc = NTOK2LOC(parser, equal), - .end_keyword_loc = NTOK2LOC(parser, end_keyword) - }; - - return node; + return pm_def_node_new( + parser->arena, + ++parser->node_id, + 0, + (end_keyword == NULL) ? PM_LOCATION_INIT_TOKEN_NODE(parser, def_keyword, body) : PM_LOCATION_INIT_TOKENS(parser, def_keyword, end_keyword), + name, + TOK2LOC(parser, name_loc), + receiver, + parameters, + body, + *locals, + TOK2LOC(parser, def_keyword), + NTOK2LOC(parser, operator), + NTOK2LOC(parser, lparen), + NTOK2LOC(parser, rparen), + NTOK2LOC(parser, equal), + NTOK2LOC(parser, end_keyword) + ); } /** @@ -3719,17 +3974,16 @@ pm_def_node_create( */ 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 = PM_NODE_INIT(parser, PM_DEFINED_NODE, 0, (rparen == NULL) ? PM_LOCATION_INIT_TOKEN_NODE(parser, keyword, value) : PM_LOCATION_INIT_TOKENS(parser, keyword, rparen)), - .lparen_loc = NTOK2LOC(parser, lparen), - .value = value, - .rparen_loc = NTOK2LOC(parser, rparen), - .keyword_loc = TOK2LOC(parser, keyword) - }; - - return node; + return pm_defined_node_new( + parser->arena, + ++parser->node_id, + 0, + (rparen == NULL) ? PM_LOCATION_INIT_TOKEN_NODE(parser, keyword, value) : PM_LOCATION_INIT_TOKENS(parser, keyword, rparen), + NTOK2LOC(parser, lparen), + value, + NTOK2LOC(parser, rparen), + TOK2LOC(parser, keyword) + ); } /** @@ -3737,16 +3991,15 @@ pm_defined_node_create(pm_parser_t *parser, const pm_token_t *lparen, pm_node_t */ 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 = PM_NODE_INIT(parser, PM_ELSE_NODE, 0, ((end_keyword == NULL) && (statements != NULL)) ? PM_LOCATION_INIT_TOKEN_NODE(parser, else_keyword, statements) : PM_LOCATION_INIT_TOKENS(parser, else_keyword, end_keyword)), - .else_keyword_loc = TOK2LOC(parser, else_keyword), - .statements = statements, - .end_keyword_loc = NTOK2LOC(parser, end_keyword) - }; - - return node; + return pm_else_node_new( + parser->arena, + ++parser->node_id, + 0, + ((end_keyword == NULL) && (statements != NULL)) ? PM_LOCATION_INIT_TOKEN_NODE(parser, else_keyword, statements) : PM_LOCATION_INIT_TOKENS(parser, else_keyword, end_keyword), + TOK2LOC(parser, else_keyword), + statements, + NTOK2LOC(parser, end_keyword) + ); } /** @@ -3754,16 +4007,15 @@ pm_else_node_create(pm_parser_t *parser, const pm_token_t *else_keyword, pm_stat */ 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(parser, PM_EMBEDDED_STATEMENTS_NODE, 0, PM_LOCATION_INIT_TOKENS(parser, opening, closing)), - .opening_loc = TOK2LOC(parser, opening), - .statements = statements, - .closing_loc = TOK2LOC(parser, closing) - }; - - return node; + return pm_embedded_statements_node_new( + parser->arena, + ++parser->node_id, + 0, + PM_LOCATION_INIT_TOKENS(parser, opening, closing), + TOK2LOC(parser, opening), + statements, + TOK2LOC(parser, closing) + ); } /** @@ -3771,15 +4023,14 @@ pm_embedded_statements_node_create(pm_parser_t *parser, const pm_token_t *openin */ 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(parser, PM_EMBEDDED_VARIABLE_NODE, 0, PM_LOCATION_INIT_TOKEN_NODE(parser, operator, variable)), - .operator_loc = TOK2LOC(parser, operator), - .variable = variable - }; - - return node; + return pm_embedded_variable_node_new( + parser->arena, + ++parser->node_id, + 0, + PM_LOCATION_INIT_TOKEN_NODE(parser, operator, variable), + TOK2LOC(parser, operator), + variable + ); } /** @@ -3787,16 +4038,15 @@ pm_embedded_variable_node_create(pm_parser_t *parser, const pm_token_t *operator */ 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(parser, PM_ENSURE_NODE, 0, PM_LOCATION_INIT_TOKENS(parser, ensure_keyword, end_keyword)), - .ensure_keyword_loc = TOK2LOC(parser, ensure_keyword), - .statements = statements, - .end_keyword_loc = TOK2LOC(parser, end_keyword) - }; - - return node; + return pm_ensure_node_new( + parser->arena, + ++parser->node_id, + 0, + PM_LOCATION_INIT_TOKENS(parser, ensure_keyword, end_keyword), + TOK2LOC(parser, ensure_keyword), + statements, + TOK2LOC(parser, end_keyword) + ); } /** @@ -3805,13 +4055,13 @@ pm_ensure_node_create(pm_parser_t *parser, const pm_token_t *ensure_keyword, pm_ 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(parser, PM_FALSE_NODE, PM_NODE_FLAG_STATIC_LITERAL, PM_LOCATION_INIT_TOKEN(parser, token)) - }; - return node; + return pm_false_node_new( + parser->arena, + ++parser->node_id, + PM_NODE_FLAG_STATIC_LITERAL, + PM_LOCATION_INIT_TOKEN(parser, token) + ); } /** @@ -3820,8 +4070,6 @@ pm_false_node_create(pm_parser_t *parser, const pm_token_t *token) { */ 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; @@ -3842,21 +4090,25 @@ pm_find_pattern_node_create(pm_parser_t *parser, pm_node_list_t *nodes) { #else pm_node_t *right_splat_node = right; #endif - *node = (pm_find_pattern_node_t) { - .base = PM_NODE_INIT(parser, PM_FIND_PATTERN_NODE, 0, PM_LOCATION_INIT_NODES(left, right)), - .constant = NULL, - .left = left_splat_node, - .right = right_splat_node, - .requireds = { 0 }, - .opening_loc = { 0 }, - .closing_loc = { 0 } - }; + + pm_find_pattern_node_t *node = pm_find_pattern_node_new( + parser->arena, + ++parser->node_id, + 0, + PM_LOCATION_INIT_NODES(left, right), + NULL, + left_splat_node, + ((pm_node_list_t) { 0 }), + right_splat_node, + ((pm_location_t) { 0 }), + ((pm_location_t) { 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]); + pm_node_list_append(parser->arena, &node->requireds, nodes->nodes[index]); } return node; @@ -3873,7 +4125,8 @@ pm_double_parse(pm_parser_t *parser, const pm_token_t *token) { // First, get a buffer of the content. size_t length = (size_t) diff; - char *buffer = xmalloc(sizeof(char) * (length + 1)); + const size_t buffer_size = sizeof(char) * (length + 1); + char *buffer = xmalloc(buffer_size); memcpy((void *) buffer, token->start, length); // Next, determine if we need to replace the decimal point because of @@ -3908,7 +4161,7 @@ pm_double_parse(pm_parser_t *parser, const pm_token_t *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); + xfree_sized(buffer, buffer_size); return 0.0; } @@ -3926,12 +4179,12 @@ pm_double_parse(pm_parser_t *parser, const pm_token_t *token) { ellipsis = ""; } - pm_diagnostic_list_append_format(&parser->warning_list, PM_TOKEN_START(parser, token), PM_TOKEN_LENGTH(token), PM_WARN_FLOAT_OUT_OF_RANGE, warn_width, (const char *) token->start, ellipsis); + pm_diagnostic_list_append_format(&parser->metadata_arena, &parser->warning_list, PM_TOKEN_START(parser, token), PM_TOKEN_LENGTH(token), 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); + xfree_sized(buffer, buffer_size); return value; } @@ -3941,14 +4194,14 @@ pm_double_parse(pm_parser_t *parser, const pm_token_t *token) { 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(parser, PM_FLOAT_NODE, PM_NODE_FLAG_STATIC_LITERAL, PM_LOCATION_INIT_TOKEN(parser, token)), - .value = pm_double_parse(parser, token) - }; - return node; + return pm_float_node_new( + parser->arena, + ++parser->node_id, + PM_NODE_FLAG_STATIC_LITERAL, + PM_LOCATION_INIT_TOKEN(parser, token), + pm_double_parse(parser, token) + ); } /** @@ -3958,17 +4211,17 @@ 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(parser, PM_IMAGINARY_NODE, PM_NODE_FLAG_STATIC_LITERAL, PM_LOCATION_INIT_TOKEN(parser, token)), - .numeric = UP(pm_float_node_create(parser, &((pm_token_t) { + return pm_imaginary_node_new( + parser->arena, + ++parser->node_id, + PM_NODE_FLAG_STATIC_LITERAL, + PM_LOCATION_INIT_TOKEN(parser, token), + UP(pm_float_node_create(parser, &((pm_token_t) { .type = PM_TOKEN_FLOAT, .start = token->start, .end = token->end - 1 }))) - }; - - return node; + ); } /** @@ -3978,12 +4231,14 @@ 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(parser, PM_RATIONAL_NODE, PM_INTEGER_BASE_FLAGS_DECIMAL | PM_NODE_FLAG_STATIC_LITERAL, PM_LOCATION_INIT_TOKEN(parser, token)), - .numerator = { 0 }, - .denominator = { 0 } - }; + pm_rational_node_t *node = pm_rational_node_new( + parser->arena, + ++parser->node_id, + PM_INTEGER_BASE_FLAGS_DECIMAL | PM_NODE_FLAG_STATIC_LITERAL, + PM_LOCATION_INIT_TOKEN(parser, token), + ((pm_integer_t) { 0 }), + ((pm_integer_t) { 0 }) + ); const uint8_t *start = token->start; const uint8_t *end = token->end - 1; // r @@ -4017,9 +4272,11 @@ pm_float_node_rational_create(pm_parser_t *parser, const pm_token_t *token) { 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); + xfree_sized(digits, length); pm_integers_reduce(&node->numerator, &node->denominator); + pm_integer_arena_move(parser->arena, &node->numerator); + pm_integer_arena_move(parser->arena, &node->denominator); return node; } @@ -4031,17 +4288,17 @@ 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(parser, PM_IMAGINARY_NODE, PM_NODE_FLAG_STATIC_LITERAL, PM_LOCATION_INIT_TOKEN(parser, token)), - .numeric = UP(pm_float_node_rational_create(parser, &((pm_token_t) { + return pm_imaginary_node_new( + parser->arena, + ++parser->node_id, + PM_NODE_FLAG_STATIC_LITERAL, + PM_LOCATION_INIT_TOKEN(parser, token), + UP(pm_float_node_rational_create(parser, &((pm_token_t) { .type = PM_TOKEN_FLOAT_RATIONAL, .start = token->start, .end = token->end - 1 }))) - }; - - return node; + ); } /** @@ -4058,20 +4315,19 @@ pm_for_node_create( 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(parser, PM_FOR_NODE, 0, PM_LOCATION_INIT_TOKENS(parser, for_keyword, end_keyword)), - .index = index, - .collection = collection, - .statements = statements, - .for_keyword_loc = TOK2LOC(parser, for_keyword), - .in_keyword_loc = TOK2LOC(parser, in_keyword), - .do_keyword_loc = NTOK2LOC(parser, do_keyword), - .end_keyword_loc = TOK2LOC(parser, end_keyword) - }; - - return node; + return pm_for_node_new( + parser->arena, + ++parser->node_id, + 0, + PM_LOCATION_INIT_TOKENS(parser, for_keyword, end_keyword), + index, + collection, + statements, + TOK2LOC(parser, for_keyword), + TOK2LOC(parser, in_keyword), + NTOK2LOC(parser, do_keyword), + TOK2LOC(parser, end_keyword) + ); } /** @@ -4080,13 +4336,13 @@ pm_for_node_create( 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(parser, PM_FORWARDING_ARGUMENTS_NODE, 0, PM_LOCATION_INIT_TOKEN(parser, token)) - }; - return node; + return pm_forwarding_arguments_node_new( + parser->arena, + ++parser->node_id, + 0, + PM_LOCATION_INIT_TOKEN(parser, token) + ); } /** @@ -4095,13 +4351,13 @@ pm_forwarding_arguments_node_create(pm_parser_t *parser, const pm_token_t *token 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(parser, PM_FORWARDING_PARAMETER_NODE, 0, PM_LOCATION_INIT_TOKEN(parser, token)) - }; - return node; + return pm_forwarding_parameter_node_new( + parser->arena, + ++parser->node_id, + 0, + PM_LOCATION_INIT_TOKEN(parser, token) + ); } /** @@ -4111,19 +4367,19 @@ 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 = PM_NODE_INIT(parser, PM_FORWARDING_SUPER_NODE, 0, (block == NULL) ? PM_LOCATION_INIT_TOKEN(parser, token) : PM_LOCATION_INIT_TOKEN_NODE(parser, token, block)), - .block = block - }; - - return node; + return pm_forwarding_super_node_new( + parser->arena, + ++parser->node_id, + 0, + (block == NULL) ? PM_LOCATION_INIT_TOKEN(parser, token) : PM_LOCATION_INIT_TOKEN_NODE(parser, token, block), + block + ); } /** @@ -4132,18 +4388,17 @@ pm_forwarding_super_node_create(pm_parser_t *parser, const pm_token_t *token, pm */ 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(parser, PM_HASH_PATTERN_NODE, 0, PM_LOCATION_INIT_TOKENS(parser, opening, closing)), - .constant = NULL, - .opening_loc = TOK2LOC(parser, opening), - .closing_loc = TOK2LOC(parser, closing), - .elements = { 0 }, - .rest = NULL - }; - - return node; + return pm_hash_pattern_node_new( + parser->arena, + ++parser->node_id, + 0, + PM_LOCATION_INIT_TOKENS(parser, opening, closing), + NULL, + ((pm_node_list_t) { 0 }), + NULL, + TOK2LOC(parser, opening), + TOK2LOC(parser, closing) + ); } /** @@ -4151,8 +4406,6 @@ pm_hash_pattern_node_empty_create(pm_parser_t *parser, const pm_token_t *opening */ 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); - uint32_t start; uint32_t end; @@ -4170,16 +4423,19 @@ pm_hash_pattern_node_node_list_create(pm_parser_t *parser, pm_node_list_t *eleme end = PM_NODE_END(rest); } - *node = (pm_hash_pattern_node_t) { - .base = PM_NODE_INIT(parser, PM_HASH_PATTERN_NODE, 0, ((pm_location_t) { .start = start, .length = U32(end - start) })), - .constant = NULL, - .elements = { 0 }, - .rest = rest, - .opening_loc = { 0 }, - .closing_loc = { 0 } - }; + pm_hash_pattern_node_t *node = pm_hash_pattern_node_new( + parser->arena, + ++parser->node_id, + 0, + ((pm_location_t) { .start = start, .length = U32(end - start) }), + NULL, + ((pm_node_list_t) { 0 }), + rest, + ((pm_location_t) { 0 }), + ((pm_location_t) { 0 }) + ); - pm_node_list_concat(&node->elements, elements); + pm_node_list_concat(parser->arena, &node->elements, elements); return node; } @@ -4209,17 +4465,17 @@ pm_global_variable_write_name(pm_parser_t *parser, const pm_node_t *target) { 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(parser, PM_GLOBAL_VARIABLE_AND_WRITE_NODE, 0, PM_LOCATION_INIT_NODES(target, value)), - .name = pm_global_variable_write_name(parser, target), - .name_loc = target->location, - .operator_loc = TOK2LOC(parser, operator), - .value = value - }; - return node; + return pm_global_variable_and_write_node_new( + parser->arena, + ++parser->node_id, + 0, + PM_LOCATION_INIT_NODES(target, value), + pm_global_variable_write_name(parser, target), + target->location, + TOK2LOC(parser, operator), + value + ); } /** @@ -4227,18 +4483,17 @@ pm_global_variable_and_write_node_create(pm_parser_t *parser, pm_node_t *target, */ 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(parser, PM_GLOBAL_VARIABLE_OPERATOR_WRITE_NODE, 0, PM_LOCATION_INIT_NODES(target, value)), - .name = pm_global_variable_write_name(parser, target), - .name_loc = target->location, - .binary_operator_loc = TOK2LOC(parser, operator), - .value = value, - .binary_operator = pm_parser_constant_id_raw(parser, operator->start, operator->end - 1) - }; - - return node; + return pm_global_variable_operator_write_node_new( + parser->arena, + ++parser->node_id, + 0, + PM_LOCATION_INIT_NODES(target, value), + pm_global_variable_write_name(parser, target), + target->location, + TOK2LOC(parser, operator), + value, + pm_parser_constant_id_raw(parser, operator->start, operator->end - 1) + ); } /** @@ -4247,17 +4502,17 @@ pm_global_variable_operator_write_node_create(pm_parser_t *parser, pm_node_t *ta 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(parser, PM_GLOBAL_VARIABLE_OR_WRITE_NODE, 0, PM_LOCATION_INIT_NODES(target, value)), - .name = pm_global_variable_write_name(parser, target), - .name_loc = target->location, - .operator_loc = TOK2LOC(parser, operator), - .value = value - }; - return node; + return pm_global_variable_or_write_node_new( + parser->arena, + ++parser->node_id, + 0, + PM_LOCATION_INIT_NODES(target, value), + pm_global_variable_write_name(parser, target), + target->location, + TOK2LOC(parser, operator), + value + ); } /** @@ -4265,14 +4520,13 @@ pm_global_variable_or_write_node_create(pm_parser_t *parser, pm_node_t *target, */ 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(parser, PM_GLOBAL_VARIABLE_READ_NODE, 0, PM_LOCATION_INIT_TOKEN(parser, name)), - .name = pm_parser_constant_id_token(parser, name) - }; - - return node; + return pm_global_variable_read_node_new( + parser->arena, + ++parser->node_id, + 0, + PM_LOCATION_INIT_TOKEN(parser, name), + pm_parser_constant_id_token(parser, name) + ); } /** @@ -4280,14 +4534,13 @@ pm_global_variable_read_node_create(pm_parser_t *parser, const pm_token_t *name) */ 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(parser, PM_GLOBAL_VARIABLE_READ_NODE, 0, PM_LOCATION_INIT_UNSET), - .name = name - }; - - return node; + return pm_global_variable_read_node_new( + parser->arena, + ++parser->node_id, + 0, + PM_LOCATION_INIT_UNSET, + name + ); } /** @@ -4295,18 +4548,16 @@ pm_global_variable_read_node_synthesized_create(pm_parser_t *parser, pm_constant */ 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(parser, PM_GLOBAL_VARIABLE_WRITE_NODE, flags, PM_LOCATION_INIT_NODES(target, value)), - .name = pm_global_variable_write_name(parser, target), - .name_loc = target->location, - .operator_loc = TOK2LOC(parser, operator), - .value = value - }; - - return node; + return pm_global_variable_write_node_new( + parser->arena, + ++parser->node_id, + pm_implicit_array_write_flags(value, PM_WRITE_NODE_FLAGS_IMPLICIT_ARRAY), + PM_LOCATION_INIT_NODES(target, value), + pm_global_variable_write_name(parser, target), + target->location, + value, + TOK2LOC(parser, operator) + ); } /** @@ -4314,17 +4565,16 @@ pm_global_variable_write_node_create(pm_parser_t *parser, pm_node_t *target, con */ 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(parser, PM_GLOBAL_VARIABLE_WRITE_NODE, 0, PM_LOCATION_INIT_UNSET), - .name = name, - .name_loc = { 0 }, - .operator_loc = { 0 }, - .value = value - }; - - return node; + return pm_global_variable_write_node_new( + parser->arena, + ++parser->node_id, + 0, + PM_LOCATION_INIT_UNSET, + name, + ((pm_location_t) { 0 }), + value, + ((pm_location_t) { 0 }) + ); } /** @@ -4333,24 +4583,24 @@ pm_global_variable_write_node_synthesized_create(pm_parser_t *parser, pm_constan 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(parser, PM_HASH_NODE, PM_NODE_FLAG_STATIC_LITERAL, PM_LOCATION_INIT_TOKEN(parser, opening)), - .opening_loc = TOK2LOC(parser, opening), - .closing_loc = { 0 }, - .elements = { 0 } - }; - return node; + return pm_hash_node_new( + parser->arena, + ++parser->node_id, + PM_NODE_FLAG_STATIC_LITERAL, + PM_LOCATION_INIT_TOKEN(parser, opening), + TOK2LOC(parser, opening), + ((pm_node_list_t) { 0 }), + ((pm_location_t) { 0 }) + ); } /** * 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); +static PRISM_INLINE void +pm_hash_node_elements_append(pm_arena_t *arena, pm_hash_node_t *hash, pm_node_t *element) { + pm_node_list_append(arena, &hash->elements, element); bool static_literal = PM_NODE_TYPE_P(element, PM_ASSOC_NODE); if (static_literal) { @@ -4365,7 +4615,7 @@ pm_hash_node_elements_append(pm_hash_node_t *hash, pm_node_t *element) { } } -static inline void +static PRISM_INLINE void pm_hash_node_closing_loc_set(const pm_parser_t *parser, pm_hash_node_t *hash, pm_token_t *token) { PM_NODE_LENGTH_SET_TOKEN(parser, hash, token); hash->closing_loc = TOK2LOC(parser, token); @@ -4384,7 +4634,6 @@ pm_if_node_create(pm_parser_t *parser, 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); uint32_t start = PM_TOKEN_START(parser, if_keyword); uint32_t end; @@ -4399,17 +4648,18 @@ pm_if_node_create(pm_parser_t *parser, end = PM_NODE_END(predicate); } - *node = (pm_if_node_t) { - .base = PM_NODE_INIT(parser, PM_IF_NODE, PM_NODE_FLAG_NEWLINE, ((pm_location_t) { .start = start, .length = U32(end - start) })), - .if_keyword_loc = TOK2LOC(parser, if_keyword), - .predicate = predicate, - .then_keyword_loc = NTOK2LOC(parser, then_keyword), - .statements = statements, - .subsequent = subsequent, - .end_keyword_loc = NTOK2LOC(parser, end_keyword) - }; - - return node; + return pm_if_node_new( + parser->arena, + ++parser->node_id, + PM_NODE_FLAG_NEWLINE, + ((pm_location_t) { .start = start, .length = U32(end - start) }), + TOK2LOC(parser, if_keyword), + predicate, + NTOK2LOC(parser, then_keyword), + statements, + subsequent, + NTOK2LOC(parser, end_keyword) + ); } /** @@ -4418,22 +4668,22 @@ pm_if_node_create(pm_parser_t *parser, 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(parser, PM_IF_NODE, PM_NODE_FLAG_NEWLINE, PM_LOCATION_INIT_NODES(statement, predicate)), - .if_keyword_loc = TOK2LOC(parser, if_keyword), - .predicate = predicate, - .then_keyword_loc = { 0 }, - .statements = statements, - .subsequent = NULL, - .end_keyword_loc = { 0 } - }; - - return node; + return pm_if_node_new( + parser->arena, + ++parser->node_id, + PM_NODE_FLAG_NEWLINE, + PM_LOCATION_INIT_NODES(statement, predicate), + TOK2LOC(parser, if_keyword), + predicate, + ((pm_location_t) { 0 }), + statements, + NULL, + ((pm_location_t) { 0 }) + ); } /** @@ -4451,29 +4701,27 @@ pm_if_node_ternary_create(pm_parser_t *parser, pm_node_t *predicate, const pm_to pm_statements_node_body_append(parser, else_statements, false_expression, true); pm_else_node_t *else_node = pm_else_node_create(parser, colon, else_statements, NULL); - pm_if_node_t *node = PM_NODE_ALLOC(parser, pm_if_node_t); - - *node = (pm_if_node_t) { - .base = PM_NODE_INIT(parser, PM_IF_NODE, PM_NODE_FLAG_NEWLINE, PM_LOCATION_INIT_NODES(predicate, false_expression)), - .if_keyword_loc = { 0 }, - .predicate = predicate, - .then_keyword_loc = TOK2LOC(parser, qmark), - .statements = if_statements, - .subsequent = UP(else_node), - .end_keyword_loc = { 0 } - }; - - return node; - + return pm_if_node_new( + parser->arena, + ++parser->node_id, + PM_NODE_FLAG_NEWLINE, + PM_LOCATION_INIT_NODES(predicate, false_expression), + ((pm_location_t) { 0 }), + predicate, + TOK2LOC(parser, qmark), + if_statements, + UP(else_node), + ((pm_location_t) { 0 }) + ); } -static inline void +static PRISM_INLINE void pm_if_node_end_keyword_loc_set(const pm_parser_t *parser, pm_if_node_t *node, const pm_token_t *keyword) { PM_NODE_LENGTH_SET_TOKEN(parser, node, keyword); node->end_keyword_loc = TOK2LOC(parser, keyword); } -static inline void +static PRISM_INLINE void pm_else_node_end_keyword_loc_set(const pm_parser_t *parser, pm_else_node_t *node, const pm_token_t *keyword) { PM_NODE_LENGTH_SET_TOKEN(parser, node, keyword); node->end_keyword_loc = TOK2LOC(parser, keyword); @@ -4484,14 +4732,13 @@ pm_else_node_end_keyword_loc_set(const pm_parser_t *parser, pm_else_node_t *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(parser, PM_IMPLICIT_NODE, 0, PM_LOCATION_INIT_NODE(value)), - .value = value - }; - - return node; + return pm_implicit_node_new( + parser->arena, + ++parser->node_id, + 0, + PM_LOCATION_INIT_NODE(value), + value + ); } /** @@ -4501,13 +4748,12 @@ 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(parser, PM_IMPLICIT_REST_NODE, 0, PM_LOCATION_INIT_TOKEN(parser, token)) - }; - - return node; + return pm_implicit_rest_node_new( + parser->arena, + ++parser->node_id, + 0, + PM_LOCATION_INIT_TOKEN(parser, token) + ); } /** @@ -4516,23 +4762,33 @@ pm_implicit_rest_node_create(pm_parser_t *parser, const pm_token_t *token) { 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(parser, PM_INTEGER_NODE, base | PM_NODE_FLAG_STATIC_LITERAL, PM_LOCATION_INIT_TOKEN(parser, token)), - .value = { 0 } - }; + pm_integer_node_t *node = pm_integer_node_new( + parser->arena, + ++parser->node_id, + base | PM_NODE_FLAG_STATIC_LITERAL, + PM_LOCATION_INIT_TOKEN(parser, token), + ((pm_integer_t) { 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; + if (parser->integer.lexed) { + // The value was already computed during lexing. + node->value.value = parser->integer.value; + parser->integer.lexed = false; + } else { + 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); + pm_integer_arena_move(parser->arena, &node->value); } - pm_integer_parse(&node->value, integer_base, token->start, token->end); return node; } @@ -4544,17 +4800,17 @@ 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(parser, PM_IMAGINARY_NODE, PM_NODE_FLAG_STATIC_LITERAL, PM_LOCATION_INIT_TOKEN(parser, token)), - .numeric = UP(pm_integer_node_create(parser, base, &((pm_token_t) { + return pm_imaginary_node_new( + parser->arena, + ++parser->node_id, + PM_NODE_FLAG_STATIC_LITERAL, + PM_LOCATION_INIT_TOKEN(parser, token), + UP(pm_integer_node_create(parser, base, &((pm_token_t) { .type = PM_TOKEN_INTEGER, .start = token->start, .end = token->end - 1 }))) - }; - - return node; + ); } /** @@ -4565,12 +4821,14 @@ 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(parser, PM_RATIONAL_NODE, base | PM_NODE_FLAG_STATIC_LITERAL, PM_LOCATION_INIT_TOKEN(parser, token)), - .numerator = { 0 }, - .denominator = { .value = 1, 0 } - }; + pm_rational_node_t *node = pm_rational_node_new( + parser->arena, + ++parser->node_id, + base | PM_NODE_FLAG_STATIC_LITERAL, + PM_LOCATION_INIT_TOKEN(parser, token), + ((pm_integer_t) { 0 }), + ((pm_integer_t) { .value = 1 }) + ); pm_integer_base_t integer_base = PM_INTEGER_BASE_DECIMAL; switch (base) { @@ -4582,6 +4840,7 @@ pm_integer_node_rational_create(pm_parser_t *parser, pm_node_flags_t base, const } pm_integer_parse(&node->numerator, integer_base, token->start, token->end - 1); + pm_integer_arena_move(parser->arena, &node->numerator); return node; } @@ -4594,17 +4853,17 @@ 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(parser, PM_IMAGINARY_NODE, PM_NODE_FLAG_STATIC_LITERAL, PM_LOCATION_INIT_TOKEN(parser, token)), - .numeric = UP(pm_integer_node_rational_create(parser, base, &((pm_token_t) { + return pm_imaginary_node_new( + parser->arena, + ++parser->node_id, + PM_NODE_FLAG_STATIC_LITERAL, + PM_LOCATION_INIT_TOKEN(parser, token), + 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; + ); } /** @@ -4612,8 +4871,6 @@ pm_integer_node_rational_imaginary_create(pm_parser_t *parser, pm_node_flags_t b */ 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); - uint32_t start = PM_TOKEN_START(parser, in_keyword); uint32_t end; @@ -4625,15 +4882,16 @@ pm_in_node_create(pm_parser_t *parser, pm_node_t *pattern, pm_statements_node_t end = PM_NODE_END(pattern); } - *node = (pm_in_node_t) { - .base = PM_NODE_INIT(parser, PM_IN_NODE, 0, ((pm_location_t) { .start = start, .length = U32(end - start) })), - .pattern = pattern, - .statements = statements, - .in_loc = TOK2LOC(parser, in_keyword), - .then_loc = NTOK2LOC(parser, then_keyword) - }; - - return node; + return pm_in_node_new( + parser->arena, + ++parser->node_id, + 0, + ((pm_location_t) { .start = start, .length = U32(end - start) }), + pattern, + statements, + TOK2LOC(parser, in_keyword), + NTOK2LOC(parser, then_keyword) + ); } /** @@ -4642,17 +4900,17 @@ pm_in_node_create(pm_parser_t *parser, pm_node_t *pattern, pm_statements_node_t 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(parser, PM_INSTANCE_VARIABLE_AND_WRITE_NODE, 0, PM_LOCATION_INIT_NODES(target, value)), - .name = target->name, - .name_loc = target->base.location, - .operator_loc = TOK2LOC(parser, operator), - .value = value - }; - return node; + return pm_instance_variable_and_write_node_new( + parser->arena, + ++parser->node_id, + 0, + PM_LOCATION_INIT_NODES(target, value), + target->name, + target->base.location, + TOK2LOC(parser, operator), + value + ); } /** @@ -4660,18 +4918,17 @@ pm_instance_variable_and_write_node_create(pm_parser_t *parser, pm_instance_vari */ 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(parser, PM_INSTANCE_VARIABLE_OPERATOR_WRITE_NODE, 0, PM_LOCATION_INIT_NODES(target, value)), - .name = target->name, - .name_loc = target->base.location, - .binary_operator_loc = TOK2LOC(parser, operator), - .value = value, - .binary_operator = pm_parser_constant_id_raw(parser, operator->start, operator->end - 1) - }; - - return node; + return pm_instance_variable_operator_write_node_new( + parser->arena, + ++parser->node_id, + 0, + PM_LOCATION_INIT_NODES(target, value), + target->name, + target->base.location, + TOK2LOC(parser, operator), + value, + pm_parser_constant_id_raw(parser, operator->start, operator->end - 1) + ); } /** @@ -4680,17 +4937,17 @@ pm_instance_variable_operator_write_node_create(pm_parser_t *parser, pm_instance 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(parser, PM_INSTANCE_VARIABLE_OR_WRITE_NODE, 0, PM_LOCATION_INIT_NODES(target, value)), - .name = target->name, - .name_loc = target->base.location, - .operator_loc = TOK2LOC(parser, operator), - .value = value - }; - return node; + return pm_instance_variable_or_write_node_new( + parser->arena, + ++parser->node_id, + 0, + PM_LOCATION_INIT_NODES(target, value), + target->name, + target->base.location, + TOK2LOC(parser, operator), + value + ); } /** @@ -4699,14 +4956,14 @@ pm_instance_variable_or_write_node_create(pm_parser_t *parser, pm_instance_varia 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(parser, PM_INSTANCE_VARIABLE_READ_NODE, 0, PM_LOCATION_INIT_TOKEN(parser, token)), - .name = pm_parser_constant_id_token(parser, token) - }; - return node; + return pm_instance_variable_read_node_new( + parser->arena, + ++parser->node_id, + 0, + PM_LOCATION_INIT_TOKEN(parser, token), + pm_parser_constant_id_token(parser, token) + ); } /** @@ -4715,18 +4972,16 @@ pm_instance_variable_read_node_create(pm_parser_t *parser, const pm_token_t *tok */ 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(parser, PM_INSTANCE_VARIABLE_WRITE_NODE, flags, PM_LOCATION_INIT_NODES(read_node, value)), - .name = read_node->name, - .name_loc = read_node->base.location, - .operator_loc = TOK2LOC(parser, operator), - .value = value - }; - - return node; + return pm_instance_variable_write_node_new( + parser->arena, + ++parser->node_id, + pm_implicit_array_write_flags(value, PM_WRITE_NODE_FLAGS_IMPLICIT_ARRAY), + PM_LOCATION_INIT_NODES(read_node, value), + read_node->name, + read_node->base.location, + value, + TOK2LOC(parser, operator) + ); } /** @@ -4735,7 +4990,7 @@ pm_instance_variable_write_node_create(pm_parser_t *parser, pm_instance_variable * literals. */ static void -pm_interpolated_node_append(pm_node_t *node, pm_node_list_t *parts, pm_node_t *part) { +pm_interpolated_node_append(pm_arena_t *arena, 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); @@ -4770,7 +5025,7 @@ pm_interpolated_node_append(pm_node_t *node, pm_node_list_t *parts, pm_node_t *p break; } - pm_node_list_append(parts, part); + pm_node_list_append(arena, parts, part); } /** @@ -4778,20 +5033,19 @@ pm_interpolated_node_append(pm_node_t *node, pm_node_list_t *parts, pm_node_t *p */ 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(parser, PM_INTERPOLATED_REGULAR_EXPRESSION_NODE, PM_NODE_FLAG_STATIC_LITERAL, PM_LOCATION_INIT_TOKEN(parser, opening)), - .opening_loc = TOK2LOC(parser, opening), - .closing_loc = TOK2LOC(parser, opening), - .parts = { 0 } - }; - - return node; + return pm_interpolated_regular_expression_node_new( + parser->arena, + ++parser->node_id, + PM_NODE_FLAG_STATIC_LITERAL, + PM_LOCATION_INIT_TOKEN(parser, opening), + TOK2LOC(parser, opening), + ((pm_node_list_t) { 0 }), + TOK2LOC(parser, opening) + ); } -static inline void -pm_interpolated_regular_expression_node_append(pm_interpolated_regular_expression_node_t *node, pm_node_t *part) { +static PRISM_INLINE void +pm_interpolated_regular_expression_node_append(pm_arena_t *arena, pm_interpolated_regular_expression_node_t *node, pm_node_t *part) { if (PM_NODE_START(node) > PM_NODE_START(part)) { PM_NODE_START_SET_NODE(node, part); } @@ -4799,10 +5053,10 @@ pm_interpolated_regular_expression_node_append(pm_interpolated_regular_expressio PM_NODE_LENGTH_SET_NODE(node, part); } - pm_interpolated_node_append(UP(node), &node->parts, part); + pm_interpolated_node_append(arena, UP(node), &node->parts, part); } -static inline void +static PRISM_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 = TOK2LOC(parser, closing); PM_NODE_LENGTH_SET_TOKEN(parser, node, closing); @@ -4832,8 +5086,8 @@ pm_interpolated_regular_expression_node_closing_set(pm_parser_t *parser, pm_inte * 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) { +static PRISM_INLINE void +pm_interpolated_string_node_append(pm_arena_t *arena, 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)) @@ -4918,7 +5172,7 @@ pm_interpolated_string_node_append(pm_interpolated_string_node_t *node, pm_node_ break; } - pm_node_list_append(&node->parts, part); + pm_node_list_append(arena, &node->parts, part); #undef CLEAR_FLAGS #undef MUTABLE_FLAGS @@ -4929,7 +5183,6 @@ pm_interpolated_string_node_append(pm_interpolated_string_node_t *node, pm_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) { @@ -4944,17 +5197,20 @@ pm_interpolated_string_node_create(pm_parser_t *parser, const pm_token_t *openin uint32_t start = opening == NULL ? 0 : PM_TOKEN_START(parser, opening); uint32_t end = closing == NULL ? 0 : PM_TOKEN_END(parser, closing); - *node = (pm_interpolated_string_node_t) { - .base = PM_NODE_INIT(parser, PM_INTERPOLATED_STRING_NODE, flags, ((pm_location_t) { .start = start, .length = U32(end - start) })), - .opening_loc = NTOK2LOC(parser, opening), - .closing_loc = NTOK2LOC(parser, closing), - .parts = { 0 } - }; + pm_interpolated_string_node_t *node = pm_interpolated_string_node_new( + parser->arena, + ++parser->node_id, + flags, + ((pm_location_t) { .start = start, .length = U32(end - start) }), + NTOK2LOC(parser, opening), + ((pm_node_list_t) { 0 }), + NTOK2LOC(parser, closing) + ); if (parts != NULL) { pm_node_t *part; PM_NODE_LIST_FOREACH(parts, index, part) { - pm_interpolated_string_node_append(node, part); + pm_interpolated_string_node_append(parser->arena, node, part); } } @@ -4971,12 +5227,12 @@ pm_interpolated_string_node_closing_set(const pm_parser_t *parser, pm_interpolat } static void -pm_interpolated_symbol_node_append(pm_interpolated_symbol_node_t *node, pm_node_t *part) { +pm_interpolated_symbol_node_append(pm_arena_t *arena, pm_interpolated_symbol_node_t *node, pm_node_t *part) { if (node->parts.size == 0 && node->opening_loc.length == 0) { PM_NODE_START_SET_NODE(node, part); } - pm_interpolated_node_append(UP(node), &node->parts, part); + pm_interpolated_node_append(arena, UP(node), &node->parts, part); if (PM_NODE_END(part) > PM_NODE_END(node)) { PM_NODE_LENGTH_SET_NODE(node, part); @@ -4994,22 +5250,23 @@ pm_interpolated_symbol_node_closing_loc_set(const pm_parser_t *parser, pm_interp */ 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); - uint32_t start = opening == NULL ? 0 : PM_TOKEN_START(parser, opening); uint32_t end = closing == NULL ? 0 : PM_TOKEN_END(parser, closing); - *node = (pm_interpolated_symbol_node_t) { - .base = PM_NODE_INIT(parser, PM_INTERPOLATED_SYMBOL_NODE, PM_NODE_FLAG_STATIC_LITERAL, ((pm_location_t) { .start = start, .length = U32(end - start) })), - .opening_loc = NTOK2LOC(parser, opening), - .closing_loc = NTOK2LOC(parser, closing), - .parts = { 0 } - }; + pm_interpolated_symbol_node_t *node = pm_interpolated_symbol_node_new( + parser->arena, + ++parser->node_id, + PM_NODE_FLAG_STATIC_LITERAL, + ((pm_location_t) { .start = start, .length = U32(end - start) }), + NTOK2LOC(parser, opening), + ((pm_node_list_t) { 0 }), + NTOK2LOC(parser, closing) + ); if (parts != NULL) { pm_node_t *part; PM_NODE_LIST_FOREACH(parts, index, part) { - pm_interpolated_symbol_node_append(node, part); + pm_interpolated_symbol_node_append(parser->arena, node, part); } } @@ -5021,25 +5278,24 @@ pm_interpolated_symbol_node_create(pm_parser_t *parser, const pm_token_t *openin */ 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(parser, PM_INTERPOLATED_X_STRING_NODE, 0, PM_LOCATION_INIT_TOKENS(parser, opening, closing)), - .opening_loc = TOK2LOC(parser, opening), - .closing_loc = TOK2LOC(parser, closing), - .parts = { 0 } - }; - - return node; + return pm_interpolated_x_string_node_new( + parser->arena, + ++parser->node_id, + 0, + PM_LOCATION_INIT_TOKENS(parser, opening, closing), + TOK2LOC(parser, opening), + ((pm_node_list_t) { 0 }), + TOK2LOC(parser, closing) + ); } -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); +static PRISM_INLINE void +pm_interpolated_xstring_node_append(pm_arena_t *arena, pm_interpolated_x_string_node_t *node, pm_node_t *part) { + pm_interpolated_node_append(arena, UP(node), &node->parts, part); PM_NODE_LENGTH_SET_NODE(node, part); } -static inline void +static PRISM_INLINE void pm_interpolated_xstring_node_closing_set(const pm_parser_t *parser, pm_interpolated_x_string_node_t *node, const pm_token_t *closing) { node->closing_loc = TOK2LOC(parser, closing); PM_NODE_LENGTH_SET_TOKEN(parser, node, closing); @@ -5050,13 +5306,12 @@ pm_interpolated_xstring_node_closing_set(const pm_parser_t *parser, pm_interpola */ 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(parser, PM_IT_LOCAL_VARIABLE_READ_NODE, 0, PM_LOCATION_INIT_TOKEN(parser, name)), - }; - - return node; + return pm_it_local_variable_read_node_new( + parser->arena, + ++parser->node_id, + 0, + PM_LOCATION_INIT_TOKEN(parser, name) + ); } /** @@ -5064,13 +5319,12 @@ pm_it_local_variable_read_node_create(pm_parser_t *parser, const pm_token_t *nam */ 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(parser, PM_IT_PARAMETERS_NODE, 0, PM_LOCATION_INIT_TOKENS(parser, opening, closing)), - }; - - return node; + return pm_it_parameters_node_new( + parser->arena, + ++parser->node_id, + 0, + PM_LOCATION_INIT_TOKENS(parser, opening, closing) + ); } /** @@ -5078,28 +5332,27 @@ pm_it_parameters_node_create(pm_parser_t *parser, const pm_token_t *opening, con */ 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(parser, PM_KEYWORD_HASH_NODE, PM_KEYWORD_HASH_NODE_FLAGS_SYMBOL_KEYS, PM_LOCATION_INIT_UNSET), - .elements = { 0 } - }; - - return node; + return pm_keyword_hash_node_new( + parser->arena, + ++parser->node_id, + PM_KEYWORD_HASH_NODE_FLAGS_SYMBOL_KEYS, + PM_LOCATION_INIT_UNSET, + ((pm_node_list_t) { 0 }) + ); } /** * 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) { +pm_keyword_hash_node_elements_append(pm_arena_t *arena, 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); + pm_node_list_append(arena, &hash->elements, element); if (PM_NODE_LENGTH(hash) == 0) { PM_NODE_START_SET_NODE(hash, element); } @@ -5111,15 +5364,14 @@ pm_keyword_hash_node_elements_append(pm_keyword_hash_node_t *hash, pm_node_t *el */ 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(parser, PM_REQUIRED_KEYWORD_PARAMETER_NODE, 0, PM_LOCATION_INIT_TOKEN(parser, name)), - .name = pm_parser_constant_id_raw(parser, name->start, name->end - 1), - .name_loc = TOK2LOC(parser, name), - }; - - return node; + return pm_required_keyword_parameter_node_new( + parser->arena, + ++parser->node_id, + 0, + PM_LOCATION_INIT_TOKEN(parser, name), + pm_parser_constant_id_raw(parser, name->start, name->end - 1), + TOK2LOC(parser, name) + ); } /** @@ -5127,16 +5379,15 @@ pm_required_keyword_parameter_node_create(pm_parser_t *parser, const pm_token_t */ 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(parser, PM_OPTIONAL_KEYWORD_PARAMETER_NODE, 0, PM_LOCATION_INIT_TOKEN_NODE(parser, name, value)), - .name = pm_parser_constant_id_raw(parser, name->start, name->end - 1), - .name_loc = TOK2LOC(parser, name), - .value = value - }; - - return node; + return pm_optional_keyword_parameter_node_new( + parser->arena, + ++parser->node_id, + 0, + PM_LOCATION_INIT_TOKEN_NODE(parser, name, value), + pm_parser_constant_id_raw(parser, name->start, name->end - 1), + TOK2LOC(parser, name), + value + ); } /** @@ -5144,16 +5395,15 @@ pm_optional_keyword_parameter_node_create(pm_parser_t *parser, const pm_token_t */ 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 = PM_NODE_INIT(parser, PM_KEYWORD_REST_PARAMETER_NODE, 0, (name == NULL) ? PM_LOCATION_INIT_TOKEN(parser, operator) : PM_LOCATION_INIT_TOKENS(parser, operator, name)), - .name = name == NULL ? 0 : pm_parser_constant_id_token(parser, name), - .name_loc = NTOK2LOC(parser, name), - .operator_loc = TOK2LOC(parser, operator) - }; - - return node; + return pm_keyword_rest_parameter_node_new( + parser->arena, + ++parser->node_id, + 0, + (name == NULL) ? PM_LOCATION_INIT_TOKEN(parser, operator) : PM_LOCATION_INIT_TOKENS(parser, operator, name), + name == NULL ? 0 : pm_parser_constant_id_token(parser, name), + NTOK2LOC(parser, name), + TOK2LOC(parser, operator) + ); } /** @@ -5169,19 +5419,18 @@ pm_lambda_node_create( 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(parser, PM_LAMBDA_NODE, 0, PM_LOCATION_INIT_TOKENS(parser, operator, closing)), - .locals = *locals, - .operator_loc = TOK2LOC(parser, operator), - .opening_loc = TOK2LOC(parser, opening), - .closing_loc = TOK2LOC(parser, closing), - .parameters = parameters, - .body = body - }; - - return node; + return pm_lambda_node_new( + parser->arena, + ++parser->node_id, + 0, + PM_LOCATION_INIT_TOKENS(parser, operator, closing), + *locals, + TOK2LOC(parser, operator), + TOK2LOC(parser, opening), + TOK2LOC(parser, closing), + parameters, + body + ); } /** @@ -5191,18 +5440,18 @@ 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(parser, PM_LOCAL_VARIABLE_AND_WRITE_NODE, 0, PM_LOCATION_INIT_NODES(target, value)), - .name_loc = target->location, - .operator_loc = TOK2LOC(parser, operator), - .value = value, - .name = name, - .depth = depth - }; - return node; + return pm_local_variable_and_write_node_new( + parser->arena, + ++parser->node_id, + 0, + PM_LOCATION_INIT_NODES(target, value), + target->location, + TOK2LOC(parser, operator), + value, + name, + depth + ); } /** @@ -5210,19 +5459,18 @@ pm_local_variable_and_write_node_create(pm_parser_t *parser, pm_node_t *target, */ 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(parser, PM_LOCAL_VARIABLE_OPERATOR_WRITE_NODE, 0, PM_LOCATION_INIT_NODES(target, value)), - .name_loc = target->location, - .binary_operator_loc = TOK2LOC(parser, operator), - .value = value, - .name = name, - .binary_operator = pm_parser_constant_id_raw(parser, operator->start, operator->end - 1), - .depth = depth - }; - - return node; + return pm_local_variable_operator_write_node_new( + parser->arena, + ++parser->node_id, + 0, + PM_LOCATION_INIT_NODES(target, value), + target->location, + TOK2LOC(parser, operator), + value, + name, + pm_parser_constant_id_raw(parser, operator->start, operator->end - 1), + depth + ); } /** @@ -5232,18 +5480,18 @@ 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(parser, PM_LOCAL_VARIABLE_OR_WRITE_NODE, 0, PM_LOCATION_INIT_NODES(target, value)), - .name_loc = target->location, - .operator_loc = TOK2LOC(parser, operator), - .value = value, - .name = name, - .depth = depth - }; - return node; + return pm_local_variable_or_write_node_new( + parser->arena, + ++parser->node_id, + 0, + PM_LOCATION_INIT_NODES(target, value), + target->location, + TOK2LOC(parser, operator), + value, + name, + depth + ); } /** @@ -5253,15 +5501,14 @@ 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(parser, PM_LOCAL_VARIABLE_READ_NODE, 0, PM_LOCATION_INIT_TOKEN(parser, name)), - .name = name_id, - .depth = depth - }; - - return node; + return pm_local_variable_read_node_new( + parser->arena, + ++parser->node_id, + 0, + PM_LOCATION_INIT_TOKEN(parser, name), + name_id, + depth + ); } /** @@ -5288,25 +5535,23 @@ pm_local_variable_read_node_missing_create(pm_parser_t *parser, const pm_token_t */ 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(parser, PM_LOCAL_VARIABLE_WRITE_NODE, flags, ((pm_location_t) { .start = name_loc->start, .length = PM_NODE_END(value) - name_loc->start })), - .name = name, - .depth = depth, - .value = value, - .name_loc = *name_loc, - .operator_loc = TOK2LOC(parser, operator) - }; - - return node; + return pm_local_variable_write_node_new( + parser->arena, + ++parser->node_id, + pm_implicit_array_write_flags(value, PM_WRITE_NODE_FLAGS_IMPLICIT_ARRAY), + ((pm_location_t) { .start = name_loc->start, .length = PM_NODE_END(value) - name_loc->start }), + name, + depth, + *name_loc, + value, + TOK2LOC(parser, operator) + ); } /** * Returns true if the given bounds comprise `it`. */ -static inline bool +static PRISM_INLINE bool pm_token_is_it(const uint8_t *start, const uint8_t *end) { return (end - start == 2) && (start[0] == 'i') && (start[1] == 't'); } @@ -5315,7 +5560,7 @@ pm_token_is_it(const uint8_t *start, const uint8_t *end) { * Returns true if the given bounds comprise a numbered parameter (i.e., they * are of the form /^_\d$/). */ -static inline bool +static PRISM_INLINE bool pm_token_is_numbered_parameter(const pm_parser_t *parser, uint32_t start, uint32_t length) { return ( (length == 2) && @@ -5329,7 +5574,7 @@ pm_token_is_numbered_parameter(const pm_parser_t *parser, uint32_t start, uint32 * Ensure the given bounds do not comprise a numbered parameter. If they do, add * an appropriate error message to the parser. */ -static inline void +static PRISM_INLINE void pm_refute_numbered_parameter(pm_parser_t *parser, uint32_t start, uint32_t length) { if (pm_token_is_numbered_parameter(parser, start, length)) { PM_PARSER_ERR_FORMAT(parser, start, length, PM_ERR_PARAMETER_NUMBERED_RESERVED, parser->start + start); @@ -5343,15 +5588,15 @@ pm_refute_numbered_parameter(pm_parser_t *parser, uint32_t start, uint32_t lengt 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->length); - 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(parser, PM_LOCAL_VARIABLE_TARGET_NODE, 0, ((pm_location_t) { .start = location->start, .length = location->length })), - .name = name, - .depth = depth - }; - return node; + return pm_local_variable_target_node_new( + parser->arena, + ++parser->node_id, + 0, + ((pm_location_t) { .start = location->start, .length = location->length }), + name, + depth + ); } /** @@ -5361,16 +5606,15 @@ 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(parser, PM_MATCH_PREDICATE_NODE, 0, PM_LOCATION_INIT_NODES(value, pattern)), - .value = value, - .pattern = pattern, - .operator_loc = TOK2LOC(parser, operator) - }; - - return node; + return pm_match_predicate_node_new( + parser->arena, + ++parser->node_id, + 0, + PM_LOCATION_INIT_NODES(value, pattern), + value, + pattern, + TOK2LOC(parser, operator) + ); } /** @@ -5380,16 +5624,15 @@ 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(parser, PM_MATCH_REQUIRED_NODE, 0, PM_LOCATION_INIT_NODES(value, pattern)), - .value = value, - .pattern = pattern, - .operator_loc = TOK2LOC(parser, operator) - }; - - return node; + return pm_match_required_node_new( + parser->arena, + ++parser->node_id, + 0, + PM_LOCATION_INIT_NODES(value, pattern), + value, + pattern, + TOK2LOC(parser, operator) + ); } /** @@ -5397,15 +5640,14 @@ pm_match_required_node_create(pm_parser_t *parser, pm_node_t *value, pm_node_t * */ 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(parser, PM_MATCH_WRITE_NODE, 0, PM_LOCATION_INIT_NODE(call)), - .call = call, - .targets = { 0 } - }; - - return node; + return pm_match_write_node_new( + parser->arena, + ++parser->node_id, + 0, + PM_LOCATION_INIT_NODE(call), + call, + ((pm_node_list_t) { 0 }) + ); } /** @@ -5413,19 +5655,18 @@ pm_match_write_node_create(pm_parser_t *parser, pm_call_node_t *call) { */ 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(parser, PM_MODULE_NODE, 0, PM_LOCATION_INIT_TOKENS(parser, module_keyword, end_keyword)), - .locals = (locals == NULL ? ((pm_constant_id_list_t) { .ids = NULL, .size = 0, .capacity = 0 }) : *locals), - .module_keyword_loc = TOK2LOC(parser, module_keyword), - .constant_path = constant_path, - .body = body, - .end_keyword_loc = TOK2LOC(parser, end_keyword), - .name = pm_parser_constant_id_token(parser, name) - }; - - return node; + return pm_module_node_new( + parser->arena, + ++parser->node_id, + 0, + PM_LOCATION_INIT_TOKENS(parser, module_keyword, end_keyword), + (locals == NULL ? ((pm_constant_id_list_t) { .ids = NULL, .size = 0, .capacity = 0 }) : *locals), + TOK2LOC(parser, module_keyword), + constant_path, + body, + TOK2LOC(parser, end_keyword), + pm_parser_constant_id_token(parser, name) + ); } /** @@ -5433,18 +5674,17 @@ pm_module_node_create(pm_parser_t *parser, pm_constant_id_list_t *locals, const */ 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(parser, PM_MULTI_TARGET_NODE, 0, PM_LOCATION_INIT_UNSET), - .lefts = { 0 }, - .rest = NULL, - .rights = { 0 }, - .lparen_loc = { 0 }, - .rparen_loc = { 0 } - }; - - return node; + return pm_multi_target_node_new( + parser->arena, + ++parser->node_id, + 0, + PM_LOCATION_INIT_UNSET, + ((pm_node_list_t) { 0 }), + NULL, + ((pm_node_list_t) { 0 }), + ((pm_location_t) { 0 }), + ((pm_location_t) { 0 }) + ); } /** @@ -5457,19 +5697,19 @@ pm_multi_target_node_targets_append(pm_parser_t *parser, pm_multi_target_node_t node->rest = target; } else { pm_parser_err_node(parser, target, PM_ERR_MULTI_ASSIGN_MULTI_SPLATS); - pm_node_list_append(&node->rights, target); + pm_node_list_append(parser->arena, &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); + pm_node_list_append(parser->arena, &node->rights, target); } } else if (node->rest == NULL) { - pm_node_list_append(&node->lefts, target); + pm_node_list_append(parser->arena, &node->lefts, target); } else { - pm_node_list_append(&node->rights, target); + pm_node_list_append(parser->arena, &node->rights, target); } if (PM_NODE_LENGTH(node) == 0 || (PM_NODE_START(node) > PM_NODE_START(target))) { @@ -5505,25 +5745,21 @@ pm_multi_target_node_closing_set(const pm_parser_t *parser, pm_multi_target_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(parser, PM_MULTI_WRITE_NODE, flags, PM_LOCATION_INIT_NODES(target, value)), - .lefts = target->lefts, - .rest = target->rest, - .rights = target->rights, - .lparen_loc = target->lparen_loc, - .rparen_loc = target->rparen_loc, - .operator_loc = TOK2LOC(parser, 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; + /* The target is no longer necessary because we have reused its children. It + * is arena-allocated so no explicit free is needed. */ + return pm_multi_write_node_new( + parser->arena, + ++parser->node_id, + pm_implicit_array_write_flags(value, PM_WRITE_NODE_FLAGS_IMPLICIT_ARRAY), + PM_LOCATION_INIT_NODES(target, value), + target->lefts, + target->rest, + target->rights, + target->lparen_loc, + target->rparen_loc, + TOK2LOC(parser, operator), + value + ); } /** @@ -5532,15 +5768,15 @@ pm_multi_write_node_create(pm_parser_t *parser, pm_multi_target_node_t *target, 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 = PM_NODE_INIT(parser, PM_NEXT_NODE, 0, (arguments == NULL) ? PM_LOCATION_INIT_TOKEN(parser, keyword) : PM_LOCATION_INIT_TOKEN_NODE(parser, keyword, arguments)), - .keyword_loc = TOK2LOC(parser, keyword), - .arguments = arguments - }; - - return node; + return pm_next_node_new( + parser->arena, + ++parser->node_id, + 0, + (arguments == NULL) ? PM_LOCATION_INIT_TOKEN(parser, keyword) : PM_LOCATION_INIT_TOKEN_NODE(parser, keyword, arguments), + arguments, + TOK2LOC(parser, keyword) + ); } /** @@ -5549,13 +5785,31 @@ pm_next_node_create(pm_parser_t *parser, const pm_token_t *keyword, pm_arguments 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(parser, PM_NIL_NODE, PM_NODE_FLAG_STATIC_LITERAL, PM_LOCATION_INIT_TOKEN(parser, token)) - }; + return pm_nil_node_new( + parser->arena, + ++parser->node_id, + PM_NODE_FLAG_STATIC_LITERAL, + PM_LOCATION_INIT_TOKEN(parser, token) + ); +} - return node; +/** + * Allocate and initialize a new NoKeywordsParameterNode node. + */ +static pm_no_block_parameter_node_t * +pm_no_block_parameter_node_create(pm_parser_t *parser, const pm_token_t *operator, const pm_token_t *keyword) { + assert(operator->type == PM_TOKEN_AMPERSAND || operator->type == PM_TOKEN_UAMPERSAND); + assert(keyword->type == PM_TOKEN_KEYWORD_NIL); + + return pm_no_block_parameter_node_new( + parser->arena, + ++parser->node_id, + 0, + PM_LOCATION_INIT_TOKENS(parser, operator, keyword), + TOK2LOC(parser, operator), + TOK2LOC(parser, keyword) + ); } /** @@ -5565,15 +5819,15 @@ 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(parser, PM_NO_KEYWORDS_PARAMETER_NODE, 0, PM_LOCATION_INIT_TOKENS(parser, operator, keyword)), - .operator_loc = TOK2LOC(parser, operator), - .keyword_loc = TOK2LOC(parser, keyword) - }; - - return node; + return pm_no_keywords_parameter_node_new( + parser->arena, + ++parser->node_id, + 0, + PM_LOCATION_INIT_TOKENS(parser, operator, keyword), + TOK2LOC(parser, operator), + TOK2LOC(parser, keyword) + ); } /** @@ -5581,14 +5835,13 @@ pm_no_keywords_parameter_node_create(pm_parser_t *parser, const pm_token_t *oper */ static pm_numbered_parameters_node_t * pm_numbered_parameters_node_create(pm_parser_t *parser, const pm_token_t *opening, const pm_token_t *closing, 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(parser, PM_NUMBERED_PARAMETERS_NODE, 0, PM_LOCATION_INIT_TOKENS(parser, opening, closing)), - .maximum = maximum - }; - - return node; + return pm_numbered_parameters_node_new( + parser->arena, + ++parser->node_id, + 0, + PM_LOCATION_INIT_TOKENS(parser, opening, closing), + maximum + ); } /** @@ -5628,7 +5881,7 @@ pm_numbered_reference_read_node_number(pm_parser_t *parser, const pm_token_t *to value = 0; } - xfree(digits); + xfree_sized(digits, sizeof(char) * (length + 1)); if ((errno == ERANGE) || (value > NTH_REF_MAX)) { PM_PARSER_WARN_FORMAT(parser, U32(start - parser->start), U32(length), PM_WARN_INVALID_NUMBERED_REFERENCE, (int) (length + 1), (const char *) token->start); @@ -5646,14 +5899,14 @@ pm_numbered_reference_read_node_number(pm_parser_t *parser, const pm_token_t *to 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(parser, PM_NUMBERED_REFERENCE_READ_NODE, 0, PM_LOCATION_INIT_TOKEN(parser, name)), - .number = pm_numbered_reference_read_node_number(parser, name) - }; - return node; + return pm_numbered_reference_read_node_new( + parser->arena, + ++parser->node_id, + 0, + PM_LOCATION_INIT_TOKEN(parser, name), + pm_numbered_reference_read_node_number(parser, name) + ); } /** @@ -5661,17 +5914,16 @@ pm_numbered_reference_read_node_create(pm_parser_t *parser, const pm_token_t *na */ 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(parser, PM_OPTIONAL_PARAMETER_NODE, 0, PM_LOCATION_INIT_TOKEN_NODE(parser, name, value)), - .name = pm_parser_constant_id_token(parser, name), - .name_loc = TOK2LOC(parser, name), - .operator_loc = TOK2LOC(parser, operator), - .value = value - }; - - return node; + return pm_optional_parameter_node_new( + parser->arena, + ++parser->node_id, + 0, + PM_LOCATION_INIT_TOKEN_NODE(parser, name, value), + pm_parser_constant_id_token(parser, name), + TOK2LOC(parser, name), + TOK2LOC(parser, operator), + value + ); } /** @@ -5681,16 +5933,15 @@ 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(parser, PM_OR_NODE, 0, PM_LOCATION_INIT_NODES(left, right)), - .left = left, - .right = right, - .operator_loc = TOK2LOC(parser, operator) - }; - - return node; + return pm_or_node_new( + parser->arena, + ++parser->node_id, + 0, + PM_LOCATION_INIT_NODES(left, right), + left, + right, + TOK2LOC(parser, operator) + ); } /** @@ -5698,20 +5949,19 @@ pm_or_node_create(pm_parser_t *parser, pm_node_t *left, const pm_token_t *operat */ 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(parser, PM_PARAMETERS_NODE, 0, PM_LOCATION_INIT_UNSET), - .rest = NULL, - .keyword_rest = NULL, - .block = NULL, - .requireds = { 0 }, - .optionals = { 0 }, - .posts = { 0 }, - .keywords = { 0 } - }; - - return node; + return pm_parameters_node_new( + parser->arena, + ++parser->node_id, + 0, + PM_LOCATION_INIT_UNSET, + ((pm_node_list_t) { 0 }), + ((pm_node_list_t) { 0 }), + NULL, + ((pm_node_list_t) { 0 }), + ((pm_node_list_t) { 0 }), + NULL, + NULL + ); } /** @@ -5732,27 +5982,27 @@ pm_parameters_node_location_set(pm_parameters_node_t *params, pm_node_t *param) * 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_requireds_append(pm_arena_t *arena, pm_parameters_node_t *params, pm_node_t *param) { pm_parameters_node_location_set(params, param); - pm_node_list_append(¶ms->requireds, param); + pm_node_list_append(arena, ¶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_optionals_append(pm_arena_t *arena, 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)); + pm_node_list_append(arena, ¶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_posts_append(pm_arena_t *arena, pm_parameters_node_t *params, pm_node_t *param) { pm_parameters_node_location_set(params, param); - pm_node_list_append(¶ms->posts, param); + pm_node_list_append(arena, ¶ms->posts, param); } /** @@ -5768,9 +6018,9 @@ pm_parameters_node_rest_set(pm_parameters_node_t *params, pm_node_t *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_keywords_append(pm_arena_t *arena, pm_parameters_node_t *params, pm_node_t *param) { pm_parameters_node_location_set(params, param); - pm_node_list_append(¶ms->keywords, param); + pm_node_list_append(arena, ¶ms->keywords, param); } /** @@ -5787,9 +6037,9 @@ pm_parameters_node_keyword_rest_set(pm_parameters_node_t *params, pm_node_t *par * 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) { +pm_parameters_node_block_set(pm_parameters_node_t *params, pm_node_t *param) { assert(params->block == NULL); - pm_parameters_node_location_set(params, UP(param)); + pm_parameters_node_location_set(params, param); params->block = param; } @@ -5798,15 +6048,14 @@ pm_parameters_node_block_set(pm_parameters_node_t *params, pm_block_parameter_no */ 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(parser, PM_PROGRAM_NODE, 0, PM_LOCATION_INIT_NODE(statements)), - .locals = *locals, - .statements = statements - }; - - return node; + return pm_program_node_new( + parser->arena, + ++parser->node_id, + 0, + PM_LOCATION_INIT_NODE(statements), + *locals, + statements + ); } /** @@ -5814,16 +6063,15 @@ pm_program_node_create(pm_parser_t *parser, pm_constant_id_list_t *locals, pm_st */ 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(parser, PM_PARENTHESES_NODE, flags, PM_LOCATION_INIT_TOKENS(parser, opening, closing)), - .body = body, - .opening_loc = TOK2LOC(parser, opening), - .closing_loc = TOK2LOC(parser, closing) - }; - - return node; + return pm_parentheses_node_new( + parser->arena, + ++parser->node_id, + flags, + PM_LOCATION_INIT_TOKENS(parser, opening, closing), + body, + TOK2LOC(parser, opening), + TOK2LOC(parser, closing) + ); } /** @@ -5831,17 +6079,16 @@ pm_parentheses_node_create(pm_parser_t *parser, const pm_token_t *opening, pm_no */ 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(parser, PM_PINNED_EXPRESSION_NODE, 0, PM_LOCATION_INIT_TOKENS(parser, operator, rparen)), - .expression = expression, - .operator_loc = TOK2LOC(parser, operator), - .lparen_loc = TOK2LOC(parser, lparen), - .rparen_loc = TOK2LOC(parser, rparen) - }; - - return node; + return pm_pinned_expression_node_new( + parser->arena, + ++parser->node_id, + 0, + PM_LOCATION_INIT_TOKENS(parser, operator, rparen), + expression, + TOK2LOC(parser, operator), + TOK2LOC(parser, lparen), + TOK2LOC(parser, rparen) + ); } /** @@ -5849,15 +6096,14 @@ pm_pinned_expression_node_create(pm_parser_t *parser, pm_node_t *expression, con */ 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(parser, PM_PINNED_VARIABLE_NODE, 0, PM_LOCATION_INIT_TOKEN_NODE(parser, operator, variable)), - .variable = variable, - .operator_loc = TOK2LOC(parser, operator) - }; - - return node; + return pm_pinned_variable_node_new( + parser->arena, + ++parser->node_id, + 0, + PM_LOCATION_INIT_TOKEN_NODE(parser, operator, variable), + variable, + TOK2LOC(parser, operator) + ); } /** @@ -5865,17 +6111,16 @@ pm_pinned_variable_node_create(pm_parser_t *parser, const pm_token_t *operator, */ 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(parser, PM_POST_EXECUTION_NODE, 0, PM_LOCATION_INIT_TOKENS(parser, keyword, closing)), - .statements = statements, - .keyword_loc = TOK2LOC(parser, keyword), - .opening_loc = TOK2LOC(parser, opening), - .closing_loc = TOK2LOC(parser, closing) - }; - - return node; + return pm_post_execution_node_new( + parser->arena, + ++parser->node_id, + 0, + PM_LOCATION_INIT_TOKENS(parser, keyword, closing), + statements, + TOK2LOC(parser, keyword), + TOK2LOC(parser, opening), + TOK2LOC(parser, closing) + ); } /** @@ -5883,17 +6128,16 @@ pm_post_execution_node_create(pm_parser_t *parser, const pm_token_t *keyword, co */ 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(parser, PM_PRE_EXECUTION_NODE, 0, PM_LOCATION_INIT_TOKENS(parser, keyword, closing)), - .statements = statements, - .keyword_loc = TOK2LOC(parser, keyword), - .opening_loc = TOK2LOC(parser, opening), - .closing_loc = TOK2LOC(parser, closing) - }; - - return node; + return pm_pre_execution_node_new( + parser->arena, + ++parser->node_id, + 0, + PM_LOCATION_INIT_TOKENS(parser, keyword, closing), + statements, + TOK2LOC(parser, keyword), + TOK2LOC(parser, opening), + TOK2LOC(parser, closing) + ); } /** @@ -5903,8 +6147,6 @@ 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 `...`. @@ -5925,14 +6167,15 @@ pm_range_node_create(pm_parser_t *parser, pm_node_t *left, const pm_token_t *ope uint32_t start = left == NULL ? PM_TOKEN_START(parser, operator) : PM_NODE_START(left); uint32_t end = right == NULL ? PM_TOKEN_END(parser, operator) : PM_NODE_END(right); - *node = (pm_range_node_t) { - .base = PM_NODE_INIT(parser, PM_RANGE_NODE, flags, ((pm_location_t) { .start = start, .length = U32(end - start) })), - .left = left, - .right = right, - .operator_loc = TOK2LOC(parser, operator) - }; - - return node; + return pm_range_node_new( + parser->arena, + ++parser->node_id, + flags, + ((pm_location_t) { .start = start, .length = U32(end - start) }), + left, + right, + TOK2LOC(parser, operator) + ); } /** @@ -5941,13 +6184,13 @@ pm_range_node_create(pm_parser_t *parser, pm_node_t *left, const pm_token_t *ope 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(parser, PM_REDO_NODE, 0, PM_LOCATION_INIT_TOKEN(parser, token)) - }; - return node; + return pm_redo_node_new( + parser->arena, + ++parser->node_id, + 0, + PM_LOCATION_INIT_TOKEN(parser, token) + ); } /** @@ -5956,24 +6199,22 @@ pm_redo_node_create(pm_parser_t *parser, const pm_token_t *token) { */ 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(parser, PM_REGULAR_EXPRESSION_NODE, flags, PM_LOCATION_INIT_TOKENS(parser, opening, closing)), - .opening_loc = TOK2LOC(parser, opening), - .content_loc = TOK2LOC(parser, content), - .closing_loc = TOK2LOC(parser, closing), - .unescaped = *unescaped - }; - - return node; + return pm_regular_expression_node_new( + parser->arena, + ++parser->node_id, + pm_regular_expression_flags_create(parser, closing) | PM_NODE_FLAG_STATIC_LITERAL, + PM_LOCATION_INIT_TOKENS(parser, opening, closing), + TOK2LOC(parser, opening), + TOK2LOC(parser, content), + TOK2LOC(parser, closing), + *unescaped + ); } /** * Allocate a new initialize a new RegularExpressionNode node. */ -static inline pm_regular_expression_node_t * +static PRISM_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); } @@ -5983,14 +6224,13 @@ pm_regular_expression_node_create(pm_parser_t *parser, const pm_token_t *opening */ 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(parser, PM_REQUIRED_PARAMETER_NODE, 0, PM_LOCATION_INIT_TOKEN(parser, token)), - .name = pm_parser_constant_id_token(parser, token) - }; - - return node; + return pm_required_parameter_node_new( + parser->arena, + ++parser->node_id, + 0, + PM_LOCATION_INIT_TOKEN(parser, token), + pm_parser_constant_id_token(parser, token) + ); } /** @@ -5998,16 +6238,15 @@ pm_required_parameter_node_create(pm_parser_t *parser, const pm_token_t *token) */ 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(parser, PM_RESCUE_MODIFIER_NODE, 0, PM_LOCATION_INIT_NODES(expression, rescue_expression)), - .expression = expression, - .keyword_loc = TOK2LOC(parser, keyword), - .rescue_expression = rescue_expression - }; - - return node; + return pm_rescue_modifier_node_new( + parser->arena, + ++parser->node_id, + 0, + PM_LOCATION_INIT_NODES(expression, rescue_expression), + expression, + TOK2LOC(parser, keyword), + rescue_expression + ); } /** @@ -6015,23 +6254,22 @@ pm_rescue_modifier_node_create(pm_parser_t *parser, pm_node_t *expression, const */ 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(parser, PM_RESCUE_NODE, 0, PM_LOCATION_INIT_TOKEN(parser, keyword)), - .keyword_loc = TOK2LOC(parser, keyword), - .operator_loc = { 0 }, - .then_keyword_loc = { 0 }, - .reference = NULL, - .statements = NULL, - .subsequent = NULL, - .exceptions = { 0 } - }; - - return node; + return pm_rescue_node_new( + parser->arena, + ++parser->node_id, + 0, + PM_LOCATION_INIT_TOKEN(parser, keyword), + TOK2LOC(parser, keyword), + ((pm_node_list_t) { 0 }), + ((pm_location_t) { 0 }), + NULL, + ((pm_location_t) { 0 }), + NULL, + NULL + ); } -static inline void +static PRISM_INLINE void pm_rescue_node_operator_set(const pm_parser_t *parser, pm_rescue_node_t *node, const pm_token_t *operator) { node->operator_loc = TOK2LOC(parser, operator); } @@ -6069,8 +6307,8 @@ pm_rescue_node_subsequent_set(pm_rescue_node_t *node, pm_rescue_node_t *subseque * 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); +pm_rescue_node_exceptions_append(pm_arena_t *arena, pm_rescue_node_t *node, pm_node_t *exception) { + pm_node_list_append(arena, &node->exceptions, exception); PM_NODE_LENGTH_SET_NODE(node, exception); } @@ -6079,16 +6317,15 @@ pm_rescue_node_exceptions_append(pm_rescue_node_t *node, pm_node_t *exception) { */ 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 = PM_NODE_INIT(parser, PM_REST_PARAMETER_NODE, 0, (name == NULL) ? PM_LOCATION_INIT_TOKEN(parser, operator) : PM_LOCATION_INIT_TOKENS(parser, operator, name)), - .name = name == NULL ? 0 : pm_parser_constant_id_token(parser, name), - .name_loc = NTOK2LOC(parser, name), - .operator_loc = TOK2LOC(parser, operator) - }; - - return node; + return pm_rest_parameter_node_new( + parser->arena, + ++parser->node_id, + 0, + (name == NULL) ? PM_LOCATION_INIT_TOKEN(parser, operator) : PM_LOCATION_INIT_TOKENS(parser, operator, name), + name == NULL ? 0 : pm_parser_constant_id_token(parser, name), + NTOK2LOC(parser, name), + TOK2LOC(parser, operator) + ); } /** @@ -6097,13 +6334,13 @@ pm_rest_parameter_node_create(pm_parser_t *parser, const pm_token_t *operator, c 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(parser, PM_RETRY_NODE, 0, PM_LOCATION_INIT_TOKEN(parser, token)) - }; - return node; + return pm_retry_node_new( + parser->arena, + ++parser->node_id, + 0, + PM_LOCATION_INIT_TOKEN(parser, token) + ); } /** @@ -6111,15 +6348,14 @@ pm_retry_node_create(pm_parser_t *parser, const pm_token_t *token) { */ 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 = PM_NODE_INIT(parser, PM_RETURN_NODE, 0, (arguments == NULL) ? PM_LOCATION_INIT_TOKEN(parser, keyword) : PM_LOCATION_INIT_TOKEN_NODE(parser, keyword, arguments)), - .keyword_loc = TOK2LOC(parser, keyword), - .arguments = arguments - }; - - return node; + return pm_return_node_new( + parser->arena, + ++parser->node_id, + 0, + (arguments == NULL) ? PM_LOCATION_INIT_TOKEN(parser, keyword) : PM_LOCATION_INIT_TOKEN_NODE(parser, keyword, arguments), + TOK2LOC(parser, keyword), + arguments + ); } /** @@ -6128,13 +6364,13 @@ pm_return_node_create(pm_parser_t *parser, const pm_token_t *keyword, pm_argumen 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(parser, PM_SELF_NODE, 0, PM_LOCATION_INIT_TOKEN(parser, token)) - }; - - return node; + return pm_self_node_new( + parser->arena, + ++parser->node_id, + 0, + PM_LOCATION_INIT_TOKEN(parser, token) + ); } /** @@ -6142,14 +6378,13 @@ pm_self_node_create(pm_parser_t *parser, const pm_token_t *token) { */ 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(parser, PM_SHAREABLE_CONSTANT_NODE, (pm_node_flags_t) value, PM_LOCATION_INIT_NODE(write)), - .write = write - }; - - return node; + return pm_shareable_constant_node_new( + parser->arena, + ++parser->node_id, + (pm_node_flags_t) value, + PM_LOCATION_INIT_NODE(write), + write + ); } /** @@ -6157,19 +6392,18 @@ pm_shareable_constant_node_create(pm_parser_t *parser, pm_node_t *write, pm_shar */ 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(parser, PM_SINGLETON_CLASS_NODE, 0, PM_LOCATION_INIT_TOKENS(parser, class_keyword, end_keyword)), - .locals = *locals, - .class_keyword_loc = TOK2LOC(parser, class_keyword), - .operator_loc = TOK2LOC(parser, operator), - .expression = expression, - .body = body, - .end_keyword_loc = TOK2LOC(parser, end_keyword) - }; - - return node; + return pm_singleton_class_node_new( + parser->arena, + ++parser->node_id, + 0, + PM_LOCATION_INIT_TOKENS(parser, class_keyword, end_keyword), + *locals, + TOK2LOC(parser, class_keyword), + TOK2LOC(parser, operator), + expression, + body, + TOK2LOC(parser, end_keyword) + ); } /** @@ -6178,13 +6412,13 @@ pm_singleton_class_node_create(pm_parser_t *parser, pm_constant_id_list_t *local 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(parser, PM_SOURCE_ENCODING_NODE, PM_NODE_FLAG_STATIC_LITERAL, PM_LOCATION_INIT_TOKEN(parser, token)) - }; - return node; + return pm_source_encoding_node_new( + parser->arena, + ++parser->node_id, + PM_NODE_FLAG_STATIC_LITERAL, + PM_LOCATION_INIT_TOKEN(parser, token) + ); } /** @@ -6192,7 +6426,6 @@ pm_source_encoding_node_create(pm_parser_t *parser, const pm_token_t *token) { */ 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; @@ -6206,12 +6439,13 @@ pm_source_file_node_create(pm_parser_t *parser, const pm_token_t *file_keyword) break; } - *node = (pm_source_file_node_t) { - .base = PM_NODE_INIT(parser, PM_SOURCE_FILE_NODE, flags, PM_LOCATION_INIT_TOKEN(parser, file_keyword)), - .filepath = parser->filepath - }; - - return node; + return pm_source_file_node_new( + parser->arena, + ++parser->node_id, + flags, + PM_LOCATION_INIT_TOKEN(parser, file_keyword), + parser->filepath + ); } /** @@ -6220,13 +6454,13 @@ pm_source_file_node_create(pm_parser_t *parser, const pm_token_t *file_keyword) 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(parser, PM_SOURCE_LINE_NODE, PM_NODE_FLAG_STATIC_LITERAL, PM_LOCATION_INIT_TOKEN(parser, token)) - }; - return node; + return pm_source_line_node_new( + parser->arena, + ++parser->node_id, + PM_NODE_FLAG_STATIC_LITERAL, + PM_LOCATION_INIT_TOKEN(parser, token) + ); } /** @@ -6234,15 +6468,14 @@ pm_source_line_node_create(pm_parser_t *parser, const pm_token_t *token) { */ 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 = PM_NODE_INIT(parser, PM_SPLAT_NODE, 0, (expression == NULL) ? PM_LOCATION_INIT_TOKEN(parser, operator) : PM_LOCATION_INIT_TOKEN_NODE(parser, operator, expression)), - .operator_loc = TOK2LOC(parser, operator), - .expression = expression - }; - - return node; + return pm_splat_node_new( + parser->arena, + ++parser->node_id, + 0, + (expression == NULL) ? PM_LOCATION_INIT_TOKEN(parser, operator) : PM_LOCATION_INIT_TOKEN_NODE(parser, operator, expression), + TOK2LOC(parser, operator), + expression + ); } /** @@ -6250,14 +6483,13 @@ pm_splat_node_create(pm_parser_t *parser, const pm_token_t *operator, pm_node_t */ 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(parser, PM_STATEMENTS_NODE, 0, PM_LOCATION_INIT_UNSET), - .body = { 0 } - }; - - return node; + return pm_statements_node_new( + parser->arena, + ++parser->node_id, + 0, + PM_LOCATION_INIT_UNSET, + ((pm_node_list_t) { 0 }) + ); } /** @@ -6272,7 +6504,7 @@ pm_statements_node_body_length(pm_statements_node_t *node) { * Update the location of the statements node based on the statement that is * being added to the list. */ -static inline void +static PRISM_INLINE void pm_statements_node_body_update(pm_statements_node_t *node, pm_node_t *statement) { if (pm_statements_node_body_length(node) == 0 || PM_NODE_START(statement) < PM_NODE_START(node)) { PM_NODE_START_SET_NODE(node, statement); @@ -6306,7 +6538,7 @@ pm_statements_node_body_append(pm_parser_t *parser, pm_statements_node_t *node, } } - pm_node_list_append(&node->body, statement); + pm_node_list_append(parser->arena, &node->body, statement); if (newline) pm_node_flag_set(statement, PM_NODE_FLAG_NEWLINE); } @@ -6314,18 +6546,17 @@ pm_statements_node_body_append(pm_parser_t *parser, pm_statements_node_t *node, * 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_prepend(pm_arena_t *arena, 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_list_prepend(arena, &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 * +static PRISM_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) { @@ -6340,15 +6571,16 @@ pm_string_node_create_unescaped(pm_parser_t *parser, const pm_token_t *opening, uint32_t start = PM_TOKEN_START(parser, opening == NULL ? content : opening); uint32_t end = PM_TOKEN_END(parser, closing == NULL ? content : closing); - *node = (pm_string_node_t) { - .base = PM_NODE_INIT(parser, PM_STRING_NODE, flags, ((pm_location_t) { .start = start, .length = U32(end - start) })), - .opening_loc = NTOK2LOC(parser, opening), - .content_loc = TOK2LOC(parser, content), - .closing_loc = NTOK2LOC(parser, closing), - .unescaped = *string - }; - - return node; + return pm_string_node_new( + parser->arena, + ++parser->node_id, + flags, + ((pm_location_t) { .start = start, .length = U32(end - start) }), + NTOK2LOC(parser, opening), + TOK2LOC(parser, content), + NTOK2LOC(parser, closing), + *string + ); } /** @@ -6376,21 +6608,21 @@ pm_string_node_create_current_string(pm_parser_t *parser, const pm_token_t *open 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 pm_location_t *end = pm_arguments_end(arguments); assert(end != NULL && "unreachable"); - *node = (pm_super_node_t) { - .base = PM_NODE_INIT(parser, PM_SUPER_NODE, 0, ((pm_location_t) { .start = PM_TOKEN_START(parser, keyword), .length = PM_LOCATION_END(end) - PM_TOKEN_START(parser, keyword) })), - .keyword_loc = TOK2LOC(parser, keyword), - .lparen_loc = arguments->opening_loc, - .arguments = arguments->arguments, - .rparen_loc = arguments->closing_loc, - .block = arguments->block - }; - - return node; + return pm_super_node_new( + parser->arena, + ++parser->node_id, + 0, + ((pm_location_t) { .start = PM_TOKEN_START(parser, keyword), .length = PM_LOCATION_END(end) - PM_TOKEN_START(parser, keyword) }), + TOK2LOC(parser, keyword), + arguments->opening_loc, + arguments->arguments, + arguments->closing_loc, + arguments->block + ); } /** @@ -6455,7 +6687,7 @@ parse_symbol_encoding_validate_other(pm_parser_t *parser, const pm_token_t *loca * 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 +static PRISM_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 @@ -6480,155 +6712,31 @@ parse_symbol_encoding(pm_parser_t *parser, const pm_token_t *location, const pm_ 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); - uint32_t start = opening == NULL ? PM_TOKEN_START(parser, value) : PM_TOKEN_START(parser, opening); uint32_t end = closing == NULL ? PM_TOKEN_END(parser, value) : PM_TOKEN_END(parser, closing); - *node = (pm_symbol_node_t) { - .base = PM_NODE_INIT(parser, PM_SYMBOL_NODE, PM_NODE_FLAG_STATIC_LITERAL | flags, ((pm_location_t) { .start = start, .length = U32(end - start) })), - .opening_loc = NTOK2LOC(parser, opening), - .value_loc = NTOK2LOC(parser, value), - .closing_loc = NTOK2LOC(parser, closing), - .unescaped = *unescaped - }; - - return node; + return pm_symbol_node_new( + parser->arena, + ++parser->node_id, + PM_NODE_FLAG_STATIC_LITERAL | flags, + ((pm_location_t) { .start = start, .length = U32(end - start) }), + NTOK2LOC(parser, opening), + NTOK2LOC(parser, value), + NTOK2LOC(parser, closing), + *unescaped + ); } /** * Allocate and initialize a new SymbolNode node. */ -static inline pm_symbol_node_t * +static PRISM_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); } @@ -6666,13 +6774,16 @@ pm_symbol_node_label_create(pm_parser_t *parser, const pm_token_t *token) { */ 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(parser, PM_SYMBOL_NODE, PM_NODE_FLAG_STATIC_LITERAL | PM_SYMBOL_FLAGS_FORCED_US_ASCII_ENCODING, PM_LOCATION_INIT_UNSET), - .value_loc = { 0 }, - .unescaped = { 0 } - }; + pm_symbol_node_t *node = pm_symbol_node_new( + parser->arena, + ++parser->node_id, + PM_NODE_FLAG_STATIC_LITERAL | PM_SYMBOL_FLAGS_FORCED_US_ASCII_ENCODING, + PM_LOCATION_INIT_UNSET, + ((pm_location_t) { 0 }), + ((pm_location_t) { 0 }), + ((pm_location_t) { 0 }), + ((pm_string_t) { 0 }) + ); pm_string_constant_init(&node->unescaped, content, strlen(content)); return node; @@ -6712,15 +6823,16 @@ pm_symbol_node_label_p(const pm_parser_t *parser, const pm_node_t *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(parser, PM_SYMBOL_NODE, PM_NODE_FLAG_STATIC_LITERAL, PM_LOCATION_INIT_TOKENS(parser, opening, closing)), - .opening_loc = TOK2LOC(parser, opening), - .value_loc = node->content_loc, - .closing_loc = TOK2LOC(parser, closing), - .unescaped = node->unescaped - }; + pm_symbol_node_t *new_node = pm_symbol_node_new( + parser->arena, + ++parser->node_id, + PM_NODE_FLAG_STATIC_LITERAL, + PM_LOCATION_INIT_TOKENS(parser, opening, closing), + TOK2LOC(parser, opening), + node->content_loc, + TOK2LOC(parser, closing), + node->unescaped + ); pm_token_t content = { .type = PM_TOKEN_IDENTIFIER, @@ -6730,11 +6842,7 @@ pm_string_node_to_symbol_node(pm_parser_t *parser, pm_string_node_t *node, const 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); - + /* The old node is arena-allocated so no explicit free is needed. */ return new_node; } @@ -6743,7 +6851,6 @@ pm_string_node_to_symbol_node(pm_parser_t *parser, pm_string_node_t *node, const */ 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) { @@ -6755,19 +6862,18 @@ pm_symbol_node_to_string_node(pm_parser_t *parser, pm_symbol_node_t *node) { break; } - *new_node = (pm_string_node_t) { - .base = PM_NODE_INIT(parser, PM_STRING_NODE, flags, PM_LOCATION_INIT_NODE(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); + pm_string_node_t *new_node = pm_string_node_new( + parser->arena, + ++parser->node_id, + flags, + PM_LOCATION_INIT_NODE(node), + node->opening_loc, + node->value_loc, + node->closing_loc, + node->unescaped + ); + /* The old node is arena-allocated so no explicit free is needed. */ return new_node; } @@ -6777,13 +6883,13 @@ pm_symbol_node_to_string_node(pm_parser_t *parser, pm_symbol_node_t *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(parser, PM_TRUE_NODE, PM_NODE_FLAG_STATIC_LITERAL, PM_LOCATION_INIT_TOKEN(parser, token)) - }; - return node; + return pm_true_node_new( + parser->arena, + ++parser->node_id, + PM_NODE_FLAG_STATIC_LITERAL, + PM_LOCATION_INIT_TOKEN(parser, token) + ); } /** @@ -6791,13 +6897,12 @@ pm_true_node_create(pm_parser_t *parser, const pm_token_t *token) { */ 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(parser, PM_TRUE_NODE, PM_NODE_FLAG_STATIC_LITERAL, PM_LOCATION_INIT_UNSET) - }; - - return node; + return pm_true_node_new( + parser->arena, + ++parser->node_id, + PM_NODE_FLAG_STATIC_LITERAL, + PM_LOCATION_INIT_UNSET + ); } /** @@ -6806,24 +6911,24 @@ pm_true_node_synthesized_create(pm_parser_t *parser) { 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(parser, PM_UNDEF_NODE, 0, PM_LOCATION_INIT_TOKEN(parser, token)), - .keyword_loc = TOK2LOC(parser, token), - .names = { 0 } - }; - return node; + return pm_undef_node_new( + parser->arena, + ++parser->node_id, + 0, + PM_LOCATION_INIT_TOKEN(parser, token), + ((pm_node_list_t) { 0 }), + TOK2LOC(parser, token) + ); } /** * Append a name to an undef node. */ static void -pm_undef_node_append(pm_undef_node_t *node, pm_node_t *name) { +pm_undef_node_append(pm_arena_t *arena, pm_undef_node_t *node, pm_node_t *name) { PM_NODE_LENGTH_SET_NODE(node, name); - pm_node_list_append(&node->names, name); + pm_node_list_append(arena, &node->names, name); } /** @@ -6832,21 +6937,20 @@ pm_undef_node_append(pm_undef_node_t *node, pm_node_t *name) { 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(parser, PM_UNLESS_NODE, PM_NODE_FLAG_NEWLINE, PM_LOCATION_INIT_TOKEN_NODE(parser, keyword, end)), - .keyword_loc = TOK2LOC(parser, keyword), - .predicate = predicate, - .then_keyword_loc = NTOK2LOC(parser, then_keyword), - .statements = statements, - .else_clause = NULL, - .end_keyword_loc = { 0 } - }; - - return node; + return pm_unless_node_new( + parser->arena, + ++parser->node_id, + PM_NODE_FLAG_NEWLINE, + PM_LOCATION_INIT_TOKEN_NODE(parser, keyword, end), + TOK2LOC(parser, keyword), + predicate, + NTOK2LOC(parser, then_keyword), + statements, + NULL, + ((pm_location_t) { 0 }) + ); } /** @@ -6855,25 +6959,25 @@ pm_unless_node_create(pm_parser_t *parser, const pm_token_t *keyword, pm_node_t 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(parser, PM_UNLESS_NODE, PM_NODE_FLAG_NEWLINE, PM_LOCATION_INIT_NODES(statement, predicate)), - .keyword_loc = TOK2LOC(parser, unless_keyword), - .predicate = predicate, - .then_keyword_loc = { 0 }, - .statements = statements, - .else_clause = NULL, - .end_keyword_loc = { 0 } - }; - - return node; + return pm_unless_node_new( + parser->arena, + ++parser->node_id, + PM_NODE_FLAG_NEWLINE, + PM_LOCATION_INIT_NODES(statement, predicate), + TOK2LOC(parser, unless_keyword), + predicate, + ((pm_location_t) { 0 }), + statements, + NULL, + ((pm_location_t) { 0 }) + ); } -static inline void +static PRISM_INLINE void pm_unless_node_end_keyword_loc_set(const pm_parser_t *parser, pm_unless_node_t *node, const pm_token_t *end_keyword) { node->end_keyword_loc = TOK2LOC(parser, end_keyword); PM_NODE_LENGTH_SET_TOKEN(parser, node, end_keyword); @@ -6907,19 +7011,19 @@ pm_loop_modifier_block_exits(pm_parser_t *parser, pm_statements_node_t *statemen */ 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(parser, PM_UNTIL_NODE, flags, PM_LOCATION_INIT_TOKENS(parser, keyword, closing)), - .keyword_loc = TOK2LOC(parser, keyword), - .do_keyword_loc = NTOK2LOC(parser, do_keyword), - .closing_loc = TOK2LOC(parser, closing), - .predicate = predicate, - .statements = statements - }; - - return node; + return pm_until_node_new( + parser->arena, + ++parser->node_id, + flags, + PM_LOCATION_INIT_TOKENS(parser, keyword, closing), + TOK2LOC(parser, keyword), + NTOK2LOC(parser, do_keyword), + TOK2LOC(parser, closing), + predicate, + statements + ); } /** @@ -6927,20 +7031,20 @@ pm_until_node_create(pm_parser_t *parser, const pm_token_t *keyword, const pm_to */ 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(parser, PM_UNTIL_NODE, flags, PM_LOCATION_INIT_NODES(statements, predicate)), - .keyword_loc = TOK2LOC(parser, keyword), - .do_keyword_loc = { 0 }, - .closing_loc = { 0 }, - .predicate = predicate, - .statements = statements - }; - - return node; + return pm_until_node_new( + parser->arena, + ++parser->node_id, + flags, + PM_LOCATION_INIT_NODES(statements, predicate), + TOK2LOC(parser, keyword), + ((pm_location_t) { 0 }), + ((pm_location_t) { 0 }), + predicate, + statements + ); } /** @@ -6948,32 +7052,31 @@ pm_until_node_modifier_create(pm_parser_t *parser, const pm_token_t *keyword, pm */ 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(parser, PM_WHEN_NODE, 0, PM_LOCATION_INIT_TOKEN(parser, keyword)), - .keyword_loc = TOK2LOC(parser, keyword), - .statements = NULL, - .then_keyword_loc = { 0 }, - .conditions = { 0 } - }; - - return node; + return pm_when_node_new( + parser->arena, + ++parser->node_id, + 0, + PM_LOCATION_INIT_TOKEN(parser, keyword), + TOK2LOC(parser, keyword), + ((pm_node_list_t) { 0 }), + ((pm_location_t) { 0 }), + NULL + ); } /** * Append a new condition to a when node. */ static void -pm_when_node_conditions_append(pm_when_node_t *node, pm_node_t *condition) { +pm_when_node_conditions_append(pm_arena_t *arena, pm_when_node_t *node, pm_node_t *condition) { PM_NODE_LENGTH_SET_NODE(node, condition); - pm_node_list_append(&node->conditions, condition); + pm_node_list_append(arena, &node->conditions, condition); } /** * Set the location of the then keyword of a when node. */ -static inline void +static PRISM_INLINE void pm_when_node_then_keyword_loc_set(const pm_parser_t *parser, pm_when_node_t *node, const pm_token_t *then_keyword) { PM_NODE_LENGTH_SET_TOKEN(parser, node, then_keyword); node->then_keyword_loc = TOK2LOC(parser, then_keyword); @@ -6996,19 +7099,19 @@ pm_when_node_statements_set(pm_when_node_t *node, pm_statements_node_t *statemen */ 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(parser, PM_WHILE_NODE, flags, PM_LOCATION_INIT_TOKENS(parser, keyword, closing)), - .keyword_loc = TOK2LOC(parser, keyword), - .do_keyword_loc = NTOK2LOC(parser, do_keyword), - .closing_loc = TOK2LOC(parser, closing), - .predicate = predicate, - .statements = statements - }; - - return node; + return pm_while_node_new( + parser->arena, + ++parser->node_id, + flags, + PM_LOCATION_INIT_TOKENS(parser, keyword, closing), + TOK2LOC(parser, keyword), + NTOK2LOC(parser, do_keyword), + TOK2LOC(parser, closing), + predicate, + statements + ); } /** @@ -7016,20 +7119,20 @@ pm_while_node_create(pm_parser_t *parser, const pm_token_t *keyword, const pm_to */ 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(parser, PM_WHILE_NODE, flags, PM_LOCATION_INIT_NODES(statements, predicate)), - .keyword_loc = TOK2LOC(parser, keyword), - .do_keyword_loc = { 0 }, - .closing_loc = { 0 }, - .predicate = predicate, - .statements = statements - }; - - return node; + return pm_while_node_new( + parser->arena, + ++parser->node_id, + flags, + PM_LOCATION_INIT_NODES(statements, predicate), + TOK2LOC(parser, keyword), + ((pm_location_t) { 0 }), + ((pm_location_t) { 0 }), + predicate, + statements + ); } /** @@ -7037,18 +7140,17 @@ pm_while_node_modifier_create(pm_parser_t *parser, const pm_token_t *keyword, pm */ 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(parser, PM_WHILE_NODE, 0, PM_LOCATION_INIT_UNSET), - .keyword_loc = { 0 }, - .do_keyword_loc = { 0 }, - .closing_loc = { 0 }, - .predicate = predicate, - .statements = statements - }; - - return node; + return pm_while_node_new( + parser->arena, + ++parser->node_id, + 0, + PM_LOCATION_INIT_UNSET, + ((pm_location_t) { 0 }), + ((pm_location_t) { 0 }), + ((pm_location_t) { 0 }), + predicate, + statements + ); } /** @@ -7057,23 +7159,22 @@ pm_while_node_synthesized_create(pm_parser_t *parser, pm_node_t *predicate, pm_s */ 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(parser, PM_X_STRING_NODE, PM_STRING_FLAGS_FROZEN, PM_LOCATION_INIT_TOKENS(parser, opening, closing)), - .opening_loc = TOK2LOC(parser, opening), - .content_loc = TOK2LOC(parser, content), - .closing_loc = TOK2LOC(parser, closing), - .unescaped = *unescaped - }; - - return node; + return pm_x_string_node_new( + parser->arena, + ++parser->node_id, + PM_STRING_FLAGS_FROZEN, + PM_LOCATION_INIT_TOKENS(parser, opening, closing), + TOK2LOC(parser, opening), + TOK2LOC(parser, content), + TOK2LOC(parser, closing), + *unescaped + ); } /** * Allocate and initialize a new XStringNode node. */ -static inline pm_x_string_node_t * +static PRISM_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); } @@ -7083,8 +7184,6 @@ pm_xstring_node_create(pm_parser_t *parser, const pm_token_t *opening, const pm_ */ 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); - uint32_t start = PM_TOKEN_START(parser, keyword); uint32_t end; @@ -7098,15 +7197,16 @@ pm_yield_node_create(pm_parser_t *parser, const pm_token_t *keyword, const pm_lo end = PM_TOKEN_END(parser, keyword); } - *node = (pm_yield_node_t) { - .base = PM_NODE_INIT(parser, PM_YIELD_NODE, 0, ((pm_location_t) { .start = start, .length = U32(end - start) })), - .keyword_loc = TOK2LOC(parser, keyword), - .lparen_loc = *lparen_loc, - .arguments = arguments, - .rparen_loc = *rparen_loc - }; - - return node; + return pm_yield_node_new( + parser->arena, + ++parser->node_id, + 0, + ((pm_location_t) { .start = start, .length = U32(end - start) }), + TOK2LOC(parser, keyword), + *lparen_loc, + arguments, + *rparen_loc + ); } /** @@ -7134,7 +7234,7 @@ pm_parser_local_depth_constant_id(pm_parser_t *parser, pm_constant_id_t constant * described by the given token. This function implicitly inserts a constant * into the constant pool. */ -static inline int +static PRISM_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)); } @@ -7142,7 +7242,7 @@ pm_parser_local_depth(pm_parser_t *parser, pm_token_t *token) { /** * Add a constant id to the local table of the current scope. */ -static inline void +static PRISM_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, U32(start - parser->start), U32(end - start), reads); } @@ -7160,7 +7260,7 @@ pm_parser_local_add_raw(pm_parser_t *parser, const uint8_t *start, const uint8_t /** * Add a local variable from a location to the current scope. */ -static inline pm_constant_id_t +static PRISM_INLINE pm_constant_id_t pm_parser_local_add_location(pm_parser_t *parser, pm_location_t *location, uint32_t reads) { return pm_parser_local_add_raw(parser, parser->start + location->start, parser->start + location->start + location->length, reads); } @@ -7168,7 +7268,7 @@ pm_parser_local_add_location(pm_parser_t *parser, pm_location_t *location, uint3 /** * Add a local variable from a token to the current scope. */ -static inline pm_constant_id_t +static PRISM_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_raw(parser, token->start, token->end, reads); } @@ -7228,8 +7328,7 @@ 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); + xfree_sized(scope, sizeof(pm_scope_t)); } /******************************************************************************/ @@ -7239,7 +7338,7 @@ pm_parser_scope_pop(pm_parser_t *parser) { /** * Pushes a value onto the stack. */ -static inline void +static PRISM_INLINE void pm_state_stack_push(pm_state_stack_t *stack, bool value) { *stack = (*stack << 1) | (value & 1); } @@ -7247,7 +7346,7 @@ pm_state_stack_push(pm_state_stack_t *stack, bool value) { /** * Pops a value off the stack. */ -static inline void +static PRISM_INLINE void pm_state_stack_pop(pm_state_stack_t *stack) { *stack >>= 1; } @@ -7255,38 +7354,38 @@ pm_state_stack_pop(pm_state_stack_t *stack) { /** * Returns the value at the top of the stack. */ -static inline bool +static PRISM_INLINE bool pm_state_stack_p(const pm_state_stack_t *stack) { return *stack & 1; } -static inline void +static PRISM_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 +static PRISM_INLINE void pm_accepts_block_stack_pop(pm_parser_t *parser) { pm_state_stack_pop(&parser->accepts_block_stack); } -static inline bool +static PRISM_INLINE bool pm_accepts_block_stack_p(pm_parser_t *parser) { return !pm_state_stack_p(&parser->accepts_block_stack); } -static inline void +static PRISM_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 +static PRISM_INLINE void pm_do_loop_stack_pop(pm_parser_t *parser) { pm_state_stack_pop(&parser->do_loop_stack); } -static inline bool +static PRISM_INLINE bool pm_do_loop_stack_p(pm_parser_t *parser) { return pm_state_stack_p(&parser->do_loop_stack); } @@ -7299,7 +7398,7 @@ pm_do_loop_stack_p(pm_parser_t *parser) { * 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 +static PRISM_INLINE uint8_t peek_at(const pm_parser_t *parser, const uint8_t *cursor) { if (cursor < parser->end) { return *cursor; @@ -7313,7 +7412,7 @@ peek_at(const pm_parser_t *parser, const uint8_t *cursor) { * adding the given offset. If that position is beyond the end of the source * then return '\0'. */ -static inline uint8_t +static PRISM_INLINE uint8_t peek_offset(pm_parser_t *parser, ptrdiff_t offset) { return peek_at(parser, parser->current.end + offset); } @@ -7322,7 +7421,7 @@ peek_offset(pm_parser_t *parser, ptrdiff_t 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 +static PRISM_INLINE uint8_t peek(const pm_parser_t *parser) { return peek_at(parser, parser->current.end); } @@ -7331,7 +7430,7 @@ peek(const pm_parser_t *parser) { * If the character to be read matches the given value, then returns true and * advances the current pointer. */ -static inline bool +static PRISM_INLINE bool match(pm_parser_t *parser, uint8_t value) { if (peek(parser) == value) { parser->current.end++; @@ -7344,7 +7443,7 @@ match(pm_parser_t *parser, uint8_t value) { * 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 +static PRISM_INLINE size_t match_eol_at(pm_parser_t *parser, const uint8_t *cursor) { if (peek_at(parser, cursor) == '\n') { return 1; @@ -7360,7 +7459,7 @@ match_eol_at(pm_parser_t *parser, const uint8_t *cursor) { * `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 +static PRISM_INLINE size_t match_eol_offset(pm_parser_t *parser, ptrdiff_t offset) { return match_eol_at(parser, parser->current.end + offset); } @@ -7370,7 +7469,7 @@ match_eol_offset(pm_parser_t *parser, ptrdiff_t offset) { * or 0 if it is not a line ending. This function is intended to be CRLF/LF * agnostic. */ -static inline size_t +static PRISM_INLINE size_t match_eol(pm_parser_t *parser) { return match_eol_at(parser, parser->current.end); } @@ -7378,7 +7477,7 @@ match_eol(pm_parser_t *parser) { /** * Skip to the next newline character or NUL byte. */ -static inline const uint8_t * +static PRISM_INLINE const uint8_t * next_newline(const uint8_t *cursor, ptrdiff_t length) { assert(length >= 0); @@ -7391,7 +7490,7 @@ next_newline(const uint8_t *cursor, ptrdiff_t length) { /** * This is equivalent to the predicate of warn_balanced in CRuby. */ -static inline bool +static PRISM_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)); } @@ -7494,7 +7593,7 @@ parser_lex_magic_comment_boolean_value(const uint8_t *value_start, uint32_t valu } } -static inline bool +static PRISM_INLINE bool pm_char_is_magic_comment_key_delimiter(const uint8_t b) { return b == '\'' || b == '"' || b == ':' || b == ';'; } @@ -7504,13 +7603,15 @@ pm_char_is_magic_comment_key_delimiter(const uint8_t b) { * found, it returns a pointer to the start of the marker. Otherwise it returns * NULL. */ -static inline const uint8_t * +static PRISM_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; + // Scan for '*' as the middle character, since it is rarer than '-' in + // typical comments and avoids repeated memchr calls for '-' that hit + // dashes in words like "foo-bar". + while ((cursor + 3 <= end) && (cursor = pm_memchr(cursor + 1, '*', (size_t) (end - cursor - 1), parser->encoding_changed, parser->encoding)) != NULL) { + if (cursor[-1] == '-' && cursor + 1 < end && cursor[1] == '-') { + return cursor - 1; } - cursor++; } return NULL; } @@ -7525,7 +7626,7 @@ parser_lex_magic_comment_emacs_marker(pm_parser_t *parser, const uint8_t *cursor * It returns true if it consumes the entire comment. Otherwise it returns * false. */ -static inline bool +static PRISM_INLINE bool parser_lex_magic_comment(pm_parser_t *parser, bool semantic_token_seen) { bool result = true; @@ -7547,11 +7648,24 @@ parser_lex_magic_comment(pm_parser_t *parser, bool semantic_token_seen) { // have a magic comment. return false; } + } else { + // Non-emacs magic comments must contain a colon for `key: value`. + // Reject early if there is no colon to avoid scanning the entire + // comment character-by-character. + if (pm_memchr(start, ':', (size_t) (end - start), parser->encoding_changed, parser->encoding) == NULL) { + return false; + } + + // Advance start past leading whitespace so the main loop begins + // directly at the key, avoiding a redundant whitespace scan. + start += pm_strspn_whitespace(start, end - start); } cursor = start; while (cursor < end) { - while (cursor < end && (pm_char_is_magic_comment_key_delimiter(*cursor) || pm_char_is_whitespace(*cursor))) cursor++; + if (indicator) { + 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++; @@ -7712,15 +7826,14 @@ parser_lex_magic_comment(pm_parser_t *parser, bool semantic_token_seen) { // When we're done, we want to free the string in case we had to // allocate memory for it. - pm_string_free(&key); + pm_string_cleanup(&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 = (pm_location_t) { .start = U32(key_start - parser->start), .length = U32(key_length) }; - magic_comment->value = (pm_location_t) { .start = U32(value_start - parser->start), .length = value_length }; - pm_list_append(&parser->magic_comment_list, (pm_list_node_t *) magic_comment); - } + pm_magic_comment_t *magic_comment = (pm_magic_comment_t *) pm_arena_alloc(&parser->metadata_arena, sizeof(pm_magic_comment_t), PRISM_ALIGNOF(pm_magic_comment_t)); + magic_comment->node.next = NULL; + magic_comment->key = (pm_location_t) { .start = U32(key_start - parser->start), .length = U32(key_length) }; + magic_comment->value = (pm_location_t) { .start = U32(value_start - parser->start), .length = value_length }; + pm_list_append(&parser->magic_comment_list, (pm_list_node_t *) magic_comment); } return result; @@ -7788,7 +7901,7 @@ static const uint32_t context_terminators[] = { [PM_CONTEXT_WHILE] = (1U << PM_TOKEN_KEYWORD_END), }; -static inline bool +static PRISM_INLINE bool context_terminator(pm_context_t context, pm_token_t *token) { return token->type < 32 && (context_terminators[context] & (1U << token->type)); } @@ -7829,7 +7942,7 @@ context_push(pm_parser_t *parser, pm_context_t context) { static void context_pop(pm_parser_t *parser) { pm_context_node_t *prev = parser->current_context->prev; - xfree(parser->current_context); + xfree_sized(parser->current_context, sizeof(pm_context_node_t)); parser->current_context = prev; } @@ -7952,7 +8065,7 @@ context_human(pm_context_t context) { /* Specific token lexers */ /******************************************************************************/ -static inline void +static PRISM_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; @@ -8067,7 +8180,7 @@ lex_numeric_prefix(pm_parser_t *parser, bool* seen_e) { pm_parser_err_current(parser, PM_ERR_INVALID_NUMBER_BINARY); } - parser->integer_base = PM_INTEGER_BASE_FLAGS_BINARY; + parser->integer.base = PM_INTEGER_BASE_FLAGS_BINARY; break; // 0o1111 is an octal number @@ -8081,7 +8194,7 @@ lex_numeric_prefix(pm_parser_t *parser, bool* seen_e) { pm_parser_err_current(parser, PM_ERR_INVALID_NUMBER_OCTAL); } - parser->integer_base = PM_INTEGER_BASE_FLAGS_OCTAL; + parser->integer.base = PM_INTEGER_BASE_FLAGS_OCTAL; break; // 01111 is an octal number @@ -8095,7 +8208,7 @@ lex_numeric_prefix(pm_parser_t *parser, bool* seen_e) { case '6': case '7': parser->current.end += pm_strspn_octal_number_validate(parser, parser->current.end); - parser->integer_base = PM_INTEGER_BASE_FLAGS_OCTAL; + parser->integer.base = PM_INTEGER_BASE_FLAGS_OCTAL; break; // 0x1111 is a hexadecimal number @@ -8109,7 +8222,7 @@ lex_numeric_prefix(pm_parser_t *parser, bool* seen_e) { pm_parser_err_current(parser, PM_ERR_INVALID_NUMBER_HEXADECIMAL); } - parser->integer_base = PM_INTEGER_BASE_FLAGS_HEXADECIMAL; + parser->integer.base = PM_INTEGER_BASE_FLAGS_HEXADECIMAL; break; // 0.xxx is a float @@ -8127,11 +8240,62 @@ lex_numeric_prefix(pm_parser_t *parser, bool* seen_e) { } } 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); + // decimal number. We compute the integer value inline to avoid + // re-scanning the digits later in pm_integer_parse. + { + const uint8_t *cursor = parser->current.end; + const uint8_t *end = parser->end; + uint64_t value = (uint64_t) (cursor[-1] - '0'); + + bool has_underscore = false; + bool prev_underscore = false; + const uint8_t *invalid = NULL; + + while (cursor < end) { + uint8_t c = *cursor; + if (c >= '0' && c <= '9') { + if (value <= UINT32_MAX) value = value * 10 + (uint64_t) (c - '0'); + prev_underscore = false; + cursor++; + } else if (c == '_') { + has_underscore = true; + if (prev_underscore && invalid == NULL) invalid = cursor; + prev_underscore = true; + cursor++; + } else { + break; + } + } + + if (has_underscore) { + if (prev_underscore && invalid == NULL) invalid = cursor - 1; + pm_strspn_number_validate(parser, parser->current.end, (size_t) (cursor - parser->current.end), invalid); + } + + if (value <= UINT32_MAX) { + parser->integer.value = (uint32_t) value; + parser->integer.lexed = true; + } + + parser->current.end = cursor; + } // Afterward, we'll lex as far as we can into an optional float suffix. - type = lex_optional_float_suffix(parser, seen_e); + // Guard the function call: the vast majority of decimal numbers are + // plain integers, so avoid the call when the next byte cannot start a + // float suffix. + { + uint8_t next = peek(parser); + if (next == '.' || next == 'e' || next == 'E') { + type = lex_optional_float_suffix(parser, seen_e); + + // If it turned out to be a float, the cached integer value is + // invalid. + if (type != PM_TOKEN_INTEGER) { + parser->integer.lexed = false; + } + } + } } // At this point we have a completed number, but we want to provide the user @@ -8150,7 +8314,8 @@ lex_numeric_prefix(pm_parser_t *parser, bool* seen_e) { 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; + parser->integer.base = PM_INTEGER_BASE_FLAGS_DECIMAL; + parser->integer.lexed = false; if (parser->current.end < parser->end) { bool seen_e = false; @@ -8299,7 +8464,7 @@ lex_global_variable(pm_parser_t *parser) { * * `type` - the expected token type * * `modifier_type` - the expected modifier token type */ -static inline pm_token_type_t +static PRISM_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; @@ -8338,6 +8503,10 @@ lex_identifier(pm_parser_t *parser, bool previous_command_start) { current_end += width; } } else { + // Fast path: scan ASCII identifier bytes using wide operations. + current_end += scan_identifier_ascii(current_end, end); + + // Byte-at-a-time fallback for the tail and any UTF-8 sequences. while ((width = char_is_identifier_utf8(current_end, end - current_end)) > 0) { current_end += width; } @@ -8397,9 +8566,15 @@ lex_identifier(pm_parser_t *parser, bool previous_command_start) { 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 (parser->enclosure_nesting == parser->lambda_enclosure_nesting) { + return PM_TOKEN_KEYWORD_DO; + } if (pm_do_loop_stack_p(parser)) { return PM_TOKEN_KEYWORD_DO_LOOP; } + if (!pm_accepts_block_stack_p(parser)) { + return PM_TOKEN_KEYWORD_DO_BLOCK; + } return PM_TOKEN_KEYWORD_DO; } @@ -8630,7 +8805,7 @@ static const bool ascii_printable_chars[] = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0 }; -static inline bool +static PRISM_INLINE bool char_is_ascii_printable(const uint8_t b) { return (b < 0x80) && ascii_printable_chars[b]; } @@ -8639,7 +8814,7 @@ char_is_ascii_printable(const uint8_t b) { * Return the value that a hexadecimal digit character represents. For example, * transform 'a' into 10, 'b' into 11, etc. */ -static inline uint8_t +static PRISM_INLINE uint8_t escape_hexadecimal_digit(const uint8_t value) { return (uint8_t) ((value <= '9') ? (value - '0') : (value & 0x7) + 9); } @@ -8649,8 +8824,8 @@ escape_hexadecimal_digit(const uint8_t value) { * 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) { +static PRISM_INLINE uint32_t +escape_unicode(pm_parser_t *parser, const uint8_t *string, size_t length, const pm_location_t *error_location, const uint8_t flags) { uint32_t value = 0; for (size_t index = 0; index < length; index++) { if (index != 0) value <<= 4; @@ -8660,7 +8835,10 @@ escape_unicode(pm_parser_t *parser, const uint8_t *string, size_t length, const // 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) { + if (flags & PM_ESCAPE_FLAG_REGEXP) { + // In regexp context, defer the error to regexp encoding + // validation where we can produce a regexp-specific message. + } else if (error_location != NULL) { pm_parser_err(parser, error_location->start, error_location->length, PM_ERR_ESCAPE_INVALID_UNICODE); } else { pm_parser_err(parser, U32(string - parser->start), U32(length), PM_ERR_ESCAPE_INVALID_UNICODE); @@ -8674,7 +8852,7 @@ escape_unicode(pm_parser_t *parser, const uint8_t *string, size_t length, const /** * Escape a single character value based on the given flags. */ -static inline uint8_t +static PRISM_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; @@ -8684,21 +8862,32 @@ escape_byte(uint8_t value, const uint8_t flags) { /** * Write a unicode codepoint to the given buffer. */ -static inline void +static PRISM_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, U32(start - parser->start), U32(end - start), PM_ERR_MIXED_ENCODING, parser->explicit_encoding->name); + if (flags & PM_ESCAPE_FLAG_REGEXP) { + // In regexp context, suppress this error — the regexp encoding + // validation will produce a more specific error message. + } else { + PM_PARSER_ERR_FORMAT(parser, U32(start - parser->start), U32(end - start), 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, U32(start - parser->start), U32(end - start), PM_ERR_ESCAPE_INVALID_UNICODE); + if (flags & PM_ESCAPE_FLAG_REGEXP) { + // In regexp context, defer the error to the regexp encoding + // validation which produces a regexp-specific message. + } else { + pm_parser_err(parser, U32(start - parser->start), U32(end - start), PM_ERR_ESCAPE_INVALID_UNICODE); + } + pm_buffer_append_byte(buffer, 0xEF); pm_buffer_append_byte(buffer, 0xBF); pm_buffer_append_byte(buffer, 0xBD); @@ -8709,11 +8898,16 @@ escape_write_unicode(pm_parser_t *parser, pm_buffer_t *buffer, const uint8_t fla * 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) { +static PRISM_INLINE void +escape_write_byte_encoded(pm_parser_t *parser, pm_buffer_t *buffer, const uint8_t flags, 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); + if (flags & PM_ESCAPE_FLAG_REGEXP) { + // In regexp context, suppress this error — the regexp encoding + // validation will produce a more specific error message. + } else { + PM_PARSER_ERR_TOKEN_FORMAT(parser, &parser->current, PM_ERR_MIXED_ENCODING, parser->encoding->name); + } } parser->explicit_encoding = parser->encoding; @@ -8737,19 +8931,19 @@ escape_write_byte_encoded(pm_parser_t *parser, pm_buffer_t *buffer, uint8_t byte * 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 +static PRISM_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); + escape_write_byte_encoded(parser, buffer, flags, byte); } /** * Write each byte of the given escaped character into the buffer. */ -static inline void +static PRISM_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) { @@ -8759,6 +8953,7 @@ escape_write_escape_encoded(pm_parser_t *parser, pm_buffer_t *buffer, pm_buffer_ } if (width == 1) { + if (*parser->current.end == '\n') pm_line_offset_list_append(&parser->metadata_arena, &parser->line_offsets, PM_TOKEN_END(parser, &parser->current) + 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. @@ -8899,7 +9094,7 @@ escape_read(pm_parser_t *parser, pm_buffer_t *buffer, pm_buffer_t *regular_expre } } - escape_write_byte_encoded(parser, buffer, value); + escape_write_byte_encoded(parser, buffer, flags, value); } else { pm_parser_err_current(parser, PM_ERR_ESCAPE_INVALID_HEXADECIMAL); } @@ -8948,7 +9143,8 @@ escape_read(pm_parser_t *parser, pm_buffer_t *buffer, pm_buffer_t *regular_expre 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. + // error instead of us because we don't know at this + // point if we're inside a comment in /x mode. pm_buffer_append_bytes(regular_expression_buffer, start, (size_t) (parser->current.end - start)); } else { pm_parser_err(parser, PM_TOKEN_END(parser, &parser->current), 0, PM_ERR_ESCAPE_INVALID_UNICODE); @@ -8964,7 +9160,7 @@ escape_read(pm_parser_t *parser, pm_buffer_t *buffer, pm_buffer_t *regular_expre extra_codepoints_start = unicode_start; } - uint32_t value = escape_unicode(parser, unicode_start, hexadecimal_length, NULL); + uint32_t value = escape_unicode(parser, unicode_start, hexadecimal_length, NULL, flags); 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); @@ -8984,7 +9180,8 @@ escape_read(pm_parser_t *parser, pm_buffer_t *buffer, pm_buffer_t *regular_expre 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. + // instead of us because we don't know at this point if + // we're inside a comment in /x mode. pm_buffer_append_bytes(regular_expression_buffer, start, (size_t) (parser->current.end - start)); } else { pm_parser_err(parser, U32(unicode_codepoints_start - parser->start), U32(parser->current.end - unicode_codepoints_start), PM_ERR_ESCAPE_INVALID_UNICODE_TERM); @@ -9005,7 +9202,7 @@ escape_read(pm_parser_t *parser, pm_buffer_t *buffer, pm_buffer_t *regular_expre PM_PARSER_ERR_FORMAT(parser, U32(start - parser->start), U32(parser->current.end - start), PM_ERR_ESCAPE_INVALID_UNICODE_SHORT, 2, start); } } else if (length == 4) { - uint32_t value = escape_unicode(parser, parser->current.end, 4, NULL); + uint32_t value = escape_unicode(parser, parser->current.end, 4, NULL, flags); if (flags & PM_ESCAPE_FLAG_REGEXP) { pm_buffer_append_bytes(regular_expression_buffer, start, (size_t) (parser->current.end + 4 - start)); @@ -9073,6 +9270,7 @@ escape_read(pm_parser_t *parser, pm_buffer_t *buffer, pm_buffer_t *regular_expre return; } + if (peeked == '\n') pm_line_offset_list_append(&parser->metadata_arena, &parser->line_offsets, PM_TOKEN_END(parser, &parser->current) + 1); parser->current.end++; escape_write_byte(parser, buffer, regular_expression_buffer, flags, escape_byte(peeked, flags | PM_ESCAPE_FLAG_CONTROL)); return; @@ -9131,6 +9329,7 @@ escape_read(pm_parser_t *parser, pm_buffer_t *buffer, pm_buffer_t *regular_expre return; } + if (peeked == '\n') pm_line_offset_list_append(&parser->metadata_arena, &parser->line_offsets, PM_TOKEN_END(parser, &parser->current) + 1); parser->current.end++; escape_write_byte(parser, buffer, regular_expression_buffer, flags, escape_byte(peeked, flags | PM_ESCAPE_FLAG_CONTROL)); return; @@ -9184,6 +9383,7 @@ escape_read(pm_parser_t *parser, pm_buffer_t *buffer, pm_buffer_t *regular_expre return; } + if (peeked == '\n') pm_line_offset_list_append(&parser->metadata_arena, &parser->line_offsets, PM_TOKEN_END(parser, &parser->current) + 1); parser->current.end++; escape_write_byte(parser, buffer, regular_expression_buffer, flags, escape_byte(peeked, flags | PM_ESCAPE_FLAG_META)); return; @@ -9191,8 +9391,9 @@ escape_read(pm_parser_t *parser, pm_buffer_t *buffer, pm_buffer_t *regular_expre } case '\r': { if (peek_offset(parser, 1) == '\n') { + pm_line_offset_list_append(&parser->metadata_arena, &parser->line_offsets, PM_TOKEN_END(parser, &parser->current) + 2); parser->current.end += 2; - escape_write_byte_encoded(parser, buffer, escape_byte('\n', flags)); + escape_write_byte_encoded(parser, buffer, flags, escape_byte('\n', flags)); return; } PRISM_FALLTHROUGH @@ -9262,10 +9463,14 @@ lex_question_mark(pm_parser_t *parser) { lex_state_set(parser, PM_LEX_STATE_END); pm_buffer_t buffer; - pm_buffer_init_capacity(&buffer, 3); + pm_buffer_init(&buffer, 3); escape_read(parser, &buffer, NULL, PM_ESCAPE_FLAG_SINGLE); - pm_string_owned_init(&parser->current_string, (uint8_t *) buffer.value, buffer.length); + + // Copy buffer data into the arena and free the heap buffer. + void *arena_data = pm_arena_memdup(parser->arena, buffer.value, buffer.length, PRISM_ALIGNOF(uint8_t)); + pm_string_constant_init(&parser->current_string, (const char *) arena_data, buffer.length); + pm_buffer_cleanup(&buffer); return PM_TOKEN_CHARACTER_LITERAL; } else { @@ -9331,20 +9536,19 @@ lex_at_variable(pm_parser_t *parser) { /** * Optionally call out to the lex callback if one is provided. */ -static inline void +static PRISM_INLINE void parser_lex_callback(pm_parser_t *parser) { - if (parser->lex_callback) { - parser->lex_callback->callback(parser->lex_callback->data, parser, &parser->current); + if (parser->lex_callback.callback) { + parser->lex_callback.callback(parser, &parser->current, parser->lex_callback.data); } } /** * Return a new comment node of the specified type. */ -static inline pm_comment_t * +static PRISM_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; + pm_comment_t *comment = (pm_comment_t *) pm_arena_alloc(&parser->metadata_arena, sizeof(pm_comment_t), PRISM_ALIGNOF(pm_comment_t)); *comment = (pm_comment_t) { .type = type, @@ -9367,7 +9571,7 @@ lex_embdoc(pm_parser_t *parser) { if (newline == NULL) { parser->current.end = parser->end; } else { - pm_newline_list_append(&parser->newline_list, U32(newline - parser->start + 1)); + pm_line_offset_list_append(&parser->metadata_arena, &parser->line_offsets, U32(newline - parser->start + 1)); parser->current.end = newline + 1; } @@ -9377,7 +9581,6 @@ lex_embdoc(pm_parser_t *parser) { // Now, create a comment that is going to be attached to the parser. const uint8_t *comment_start = parser->current.start; 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. @@ -9401,7 +9604,7 @@ lex_embdoc(pm_parser_t *parser) { if (newline == NULL) { parser->current.end = parser->end; } else { - pm_newline_list_append(&parser->newline_list, U32(newline - parser->start + 1)); + pm_line_offset_list_append(&parser->metadata_arena, &parser->line_offsets, U32(newline - parser->start + 1)); parser->current.end = newline + 1; } @@ -9421,7 +9624,7 @@ lex_embdoc(pm_parser_t *parser) { if (newline == NULL) { parser->current.end = parser->end; } else { - pm_newline_list_append(&parser->newline_list, U32(newline - parser->start + 1)); + pm_line_offset_list_append(&parser->metadata_arena, &parser->line_offsets, U32(newline - parser->start + 1)); parser->current.end = newline + 1; } @@ -9442,7 +9645,7 @@ lex_embdoc(pm_parser_t *parser) { * This happens in a couple places depending on whether or not we have already * lexed a comment. */ -static inline void +static PRISM_INLINE void parser_lex_ignored_newline(pm_parser_t *parser) { parser->current.type = PM_TOKEN_IGNORED_NEWLINE; parser_lex_callback(parser); @@ -9457,7 +9660,7 @@ parser_lex_ignored_newline(pm_parser_t *parser) { * If it is set, then we need to skip past the heredoc body and then clear the * heredoc_end field. */ -static inline void +static PRISM_INLINE void parser_flush_heredoc_end(pm_parser_t *parser) { assert(parser->heredoc_end <= parser->end); parser->next_start = parser->heredoc_end; @@ -9533,12 +9736,12 @@ typedef struct { /** * Push the given byte into the token buffer. */ -static inline void +static PRISM_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 +static PRISM_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); } @@ -9546,7 +9749,7 @@ pm_regexp_token_buffer_push_byte(pm_regexp_token_buffer_t *token_buffer, uint8_t /** * Return the width of the character at the end of the current token. */ -static inline size_t +static PRISM_INLINE size_t parser_char_width(const pm_parser_t *parser) { size_t width; if (parser->encoding_changed) { @@ -9573,36 +9776,31 @@ pm_token_buffer_push_escaped(pm_token_buffer_t *token_buffer, pm_parser_t *parse 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); + const uint8_t *start = parser->current.end; + pm_buffer_append_bytes(&token_buffer->base.buffer, start, width); + pm_buffer_append_bytes(&token_buffer->regexp_buffer, start, 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 +static PRISM_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)); + // Copy buffer data into the arena and free the heap buffer. + size_t len = pm_buffer_length(&token_buffer->buffer); + void *arena_data = pm_arena_memdup(parser->arena, pm_buffer_value(&token_buffer->buffer), len, PRISM_ALIGNOF(uint8_t)); + pm_string_constant_init(&parser->current_string, (const char *) arena_data, len); + pm_buffer_cleanup(&token_buffer->buffer); } -static inline void +static PRISM_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); + pm_token_buffer_copy(parser, &token_buffer->base); + pm_buffer_cleanup(&token_buffer->regexp_buffer); } /** @@ -9628,10 +9826,11 @@ 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)); + const uint8_t *cursor = token_buffer->base.cursor; + size_t length = (size_t) (parser->current.end - cursor); + pm_buffer_append_bytes(&token_buffer->base.buffer, cursor, length); + pm_buffer_append_bytes(&token_buffer->regexp_buffer, cursor, length); pm_regexp_token_buffer_copy(parser, token_buffer); } } @@ -9650,7 +9849,7 @@ 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); + pm_buffer_init(&token_buffer->buffer, PM_TOKEN_BUFFER_DEFAULT_SIZE); start = parser->current.start; } else { start = token_buffer->cursor; @@ -9667,8 +9866,8 @@ 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); + pm_buffer_init(&token_buffer->base.buffer, PM_TOKEN_BUFFER_DEFAULT_SIZE); + pm_buffer_init(&token_buffer->regexp_buffer, PM_TOKEN_BUFFER_DEFAULT_SIZE); start = parser->current.start; } else { start = token_buffer->base.cursor; @@ -9687,7 +9886,7 @@ pm_regexp_token_buffer_escape(pm_parser_t *parser, pm_regexp_token_buffer_t *tok * 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 +static PRISM_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; @@ -9735,7 +9934,7 @@ pm_lex_percent_delimiter(pm_parser_t *parser) { parser_flush_heredoc_end(parser); } else { // Otherwise, we'll add the newline to the list of newlines. - pm_newline_list_append(&parser->newline_list, PM_TOKEN_END(parser, &parser->current) + U32(eol_length)); + pm_line_offset_list_append(&parser->metadata_arena, &parser->line_offsets, PM_TOKEN_END(parser, &parser->current) + U32(eol_length)); } uint8_t delimiter = *parser->current.end; @@ -9783,6 +9982,12 @@ parser_lex(pm_parser_t *parser) { unsigned int semantic_token_seen = parser->semantic_token_seen; parser->semantic_token_seen = true; + // We'll jump to this label when we are about to encounter an EOF. + // If we still have lex_modes on the stack, we pop them so that cleanup + // can happen. For example, we should still continue parsing after a heredoc + // identifier, even if the heredoc body was syntax invalid. + switch_lex_modes: + switch (parser->lex_modes.current->mode) { case PM_LEX_DEFAULT: case PM_LEX_EMBEXPR: @@ -9805,17 +10010,24 @@ parser_lex(pm_parser_t *parser) { bool space_seen = false; // First, we're going to skip past any whitespace at the front of the next - // token. + // token. Skip runs of inline whitespace in bulk to avoid per-character + // stores back to parser->current.end. bool chomping = true; while (parser->current.end < parser->end && chomping) { - switch (*parser->current.end) { - case ' ': - case '\t': - case '\f': - case '\v': - parser->current.end++; + { + static const uint8_t inline_whitespace[256] = { + [' '] = 1, ['\t'] = 1, ['\f'] = 1, ['\v'] = 1 + }; + const uint8_t *scan = parser->current.end; + while (scan < parser->end && inline_whitespace[*scan]) scan++; + if (scan > parser->current.end) { + parser->current.end = scan; space_seen = true; - break; + continue; + } + } + + switch (*parser->current.end) { case '\r': if (match_eol_offset(parser, 1)) { chomping = false; @@ -9833,7 +10045,7 @@ parser_lex(pm_parser_t *parser) { parser->heredoc_end = NULL; } else { parser->current.end += eol_length + 1; - pm_newline_list_append(&parser->newline_list, PM_TOKEN_END(parser, &parser->current)); + pm_line_offset_list_append(&parser->metadata_arena, &parser->line_offsets, PM_TOKEN_END(parser, &parser->current)); space_seen = true; } } else if (pm_char_is_inline_whitespace(*parser->current.end)) { @@ -9856,6 +10068,14 @@ parser_lex(pm_parser_t *parser) { // 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) { + // We may be missing closing tokens. We should pop modes one by one + // to do the appropriate cleanup like moving next_start for heredocs. + // Only when no mode is remaining will we actually emit the EOF token. + if (parser->lex_modes.current->mode != PM_LEX_DEFAULT) { + lex_mode_pop(parser); + goto switch_lex_modes; + } + // 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 @@ -9927,7 +10147,7 @@ parser_lex(pm_parser_t *parser) { } if (parser->heredoc_end == NULL) { - pm_newline_list_append(&parser->newline_list, PM_TOKEN_END(parser, &parser->current)); + pm_line_offset_list_append(&parser->metadata_arena, &parser->line_offsets, PM_TOKEN_END(parser, &parser->current)); } } @@ -10013,8 +10233,21 @@ parser_lex(pm_parser_t *parser) { 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))) + ( + peek_at(parser, following) == 'a' && + peek_at(parser, following + 1) == 'n' && + peek_at(parser, following + 2) == 'd' && + peek_at(parser, next_content + 3) != '!' && + peek_at(parser, next_content + 3) != '?' && + !char_is_identifier(parser, following + 3, parser->end - (following + 3)) + ) || + ( + peek_at(parser, following) == 'o' && + peek_at(parser, following + 1) == 'r' && + peek_at(parser, next_content + 2) != '!' && + peek_at(parser, next_content + 2) != '?' && + !char_is_identifier(parser, following + 2, parser->end - (following + 2)) + ) ) ) { if (!lexed_comment) parser_lex_ignored_newline(parser); @@ -10085,6 +10318,8 @@ parser_lex(pm_parser_t *parser) { peek_at(parser, next_content) == 'a' && peek_at(parser, next_content + 1) == 'n' && peek_at(parser, next_content + 2) == 'd' && + peek_at(parser, next_content + 3) != '!' && + peek_at(parser, next_content + 3) != '?' && !char_is_identifier(parser, next_content + 3, parser->end - (next_content + 3)) ) { if (!lexed_comment) parser_lex_ignored_newline(parser); @@ -10101,6 +10336,8 @@ parser_lex(pm_parser_t *parser) { if ( peek_at(parser, next_content) == 'o' && peek_at(parser, next_content + 1) == 'r' && + peek_at(parser, next_content + 2) != '!' && + peek_at(parser, next_content + 2) != '?' && !char_is_identifier(parser, next_content + 2, parser->end - (next_content + 2)) ) { if (!lexed_comment) parser_lex_ignored_newline(parser); @@ -10126,7 +10363,7 @@ parser_lex(pm_parser_t *parser) { // , 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)); + PM_PARSER_ERR_TOKEN_FORMAT(parser, &parser->current, PM_ERR_ARRAY_TERM, pm_token_str(parser->current.type)); } lex_state_set(parser, PM_LEX_STATE_BEG | PM_LEX_STATE_LABEL); @@ -10436,7 +10673,7 @@ parser_lex(pm_parser_t *parser) { } 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, U32(body_start - parser->start + 1)); + pm_line_offset_list_append(&parser->metadata_arena, &parser->line_offsets, U32(body_start - parser->start + 1)); body_start++; } @@ -11077,7 +11314,7 @@ parser_lex(pm_parser_t *parser) { // 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, U32(++cursor - parser->start)); + pm_line_offset_list_append(&parser->metadata_arena, &parser->line_offsets, U32(++cursor - parser->start)); } parser->current.end = parser->end; @@ -11138,7 +11375,7 @@ parser_lex(pm_parser_t *parser) { whitespace += 1; } } else { - whitespace = pm_strspn_whitespace_newlines(parser->current.end, parser->end - parser->current.end, &parser->newline_list, PM_TOKEN_END(parser, &parser->current)); + whitespace = pm_strspn_whitespace_newlines(parser->current.end, parser->end - parser->current.end, &parser->metadata_arena, &parser->line_offsets, PM_TOKEN_END(parser, &parser->current)); } if (whitespace > 0) { @@ -11253,7 +11490,7 @@ parser_lex(pm_parser_t *parser) { LEX(PM_TOKEN_STRING_CONTENT); } else { // ... else track the newline. - pm_newline_list_append(&parser->newline_list, PM_TOKEN_END(parser, &parser->current) + 1); + pm_line_offset_list_append(&parser->metadata_arena, &parser->line_offsets, PM_TOKEN_END(parser, &parser->current) + 1); } parser->current.end++; @@ -11391,7 +11628,7 @@ parser_lex(pm_parser_t *parser) { // would have already have added the newline to the // list. if (parser->heredoc_end == NULL) { - pm_newline_list_append(&parser->newline_list, PM_TOKEN_END(parser, &parser->current)); + pm_line_offset_list_append(&parser->metadata_arena, &parser->line_offsets, PM_TOKEN_END(parser, &parser->current)); } } else { parser->current.end = breakpoint + 1; @@ -11438,7 +11675,7 @@ parser_lex(pm_parser_t *parser) { // 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, U32(breakpoint - parser->start + 1)); + pm_line_offset_list_append(&parser->metadata_arena, &parser->line_offsets, U32(breakpoint - parser->start + 1)); parser->current.end = breakpoint + 1; breakpoint = pm_strpbrk(parser, parser->current.end, breakpoints, parser->end - parser->current.end, false); break; @@ -11486,7 +11723,7 @@ parser_lex(pm_parser_t *parser) { LEX(PM_TOKEN_STRING_CONTENT); } else { // ... else track the newline. - pm_newline_list_append(&parser->newline_list, PM_TOKEN_END(parser, &parser->current) + 1); + pm_line_offset_list_append(&parser->metadata_arena, &parser->line_offsets, PM_TOKEN_END(parser, &parser->current) + 1); } parser->current.end++; @@ -11651,7 +11888,7 @@ parser_lex(pm_parser_t *parser) { // would have already have added the newline to the // list. if (parser->heredoc_end == NULL) { - pm_newline_list_append(&parser->newline_list, PM_TOKEN_END(parser, &parser->current)); + pm_line_offset_list_append(&parser->metadata_arena, &parser->line_offsets, PM_TOKEN_END(parser, &parser->current)); } } else { parser->current.end = breakpoint + 1; @@ -11703,7 +11940,7 @@ parser_lex(pm_parser_t *parser) { // for the terminator in case the terminator is a // newline character. if (parser->heredoc_end == NULL) { - pm_newline_list_append(&parser->newline_list, U32(breakpoint - parser->start + 1)); + pm_line_offset_list_append(&parser->metadata_arena, &parser->line_offsets, U32(breakpoint - parser->start + 1)); parser->current.end = breakpoint + 1; breakpoint = pm_strpbrk(parser, parser->current.end, breakpoints, parser->end - parser->current.end, true); break; @@ -11757,7 +11994,7 @@ parser_lex(pm_parser_t *parser) { LEX(PM_TOKEN_STRING_CONTENT); } else { // ... else track the newline. - pm_newline_list_append(&parser->newline_list, PM_TOKEN_END(parser, &parser->current) + 1); + pm_line_offset_list_append(&parser->metadata_arena, &parser->line_offsets, PM_TOKEN_END(parser, &parser->current) + 1); } parser->current.end++; @@ -11886,7 +12123,7 @@ parser_lex(pm_parser_t *parser) { (memcmp(terminator_start, ident_start, ident_length) == 0) ) { if (newline != NULL) { - pm_newline_list_append(&parser->newline_list, U32(newline - parser->start + 1)); + pm_line_offset_list_append(&parser->metadata_arena, &parser->line_offsets, U32(newline - parser->start + 1)); } parser->current.end = terminator_end; @@ -11917,7 +12154,7 @@ parser_lex(pm_parser_t *parser) { // 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\\#"; + uint8_t breakpoints[PM_STRPBRK_CACHE_SIZE] = "\r\n\\#"; pm_heredoc_quote_t quote = heredoc_lex_mode->quote; if (quote == PM_HEREDOC_QUOTE_SINGLE) { @@ -11958,7 +12195,7 @@ parser_lex(pm_parser_t *parser) { LEX(PM_TOKEN_STRING_CONTENT); } - pm_newline_list_append(&parser->newline_list, U32(breakpoint - parser->start + 1)); + pm_line_offset_list_append(&parser->metadata_arena, &parser->line_offsets, U32(breakpoint - parser->start + 1)); // If we have a - or ~ heredoc, then we can match after // some leading whitespace. @@ -12078,7 +12315,7 @@ parser_lex(pm_parser_t *parser) { const uint8_t *end = parser->current.end; if (parser->heredoc_end == NULL) { - pm_newline_list_append(&parser->newline_list, U32(end - parser->start + 1)); + pm_line_offset_list_append(&parser->metadata_arena, &parser->line_offsets, U32(end - parser->start + 1)); } // Here we want the buffer to only @@ -12335,7 +12572,7 @@ pm_binding_powers_t pm_binding_powers[PM_TOKEN_MAXIMUM] = { /** * Returns true if the current token is of the given type. */ -static inline bool +static PRISM_INLINE bool match1(const pm_parser_t *parser, pm_token_type_t type) { return parser->current.type == type; } @@ -12343,7 +12580,7 @@ match1(const pm_parser_t *parser, pm_token_type_t type) { /** * Returns true if the current token is of either of the given types. */ -static inline bool +static PRISM_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); } @@ -12351,7 +12588,7 @@ match2(const pm_parser_t *parser, pm_token_type_t type1, pm_token_type_t type2) /** * Returns true if the current token is any of the three given types. */ -static inline bool +static PRISM_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); } @@ -12359,15 +12596,23 @@ match3(const pm_parser_t *parser, pm_token_type_t type1, pm_token_type_t type2, /** * Returns true if the current token is any of the four given types. */ -static inline bool +static PRISM_INLINE bool match4(const pm_parser_t *parser, pm_token_type_t type1, pm_token_type_t type2, pm_token_type_t type3, pm_token_type_t type4) { return match1(parser, type1) || match1(parser, type2) || match1(parser, type3) || match1(parser, type4); } +/** + * Returns true if the current token is any of the six given types. + */ +static PRISM_INLINE bool +match6(const pm_parser_t *parser, pm_token_type_t type1, pm_token_type_t type2, pm_token_type_t type3, pm_token_type_t type4, pm_token_type_t type5, pm_token_type_t type6) { + return match1(parser, type1) || match1(parser, type2) || match1(parser, type3) || match1(parser, type4) || match1(parser, type5) || match1(parser, type6); +} + /** * Returns true if the current token is any of the seven given types. */ -static inline bool +static PRISM_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); } @@ -12375,7 +12620,7 @@ match7(const pm_parser_t *parser, pm_token_type_t type1, pm_token_type_t type2, /** * Returns true if the current token is any of the eight given types. */ -static inline bool +static PRISM_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); } @@ -12399,7 +12644,7 @@ accept1(pm_parser_t *parser, pm_token_type_t type) { * 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 +static PRISM_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); @@ -12477,16 +12722,22 @@ expect1_opening(pm_parser_t *parser, pm_token_type_t type, pm_diagnostic_id_t di parser->previous.type = 0; } +/** Flags for controlling expression parsing behavior. */ +#define PM_PARSE_ACCEPTS_COMMAND_CALL ((uint8_t) 0x1) +#define PM_PARSE_ACCEPTS_LABEL ((uint8_t) 0x2) +#define PM_PARSE_ACCEPTS_DO_BLOCK ((uint8_t) 0x4) +#define PM_PARSE_IN_ENDLESS_DEF ((uint8_t) 0x8) + 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); +parse_expression(pm_parser_t *parser, pm_binding_power_t binding_power, uint8_t flags, 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); +parse_value_expression(pm_parser_t *parser, pm_binding_power_t binding_power, uint8_t flags, pm_diagnostic_id_t diag_id, uint16_t depth) { + pm_node_t *node = parse_expression(parser, binding_power, flags, diag_id, depth); pm_assert_value_expression(parser, node); return node; } @@ -12509,7 +12760,7 @@ parse_value_expression(pm_parser_t *parser, pm_binding_power_t binding_power, bo * work in all cases, it may need to be refactored later. But it appears to work * for now. */ -static inline bool +static PRISM_INLINE bool token_begins_expression_p(pm_token_type_t type) { switch (type) { case PM_TOKEN_EQUAL_GREATER: @@ -12525,6 +12776,7 @@ token_begins_expression_p(pm_token_type_t type) { case PM_TOKEN_EOF: case PM_TOKEN_LAMBDA_BEGIN: case PM_TOKEN_KEYWORD_DO: + case PM_TOKEN_KEYWORD_DO_BLOCK: case PM_TOKEN_KEYWORD_DO_LOOP: case PM_TOKEN_KEYWORD_END: case PM_TOKEN_KEYWORD_ELSE: @@ -12570,14 +12822,14 @@ token_begins_expression_p(pm_token_type_t type) { * 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) { +parse_starred_expression(pm_parser_t *parser, pm_binding_power_t binding_power, uint8_t flags, 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)); + pm_node_t *expression = parse_value_expression(parser, binding_power, (uint8_t) (flags & PM_PARSE_ACCEPTS_DO_BLOCK), 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); + return parse_value_expression(parser, binding_power, flags, diag_id, depth); } static bool @@ -12667,16 +12919,12 @@ parse_write_name(pm_parser_t *parser, pm_constant_id_t *name_field) { // 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; + uint8_t *name = (uint8_t *) pm_arena_alloc(parser->arena, length + 1, 1); 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); + *name_field = pm_constant_pool_insert_owned(&parser->metadata_arena, &parser->constant_pool, name, length + 1); } /** @@ -12701,7 +12949,6 @@ parse_unwriteable_target(pm_parser_t *parser, pm_node_t *target) { pm_constant_id_t name = pm_parser_constant_id_raw(parser, parser->start + PM_NODE_START(target), parser->start + PM_NODE_END(target)); 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); } @@ -12780,7 +13027,6 @@ parse_target(pm_parser_t *parser, pm_node_t *target, bool multiple, bool splat_p 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; } @@ -12831,7 +13077,6 @@ parse_target(pm_parser_t *parser, pm_node_t *target, bool multiple, bool splat_p // =, so we know it's a local variable write. pm_location_t message_loc = call->message_loc; pm_constant_id_t name = pm_parser_local_add_location(parser, &message_loc, 0); - pm_node_destroy(parser, target); return UP(pm_local_variable_target_node_create(parser, &message_loc, name, 0)); } @@ -12906,11 +13151,9 @@ 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: { @@ -12929,7 +13172,6 @@ parse_write(pm_parser_t *parser, pm_node_t *target, pm_token_t *operator, pm_nod 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: @@ -12938,7 +13180,6 @@ parse_write(pm_parser_t *parser, pm_node_t *target, pm_token_t *operator, pm_nod 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: { @@ -12956,7 +13197,6 @@ parse_write(pm_parser_t *parser, pm_node_t *target, pm_token_t *operator, pm_nod } pm_locals_unread(&scope->locals, name); - pm_node_destroy(parser, target); return UP(pm_local_variable_write_node_create(parser, name, depth, value, &location, operator)); } @@ -12965,13 +13205,11 @@ parse_write(pm_parser_t *parser, pm_node_t *target, pm_token_t *operator, pm_nod 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: @@ -13016,7 +13254,6 @@ parse_write(pm_parser_t *parser, pm_node_t *target, pm_token_t *operator, pm_nod pm_refute_numbered_parameter(parser, message_loc.start, message_loc.length); pm_parser_local_add_location(parser, &message_loc, 0); - pm_node_destroy(parser, target); pm_constant_id_t constant_id = pm_parser_constant_id_raw(parser, parser->start + PM_LOCATION_START(&message_loc), parser->start + PM_LOCATION_END(&message_loc)); target = UP(pm_local_variable_write_node_create(parser, constant_id, 0, value, &message_loc, operator)); @@ -13038,7 +13275,7 @@ parse_write(pm_parser_t *parser, pm_node_t *target, pm_token_t *operator, pm_nod pm_arguments_node_t *arguments = pm_arguments_node_create(parser); call->arguments = arguments; - pm_arguments_node_arguments_append(arguments, value); + pm_arguments_node_arguments_append(parser->arena, arguments, value); PM_NODE_LENGTH_SET_NODE(call, arguments); call->equal_loc = TOK2LOC(parser, operator); @@ -13057,7 +13294,7 @@ parse_write(pm_parser_t *parser, pm_node_t *target, pm_token_t *operator, pm_nod call->arguments = pm_arguments_node_create(parser); } - pm_arguments_node_arguments_append(call->arguments, value); + pm_arguments_node_arguments_append(parser->arena, call->arguments, value); PM_NODE_LENGTH_SET_NODE(target, value); // Replace the name with "[]=". @@ -13082,7 +13319,6 @@ parse_write(pm_parser_t *parser, pm_node_t *target, pm_token_t *operator, pm_nod // 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: @@ -13116,7 +13352,6 @@ parse_unwriteable_write(pm_parser_t *parser, pm_node_t *target, const pm_token_t pm_constant_id_t name = pm_parser_local_add_location(parser, &target->location, 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); } @@ -13150,7 +13385,7 @@ parse_targets(pm_parser_t *parser, pm_node_t *first_target, pm_binding_power_t b 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_expression(parser, binding_power, PM_PARSE_ACCEPTS_DO_BLOCK, PM_ERR_EXPECT_EXPRESSION_AFTER_STAR, (uint16_t) (depth + 1)); name = parse_target(parser, name, true, true); } @@ -13159,13 +13394,13 @@ parse_targets(pm_parser_t *parser, pm_node_t *first_target, pm_binding_power_t b 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)); + pm_node_t *target = parse_expression(parser, binding_power, PM_PARSE_ACCEPTS_DO_BLOCK, 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)); + pm_node_t *target = parse_expression(parser, binding_power, PM_PARSE_ACCEPTS_DO_BLOCK, 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); @@ -13217,7 +13452,7 @@ parse_statements(pm_parser_t *parser, pm_context_t context, uint16_t depth) { 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_node_t *node = parse_expression(parser, PM_BINDING_POWER_STATEMENT, PM_PARSE_ACCEPTS_COMMAND_CALL | PM_PARSE_ACCEPTS_DO_BLOCK, 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 @@ -13275,13 +13510,14 @@ parse_statements(pm_parser_t *parser, pm_context_t context, uint16_t depth) { // 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)); + PM_PARSER_ERR_TOKEN_FORMAT(parser, &parser->current, PM_ERR_EXPECT_EOL_AFTER_STATEMENT, pm_token_str(parser->current.type)); parser->previous.start = parser->previous.end; parser->previous.type = 0; } } context_pop(parser); + bool last_value = true; switch (context) { case PM_CONTEXT_BEGIN_ENSURE: @@ -13302,23 +13538,24 @@ parse_statements(pm_parser_t *parser, pm_context_t context, uint16_t depth) { */ 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, parser->start_line, literals, node, true); + const pm_node_t *duplicated = pm_static_literals_add(&parser->line_offsets, parser->start, parser->start_line, literals, node, true); if (duplicated != NULL) { pm_buffer_t buffer = { 0 }; - pm_static_literal_inspect(&buffer, &parser->newline_list, parser->start, parser->start_line, parser->encoding->name, duplicated); + pm_static_literal_inspect(&buffer, &parser->line_offsets, parser->start, parser->start_line, parser->encoding->name, duplicated); pm_diagnostic_list_append_format( + &parser->metadata_arena, &parser->warning_list, duplicated->location.start, duplicated->location.length, PM_WARN_DUPLICATED_HASH_KEY, (int) pm_buffer_length(&buffer), pm_buffer_value(&buffer), - pm_newline_list_line_column(&parser->newline_list, PM_NODE_START(node), parser->start_line).line + pm_line_offset_list_line_column(&parser->line_offsets, PM_NODE_START(node), parser->start_line).line ); - pm_buffer_free(&buffer); + pm_buffer_cleanup(&buffer); } } @@ -13330,14 +13567,15 @@ 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, parser->start_line, literals, node, false)) != NULL) { + if ((previous = pm_static_literals_add(&parser->line_offsets, parser->start, parser->start_line, literals, node, false)) != NULL) { pm_diagnostic_list_append_format( + &parser->metadata_arena, &parser->warning_list, PM_NODE_START(node), PM_NODE_LENGTH(node), PM_WARN_DUPLICATED_WHEN_CLAUSE, - pm_newline_list_line_column(&parser->newline_list, PM_NODE_START(node), parser->start_line).line, - pm_newline_list_line_column(&parser->newline_list, PM_NODE_START(previous), parser->start_line).line + pm_line_offset_list_line_column(&parser->line_offsets, PM_NODE_START(node), parser->start_line).line, + pm_line_offset_list_line_column(&parser->line_offsets, PM_NODE_START(previous), parser->start_line).line ); } } @@ -13365,9 +13603,9 @@ parse_assocs(pm_parser_t *parser, pm_static_literals_t *literals, pm_node_t *nod // 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)); + value = parse_value_expression(parser, PM_BINDING_POWER_DEFINED, PM_PARSE_ACCEPTS_DO_BLOCK, 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)); + value = parse_value_expression(parser, PM_BINDING_POWER_DEFINED, PM_PARSE_ACCEPTS_DO_BLOCK, PM_ERR_EXPECT_EXPRESSION_AFTER_SPLAT_HASH, (uint16_t) (depth + 1)); } else { pm_parser_scope_forwarding_keywords_check(parser, &operator); } @@ -13386,7 +13624,7 @@ parse_assocs(pm_parser_t *parser, pm_static_literals_t *literals, pm_node_t *nod 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)); + value = parse_value_expression(parser, PM_BINDING_POWER_DEFINED, PM_PARSE_ACCEPTS_DO_BLOCK, 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 }; @@ -13416,7 +13654,7 @@ parse_assocs(pm_parser_t *parser, pm_static_literals_t *literals, pm_node_t *nod break; } default: { - pm_node_t *key = parse_value_expression(parser, PM_BINDING_POWER_DEFINED, false, true, PM_ERR_HASH_KEY, (uint16_t) (depth + 1)); + pm_node_t *key = parse_value_expression(parser, PM_BINDING_POWER_DEFINED, PM_PARSE_ACCEPTS_DO_BLOCK | PM_PARSE_ACCEPTS_LABEL, PM_ERR_HASH_KEY, (uint16_t) (depth + 1)); // Hash keys that are strings are automatically frozen. We will // mark that here. @@ -13432,16 +13670,16 @@ parse_assocs(pm_parser_t *parser, pm_static_literals_t *literals, pm_node_t *nod 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)); + pm_node_t *value = parse_value_expression(parser, PM_BINDING_POWER_DEFINED, PM_PARSE_ACCEPTS_DO_BLOCK, PM_ERR_HASH_VALUE, (uint16_t) (depth + 1)); element = UP(pm_assoc_node_create(parser, key, NTOK2PTR(operator), value)); break; } } if (PM_NODE_TYPE_P(node, PM_HASH_NODE)) { - pm_hash_node_elements_append((pm_hash_node_t *) node, element); + pm_hash_node_elements_append(parser->arena, (pm_hash_node_t *) node, element); } else { - pm_keyword_hash_node_elements_append((pm_keyword_hash_node_t *) node, element); + pm_keyword_hash_node_elements_append(parser->arena, (pm_keyword_hash_node_t *) node, element); } // If there's no comma after the element, then we're done. @@ -13462,7 +13700,7 @@ parse_assocs(pm_parser_t *parser, pm_static_literals_t *literals, pm_node_t *nod return contains_keyword_splat; } -static inline bool +static PRISM_INLINE bool argument_allowed_for_bare_hash(pm_parser_t *parser, pm_node_t *argument) { if (pm_symbol_node_label_p(parser, argument)) { return true; @@ -13489,20 +13727,20 @@ argument_allowed_for_bare_hash(pm_parser_t *parser, pm_node_t *argument) { /** * Append an argument to a list of arguments. */ -static inline void +static PRISM_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); + pm_arguments_node_arguments_append(parser->arena, 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) { +parse_arguments(pm_parser_t *parser, pm_arguments_t *arguments, bool accepts_forwarding, pm_token_type_t terminator, uint8_t flags, 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 @@ -13542,9 +13780,9 @@ parse_arguments(pm_parser_t *parser, pm_arguments_t *arguments, bool accepts_for 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_node_flags_t node_flags = PM_ARGUMENTS_NODE_FLAGS_CONTAINS_KEYWORDS; + if (contains_keyword_splat) node_flags |= PM_ARGUMENTS_NODE_FLAGS_CONTAINS_KEYWORD_SPLAT; + pm_node_flag_set(UP(arguments->arguments), node_flags); pm_static_literals_free(&hash_keys); parsed_bare_hash = true; @@ -13557,7 +13795,7 @@ parse_arguments(pm_parser_t *parser, pm_arguments_t *arguments, bool accepts_for 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)); + expression = parse_value_expression(parser, PM_BINDING_POWER_DEFINED, flags & PM_PARSE_ACCEPTS_DO_BLOCK, PM_ERR_EXPECT_ARGUMENT, (uint16_t) (depth + 1)); } else { pm_parser_scope_forwarding_block_check(parser, &operator); } @@ -13587,7 +13825,7 @@ parse_arguments(pm_parser_t *parser, pm_arguments_t *arguments, bool accepts_for 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)); + pm_node_t *expression = parse_value_expression(parser, PM_BINDING_POWER_DEFINED, flags & PM_PARSE_ACCEPTS_DO_BLOCK, PM_ERR_EXPECT_EXPRESSION_AFTER_SPLAT, (uint16_t) (depth + 1)); if (parsed_bare_hash) { pm_parser_err(parser, PM_TOKEN_START(parser, &operator), PM_NODE_END(expression) - PM_TOKEN_START(parser, &operator), PM_ERR_ARGUMENT_SPLAT_AFTER_ASSOC_SPLAT); @@ -13608,7 +13846,7 @@ parse_arguments(pm_parser_t *parser, pm_arguments_t *arguments, bool accepts_for // 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)); + pm_node_t *right = parse_expression(parser, PM_BINDING_POWER_RANGE, flags & PM_PARSE_ACCEPTS_DO_BLOCK, 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 @@ -13637,7 +13875,7 @@ parse_arguments(pm_parser_t *parser, pm_arguments_t *arguments, bool accepts_for 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)); + argument = parse_value_expression(parser, PM_BINDING_POWER_DEFINED, (flags & PM_PARSE_ACCEPTS_DO_BLOCK) | (!parsed_first_argument ? PM_PARSE_ACCEPTS_COMMAND_CALL : 0u) | PM_PARSE_ACCEPTS_LABEL, PM_ERR_EXPECT_ARGUMENT, (uint16_t) (depth + 1)); } bool contains_keywords = false; @@ -13661,10 +13899,10 @@ parse_arguments(pm_parser_t *parser, pm_arguments_t *arguments, bool accepts_for 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)); + pm_node_t *value = parse_value_expression(parser, PM_BINDING_POWER_DEFINED, flags & PM_PARSE_ACCEPTS_DO_BLOCK, PM_ERR_HASH_VALUE, (uint16_t) (depth + 1)); argument = UP(pm_assoc_node_create(parser, argument, NTOK2PTR(operator), value)); - pm_keyword_hash_node_elements_append(bare_hash, argument); + pm_keyword_hash_node_elements_append(parser->arena, bare_hash, argument); argument = UP(bare_hash); // Then parse more if we have a comma @@ -13681,10 +13919,10 @@ parse_arguments(pm_parser_t *parser, pm_arguments_t *arguments, bool accepts_for 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); + pm_node_flags_t node_flags = 0; + if (contains_keywords) node_flags |= PM_ARGUMENTS_NODE_FLAGS_CONTAINS_KEYWORDS; + if (contains_keyword_splat) node_flags |= PM_ARGUMENTS_NODE_FLAGS_CONTAINS_KEYWORD_SPLAT; + pm_node_flag_set(UP(arguments->arguments), node_flags); break; } @@ -13875,6 +14113,43 @@ update_parameter_state(pm_parser_t *parser, pm_token_t *token, pm_parameters_ord return true; } +static PRISM_INLINE void +parse_parameters_handle_trailing_comma( + pm_parser_t *parser, + pm_parameters_node_t *params, + pm_parameters_order_t order, + bool in_block, + bool allows_trailing_comma +) { + if (!allows_trailing_comma) { + pm_parser_err_previous(parser, PM_ERR_PARAMETER_WILD_LOOSE_COMMA); + return; + } + + if (in_block) { + if (order >= PM_PARAMETERS_ORDER_NAMED) { + // foo do |bar,|; end + 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(parser->arena, params, UP(param)); + } + } else { + // foo do |*bar,|; end + pm_parser_err_previous(parser, PM_ERR_PARAMETER_WILD_LOOSE_COMMA); + } + } else { + // https://bugs.ruby-lang.org/issues/19107 + // Allow `def foo(bar,); end`, `def foo(*bar,); end`, etc. but not `def foo(...,); end` + if (parser->version < PM_OPTIONS_VERSION_CRUBY_4_1 || order == PM_PARAMETERS_ORDER_NOTHING_AFTER) { + pm_parser_err_previous(parser, PM_ERR_PARAMETER_WILD_LOOSE_COMMA); + } + } +} + /** * Parse a list of parameters on a method definition. */ @@ -13887,6 +14162,7 @@ parse_parameters( bool allows_forwarding_parameters, bool accepts_blocks_in_defaults, bool in_block, + pm_diagnostic_id_t diag_id_forwarding, uint16_t depth ) { pm_do_loop_stack_push(parser, false); @@ -13903,9 +14179,9 @@ parse_parameters( pm_node_t *param = UP(parse_required_destructured_parameter(parser)); if (order > PM_PARAMETERS_ORDER_AFTER_OPTIONAL) { - pm_parameters_node_requireds_append(params, param); + pm_parameters_node_requireds_append(parser->arena, params, param); } else { - pm_parameters_node_posts_append(params, param); + pm_parameters_node_posts_append(parser->arena, params, param); } break; } @@ -13915,33 +14191,40 @@ parse_parameters( parser_lex(parser); pm_token_t operator = parser->previous; - pm_token_t name = { 0 }; + pm_node_t *param; - 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); + if (parser->version >= PM_OPTIONS_VERSION_CRUBY_4_1 && accept1(parser, PM_TOKEN_KEYWORD_NIL)) { + param = (pm_node_t *) pm_no_block_parameter_node_create(parser, &operator, &parser->previous); } else { - parser->current_scope->parameters |= PM_SCOPE_PARAMETERS_FORWARDING_BLOCK; - } + pm_token_t name = {0}; - pm_block_parameter_node_t *param = pm_block_parameter_node_create(parser, NTOK2PTR(name), &operator); - if (repeated) { - pm_node_flag_set_repeated_parameter(UP(param)); + 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 { + parser->current_scope->parameters |= PM_SCOPE_PARAMETERS_FORWARDING_BLOCK; + } + + param = (pm_node_t *) pm_block_parameter_node_create(parser, NTOK2PTR(name), &operator); + if (repeated) { + pm_node_flag_set_repeated_parameter(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)); + pm_parser_err_node(parser, param, PM_ERR_PARAMETER_BLOCK_MULTI); + pm_parameters_node_posts_append(parser->arena, params, param); } break; } case PM_TOKEN_UDOT_DOT_DOT: { if (!allows_forwarding_parameters) { - pm_parser_err_current(parser, PM_ERR_ARGUMENT_NO_FORWARDING_ELLIPSES); + pm_parser_err_current(parser, diag_id_forwarding); } bool succeeded = update_parameter_state(parser, &parser->current, &order); @@ -13954,7 +14237,7 @@ parse_parameters( // If we already have a keyword rest parameter, then we replace it with the // forwarding parameter and move the keyword rest parameter to the posts list. pm_node_t *keyword_rest = params->keyword_rest; - pm_parameters_node_posts_append(params, keyword_rest); + pm_parameters_node_posts_append(parser->arena, params, keyword_rest); if (succeeded) pm_parser_err_previous(parser, PM_ERR_PARAMETER_UNEXPECTED_FWD); params->keyword_rest = NULL; } @@ -14007,7 +14290,7 @@ parse_parameters( 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)); + pm_node_t *value = parse_value_expression(parser, binding_power, PM_PARSE_ACCEPTS_DO_BLOCK, 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); @@ -14015,7 +14298,7 @@ parse_parameters( if (repeated) { pm_node_flag_set_repeated_parameter(UP(param)); } - pm_parameters_node_optionals_append(params, param); + pm_parameters_node_optionals_append(parser->arena, params, param); // If the value of the parameter increased the number of // reads of that parameter, then we need to warn that we @@ -14038,13 +14321,13 @@ parse_parameters( if (repeated) { pm_node_flag_set_repeated_parameter(UP(param)); } - pm_parameters_node_requireds_append(params, UP(param)); + pm_parameters_node_requireds_append(parser->arena, 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)); + pm_parameters_node_posts_append(parser->arena, params, UP(param)); } break; @@ -14080,7 +14363,7 @@ parse_parameters( pm_node_flag_set_repeated_parameter(param); } - pm_parameters_node_keywords_append(params, param); + pm_parameters_node_keywords_append(parser->arena, params, param); break; } case PM_TOKEN_SEMICOLON: @@ -14097,7 +14380,7 @@ parse_parameters( pm_node_flag_set_repeated_parameter(param); } - pm_parameters_node_keywords_append(params, param); + pm_parameters_node_keywords_append(parser->arena, params, param); break; } default: { @@ -14108,7 +14391,7 @@ parse_parameters( 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)); + pm_node_t *value = parse_value_expression(parser, binding_power, PM_PARSE_ACCEPTS_DO_BLOCK, 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)) { @@ -14126,7 +14409,7 @@ parse_parameters( } context_pop(parser); - pm_parameters_node_keywords_append(params, param); + pm_parameters_node_keywords_append(parser->arena, 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 @@ -14167,7 +14450,7 @@ parse_parameters( 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); + pm_parameters_node_posts_append(parser->arena, params, param); } break; @@ -14209,27 +14492,14 @@ parse_parameters( 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); + pm_parameters_node_posts_append(parser->arena, 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); - } + parse_parameters_handle_trailing_comma(parser, params, order, in_block, allows_trailing_comma); } parsing = false; @@ -14262,7 +14532,6 @@ parse_parameters( // If we don't have any parameters, return `NULL` instead of an empty `ParametersNode`. if (PM_NODE_START(params) == PM_NODE_END(params)) { - pm_node_destroy(parser, UP(params)); return NULL; } @@ -14279,13 +14548,13 @@ token_newline_index(const pm_parser_t *parser) { // 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; + return parser->line_offsets.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, PM_TOKEN_START(parser, &parser->current), 0); + return (size_t) pm_line_offset_list_line(&parser->line_offsets, PM_TOKEN_START(parser, &parser->current), 0); } } @@ -14295,7 +14564,7 @@ token_newline_index(const pm_parser_t *parser) { */ 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 *cursor = parser->start + parser->line_offsets.offsets[newline_index]; const uint8_t *end = token->start; // Skip over the BOM if it is present. @@ -14384,7 +14653,7 @@ typedef enum { * 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 +static PRISM_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; @@ -14402,7 +14671,7 @@ parse_rescues(pm_parser_t *parser, size_t opening_newline_index, const pm_token_ parser_lex(parser); pm_rescue_node_operator_set(parser, rescue, &parser->previous); - pm_node_t *reference = parse_expression(parser, PM_BINDING_POWER_INDEX, false, false, PM_ERR_RESCUE_VARIABLE, (uint16_t) (depth + 1)); + pm_node_t *reference = parse_expression(parser, PM_BINDING_POWER_INDEX, PM_PARSE_ACCEPTS_DO_BLOCK, PM_ERR_RESCUE_VARIABLE, (uint16_t) (depth + 1)); reference = parse_target(parser, reference, false, false); pm_rescue_node_reference_set(rescue, reference); @@ -14421,7 +14690,7 @@ parse_rescues(pm_parser_t *parser, size_t opening_newline_index, const pm_token_ 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); + pm_rescue_node_exceptions_append(parser->arena, rescue, expression); // If we hit a newline, then this is the end of the rescue expression. We // can continue on to parse the statements. @@ -14432,7 +14701,7 @@ parse_rescues(pm_parser_t *parser, size_t opening_newline_index, const pm_token_ if (accept1(parser, PM_TOKEN_EQUAL_GREATER)) { pm_rescue_node_operator_set(parser, rescue, &parser->previous); - pm_node_t *reference = parse_expression(parser, PM_BINDING_POWER_INDEX, false, false, PM_ERR_RESCUE_VARIABLE, (uint16_t) (depth + 1)); + pm_node_t *reference = parse_expression(parser, PM_BINDING_POWER_INDEX, PM_PARSE_ACCEPTS_DO_BLOCK, PM_ERR_RESCUE_VARIABLE, (uint16_t) (depth + 1)); reference = parse_target(parser, reference, false, false); pm_rescue_node_reference_set(rescue, reference); @@ -14618,6 +14887,7 @@ parse_block_parameters( false, accepts_blocks_in_defaults, true, + is_lambda_literal ? PM_ERR_ARGUMENT_NO_FORWARDING_ELLIPSES_LAMBDA : PM_ERR_ARGUMENT_NO_FORWARDING_ELLIPSES_BLOCK, (uint16_t) (depth + 1) ); if (!is_lambda_literal) { @@ -14659,7 +14929,7 @@ parse_block_parameters( 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); + pm_block_parameters_node_append_local(parser->arena, block_parameters, local); } while (accept1(parser, PM_TOKEN_COMMA)); } } @@ -14840,8 +15110,19 @@ parse_block(pm_parser_t *parser, uint16_t depth) { * 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) { +parse_arguments_list(pm_parser_t *parser, pm_arguments_t *arguments, bool accepts_block, uint8_t flags, uint16_t depth) { + /* Fast path: if the current token can't begin an expression and isn't + * a parenthesis, block opener, or splat/block-pass operator, there are + * no arguments to parse. */ + if ( + !token_begins_expression_p(parser->current.type) && + !match6(parser, PM_TOKEN_PARENTHESIS_LEFT, PM_TOKEN_KEYWORD_DO, PM_TOKEN_KEYWORD_DO_BLOCK, PM_TOKEN_USTAR, PM_TOKEN_USTAR_STAR, PM_TOKEN_UAMPERSAND) + ) { + return false; + } + bool found = false; + bool parsed_command_args = false; if (accept1(parser, PM_TOKEN_PARENTHESIS_LEFT)) { found |= true; @@ -14851,10 +15132,10 @@ parse_arguments_list(pm_parser_t *parser, pm_arguments_t *arguments, bool accept arguments->closing_loc = TOK2LOC(parser, &parser->previous); } else { pm_accepts_block_stack_push(parser, true); - parse_arguments(parser, arguments, accepts_block, PM_TOKEN_PARENTHESIS_RIGHT, (uint16_t) (depth + 1)); + parse_arguments(parser, arguments, accepts_block, PM_TOKEN_PARENTHESIS_RIGHT, (uint8_t) (flags & ~PM_PARSE_ACCEPTS_DO_BLOCK), (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)); + PM_PARSER_ERR_TOKEN_FORMAT(parser, &parser->current, PM_ERR_ARGUMENT_TERM_PAREN, pm_token_str(parser->current.type)); parser->previous.start = parser->previous.end; parser->previous.type = 0; } @@ -14862,20 +15143,21 @@ parse_arguments_list(pm_parser_t *parser, pm_arguments_t *arguments, bool accept pm_accepts_block_stack_pop(parser); arguments->closing_loc = TOK2LOC(parser, &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)) { + } else if ((flags & PM_PARSE_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; + parsed_command_args = 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)); + parse_arguments(parser, arguments, accepts_block, PM_TOKEN_EOF, flags, (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_PARSER_ERR_TOKEN_FORMAT(parser, &parser->previous, PM_ERR_EXPECT_ARGUMENT, pm_token_str(parser->current.type)); } pm_accepts_block_stack_pop(parser); @@ -14894,6 +15176,9 @@ parse_arguments_list(pm_parser_t *parser, pm_arguments_t *arguments, bool accept } else if (pm_accepts_block_stack_p(parser) && accept1(parser, PM_TOKEN_KEYWORD_DO)) { found |= true; block = parse_block(parser, (uint16_t) (depth + 1)); + } else if (parsed_command_args && pm_accepts_block_stack_p(parser) && (flags & PM_PARSE_ACCEPTS_DO_BLOCK) && accept1(parser, PM_TOKEN_KEYWORD_DO_BLOCK)) { + found |= true; + block = parse_block(parser, (uint16_t) (depth + 1)); } if (block != NULL) { @@ -14906,7 +15191,7 @@ parse_arguments_list(pm_parser_t *parser, pm_arguments_t *arguments, bool accept if (arguments->arguments == NULL) { arguments->arguments = pm_arguments_node_create(parser); } - pm_arguments_node_arguments_append(arguments->arguments, arguments->block); + pm_arguments_node_arguments_append(parser->arena, arguments->arguments, arguments->block); } arguments->block = UP(block); } @@ -15050,7 +15335,7 @@ parse_block_exit(pm_parser_t *parser, pm_node_t *node) { // 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); + pm_node_list_append(parser->arena, parser->current_block_exits, node); return; case PM_CONTEXT_BEGIN_ELSE: case PM_CONTEXT_BEGIN_ENSURE: @@ -15141,7 +15426,7 @@ pop_block_exits(pm_parser_t *parser, pm_node_list_t *previous_block_exits) { // 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); + pm_node_list_concat(parser->arena, 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 @@ -15151,11 +15436,11 @@ pop_block_exits(pm_parser_t *parser, pm_node_list_t *previous_block_exits) { } } -static inline pm_node_t * +static PRISM_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)); + pm_node_t *predicate = parse_value_expression(parser, binding_power, PM_PARSE_ACCEPTS_COMMAND_CALL | PM_PARSE_ACCEPTS_DO_BLOCK, 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); @@ -15173,7 +15458,7 @@ parse_predicate(pm_parser_t *parser, pm_binding_power_t binding_power, pm_contex return predicate; } -static inline pm_node_t * +static PRISM_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); @@ -15299,8 +15584,6 @@ parse_conditional(pm_parser_t *parser, pm_context_t context, size_t opening_newl } pop_block_exits(parser, previous_block_exits); - pm_node_list_free(¤t_block_exits); - return parent; } @@ -15311,7 +15594,7 @@ parse_conditional(pm_parser_t *parser, pm_context_t context, size_t opening_newl #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_DEFINED: case PM_TOKEN_KEYWORD_DO: case PM_TOKEN_KEYWORD_DO_BLOCK: 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: \ @@ -15374,7 +15657,7 @@ PM_STATIC_ASSERT(__LINE__, ((int) PM_STRING_FLAGS_FORCED_UTF8_ENCODING) == ((int * 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 +static PRISM_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) { @@ -15433,7 +15716,7 @@ parse_string_part(pm_parser_t *parser, uint16_t depth) { pm_token_t opening = parser->previous; pm_statements_node_t *statements = NULL; - if (!match1(parser, PM_TOKEN_EMBEXPR_END)) { + if (!match3(parser, PM_TOKEN_EMBEXPR_END, PM_TOKEN_HEREDOC_END, PM_TOKEN_EOF)) { pm_accepts_block_stack_push(parser, true); statements = parse_statements(parser, PM_CONTEXT_EMBEXPR, (uint16_t) (depth + 1)); pm_accepts_block_stack_pop(parser); @@ -15615,11 +15898,11 @@ parse_symbol(pm_parser_t *parser, pm_lex_mode_t *lex_mode, pm_lex_state_t next_s } pm_interpolated_symbol_node_t *symbol = pm_interpolated_symbol_node_create(parser, &opening, NULL, &opening); - if (part) pm_interpolated_symbol_node_append(symbol, part); + if (part) pm_interpolated_symbol_node_append(parser->arena, 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); + pm_interpolated_symbol_node_append(parser->arena, symbol, part); } } @@ -15654,10 +15937,10 @@ parse_symbol(pm_parser_t *parser, pm_lex_mode_t *lex_mode, pm_lex_state_t next_s if (match1(parser, PM_TOKEN_STRING_CONTENT)) { pm_interpolated_symbol_node_t *symbol = pm_interpolated_symbol_node_create(parser, &opening, NULL, &opening); pm_node_t *part = UP(pm_string_node_create_unescaped(parser, NULL, &content, NULL, &unescaped)); - pm_interpolated_symbol_node_append(symbol, part); + pm_interpolated_symbol_node_append(parser->arena, symbol, part); part = UP(pm_string_node_create_unescaped(parser, NULL, &parser->current, NULL, &parser->current_string)); - pm_interpolated_symbol_node_append(symbol, part); + pm_interpolated_symbol_node_append(parser->arena, symbol, part); if (next_state != PM_LEX_STATE_NONE) { lex_state_set(parser, next_state); @@ -15691,7 +15974,7 @@ parse_symbol(pm_parser_t *parser, pm_lex_mode_t *lex_mode, pm_lex_state_t next_s * 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 * +static PRISM_INLINE pm_node_t * parse_undef_argument(pm_parser_t *parser, uint16_t depth) { switch (parser->current.type) { case PM_CASE_OPERATOR: @@ -15726,7 +16009,7 @@ parse_undef_argument(pm_parser_t *parser, uint16_t depth) { * 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 * +static PRISM_INLINE pm_node_t * parse_alias_argument(pm_parser_t *parser, bool first, uint16_t depth) { switch (parser->current.type) { case PM_CASE_OPERATOR: @@ -15796,12 +16079,12 @@ parse_variable(pm_parser_t *parser) { } 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); + pm_node_list_append(parser->arena, ¤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); + pm_node_list_append(parser->arena, ¤t_scope->implicit_parameters, node); return node; } @@ -15834,7 +16117,7 @@ parse_variable_call(pm_parser_t *parser) { * parser. If it does not match a valid method definition name, then a missing * token is returned. */ -static inline pm_token_t +static PRISM_INLINE pm_token_t parse_method_definition_name(pm_parser_t *parser) { switch (parser->current.type) { case PM_CASE_KEYWORD: @@ -15851,22 +16134,31 @@ parse_method_definition_name(pm_parser_t *parser) { 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)); + PM_PARSER_ERR_TOKEN_FORMAT(parser, &parser->current, PM_ERR_DEF_NAME, pm_token_str(parser->current.type)); return (pm_token_t) { .type = 0, .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); +parse_heredoc_dedent_string(pm_arena_t *arena, pm_string_t *string, size_t common_whitespace) { + // Make a writable copy in the arena if the string isn't already writable. + // We keep a mutable pointer to the arena memory so we can memmove into it + // below without casting away const from the string's source field. + uint8_t *writable; + + if (string->type != PM_STRING_OWNED) { + size_t length = pm_string_length(string); + writable = (uint8_t *) pm_arena_memdup(arena, pm_string_source(string), length, PRISM_ALIGNOF(uint8_t)); + pm_string_constant_init(string, (const char *) writable, length); + } else { + writable = (uint8_t *) string->source; + } // 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_cursor = writable; const uint8_t *source_end = source_cursor + dest_length; // We're going to move bytes backward in the string when we get leading @@ -15890,10 +16182,23 @@ parse_heredoc_dedent_string(pm_string_t *string, size_t common_whitespace) { dest_length--; } - memmove((uint8_t *) string->source, source_cursor, (size_t) (source_end - source_cursor)); + memmove(writable, source_cursor, (size_t) (source_end - source_cursor)); string->length = dest_length; } +/** + * If we end up trimming all of the whitespace from a node and it isn't + * part of a line continuation, then we'll drop it from the list entirely. + */ +static PRISM_INLINE bool +heredoc_dedent_discard_string_node(pm_parser_t *parser, pm_string_node_t *string_node) { + if (string_node->unescaped.length == 0) { + const uint8_t *cursor = parser->start + PM_LOCATION_START(&string_node->content_loc); + return pm_memchr(cursor, '\\', string_node->content_loc.length, parser->encoding_changed, parser->encoding) == NULL; + } + return false; +} + /** * Take a heredoc node that is indented by a ~ and trim the leading whitespace. */ @@ -15904,8 +16209,7 @@ parse_heredoc_dedent(pm_parser_t *parser, pm_node_list_t *nodes, size_t common_w 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. + // keep around two indices: a read and a write. size_t write_index = 0; pm_node_t *node; @@ -15921,11 +16225,10 @@ parse_heredoc_dedent(pm_parser_t *parser, pm_node_list_t *nodes, size_t common_w pm_string_node_t *string_node = ((pm_string_node_t *) node); if (dedent_next) { - parse_heredoc_dedent_string(&string_node->unescaped, common_whitespace); + parse_heredoc_dedent_string(parser->arena, &string_node->unescaped, common_whitespace); } - if (string_node->unescaped.length == 0) { - pm_node_destroy(parser, node); + if (heredoc_dedent_discard_string_node(parser, string_node)) { } else { nodes->nodes[write_index++] = node; } @@ -15948,7 +16251,7 @@ parse_strings_empty_content(const uint8_t *location) { /** * Parse a set of strings that could be concatenated together. */ -static inline pm_node_t * +static PRISM_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; @@ -16014,18 +16317,16 @@ parse_strings(pm_parser_t *parser, pm_node_t *current, bool accepts_label, uint1 if (match1(parser, PM_TOKEN_STRING_CONTENT)) { pm_node_list_t parts = { 0 }; pm_node_t *part = UP(pm_string_node_create_unescaped(parser, NULL, &content, NULL, &unescaped)); - pm_node_list_append(&parts, part); + pm_node_list_append(parser->arena, &parts, part); do { part = UP(pm_string_node_create_current_string(parser, NULL, &parser->current, NULL)); - pm_node_list_append(&parts, part); + pm_node_list_append(parser->arena, &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); @@ -16035,7 +16336,7 @@ parse_strings(pm_parser_t *parser, pm_node_t *current, bool accepts_label, uint1 } 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)); + PM_PARSER_ERR_TOKEN_FORMAT(parser, &parser->previous, PM_ERR_STRING_LITERAL_TERM, pm_token_str(parser->previous.type)); parser->previous.start = parser->previous.end; parser->previous.type = 0; node = UP(pm_string_node_create_unescaped(parser, &opening, &content, &parser->previous, &unescaped)); @@ -16074,11 +16375,11 @@ parse_strings(pm_parser_t *parser, pm_node_t *current, bool accepts_label, uint1 pm_node_list_t parts = { 0 }; pm_node_t *part = UP(pm_string_node_create_unescaped(parser, NULL, &parser->previous, NULL, &unescaped)); pm_node_flag_set(part, parse_unescaped_encoding(parser)); - pm_node_list_append(&parts, part); + pm_node_list_append(parser->arena, &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); + pm_node_list_append(parser->arena, &parts, part); } } @@ -16092,8 +16393,6 @@ parse_strings(pm_parser_t *parser, pm_node_t *current, bool accepts_label, uint1 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 @@ -16104,7 +16403,7 @@ parse_strings(pm_parser_t *parser, pm_node_t *current, bool accepts_label, uint1 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); + pm_node_list_append(parser->arena, &parts, part); } } @@ -16118,8 +16417,6 @@ parse_strings(pm_parser_t *parser, pm_node_t *current, bool accepts_label, uint1 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) { @@ -16150,11 +16447,11 @@ parse_strings(pm_parser_t *parser, pm_node_t *current, bool accepts_label, uint1 concating = true; pm_interpolated_string_node_t *container = pm_interpolated_string_node_create(parser, NULL, NULL, NULL); - pm_interpolated_string_node_append(container, current); + pm_interpolated_string_node_append(parser->arena, container, current); current = UP(container); } - pm_interpolated_string_node_append((pm_interpolated_string_node_t *) current, node); + pm_interpolated_string_node_append(parser->arena, (pm_interpolated_string_node_t *) current, node); } } @@ -16181,7 +16478,7 @@ parse_pattern_capture(pm_parser_t *parser, pm_constant_id_list_t *captures, pm_c if (pm_constant_id_list_includes(captures, capture)) { pm_parser_err(parser, location->start, location->length, PM_ERR_PATTERN_CAPTURE_DUPLICATE); } else { - pm_constant_id_list_append(captures, capture); + pm_constant_id_list_append(parser->arena, captures, capture); } } @@ -16301,7 +16598,7 @@ parse_pattern_constant_path(pm_parser_t *parser, pm_constant_id_list_t *captures // 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); + pm_array_pattern_node_requireds_append(parser->arena, pattern_node, inner); return UP(pattern_node); } @@ -16444,7 +16741,7 @@ parse_pattern_hash_implicit_value(pm_parser_t *parser, pm_constant_id_list_t *ca */ 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, parser->start_line, keys, node, true) != NULL) { + if (pm_static_literals_add(&parser->line_offsets, parser->start, parser->start_line, keys, node, true) != NULL) { pm_parser_err_node(parser, node, PM_ERR_PATTERN_HASH_KEY_DUPLICATE); } } @@ -16479,7 +16776,7 @@ parse_pattern_hash(pm_parser_t *parser, pm_constant_id_list_t *captures, pm_node } pm_node_t *assoc = UP(pm_assoc_node_create(parser, first_node, NULL, value)); - pm_node_list_append(&assocs, assoc); + pm_node_list_append(parser->arena, &assocs, assoc); break; } } @@ -16494,7 +16791,7 @@ parse_pattern_hash(pm_parser_t *parser, pm_constant_id_list_t *captures, pm_node pm_node_t *value = UP(pm_missing_node_create(parser, PM_NODE_START(first_node), PM_NODE_LENGTH(first_node))); pm_node_t *assoc = UP(pm_assoc_node_create(parser, first_node, NULL, value)); - pm_node_list_append(&assocs, assoc); + pm_node_list_append(parser->arena, &assocs, assoc); break; } } @@ -16518,7 +16815,7 @@ parse_pattern_hash(pm_parser_t *parser, pm_constant_id_list_t *captures, pm_node rest = assoc; } else { pm_parser_err_node(parser, assoc, PM_ERR_PATTERN_EXPRESSION_AFTER_REST); - pm_node_list_append(&assocs, assoc); + pm_node_list_append(parser->arena, &assocs, assoc); } } else { pm_node_t *key; @@ -16559,12 +16856,12 @@ parse_pattern_hash(pm_parser_t *parser, pm_constant_id_list_t *captures, pm_node pm_parser_err_node(parser, assoc, PM_ERR_PATTERN_EXPRESSION_AFTER_REST); } - pm_node_list_append(&assocs, assoc); + pm_node_list_append(parser->arena, &assocs, assoc); } } pm_hash_pattern_node_t *node = pm_hash_pattern_node_node_list_create(parser, &assocs, rest); - xfree(assocs.nodes); + // assocs.nodes is arena-allocated; no explicit free needed. pm_static_literals_free(&keys); return node; @@ -16646,7 +16943,7 @@ parse_pattern_primitive(pm_parser_t *parser, pm_constant_id_list_t *captures, pm } pm_array_pattern_node_t *node = pm_array_pattern_node_empty_create(parser, &opening, &closing); - pm_array_pattern_node_requireds_append(node, inner); + pm_array_pattern_node_requireds_append(parser->arena, node, inner); return UP(node); } case PM_TOKEN_BRACE_LEFT: { @@ -16673,10 +16970,10 @@ parse_pattern_primitive(pm_parser_t *parser, pm_constant_id_list_t *captures, pm 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)); + first_node = parse_expression(parser, PM_BINDING_POWER_MAX, PM_PARSE_ACCEPTS_DO_BLOCK | PM_PARSE_ACCEPTS_LABEL, 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)); + PM_PARSER_ERR_TOKEN_FORMAT(parser, &parser->current, PM_ERR_PATTERN_HASH_KEY, pm_token_str(parser->current.type)); parser_lex(parser); first_node = UP(pm_missing_node_create(parser, PM_TOKEN_START(parser, &parser->previous), PM_TOKEN_LENGTH(&parser->previous))); @@ -16709,7 +17006,7 @@ parse_pattern_primitive(pm_parser_t *parser, pm_constant_id_list_t *captures, pm // 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)); + pm_node_t *right = parse_expression(parser, PM_BINDING_POWER_MAX, PM_PARSE_ACCEPTS_DO_BLOCK, PM_ERR_PATTERN_EXPRESSION_AFTER_RANGE, (uint16_t) (depth + 1)); return UP(pm_range_node_create(parser, NULL, &operator, right)); } default: { @@ -16720,7 +17017,7 @@ parse_pattern_primitive(pm_parser_t *parser, pm_constant_id_list_t *captures, pm } } case PM_CASE_PRIMITIVE: { - pm_node_t *node = parse_expression(parser, PM_BINDING_POWER_MAX, false, true, diag_id, (uint16_t) (depth + 1)); + pm_node_t *node = parse_expression(parser, PM_BINDING_POWER_MAX, PM_PARSE_ACCEPTS_LABEL | PM_PARSE_ACCEPTS_DO_BLOCK, 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(parser, node)) return node; @@ -16731,7 +17028,6 @@ parse_pattern_primitive(pm_parser_t *parser, pm_constant_id_list_t *captures, pm pm_missing_node_t *missing_node = pm_missing_node_create(parser, PM_NODE_START(node), PM_NODE_LENGTH(node)); pm_node_unreference(parser, node); - pm_node_destroy(parser, node); return UP(missing_node); } @@ -16744,7 +17040,7 @@ parse_pattern_primitive(pm_parser_t *parser, pm_constant_id_list_t *captures, pm // 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)); + pm_node_t *right = parse_expression(parser, PM_BINDING_POWER_MAX, PM_PARSE_ACCEPTS_DO_BLOCK, PM_ERR_PATTERN_EXPRESSION_AFTER_RANGE, (uint16_t) (depth + 1)); return UP(pm_range_node_create(parser, node, &operator, right)); } default: @@ -16809,7 +17105,7 @@ parse_pattern_primitive(pm_parser_t *parser, pm_constant_id_list_t *captures, pm 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)); + pm_node_t *expression = parse_value_expression(parser, PM_BINDING_POWER_COMPOSITION, PM_PARSE_ACCEPTS_DO_BLOCK | PM_PARSE_ACCEPTS_COMMAND_CALL, PM_ERR_PATTERN_EXPRESSION_AFTER_PIN, (uint16_t) (depth + 1)); parser->pattern_matching_newlines = previous_pattern_matching_newlines; accept1(parser, PM_TOKEN_NEWLINE); @@ -17043,14 +17339,14 @@ parse_pattern(pm_parser_t *parser, pm_constant_id_list_t *captures, uint8_t flag // 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); + pm_node_list_append(parser->arena, &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); + pm_node_list_append(parser->arena, &nodes, node); trailing_rest = true; break; } @@ -17070,7 +17366,7 @@ parse_pattern(pm_parser_t *parser, pm_constant_id_list_t *captures, uint8_t flag node = parse_pattern_primitives(parser, captures, NULL, PM_ERR_PATTERN_EXPRESSION_AFTER_COMMA, (uint16_t) (depth + 1)); } - pm_node_list_append(&nodes, node); + pm_node_list_append(parser->arena, &nodes, node); } // If the first pattern and the last pattern are rest patterns, then we @@ -17091,7 +17387,7 @@ parse_pattern(pm_parser_t *parser, pm_constant_id_list_t *captures, uint8_t flag } } - xfree(nodes.nodes); + // nodes.nodes is arena-allocated; no explicit free needed. } 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. @@ -17106,7 +17402,7 @@ parse_pattern(pm_parser_t *parser, pm_constant_id_list_t *captures, uint8_t flag * from its start bounds. If it's a compound node, then we will recursively * apply this function to its value. */ -static inline void +static PRISM_INLINE void parse_negative_numeric(pm_node_t *node) { switch (PM_NODE_TYPE(node)) { case PM_INTEGER_NODE: { @@ -17150,22 +17446,22 @@ 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)); + PM_PARSER_ERR_TOKEN_FORMAT(parser, &parser->previous, diag_id, pm_token_str(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)); + PM_PARSER_ERR_TOKEN_FORMAT(parser, &parser->current, diag_id, pm_token_str(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)); + const char *human = (parser->current.type == PM_TOKEN_EOF ? "end-of-input" : pm_token_str(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)); + PM_PARSER_ERR_TOKEN_FORMAT(parser, &parser->current, diag_id, pm_token_str(parser->current.type)); break; } default: @@ -17363,67 +17659,103 @@ parse_yield(pm_parser_t *parser, const pm_node_t *node) { } /** - * This struct is used to pass information between the regular expression parser - * and the error callback. + * Determine if a given call node looks like a "command", which means it has + * arguments but does not have parentheses. */ -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; +static PRISM_INLINE bool +pm_call_node_command_p(const pm_call_node_t *node) { + return ( + (node->opening_loc.length == 0) && + (node->block == NULL || PM_NODE_TYPE_P(node->block, PM_BLOCK_ARGUMENT_NODE)) && + (node->arguments != NULL || node->block != NULL) + ); +} /** - * This callback is called when the regular expression parser encounters a - * syntax error. + * Returns true if the given node is a command-style call (a method call without + * parentheses that has arguments), excluding operator calls (e.g., a + b) which + * satisfy the same structural criteria but are not commands. */ -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_token_t location; +static bool +pm_command_call_value_p(const pm_node_t *node) { + switch (PM_NODE_TYPE(node)) { + case PM_CALL_NODE: { + const pm_call_node_t *call = (const pm_call_node_t *) node; - if (callback_data->shared) { - location = (pm_token_t) { .type = 0, .start = start, .end = end }; - } else { - location = (pm_token_t) { .type = 0, .start = callback_data->start, .end = callback_data->end }; - } + // Command-style calls (e.g., foo bar, obj.foo bar). + // Attribute writes (e.g., a.b = 1) are not commands. + if (pm_call_node_command_p(call) && !PM_NODE_FLAG_P(node, PM_CALL_NODE_FLAGS_ATTRIBUTE_WRITE) && (call->receiver == NULL || call->call_operator_loc.length > 0)) { + return true; + } + + // A `!` or `not` prefix wrapping a command call (e.g., + // `!foo bar`, `not foo bar`) is also a command-call value. + if (call->receiver != NULL && call->arguments == NULL && call->opening_loc.length == 0 && call->call_operator_loc.length == 0) { + return pm_command_call_value_p(call->receiver); + } - PM_PARSER_ERR_FORMAT(callback_data->parser, PM_TOKEN_START(callback_data->parser, &location), PM_TOKEN_LENGTH(&location), PM_ERR_REGEXP_PARSE_ERROR, message); + return false; + } + case PM_SUPER_NODE: { + const pm_super_node_t *cast = (const pm_super_node_t *) node; + return cast->lparen_loc.length == 0 && (cast->arguments != NULL || cast->block != NULL); + } + case PM_YIELD_NODE: { + const pm_yield_node_t *cast = (const pm_yield_node_t *) node; + return cast->lparen_loc.length == 0 && cast->arguments != NULL; + } + case PM_RESCUE_MODIFIER_NODE: + return pm_command_call_value_p(((const pm_rescue_modifier_node_t *) node)->expression); + case PM_DEF_NODE: { + const pm_def_node_t *cast = (const pm_def_node_t *) node; + if (cast->equal_loc.length > 0 && cast->body != NULL) { + const pm_node_t *body = cast->body; + if (PM_NODE_TYPE_P(body, PM_STATEMENTS_NODE)) { + body = ((const pm_statements_node_t *) body)->body.nodes[((const pm_statements_node_t *) body)->body.size - 1]; + } + return pm_command_call_value_p(body); + } + return false; + } + default: + return false; + } } /** - * Parse the errors for the regular expression and add them to the parser. + * Returns true if the given node is a block call: a command + * with a do-block, or any call chained (via `.`, `::`, `&.`) from such a node. + * Block calls can only be followed by call chaining, composition (and/or), and + * modifier operators. */ -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 = parser->start + PM_NODE_START(node), - .end = parser->start + PM_NODE_END(node), - .shared = unescaped->type == PM_STRING_SHARED - }; +static bool +pm_block_call_p(const pm_node_t *node) { + while (PM_NODE_TYPE_P(node, PM_CALL_NODE)) { + const pm_call_node_t *call = (const pm_call_node_t *) node; + if (call->opening_loc.length > 0) return false; + + // Root: command with do-block (e.g., `foo bar do end`). + if (call->arguments != NULL && call->block != NULL && PM_NODE_TYPE_P(call->block, PM_BLOCK_NODE)) { + return true; + } + + // Walk up the receiver chain (e.g., `foo bar do end.baz`). + if (call->call_operator_loc.length > 0 && call->receiver != NULL) { + node = call->receiver; + continue; + } - 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); + return false; + } + + return false; } /** * 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) { +static PRISM_INLINE pm_node_t * +parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, uint8_t flags, pm_diagnostic_id_t diag_id, uint16_t depth) { switch (parser->current.type) { case PM_TOKEN_BRACKET_LEFT_ARRAY: { parser_lex(parser); @@ -17452,7 +17784,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b } else { // If there was no comma, then we need to add a syntax // error. - PM_PARSER_ERR_FORMAT(parser, PM_TOKEN_END(parser, &parser->previous), 0, PM_ERR_ARRAY_SEPARATOR, pm_token_type_human(parser->current.type)); + PM_PARSER_ERR_FORMAT(parser, PM_TOKEN_END(parser, &parser->previous), 0, PM_ERR_ARRAY_SEPARATOR, pm_token_str(parser->current.type)); parser->previous.start = parser->previous.end; parser->previous.type = 0; } @@ -17472,7 +17804,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b 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)); + expression = parse_value_expression(parser, PM_BINDING_POWER_DEFINED, flags & PM_PARSE_ACCEPTS_DO_BLOCK, PM_ERR_ARRAY_EXPRESSION_AFTER_STAR, (uint16_t) (depth + 1)); } element = UP(pm_splat_node_create(parser, &operator, expression)); @@ -17484,14 +17816,14 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b 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)) { + if (!match8(parser, PM_TOKEN_EOF, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON, PM_TOKEN_KEYWORD_DO_BLOCK, 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)); + element = parse_value_expression(parser, PM_BINDING_POWER_DEFINED, (flags & PM_PARSE_ACCEPTS_DO_BLOCK) | PM_PARSE_ACCEPTS_LABEL, PM_ERR_ARRAY_EXPRESSION, (uint16_t) (depth + 1)); if (pm_symbol_node_label_p(parser, element) || accept1(parser, PM_TOKEN_EQUAL_GREATER)) { if (parsed_bare_hash) { @@ -17507,9 +17839,9 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b 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)); + pm_node_t *value = parse_value_expression(parser, PM_BINDING_POWER_DEFINED, flags & PM_PARSE_ACCEPTS_DO_BLOCK, PM_ERR_HASH_VALUE, (uint16_t) (depth + 1)); pm_node_t *assoc = UP(pm_assoc_node_create(parser, element, NTOK2PTR(operator), value)); - pm_keyword_hash_node_elements_append(hash, assoc); + pm_keyword_hash_node_elements_append(parser->arena, hash, assoc); element = UP(hash); if (accept1(parser, PM_TOKEN_COMMA) && !match1(parser, PM_TOKEN_BRACKET_RIGHT)) { @@ -17521,14 +17853,14 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b } } - pm_array_node_elements_append(array, element); + pm_array_node_elements_append(parser->arena, 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)); + PM_PARSER_ERR_TOKEN_FORMAT(parser, &parser->current, PM_ERR_ARRAY_TERM, pm_token_str(parser->current.type)); parser->previous.start = parser->previous.end; parser->previous.type = 0; } @@ -17541,7 +17873,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b 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_flags_t paren_flags = 0; pm_node_list_t current_block_exits = { 0 }; pm_node_list_t *previous_block_exits = push_block_exits(parser, ¤t_block_exits); @@ -17549,7 +17881,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b parser_lex(parser); while (true) { if (accept1(parser, PM_TOKEN_SEMICOLON)) { - flags |= PM_PARENTHESES_NODE_FLAGS_MULTIPLE_STATEMENTS; + paren_flags |= PM_PARENTHESES_NODE_FLAGS_MULTIPLE_STATEMENTS; } else if (!accept1(parser, PM_TOKEN_NEWLINE)) { break; } @@ -17559,18 +17891,15 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b // 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)); + return UP(pm_parentheses_node_create(parser, &opening, NULL, &parser->previous, paren_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)); + pm_node_t *statement = parse_expression(parser, PM_BINDING_POWER_STATEMENT, PM_PARSE_ACCEPTS_COMMAND_CALL | PM_PARSE_ACCEPTS_DO_BLOCK, PM_ERR_CANNOT_PARSE_EXPRESSION, (uint16_t) (depth + 1)); context_pop(parser); // Determine if this statement is followed by a terminator. In the @@ -17580,7 +17909,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b if (accept1(parser, PM_TOKEN_SEMICOLON)) { terminator_found = true; - flags |= PM_PARENTHESES_NODE_FLAGS_MULTIPLE_STATEMENTS; + paren_flags |= PM_PARENTHESES_NODE_FLAGS_MULTIPLE_STATEMENTS; } else if (accept1(parser, PM_TOKEN_NEWLINE)) { terminator_found = true; } @@ -17588,7 +17917,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b if (terminator_found) { while (true) { if (accept1(parser, PM_TOKEN_SEMICOLON)) { - flags |= PM_PARENTHESES_NODE_FLAGS_MULTIPLE_STATEMENTS; + paren_flags |= PM_PARENTHESES_NODE_FLAGS_MULTIPLE_STATEMENTS; } else if (!accept1(parser, PM_TOKEN_NEWLINE)) { break; } @@ -17605,9 +17934,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b 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 @@ -17661,14 +17988,14 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b 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)); + return UP(pm_parentheses_node_create(parser, &opening, UP(statements), &parser->previous, paren_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; + paren_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); @@ -17676,12 +18003,12 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b // 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)); + PM_PARSER_ERR_TOKEN_FORMAT(parser, &parser->current, PM_ERR_EXPECT_EOL_AFTER_STATEMENT, pm_token_str(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_node_t *node = parse_expression(parser, PM_BINDING_POWER_STATEMENT, PM_PARSE_ACCEPTS_COMMAND_CALL | PM_PARSE_ACCEPTS_DO_BLOCK, 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 @@ -17707,7 +18034,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b } 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)); + PM_PARSER_ERR_TOKEN_FORMAT(parser, &parser->current, PM_ERR_EXPECT_EOL_AFTER_STATEMENT, pm_token_str(parser->current.type)); } } @@ -17743,10 +18070,8 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b } 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)); + return UP(pm_parentheses_node_create(parser, &opening, UP(statements), &parser->previous, paren_flags)); } case PM_TOKEN_BRACE_LEFT: { // If we were passed a current_hash_keys via the parser, then that @@ -17831,12 +18156,12 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b // 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))) || + ((flags & PM_PARSE_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)); + parse_arguments_list(parser, &arguments, true, flags, (uint16_t) (depth + 1)); return UP(pm_call_node_fcall_create(parser, &constant, &arguments)); } @@ -17868,7 +18193,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b 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)); + pm_node_t *right = parse_expression(parser, pm_binding_powers[operator.type].left, flags & PM_PARSE_ACCEPTS_DO_BLOCK, 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 @@ -17936,7 +18261,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b 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))) { + if (parse_arguments_list(parser, &arguments, true, flags, (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); @@ -17958,12 +18283,12 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b // 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))) || + ((flags & PM_PARSE_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)); + parse_arguments_list(parser, &arguments, true, flags, (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)) { @@ -17986,7 +18311,6 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b } } - pm_node_destroy(parser, node); return UP(fcall); } } @@ -18051,7 +18375,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b } if (lex_mode.indent == PM_HEREDOC_INDENT_TILDE && (common_whitespace != (size_t) -1) && (common_whitespace != 0)) { - parse_heredoc_dedent_string(&cast->unescaped, common_whitespace); + parse_heredoc_dedent_string(parser->arena, &cast->unescaped, common_whitespace); } node = UP(cast); @@ -18061,11 +18385,11 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b // 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); + pm_node_list_append(parser->arena, &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); + pm_node_list_append(parser->arena, &parts, part); } } @@ -18082,7 +18406,6 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b 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(parser, cast, &parser->previous); @@ -18122,22 +18445,22 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b return node; } case PM_TOKEN_INTEGER: { - pm_node_flags_t base = parser->integer_base; + 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; + 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; + 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; + pm_node_flags_t base = parser->integer.base; parser_lex(parser); return UP(pm_integer_node_rational_imaginary_create(parser, base, &parser->previous)); } @@ -18204,17 +18527,14 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b } 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)); + predicate = parse_value_expression(parser, PM_BINDING_POWER_COMPOSITION, (flags & PM_PARSE_ACCEPTS_DO_BLOCK) | PM_PARSE_ACCEPTS_COMMAND_CALL, 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)); } @@ -18240,15 +18560,15 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b 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_node_t *expression = parse_value_expression(parser, PM_BINDING_POWER_DEFINED, flags & PM_PARSE_ACCEPTS_DO_BLOCK, 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)); + pm_when_node_conditions_append(parser->arena, 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); + pm_node_t *condition = parse_value_expression(parser, PM_BINDING_POWER_DEFINED, flags & PM_PARSE_ACCEPTS_DO_BLOCK, PM_ERR_CASE_EXPRESSION_AFTER_WHEN, (uint16_t) (depth + 1)); + pm_when_node_conditions_append(parser->arena, when_node, condition); // If we found a missing node, then this is a syntax // error and we should stop looping. @@ -18282,7 +18602,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b } } - pm_case_node_condition_append(case_node, UP(when_node)); + pm_case_node_condition_append(parser->arena, case_node, UP(when_node)); } // If we didn't parse any conditions (in or when) then we need @@ -18321,18 +18641,17 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b 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)); + pm_node_t *predicate = parse_value_expression(parser, PM_BINDING_POWER_COMPOSITION, (flags & PM_PARSE_ACCEPTS_DO_BLOCK) | PM_PARSE_ACCEPTS_COMMAND_CALL, 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)); + pm_node_t *predicate = parse_value_expression(parser, PM_BINDING_POWER_COMPOSITION, (flags & PM_PARSE_ACCEPTS_DO_BLOCK) | PM_PARSE_ACCEPTS_COMMAND_CALL, PM_ERR_CONDITIONAL_UNLESS_PREDICATE, (uint16_t) (depth + 1)); pattern = UP(pm_unless_node_modifier_create(parser, pattern, &keyword, predicate)); } @@ -18361,7 +18680,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b // 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, NTOK2PTR(then_keyword))); - pm_case_match_node_condition_append(case_node, condition); + pm_case_match_node_condition_append(parser->arena, case_node, condition); } // If we didn't parse any conditions (in or when) then we need @@ -18401,8 +18720,6 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b } pop_block_exits(parser, previous_block_exits); - pm_node_list_free(¤t_block_exits); - return node; } case PM_TOKEN_KEYWORD_BEGIN: { @@ -18429,10 +18746,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b PM_NODE_LENGTH_SET_TOKEN(parser, begin_node, &parser->previous); pm_begin_node_end_keyword_set(parser, 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: { @@ -18457,8 +18771,6 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b } 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: @@ -18477,11 +18789,11 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b 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)); + parse_arguments(parser, &arguments, false, PM_TOKEN_EOF, flags, (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)); + if (!(flags & PM_PARSE_ACCEPTS_COMMAND_CALL) && arguments.arguments != NULL) { + PM_PARSER_ERR_TOKEN_FORMAT(parser, &next, PM_ERR_EXPECT_EOL_AFTER_STATEMENT, pm_token_str(next.type)); } } } @@ -18512,7 +18824,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b pm_token_t keyword = parser->previous; pm_arguments_t arguments = { 0 }; - parse_arguments_list(parser, &arguments, true, accepts_command_call, (uint16_t) (depth + 1)); + parse_arguments_list(parser, &arguments, true, flags, (uint16_t) (depth + 1)); if ( arguments.opening_loc.length == 0 && @@ -18529,7 +18841,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b pm_token_t keyword = parser->previous; pm_arguments_t arguments = { 0 }; - parse_arguments_list(parser, &arguments, false, accepts_command_call, (uint16_t) (depth + 1)); + parse_arguments_list(parser, &arguments, false, flags, (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 @@ -18538,7 +18850,6 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b 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; } @@ -18559,11 +18870,11 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b 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_node_t *expression = parse_value_expression(parser, PM_BINDING_POWER_COMPOSITION, (flags & PM_PARSE_ACCEPTS_DO_BLOCK) | PM_PARSE_ACCEPTS_COMMAND_CALL, 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_PARSER_ERR_TOKEN_FORMAT(parser, &parser->current, PM_ERR_EXPECT_SINGLETON_CLASS_DELIMITER, pm_token_str(parser->current.type)); } pm_node_t *statements = NULL; @@ -18589,12 +18900,10 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b 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_node_t *constant_path = parse_expression(parser, PM_BINDING_POWER_INDEX, flags & PM_PARSE_ACCEPTS_DO_BLOCK, 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); @@ -18610,7 +18919,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b 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)); + superclass = parse_value_expression(parser, PM_BINDING_POWER_COMPOSITION, (flags & PM_PARSE_ACCEPTS_DO_BLOCK) | PM_PARSE_ACCEPTS_COMMAND_CALL, PM_ERR_CLASS_SUPERCLASS, (uint16_t) (depth + 1)); } else { superclass = NULL; } @@ -18654,8 +18963,6 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b } 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, NTOK2PTR(inheritance_operator), superclass, statements, &parser->previous)); } case PM_TOKEN_KEYWORD_DEF: { @@ -18772,7 +19079,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b 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)); + PM_PARSER_ERR_TOKEN_FORMAT(parser, &identifier, PM_ERR_DEF_NAME, pm_token_str(identifier.type)); } name = identifier; @@ -18788,7 +19095,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b 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)); + pm_node_t *expression = parse_value_expression(parser, PM_BINDING_POWER_COMPOSITION, (flags & PM_PARSE_ACCEPTS_DO_BLOCK) | PM_PARSE_ACCEPTS_COMMAND_CALL, PM_ERR_DEF_RECEIVER, (uint16_t) (depth + 1)); accept1(parser, PM_TOKEN_NEWLINE); expect1(parser, PM_TOKEN_PARENTHESIS_RIGHT, PM_ERR_EXPECT_RPAREN); @@ -18826,7 +19133,19 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b 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)); + // https://bugs.ruby-lang.org/issues/19107 + bool allow_trailing_comma = parser->version >= PM_OPTIONS_VERSION_CRUBY_4_1; + params = parse_parameters( + parser, + PM_BINDING_POWER_DEFINED, + true, + allow_trailing_comma, + true, + true, + false, + PM_ERR_ARGUMENT_NO_FORWARDING_ELLIPSES, + (uint16_t) (depth + 1) + ); } lex_state_set(parser, PM_LEX_STATE_BEG); @@ -18834,7 +19153,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b 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)); + PM_PARSER_ERR_TOKEN_FORMAT(parser, &parser->current, PM_ERR_DEF_PARAMS_TERM_PAREN, pm_token_str(parser->current.type)); parser->previous.start = parser->previous.end; parser->previous.type = 0; } @@ -18849,7 +19168,17 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b lex_state_set(parser, parser->lex_state | PM_LEX_STATE_LABEL); } - params = parse_parameters(parser, PM_BINDING_POWER_DEFINED, false, false, true, true, false, (uint16_t) (depth + 1)); + params = parse_parameters( + parser, + PM_BINDING_POWER_DEFINED, + false, + false, + true, + true, + false, + PM_ERR_ARGUMENT_NO_FORWARDING_ELLIPSES, + (uint16_t) (depth + 1) + ); // Reject `def * = 1` and similar. We have to specifically check // for them because they create ambiguity with optional arguments. @@ -18888,26 +19217,57 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b pm_do_loop_stack_push(parser, false); statements = UP(pm_statements_node_create(parser)); - bool allow_command_call; + uint8_t allow_flags; if (parser->version >= PM_OPTIONS_VERSION_CRUBY_4_0) { - allow_command_call = accepts_command_call; + allow_flags = flags & PM_PARSE_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; + allow_flags = (binding_power == PM_BINDING_POWER_ASSIGNMENT || binding_power < PM_BINDING_POWER_COMPOSITION) ? PM_PARSE_ACCEPTS_COMMAND_CALL : 0; } - pm_node_t *statement = parse_expression(parser, PM_BINDING_POWER_DEFINED + 1, allow_command_call, false, PM_ERR_DEF_ENDLESS, (uint16_t) (depth + 1)); + // Inside a def body, we push true onto the + // accepts_block_stack so that `do` is lexed as + // PM_TOKEN_KEYWORD_DO (which can only start a block for + // primary-level constructs, not commands). During command + // argument parsing, the stack is pushed to false, causing + // `do` to be lexed as PM_TOKEN_KEYWORD_DO_BLOCK, which + // is not consumed inside the endless def body and instead + // left for the outer context. + pm_accepts_block_stack_push(parser, true); + pm_node_t *statement = parse_expression(parser, PM_BINDING_POWER_DEFINED + 1, allow_flags | PM_PARSE_IN_ENDLESS_DEF, PM_ERR_DEF_ENDLESS, (uint16_t) (depth + 1)); + pm_accepts_block_stack_pop(parser); + + // If an unconsumed PM_TOKEN_KEYWORD_DO follows the body, + // it is an error (e.g., `def f = 1 do end`). + // PM_TOKEN_KEYWORD_DO_BLOCK is intentionally not caught + // here — it should bubble up to the outer context (e.g., + // `private def f = puts "Hello" do end` where the block + // attaches to `private`). + if (accept1(parser, PM_TOKEN_KEYWORD_DO)) { + pm_block_node_t *block = parse_block(parser, (uint16_t) (depth + 1)); + pm_parser_err_node(parser, UP(block), PM_ERR_DEF_ENDLESS_DO_BLOCK); + } 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)); + + // In the Ruby grammar, the rescue value of an endless + // method command excludes and/or and in/=>. + pm_node_t *value = parse_expression(parser, PM_BINDING_POWER_MATCH + 1, flags & PM_PARSE_ACCEPTS_DO_BLOCK, PM_ERR_RESCUE_MODIFIER_VALUE, (uint16_t) (depth + 1)); context_pop(parser); statement = UP(pm_rescue_modifier_node_create(parser, statement, &rescue_keyword, value)); } + // A nested endless def whose body is a command call (e.g., + // `def f = def g = foo bar`) is a command assignment and + // cannot appear as a def body. + if (PM_NODE_TYPE_P(statement, PM_DEF_NODE) && pm_command_call_value_p(statement)) { + PM_PARSER_ERR_NODE_FORMAT(parser, statement, PM_ERR_EXPECT_EOL_AFTER_STATEMENT, pm_token_str(parser->current.type)); + } + pm_statements_node_body_append(parser, (pm_statements_node_t *) statements, statement, false); pm_do_loop_stack_pop(parser); context_pop(parser); @@ -18955,7 +19315,6 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b pm_constant_id_t name_id = pm_parser_constant_id_raw(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, @@ -18991,7 +19350,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b expression = UP(pm_parentheses_node_create(parser, &lparen, NULL, &parser->previous, 0)); lparen = (pm_token_t) { 0 }; } else { - expression = parse_expression(parser, PM_BINDING_POWER_COMPOSITION, true, false, PM_ERR_DEFINED_EXPRESSION, (uint16_t) (depth + 1)); + expression = parse_expression(parser, PM_BINDING_POWER_COMPOSITION, PM_PARSE_ACCEPTS_COMMAND_CALL | PM_PARSE_ACCEPTS_DO_BLOCK, PM_ERR_DEFINED_EXPRESSION, (uint16_t) (depth + 1)); if (!parser->recovering) { accept1(parser, PM_TOKEN_NEWLINE); @@ -19000,7 +19359,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b } } } else { - expression = parse_expression(parser, PM_BINDING_POWER_DEFINED, false, false, PM_ERR_DEFINED_EXPRESSION, (uint16_t) (depth + 1)); + expression = parse_expression(parser, PM_BINDING_POWER_DEFINED, flags & PM_PARSE_ACCEPTS_DO_BLOCK, PM_ERR_DEFINED_EXPRESSION, (uint16_t) (depth + 1)); } context_pop(parser); @@ -19049,12 +19408,12 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b 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)); + name = parse_expression(parser, PM_BINDING_POWER_INDEX, flags & PM_PARSE_ACCEPTS_DO_BLOCK, 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)); + index = parse_expression(parser, PM_BINDING_POWER_INDEX, flags & PM_PARSE_ACCEPTS_DO_BLOCK, 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, PM_TOKEN_START(parser, &for_keyword), PM_TOKEN_LENGTH(&for_keyword))); @@ -19073,7 +19432,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b 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_node_t *collection = parse_value_expression(parser, PM_BINDING_POWER_COMPOSITION, (flags & PM_PARSE_ACCEPTS_DO_BLOCK) | PM_PARSE_ACCEPTS_COMMAND_CALL, PM_ERR_FOR_COLLECTION, (uint16_t) (depth + 1)); pm_do_loop_stack_pop(parser); pm_token_t do_keyword = { 0 }; @@ -19081,7 +19440,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b do_keyword = parser->previous; } else { 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_PARSER_ERR_TOKEN_FORMAT(parser, &parser->current, PM_ERR_EXPECT_FOR_DELIMITER, pm_token_str(parser->current.type)); } } @@ -19115,9 +19474,8 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b 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); + pm_undef_node_append(parser->arena, undef, name); while (match1(parser, PM_TOKEN_COMMA)) { lex_state_set(parser, PM_LEX_STATE_FNAME | PM_LEX_STATE_FITEM); @@ -19125,11 +19483,10 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b 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); + pm_undef_node_append(parser->arena, undef, name); } } @@ -19142,10 +19499,14 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b 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)) { + // The `not` keyword without parentheses is only valid in contexts + // where it would be parsed as an expression (i.e., at or below + // the `not` binding power level). In other contexts (e.g., method + // arguments, array elements, assignment right-hand sides), + // parentheses are required: `not(x)`. An exception is made for + // endless def bodies, where `not` is valid as both `arg` and + // `command` (e.g., `def f = not 1`, `def f = not foo bar`). + if (binding_power > PM_BINDING_POWER_NOT && !(flags & PM_PARSE_IN_ENDLESS_DEF) && !match1(parser, PM_TOKEN_PARENTHESIS_LEFT)) { if (match1(parser, PM_TOKEN_PARENTHESIS_LEFT_PARENTHESES)) { pm_parser_err(parser, PM_TOKEN_END(parser, &parser->previous), 1, PM_ERR_EXPECT_LPAREN_AFTER_NOT_LPAREN); } else { @@ -19165,7 +19526,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b receiver = UP(pm_parentheses_node_create(parser, &lparen, NULL, &parser->previous, 0)); } else { arguments.opening_loc = TOK2LOC(parser, &lparen); - receiver = parse_expression(parser, PM_BINDING_POWER_COMPOSITION, true, false, PM_ERR_NOT_EXPRESSION, (uint16_t) (depth + 1)); + receiver = parse_expression(parser, PM_BINDING_POWER_COMPOSITION, PM_PARSE_ACCEPTS_COMMAND_CALL | PM_PARSE_ACCEPTS_DO_BLOCK, PM_ERR_NOT_EXPRESSION, (uint16_t) (depth + 1)); if (!parser->recovering) { accept1(parser, PM_TOKEN_NEWLINE); @@ -19174,7 +19535,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b } } } else { - receiver = parse_expression(parser, PM_BINDING_POWER_NOT, true, false, PM_ERR_NOT_EXPRESSION, (uint16_t) (depth + 1)); + receiver = parse_expression(parser, PM_BINDING_POWER_NOT, (flags & PM_PARSE_ACCEPTS_DO_BLOCK) | PM_PARSE_ACCEPTS_COMMAND_CALL, PM_ERR_NOT_EXPRESSION, (uint16_t) (depth + 1)); } return UP(pm_call_node_not_create(parser, receiver, &message, &arguments)); @@ -19193,14 +19554,13 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b 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_node_t *constant_path = parse_expression(parser, PM_BINDING_POWER_INDEX, flags & PM_PARSE_ACCEPTS_DO_BLOCK, 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 = 0, .start = parser->previous.end, .end = parser->previous.end }; return UP(pm_module_node_create(parser, NULL, &module_keyword, constant_path, &missing, NULL, &missing)); @@ -19249,7 +19609,6 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b } 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)); } @@ -19286,7 +19645,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b 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_node_t *predicate = parse_value_expression(parser, PM_BINDING_POWER_COMPOSITION, (flags & PM_PARSE_ACCEPTS_DO_BLOCK) | PM_PARSE_ACCEPTS_COMMAND_CALL, PM_ERR_CONDITIONAL_UNTIL_PREDICATE, (uint16_t) (depth + 1)); pm_do_loop_stack_pop(parser); context_pop(parser); @@ -19319,7 +19678,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b 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_node_t *predicate = parse_value_expression(parser, PM_BINDING_POWER_COMPOSITION, (flags & PM_PARSE_ACCEPTS_DO_BLOCK) | PM_PARSE_ACCEPTS_COMMAND_CALL, PM_ERR_CONDITIONAL_WHILE_PREDICATE, (uint16_t) (depth + 1)); pm_do_loop_stack_pop(parser); context_pop(parser); @@ -19364,7 +19723,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b } else if (PM_NODE_TYPE_P(current, PM_INTERPOLATED_SYMBOL_NODE)) { pm_node_t *string = UP(pm_string_node_create_current_string(parser, NULL, &parser->current, NULL)); parser_lex(parser); - pm_interpolated_symbol_node_append((pm_interpolated_symbol_node_t *) current, string); + pm_interpolated_symbol_node_append(parser->arena, (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 content = { .type = PM_TOKEN_STRING_CONTENT, .start = parser->start + cast->value_loc.start, .end = parser->start + cast->value_loc.start + cast->value_loc.length }; @@ -19373,10 +19732,10 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b parser_lex(parser); pm_interpolated_symbol_node_t *interpolated = pm_interpolated_symbol_node_create(parser, NULL, NULL, NULL); - pm_interpolated_symbol_node_append(interpolated, first_string); - pm_interpolated_symbol_node_append(interpolated, second_string); + pm_interpolated_symbol_node_append(parser->arena, interpolated, first_string); + pm_interpolated_symbol_node_append(parser->arena, interpolated, second_string); - xfree(current); + // current is arena-allocated so no explicit free is needed. current = UP(interpolated); } else { assert(false && "unreachable"); @@ -19384,7 +19743,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b } if (current) { - pm_array_node_elements_append(array, current); + pm_array_node_elements_append(parser->arena, array, current); current = NULL; } else { expect1(parser, PM_TOKEN_STRING_CONTENT, PM_ERR_LIST_I_LOWER_ELEMENT); @@ -19420,7 +19779,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b } 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); + pm_array_node_elements_append(parser->arena, array, current); current = NULL; } @@ -19441,7 +19800,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b pm_node_t *string = UP(pm_string_node_create_current_string(parser, NULL, &parser->current, NULL)); parser_lex(parser); - pm_interpolated_symbol_node_append((pm_interpolated_symbol_node_t *) current, string); + pm_interpolated_symbol_node_append(parser->arena, (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 @@ -19458,10 +19817,10 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b parser_lex(parser); pm_interpolated_symbol_node_t *interpolated = pm_interpolated_symbol_node_create(parser, NULL, NULL, NULL); - pm_interpolated_symbol_node_append(interpolated, first_string); - pm_interpolated_symbol_node_append(interpolated, second_string); + pm_interpolated_symbol_node_append(parser->arena, interpolated, first_string); + pm_interpolated_symbol_node_append(parser->arena, interpolated, second_string); - xfree(current); + // current is arena-allocated so no explicit free is needed. current = UP(interpolated); } else { assert(false && "unreachable"); @@ -19483,7 +19842,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b pm_interpolated_symbol_node_t *interpolated = pm_interpolated_symbol_node_create(parser, NULL, NULL, NULL); current = UP(pm_symbol_node_to_string_node(parser, (pm_symbol_node_t *) current)); - pm_interpolated_symbol_node_append(interpolated, current); + pm_interpolated_symbol_node_append(parser->arena, interpolated, current); PM_NODE_START_SET_NODE(interpolated, current); start_location_set = true; current = UP(interpolated); @@ -19493,7 +19852,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b } pm_node_t *part = parse_string_part(parser, (uint16_t) (depth + 1)); - pm_interpolated_symbol_node_append((pm_interpolated_symbol_node_t *) current, part); + pm_interpolated_symbol_node_append(parser->arena, (pm_interpolated_symbol_node_t *) current, part); if (!start_location_set) { PM_NODE_START_SET_NODE(current, part); } @@ -19514,7 +19873,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b pm_interpolated_symbol_node_t *interpolated = pm_interpolated_symbol_node_create(parser, NULL, NULL, NULL); current = UP(pm_symbol_node_to_string_node(parser, (pm_symbol_node_t *) current)); - pm_interpolated_symbol_node_append(interpolated, current); + pm_interpolated_symbol_node_append(parser->arena, interpolated, current); PM_NODE_START_SET_NODE(interpolated, current); start_location_set = true; current = UP(interpolated); @@ -19526,7 +19885,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b } pm_node_t *part = parse_string_part(parser, (uint16_t) (depth + 1)); - pm_interpolated_symbol_node_append((pm_interpolated_symbol_node_t *) current, part); + pm_interpolated_symbol_node_append(parser->arena, (pm_interpolated_symbol_node_t *) current, part); if (!start_location_set) { PM_NODE_START_SET_NODE(current, part); } @@ -19541,7 +19900,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b // 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_array_node_elements_append(parser->arena, array, current); } pm_token_t closing = parser->current; @@ -19574,11 +19933,11 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b 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); + pm_interpolated_string_node_append(parser->arena, (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, NULL, NULL, NULL); - pm_interpolated_string_node_append(interpolated, current); - pm_interpolated_string_node_append(interpolated, string); + pm_interpolated_string_node_append(parser->arena, interpolated, current); + pm_interpolated_string_node_append(parser->arena, interpolated, string); current = UP(interpolated); } else { assert(false && "unreachable"); @@ -19587,7 +19946,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b } if (current) { - pm_array_node_elements_append(array, current); + pm_array_node_elements_append(parser->arena, array, current); current = NULL; } else { expect1(parser, PM_TOKEN_STRING_CONTENT, PM_ERR_LIST_W_LOWER_ELEMENT); @@ -19628,7 +19987,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b // 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); + pm_array_node_elements_append(parser->arena, array, current); current = NULL; } @@ -19650,15 +20009,15 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b // 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); + pm_interpolated_string_node_append(parser->arena, (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, NULL, NULL, NULL); - pm_interpolated_string_node_append(interpolated, current); - pm_interpolated_string_node_append(interpolated, string); + pm_interpolated_string_node_append(parser->arena, interpolated, current); + pm_interpolated_string_node_append(parser->arena, interpolated, string); current = UP(interpolated); } else { assert(false && "unreachable"); @@ -19679,7 +20038,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b // current into an interpolated string and add the // string node to the list of parts. pm_interpolated_string_node_t *interpolated = pm_interpolated_string_node_create(parser, NULL, NULL, NULL); - pm_interpolated_string_node_append(interpolated, current); + pm_interpolated_string_node_append(parser->arena, interpolated, current); current = UP(interpolated); } else { // If we hit an embedded variable and the current @@ -19688,7 +20047,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b } pm_node_t *part = parse_string_part(parser, (uint16_t) (depth + 1)); - pm_interpolated_string_node_append((pm_interpolated_string_node_t *) current, part); + pm_interpolated_string_node_append(parser->arena, (pm_interpolated_string_node_t *) current, part); break; } case PM_TOKEN_EMBEXPR_BEGIN: { @@ -19704,7 +20063,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b // current into an interpolated string and add the // string node to the list of parts. pm_interpolated_string_node_t *interpolated = pm_interpolated_string_node_create(parser, NULL, NULL, NULL); - pm_interpolated_string_node_append(interpolated, current); + pm_interpolated_string_node_append(parser->arena, 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 @@ -19715,7 +20074,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b } pm_node_t *part = parse_string_part(parser, (uint16_t) (depth + 1)); - pm_interpolated_string_node_append((pm_interpolated_string_node_t *) current, part); + pm_interpolated_string_node_append(parser->arena, (pm_interpolated_string_node_t *) current, part); break; } default: @@ -19727,7 +20086,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b // 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_array_node_elements_append(parser->arena, array, current); } pm_token_t closing = parser->current; @@ -19757,10 +20116,9 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b 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_regular_expression_node_t *node = pm_regular_expression_node_create(parser, &opening, &content, &parser->previous); + pm_node_flag_set(UP(node), pm_regexp_parse(parser, node, NULL, NULL)); + return UP(node); } pm_interpolated_regular_expression_node_t *interpolated; @@ -19772,7 +20130,6 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b // 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 @@ -19781,15 +20138,14 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b 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 we're not immediately followed by a =~, then we + // parse and validate now. If it is followed by a =~, + // then it will get parsed in the =~ handler where + // named captures can also be extracted. if (!match1(parser, PM_TOKEN_EQUAL_TILDE)) { - parse_regular_expression_errors(parser, node); + pm_node_flag_set(UP(node), pm_regexp_parse(parser, node, NULL, NULL)); } - pm_node_flag_set(UP(node), parse_and_validate_regular_expression_encoding(parser, &unescaped, ascii_only, FL(node))); return UP(node); } @@ -19805,7 +20161,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b pm_node_flag_set(part, PM_STRING_FLAGS_FORCED_BINARY_ENCODING); } - pm_interpolated_regular_expression_node_append(interpolated, part); + pm_interpolated_regular_expression_node_append(parser->arena, 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 @@ -19818,7 +20174,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b 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_interpolated_regular_expression_node_append(parser->arena, interpolated, part); } } @@ -19881,7 +20237,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b pm_node_t *part = UP(pm_string_node_create_unescaped(parser, NULL, &parser->previous, NULL, &unescaped)); pm_node_flag_set(part, parse_unescaped_encoding(parser)); - pm_interpolated_xstring_node_append(node, part); + pm_interpolated_xstring_node_append(parser->arena, 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 @@ -19892,7 +20248,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b 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_interpolated_xstring_node_append(parser->arena, node, part); } } @@ -19922,7 +20278,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b 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)); + name = parse_expression(parser, PM_BINDING_POWER_INDEX, flags & PM_PARSE_ACCEPTS_DO_BLOCK, PM_ERR_EXPECT_EXPRESSION_AFTER_STAR, (uint16_t) (depth + 1)); } pm_node_t *splat = UP(pm_splat_node_create(parser, &operator, name)); @@ -19941,7 +20297,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b 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_node_t *receiver = parse_expression(parser, pm_binding_powers[parser->previous.type].right, (flags & PM_PARSE_ACCEPTS_DO_BLOCK) | (binding_power < PM_BINDING_POWER_MATCH ? PM_PARSE_ACCEPTS_COMMAND_CALL : 0), 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); @@ -19954,7 +20310,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b 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_node_t *receiver = parse_expression(parser, pm_binding_powers[parser->previous.type].right, flags & PM_PARSE_ACCEPTS_DO_BLOCK, PM_ERR_UNARY_RECEIVER, (uint16_t) (depth + 1)); pm_call_node_t *node = pm_call_node_unary_create(parser, &operator, receiver, "~"); return UP(node); @@ -19966,7 +20322,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b 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_node_t *receiver = parse_expression(parser, pm_binding_powers[parser->previous.type].right, flags & PM_PARSE_ACCEPTS_DO_BLOCK, PM_ERR_UNARY_RECEIVER, (uint16_t) (depth + 1)); pm_call_node_t *node = pm_call_node_unary_create(parser, &operator, receiver, "-@"); return UP(node); @@ -19975,11 +20331,11 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b 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)); + pm_node_t *node = parse_expression(parser, pm_binding_powers[parser->previous.type].right, flags & PM_PARSE_ACCEPTS_DO_BLOCK, 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)); + pm_node_t *exponent = parse_expression(parser, pm_binding_powers[exponent_operator.type].right, flags & PM_PARSE_ACCEPTS_DO_BLOCK, 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 { @@ -20058,9 +20414,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b 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)) { @@ -20089,13 +20443,13 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b 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_node_t *receiver = parse_expression(parser, pm_binding_powers[parser->previous.type].right, flags & PM_PARSE_ACCEPTS_DO_BLOCK, 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)); + return parse_strings(parser, NULL, flags & PM_PARSE_ACCEPTS_LABEL, (uint16_t) (depth + 1)); case PM_TOKEN_SYMBOL_BEGIN: { pm_lex_mode_t lex_mode = *parser->lex_modes.current; parser_lex(parser); @@ -20118,12 +20472,12 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b // 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)); + PM_PARSER_ERR_TOKEN_FORMAT(parser, &parser->current, PM_ERR_UNEXPECTED_TOKEN_CLOSE_CONTEXT, pm_token_str(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)); + PM_PARSER_ERR_TOKEN_FORMAT(parser, &parser->current, PM_ERR_UNEXPECTED_TOKEN_IGNORE, pm_token_str(parser->current.type)); } else { pm_parser_err_prefix(parser, diag_id); } @@ -20143,8 +20497,18 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b * 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)); +parse_assignment_value(pm_parser_t *parser, pm_binding_power_t previous_binding_power, pm_binding_power_t binding_power, uint8_t flags, pm_diagnostic_id_t diag_id, uint16_t depth) { + pm_node_t *value = parse_value_expression(parser, binding_power, (flags & PM_PARSE_ACCEPTS_DO_BLOCK) | (previous_binding_power == PM_BINDING_POWER_ASSIGNMENT ? (flags & PM_PARSE_ACCEPTS_COMMAND_CALL) : (previous_binding_power < PM_BINDING_POWER_MATCH ? PM_PARSE_ACCEPTS_COMMAND_CALL : 0)), diag_id, (uint16_t) (depth + 1)); + + // Assignments whose value is a command call (e.g., a = b c) can only + // be followed by modifiers (if/unless/while/until/rescue) and not by + // operators with higher binding power. If we find one, emit an error + // and skip the operator and its right-hand side. + if (pm_binding_powers[parser->current.type].left > PM_BINDING_POWER_MODIFIER && (pm_command_call_value_p(value) || pm_block_call_p(value))) { + PM_PARSER_ERR_TOKEN_FORMAT(parser, &parser->current, PM_ERR_EXPECT_EOL_AFTER_STATEMENT, pm_token_str(parser->current.type)); + parser_lex(parser); + parse_expression(parser, pm_binding_powers[parser->previous.type].right, flags & PM_PARSE_ACCEPTS_DO_BLOCK, PM_ERR_EXPECT_EXPRESSION_AFTER_OPERATOR, (uint16_t) (depth + 1)); + } // Contradicting binding powers, the right-hand-side value of the assignment // allows the `rescue` modifier. @@ -20154,7 +20518,7 @@ parse_assignment_value(pm_parser_t *parser, pm_binding_power_t previous_binding_ 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)); + pm_node_t *right = parse_expression(parser, pm_binding_powers[PM_TOKEN_KEYWORD_RESCUE_MODIFIER].right, flags & PM_PARSE_ACCEPTS_DO_BLOCK, PM_ERR_RESCUE_MODIFIER_VALUE, (uint16_t) (depth + 1)); context_pop(parser); return UP(pm_rescue_modifier_node_create(parser, value, &rescue, right)); @@ -20212,33 +20576,46 @@ parse_assignment_value_local(pm_parser_t *parser, const pm_node_t *node) { * 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) { +parse_assignment_values(pm_parser_t *parser, pm_binding_power_t previous_binding_power, pm_binding_power_t binding_power, uint8_t flags, 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)); + pm_node_t *value = parse_starred_expression(parser, binding_power, (flags & PM_PARSE_ACCEPTS_DO_BLOCK) | (previous_binding_power == PM_BINDING_POWER_ASSIGNMENT ? (flags & PM_PARSE_ACCEPTS_COMMAND_CALL) : (previous_binding_power < PM_BINDING_POWER_MODIFIER ? PM_PARSE_ACCEPTS_COMMAND_CALL : 0)), 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))) { + // Block calls (command call + do block, e.g., `foo bar do end`) cannot + // be followed by a comma to form a multi-value RHS because each element + // of a multi-value assignment must be an `arg`, not a `block_call`. + if (previous_binding_power == PM_BINDING_POWER_STATEMENT && !pm_block_call_p(value) && (PM_NODE_TYPE_P(value, PM_SPLAT_NODE) || match1(parser, PM_TOKEN_COMMA))) { single_value = false; pm_array_node_t *array = pm_array_node_create(parser, NULL); - pm_array_node_elements_append(array, value); + pm_array_node_elements_append(parser->arena, 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); + pm_array_node_elements_append(parser->arena, array, element); if (PM_NODE_TYPE_P(element, PM_MISSING_NODE)) break; parse_assignment_value_local(parser, element); } } + // Assignments whose value is a command call (e.g., a = b c) can only + // be followed by modifiers (if/unless/while/until/rescue) and not by + // operators with higher binding power. If we find one, emit an error + // and skip the operator and its right-hand side. + if (single_value && pm_binding_powers[parser->current.type].left > PM_BINDING_POWER_MODIFIER && (pm_command_call_value_p(value) || pm_block_call_p(value))) { + PM_PARSER_ERR_TOKEN_FORMAT(parser, &parser->current, PM_ERR_EXPECT_EOL_AFTER_STATEMENT, pm_token_str(parser->current.type)); + parser_lex(parser); + parse_expression(parser, pm_binding_powers[parser->previous.type].right, flags & PM_PARSE_ACCEPTS_DO_BLOCK, PM_ERR_EXPECT_EXPRESSION_AFTER_OPERATOR, (uint16_t) (depth + 1)); + } + // 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)) { @@ -20258,7 +20635,7 @@ parse_assignment_values(pm_parser_t *parser, pm_binding_power_t previous_binding } } - 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)); + pm_node_t *right = parse_expression(parser, pm_binding_powers[PM_TOKEN_KEYWORD_RESCUE_MODIFIER].right, (flags & PM_PARSE_ACCEPTS_DO_BLOCK) | (accepts_command_call_inner ? PM_PARSE_ACCEPTS_COMMAND_CALL : 0), PM_ERR_RESCUE_MODIFIER_VALUE, (uint16_t) (depth + 1)); context_pop(parser); return UP(pm_rescue_modifier_node_create(parser, value, &rescue, right)); @@ -20279,44 +20656,17 @@ parse_call_operator_write(pm_parser_t *parser, pm_call_node_t *call_node, const 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 * +static PRISM_INLINE const uint8_t * pm_named_capture_escape_hex(pm_buffer_t *unescaped, const uint8_t *cursor, const uint8_t *end) { cursor++; @@ -20337,7 +20687,7 @@ pm_named_capture_escape_hex(pm_buffer_t *unescaped, const uint8_t *cursor, const return cursor; } -static inline const uint8_t * +static PRISM_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++; @@ -20356,7 +20706,7 @@ pm_named_capture_escape_octal(pm_buffer_t *unescaped, const uint8_t *cursor, con return cursor; } -static inline const uint8_t * +static PRISM_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++; @@ -20368,7 +20718,7 @@ pm_named_capture_escape_unicode(pm_parser_t *parser, pm_buffer_t *unescaped, con if (*cursor != '{') { size_t length = pm_strspn_hexadecimal_digit(cursor, MIN(end - cursor, 4)); - uint32_t value = escape_unicode(parser, cursor, length, error_location); + uint32_t value = escape_unicode(parser, cursor, length, error_location, 0); if (!pm_buffer_append_unicode_codepoint(unescaped, value)) { pm_buffer_append_string(unescaped, (const char *) start, (size_t) ((cursor + length) - start)); @@ -20391,7 +20741,7 @@ pm_named_capture_escape_unicode(pm_parser_t *parser, pm_buffer_t *unescaped, con if (length == 0) { break; } - uint32_t value = escape_unicode(parser, cursor, length, error_location); + uint32_t value = escape_unicode(parser, cursor, length, error_location, 0); (void) pm_buffer_append_unicode_codepoint(unescaped, value); cursor += length; @@ -20441,10 +20791,7 @@ pm_named_capture_escape(pm_parser_t *parser, pm_buffer_t *unescaped, const uint8 * 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; +parse_regular_expression_named_capture(pm_parser_t *parser, const pm_string_t *capture, bool shared, pm_regexp_name_data_t *callback_data) { pm_call_node_t *call = callback_data->call; pm_constant_id_list_t *names = &callback_data->names; @@ -20462,7 +20809,7 @@ parse_regular_expression_named_capture(const pm_string_t *capture, void *data) { // 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); + pm_named_capture_escape(parser, &unescaped, source, length, cursor, shared ? NULL : &call->receiver->location); source = (const uint8_t *) pm_buffer_value(&unescaped); length = pm_buffer_length(&unescaped); } @@ -20474,11 +20821,11 @@ parse_regular_expression_named_capture(const pm_string_t *capture, void *data) { // 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); + pm_buffer_cleanup(&unescaped); return; } - if (callback_data->shared) { + if (shared) { // If the unescaped string is a slice of the source, then we can // copy the names directly. The pointers will line up. start = source; @@ -20490,24 +20837,22 @@ parse_regular_expression_named_capture(const pm_string_t *capture, void *data) { start = parser->start + PM_NODE_START(call->receiver); end = parser->start + PM_NODE_END(call->receiver); - void *memory = xmalloc(length); - if (memory == NULL) abort(); - + uint8_t *memory = (uint8_t *) pm_arena_alloc(parser->arena, length, 1); memcpy(memory, source, length); - name = pm_parser_constant_id_owned(parser, (uint8_t *) memory, length); + name = pm_parser_constant_id_owned(parser, 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); + pm_constant_id_list_append(parser->arena, 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); + pm_buffer_cleanup(&unescaped); return; } @@ -20525,34 +20870,26 @@ parse_regular_expression_named_capture(const pm_string_t *capture, void *data) { // 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, &TOK2LOC(parser, &((pm_token_t) { .type = 0, .start = start, .end = end })), name, depth == -1 ? 0 : (uint32_t) depth)); - pm_node_list_append(&callback_data->match->targets, target); + pm_node_list_append(parser->arena, &callback_data->match->targets, target); } - pm_buffer_free(&unescaped); + pm_buffer_cleanup(&unescaped); } /** - * Potentially change a =~ with a regular expression with named captures into a - * match write node. + * Potentially change a =~ with an interpolated regular expression with named + * captures into a match write node. This is for the interpolated case where + * we have concatenated content rather than a regular expression 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, +parse_interpolated_regular_expression_named_captures(pm_parser_t *parser, const pm_string_t *content, pm_call_node_t *call, bool extended_mode) { + pm_regexp_name_data_t callback_data = { .call = call, + .match = NULL, .names = { 0 }, - .shared = content->type == PM_STRING_SHARED - }; - - parse_regular_expression_error_data_t error_data = { - .parser = parser, - .start = parser->start + PM_NODE_START(call->receiver), - .end = parser->start + PM_NODE_END(call->receiver), - .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); + pm_regexp_parse_named_captures(parser, pm_string_source(content), pm_string_length(content), false, extended_mode, parse_regular_expression_named_capture, &callback_data); if (callback_data.match != NULL) { return UP(callback_data.match); @@ -20561,8 +20898,8 @@ parse_regular_expression_named_captures(pm_parser_t *parser, const pm_string_t * } } -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) { +static PRISM_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, uint8_t flags, uint16_t depth) { pm_token_t token = parser->current; switch (token.type) { @@ -20588,7 +20925,7 @@ parse_expression_infix(pm_parser_t *parser, pm_node_t *node, pm_binding_power_t } 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)); + 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, flags, 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); @@ -20601,7 +20938,7 @@ parse_expression_infix(pm_parser_t *parser, pm_node_t *node, pm_binding_power_t 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)); + pm_node_t *value = parse_assignment_values(parser, previous_binding_power, PM_BINDING_POWER_MULTI_ASSIGNMENT + 1, flags, PM_ERR_EXPECT_EXPRESSION_AFTER_EQUAL, (uint16_t) (depth + 1)); return parse_write(parser, UP(multi_target), &token, value); } case PM_SOURCE_ENCODING_NODE: @@ -20614,7 +20951,7 @@ parse_expression_infix(pm_parser_t *parser, pm_node_t *node, pm_binding_power_t // 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)); + pm_node_t *value = parse_assignment_values(parser, previous_binding_power, binding_power, flags, PM_ERR_EXPECT_EXPRESSION_AFTER_EQUAL, (uint16_t) (depth + 1)); return parse_unwriteable_write(parser, node, &token, value); } default: @@ -20635,25 +20972,23 @@ parse_expression_infix(pm_parser_t *parser, pm_node_t *node, pm_binding_power_t 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 *value = parse_assignment_value(parser, previous_binding_power, binding_power, flags, 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 *value = parse_assignment_value(parser, previous_binding_power, binding_power, flags, 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 *value = parse_assignment_value(parser, previous_binding_power, binding_power, flags, 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); @@ -20661,30 +20996,27 @@ parse_expression_infix(pm_parser_t *parser, pm_node_t *node, pm_binding_power_t 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 *value = parse_assignment_value(parser, previous_binding_power, binding_power, flags, 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 *value = parse_assignment_value(parser, previous_binding_power, binding_power, flags, 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 *value = parse_assignment_value(parser, previous_binding_power, binding_power, flags, 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: { @@ -20696,10 +21028,9 @@ parse_expression_infix(pm_parser_t *parser, pm_node_t *node, pm_binding_power_t 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 *value = parse_assignment_value(parser, previous_binding_power, binding_power, flags, 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: { @@ -20713,10 +21044,9 @@ parse_expression_infix(pm_parser_t *parser, pm_node_t *node, pm_binding_power_t pm_constant_id_t constant_id = pm_parser_local_add_location(parser, &cast->message_loc, 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 *value = parse_assignment_value(parser, previous_binding_power, binding_power, flags, 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; } @@ -20728,7 +21058,7 @@ parse_expression_infix(pm_parser_t *parser, pm_node_t *node, pm_binding_power_t // 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)); + pm_node_t *value = parse_assignment_value(parser, previous_binding_power, binding_power, flags, PM_ERR_EXPECT_EXPRESSION_AFTER_AMPAMPEQ, (uint16_t) (depth + 1)); return UP(pm_index_and_write_node_create(parser, cast, &token, value)); } @@ -20740,7 +21070,7 @@ parse_expression_infix(pm_parser_t *parser, pm_node_t *node, pm_binding_power_t } 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)); + pm_node_t *value = parse_assignment_value(parser, previous_binding_power, binding_power, flags, 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: { @@ -20767,25 +21097,23 @@ parse_expression_infix(pm_parser_t *parser, pm_node_t *node, pm_binding_power_t 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 *value = parse_assignment_value(parser, previous_binding_power, binding_power, flags, 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 *value = parse_assignment_value(parser, previous_binding_power, binding_power, flags, 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 *value = parse_assignment_value(parser, previous_binding_power, binding_power, flags, 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); @@ -20793,30 +21121,27 @@ parse_expression_infix(pm_parser_t *parser, pm_node_t *node, pm_binding_power_t 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 *value = parse_assignment_value(parser, previous_binding_power, binding_power, flags, 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 *value = parse_assignment_value(parser, previous_binding_power, binding_power, flags, 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 *value = parse_assignment_value(parser, previous_binding_power, binding_power, flags, 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: { @@ -20828,10 +21153,9 @@ parse_expression_infix(pm_parser_t *parser, pm_node_t *node, pm_binding_power_t 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 *value = parse_assignment_value(parser, previous_binding_power, binding_power, flags, 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: { @@ -20845,10 +21169,9 @@ parse_expression_infix(pm_parser_t *parser, pm_node_t *node, pm_binding_power_t pm_constant_id_t constant_id = pm_parser_local_add_location(parser, &cast->message_loc, 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 *value = parse_assignment_value(parser, previous_binding_power, binding_power, flags, 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; } @@ -20860,7 +21183,7 @@ parse_expression_infix(pm_parser_t *parser, pm_node_t *node, pm_binding_power_t // 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)); + pm_node_t *value = parse_assignment_value(parser, previous_binding_power, binding_power, flags, PM_ERR_EXPECT_EXPRESSION_AFTER_PIPEPIPEEQ, (uint16_t) (depth + 1)); return UP(pm_index_or_write_node_create(parser, cast, &token, value)); } @@ -20872,7 +21195,7 @@ parse_expression_infix(pm_parser_t *parser, pm_node_t *node, pm_binding_power_t } 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)); + pm_node_t *value = parse_assignment_value(parser, previous_binding_power, binding_power, flags, 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: { @@ -20909,25 +21232,23 @@ parse_expression_infix(pm_parser_t *parser, pm_node_t *node, pm_binding_power_t 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 *value = parse_assignment_value(parser, previous_binding_power, binding_power, flags, 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 *value = parse_assignment_value(parser, previous_binding_power, binding_power, flags, 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 *value = parse_assignment_value(parser, previous_binding_power, binding_power, flags, 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); @@ -20935,30 +21256,27 @@ parse_expression_infix(pm_parser_t *parser, pm_node_t *node, pm_binding_power_t 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 *value = parse_assignment_value(parser, previous_binding_power, binding_power, flags, 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 *value = parse_assignment_value(parser, previous_binding_power, binding_power, flags, 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 *value = parse_assignment_value(parser, previous_binding_power, binding_power, flags, 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: { @@ -20970,10 +21288,9 @@ parse_expression_infix(pm_parser_t *parser, pm_node_t *node, pm_binding_power_t 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 *value = parse_assignment_value(parser, previous_binding_power, binding_power, flags, 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: { @@ -20986,10 +21303,9 @@ parse_expression_infix(pm_parser_t *parser, pm_node_t *node, pm_binding_power_t if (PM_NODE_FLAG_P(cast, PM_CALL_NODE_FLAGS_VARIABLE_CALL)) { pm_refute_numbered_parameter(parser, cast->message_loc.start, cast->message_loc.length); pm_constant_id_t constant_id = pm_parser_local_add_location(parser, &cast->message_loc, 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 *value = parse_assignment_value(parser, previous_binding_power, binding_power, flags, 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; } @@ -20997,7 +21313,7 @@ parse_expression_infix(pm_parser_t *parser, pm_node_t *node, pm_binding_power_t // 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)); + pm_node_t *value = parse_assignment_value(parser, previous_binding_power, binding_power, flags, PM_ERR_EXPECT_EXPRESSION_AFTER_OPERATOR, (uint16_t) (depth + 1)); return UP(pm_index_operator_write_node_create(parser, cast, &token, value)); } @@ -21009,7 +21325,7 @@ parse_expression_infix(pm_parser_t *parser, pm_node_t *node, pm_binding_power_t } 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)); + pm_node_t *value = parse_assignment_value(parser, previous_binding_power, binding_power, flags, 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: { @@ -21023,7 +21339,7 @@ parse_expression_infix(pm_parser_t *parser, pm_node_t *node, pm_binding_power_t // 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)); + PM_PARSER_ERR_TOKEN_FORMAT(parser, &parser->previous, PM_ERR_EXPECT_EXPRESSION_AFTER_OPERATOR, pm_token_str(parser->current.type)); return node; } } @@ -21031,14 +21347,14 @@ parse_expression_infix(pm_parser_t *parser, pm_node_t *node, pm_binding_power_t 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)); + pm_node_t *right = parse_expression(parser, binding_power, (flags & PM_PARSE_ACCEPTS_DO_BLOCK) | (parser->previous.type == PM_TOKEN_KEYWORD_AND ? PM_PARSE_ACCEPTS_COMMAND_CALL : 0), 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)); + pm_node_t *right = parse_expression(parser, binding_power, (flags & PM_PARSE_ACCEPTS_DO_BLOCK) | (parser->previous.type == PM_TOKEN_KEYWORD_OR ? PM_PARSE_ACCEPTS_COMMAND_CALL : 0), 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: { @@ -21050,7 +21366,7 @@ parse_expression_infix(pm_parser_t *parser, pm_node_t *node, pm_binding_power_t // // 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)); + pm_node_t *argument = parse_expression(parser, binding_power, flags & PM_PARSE_ACCEPTS_DO_BLOCK, 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); @@ -21095,14 +21411,25 @@ parse_expression_infix(pm_parser_t *parser, pm_node_t *node, pm_binding_power_t 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); + result = parse_interpolated_regular_expression_named_captures(parser, &owned, call, PM_NODE_FLAG_P(node, PM_REGULAR_EXPRESSION_FLAGS_EXTENDED)); + pm_string_cleanup(&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)); + // If we have a regular expression node, then we can parse + // the named captures and validate encoding in one pass. + pm_regular_expression_node_t *regexp = (pm_regular_expression_node_t *) node; + + pm_regexp_name_data_t name_data = { + .call = call, + .match = NULL, + .names = { 0 }, + }; + + pm_node_flag_set(UP(regexp), pm_regexp_parse(parser, regexp, parse_regular_expression_named_capture, &name_data)); + + if (name_data.match != NULL) { + result = UP(name_data.match); + } } return result; @@ -21134,21 +21461,21 @@ parse_expression_infix(pm_parser_t *parser, pm_node_t *node, pm_binding_power_t 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)); + PM_PARSER_ERR_TOKEN_FORMAT(parser, &operator, PM_ERR_EXPECT_EOL_AFTER_STATEMENT, pm_token_str(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)); + PM_PARSER_ERR_TOKEN_FORMAT(parser, &operator, PM_ERR_EXPECT_EOL_AFTER_STATEMENT, pm_token_str(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)); + PM_PARSER_ERR_TOKEN_FORMAT(parser, &operator, PM_ERR_EXPECT_EOL_AFTER_STATEMENT, pm_token_str(operator.type)); } break; } @@ -21156,7 +21483,7 @@ parse_expression_infix(pm_parser_t *parser, pm_node_t *node, pm_binding_power_t break; } - pm_node_t *argument = parse_expression(parser, binding_power, false, false, PM_ERR_EXPECT_EXPRESSION_AFTER_OPERATOR, (uint16_t) (depth + 1)); + pm_node_t *argument = parse_expression(parser, binding_power, flags & PM_PARSE_ACCEPTS_DO_BLOCK, 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: @@ -21168,7 +21495,7 @@ parse_expression_infix(pm_parser_t *parser, pm_node_t *node, pm_binding_power_t } parser_lex(parser); - pm_node_t *argument = parse_expression(parser, binding_power, false, false, PM_ERR_EXPECT_EXPRESSION_AFTER_OPERATOR, (uint16_t) (depth + 1)); + pm_node_t *argument = parse_expression(parser, binding_power, flags & PM_PARSE_ACCEPTS_DO_BLOCK, 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: @@ -21187,21 +21514,21 @@ parse_expression_infix(pm_parser_t *parser, pm_node_t *node, pm_binding_power_t 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)); + PM_PARSER_ERR_TOKEN_FORMAT(parser, &operator, PM_ERR_EXPECT_EOL_AFTER_STATEMENT, pm_token_str(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)); + PM_PARSER_ERR_TOKEN_FORMAT(parser, &operator, PM_ERR_EXPECT_EOL_AFTER_STATEMENT, pm_token_str(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)); + PM_PARSER_ERR_TOKEN_FORMAT(parser, &operator, PM_ERR_EXPECT_EOL_AFTER_STATEMENT, pm_token_str(operator.type)); } break; } @@ -21222,12 +21549,12 @@ parse_expression_infix(pm_parser_t *parser, pm_node_t *node, pm_binding_power_t break; } default: { - PM_PARSER_ERR_TOKEN_FORMAT(parser, &parser->current, PM_ERR_EXPECT_MESSAGE, pm_token_type_human(parser->current.type)); + PM_PARSER_ERR_TOKEN_FORMAT(parser, &parser->current, PM_ERR_EXPECT_MESSAGE, pm_token_str(parser->current.type)); message = (pm_token_t) { .type = 0, .start = parser->previous.end, .end = parser->previous.end }; } } - parse_arguments_list(parser, &arguments, true, accepts_command_call, (uint16_t) (depth + 1)); + parse_arguments_list(parser, &arguments, true, flags, (uint16_t) (depth + 1)); pm_call_node_t *call = pm_call_node_call_create(parser, node, &operator, &message, &arguments); if ( @@ -21247,7 +21574,7 @@ parse_expression_infix(pm_parser_t *parser, pm_node_t *node, pm_binding_power_t 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)); + right = parse_expression(parser, binding_power, flags & PM_PARSE_ACCEPTS_DO_BLOCK, PM_ERR_EXPECT_EXPRESSION_AFTER_OPERATOR, (uint16_t) (depth + 1)); } return UP(pm_range_node_create(parser, node, &token, right)); @@ -21256,14 +21583,14 @@ parse_expression_infix(pm_parser_t *parser, pm_node_t *node, pm_binding_power_t 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)); + pm_node_t *predicate = parse_value_expression(parser, binding_power, (flags & PM_PARSE_ACCEPTS_DO_BLOCK) | PM_PARSE_ACCEPTS_COMMAND_CALL, 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)); + pm_node_t *predicate = parse_value_expression(parser, binding_power, (flags & PM_PARSE_ACCEPTS_DO_BLOCK) | PM_PARSE_ACCEPTS_COMMAND_CALL, 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: { @@ -21271,7 +21598,7 @@ parse_expression_infix(pm_parser_t *parser, pm_node_t *node, pm_binding_power_t 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)); + pm_node_t *predicate = parse_value_expression(parser, binding_power, (flags & PM_PARSE_ACCEPTS_DO_BLOCK) | PM_PARSE_ACCEPTS_COMMAND_CALL, 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: { @@ -21279,7 +21606,7 @@ parse_expression_infix(pm_parser_t *parser, pm_node_t *node, pm_binding_power_t 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)); + pm_node_t *predicate = parse_value_expression(parser, binding_power, (flags & PM_PARSE_ACCEPTS_DO_BLOCK) | PM_PARSE_ACCEPTS_COMMAND_CALL, 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: { @@ -21290,7 +21617,7 @@ parse_expression_infix(pm_parser_t *parser, pm_node_t *node, pm_binding_power_t 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)); + pm_node_t *true_expression = parse_expression(parser, PM_BINDING_POWER_DEFINED, flags & PM_PARSE_ACCEPTS_DO_BLOCK, PM_ERR_TERNARY_EXPRESSION_TRUE, (uint16_t) (depth + 1)); if (parser->recovering) { // If parsing the true expression of this ternary resulted in a syntax @@ -21304,8 +21631,6 @@ parse_expression_infix(pm_parser_t *parser, pm_node_t *node, pm_binding_power_t 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)); } @@ -21313,12 +21638,10 @@ parse_expression_infix(pm_parser_t *parser, pm_node_t *node, pm_binding_power_t 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)); + pm_node_t *false_expression = parse_expression(parser, PM_BINDING_POWER_DEFINED, flags & PM_PARSE_ACCEPTS_DO_BLOCK, 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: { @@ -21332,7 +21655,7 @@ parse_expression_infix(pm_parser_t *parser, pm_node_t *node, pm_binding_power_t 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))) + ((flags & PM_PARSE_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 @@ -21343,7 +21666,7 @@ parse_expression_infix(pm_parser_t *parser, pm_node_t *node, pm_binding_power_t pm_token_t message = parser->previous; pm_arguments_t arguments = { 0 }; - parse_arguments_list(parser, &arguments, true, accepts_command_call, (uint16_t) (depth + 1)); + parse_arguments_list(parser, &arguments, true, flags, (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. @@ -21367,7 +21690,7 @@ parse_expression_infix(pm_parser_t *parser, pm_node_t *node, pm_binding_power_t // 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)); + parse_arguments_list(parser, &arguments, true, flags, (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. @@ -21396,7 +21719,7 @@ parse_expression_infix(pm_parser_t *parser, pm_node_t *node, pm_binding_power_t 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)); + pm_node_t *value = parse_expression(parser, binding_power, (uint8_t) ((flags & PM_PARSE_ACCEPTS_DO_BLOCK) | PM_PARSE_ACCEPTS_COMMAND_CALL), PM_ERR_RESCUE_MODIFIER_VALUE, (uint16_t) (depth + 1)); context_pop(parser); return UP(pm_rescue_modifier_node_create(parser, node, &token, value)); @@ -21409,7 +21732,7 @@ parse_expression_infix(pm_parser_t *parser, pm_node_t *node, pm_binding_power_t 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)); + parse_arguments(parser, &arguments, false, PM_TOKEN_BRACKET_RIGHT, (uint8_t) (flags & ~PM_PARSE_ACCEPTS_DO_BLOCK), (uint16_t) (depth + 1)); pm_accepts_block_stack_pop(parser); expect1(parser, PM_TOKEN_BRACKET_RIGHT, PM_ERR_EXPECT_RBRACKET); } @@ -21440,7 +21763,7 @@ parse_expression_infix(pm_parser_t *parser, pm_node_t *node, pm_binding_power_t if (arguments.arguments == NULL) { arguments.arguments = pm_arguments_node_create(parser); } - pm_arguments_node_arguments_append(arguments.arguments, arguments.block); + pm_arguments_node_arguments_append(parser->arena, arguments.arguments, arguments.block); } arguments.block = UP(block); @@ -21461,7 +21784,6 @@ parse_expression_infix(pm_parser_t *parser, pm_node_t *node, pm_binding_power_t 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)); } @@ -21478,7 +21800,6 @@ parse_expression_infix(pm_parser_t *parser, pm_node_t *node, pm_binding_power_t 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)); } @@ -21493,16 +21814,83 @@ parse_expression_infix(pm_parser_t *parser, pm_node_t *node, pm_binding_power_t #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. + * Some nodes act as statements and limit which operators can follow. This + * function inspects the node and the upcoming token to determine whether the + * expression loop should stop. It is called both after prefix parsing and after + * each infix operator. + * + * As a side effect, this function also attaches do-blocks to command-style call + * nodes when appropriate. + * + * Returns true if the expression loop should stop (i.e., the next operator + * should not be consumed). */ -static inline bool -pm_call_node_command_p(const pm_call_node_t *node) { - return ( - (node->opening_loc.length == 0) && - (node->block == NULL || PM_NODE_TYPE_P(node->block, PM_BLOCK_ARGUMENT_NODE)) && - (node->arguments != NULL || node->block != NULL) - ); +static bool +parse_expression_terminator(pm_parser_t *parser, pm_node_t *node) { + pm_binding_power_t left = pm_binding_powers[parser->current.type].left; + + switch (PM_NODE_TYPE(node)) { + case PM_MULTI_WRITE_NODE: + case PM_RETURN_NODE: + case PM_BREAK_NODE: + case PM_NEXT_NODE: + return left > PM_BINDING_POWER_MODIFIER; + 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: + return PM_NODE_FLAG_P(node, PM_WRITE_NODE_FLAGS_IMPLICIT_ARRAY) && left > PM_BINDING_POWER_MODIFIER; + case PM_CALL_NODE: { + // Calls with an implicit array on the right-hand side are + // statements and can only be followed by modifiers. + if (PM_NODE_FLAG_P(node, PM_CALL_NODE_FLAGS_IMPLICIT_ARRAY)) { + return left > PM_BINDING_POWER_MODIFIER; + } + + // Command-style calls (including block commands like + // `foo bar do end`) can only be followed by composition + // (and/or) and modifier (if/unless/etc.) operators. + if (pm_command_call_value_p(node)) { + return left > PM_BINDING_POWER_COMPOSITION; + } + + // A block call (command with do-block, or any call chained + // from one) can only be followed by call chaining (., ::, + // &.), composition (and/or), and modifier operators. + if (pm_block_call_p(node)) { + return left > PM_BINDING_POWER_COMPOSITION && left < PM_BINDING_POWER_CALL; + } + + return false; + } + case PM_SUPER_NODE: + case PM_YIELD_NODE: + // Command-style super/yield (without parens) can only be followed + // by composition and modifier operators. + if (pm_command_call_value_p(node)) { + return left > PM_BINDING_POWER_COMPOSITION; + } + return false; + case PM_DEF_NODE: + // An endless method whose body is a command-style call (e.g., + // `def f = foo bar`) is a command assignment and can only be + // followed by modifiers. + return left > PM_BINDING_POWER_MODIFIER && pm_command_call_value_p(node); + case PM_RESCUE_MODIFIER_NODE: + // A rescue modifier whose handler is a pattern match (=> or in) + // produces a statement and cannot be followed by operators above + // the modifier level. + if (left > PM_BINDING_POWER_MODIFIER) { + pm_rescue_modifier_node_t *cast = (pm_rescue_modifier_node_t *) node; + pm_node_t *rescue_expression = cast->rescue_expression; + return PM_NODE_TYPE_P(rescue_expression, PM_MATCH_REQUIRED_NODE) || PM_NODE_TYPE_P(rescue_expression, PM_MATCH_PREDICATE_NODE); + } + return false; + default: + return false; + } } /** @@ -21514,45 +21902,39 @@ pm_call_node_command_p(const pm_call_node_t *node) { * 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) { +parse_expression(pm_parser_t *parser, pm_binding_power_t binding_power, uint8_t flags, 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, PM_TOKEN_START(parser, &parser->current), PM_TOKEN_LENGTH(&parser->current))); } - pm_node_t *node = parse_expression_prefix(parser, binding_power, accepts_command_call, accepts_label, diag_id, depth); + pm_node_t *node = parse_expression_prefix(parser, binding_power, flags, diag_id, depth); + // Some prefix nodes are statements and can only be followed by modifiers + // (if/unless/while/until/rescue) or nothing at all. We check these cheaply + // here before entering the infix loop. 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: + return 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)) { + case PM_SUPER_NODE: + case PM_YIELD_NODE: + case PM_DEF_NODE: + if (parse_expression_terminator(parser, 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(parser, node)) { return node; } @@ -21561,8 +21943,8 @@ parse_expression(pm_parser_t *parser, pm_binding_power_t binding_power, bool acc 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. + // 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; @@ -21572,45 +21954,8 @@ parse_expression(pm_parser_t *parser, pm_binding_power_t binding_power, bool acc 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; - } + node = parse_expression_infix(parser, node, binding_power, current_binding_powers.right, flags, (uint16_t) (depth + 1)); + if (parse_expression_terminator(parser, node)) return node; // If the operator is nonassoc and we should not be able to parse the // upcoming infix operator, break. @@ -21618,7 +21963,7 @@ parse_expression(pm_parser_t *parser, pm_binding_power_t binding_power, bool acc // 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)); + PM_PARSER_ERR_TOKEN_FORMAT(parser, &parser->current, PM_ERR_NON_ASSOCIATIVE_OPERATOR, pm_token_str(parser->current.type), pm_token_str(current_token_type)); break; } @@ -21631,7 +21976,7 @@ parse_expression(pm_parser_t *parser, pm_binding_power_t binding_power, bool acc // 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)); + PM_PARSER_ERR_TOKEN_FORMAT(parser, &parser->current, PM_ERR_NON_ASSOCIATIVE_OPERATOR, pm_token_str(parser->current.type), pm_token_str(current_token_type)); break; } @@ -21643,7 +21988,7 @@ parse_expression(pm_parser_t *parser, pm_binding_power_t binding_power, bool acc } } - if (accepts_command_call) { + if (flags & PM_PARSE_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: @@ -21680,7 +22025,7 @@ parse_expression(pm_parser_t *parser, pm_binding_power_t binding_power, bool acc cast->block != NULL && PM_NODE_TYPE_P(cast->block, PM_BLOCK_NODE) ) ) { - accepts_command_call = false; + flags &= (uint8_t) ~PM_PARSE_ACCEPTS_COMMAND_CALL; } break; } @@ -21688,10 +22033,21 @@ parse_expression(pm_parser_t *parser, pm_binding_power_t binding_power, bool acc case PM_CONSTANT_PATH_NODE: break; default: - accepts_command_call = false; + flags &= (uint8_t) ~PM_PARSE_ACCEPTS_COMMAND_CALL; break; } } + + if (context_terminator(parser->current_context->context, &parser->current)) { + pm_binding_powers_t next_binding_powers = pm_binding_powers[parser->current.type]; + if ( + !next_binding_powers.binary || + binding_power > next_binding_powers.left || + (PM_NODE_TYPE_P(node, PM_CALL_NODE) && pm_call_node_command_p((pm_call_node_t *) node)) + ) { + return node; + } + } } return node; @@ -21710,6 +22066,7 @@ wrap_statements(pm_parser_t *parser, pm_statements_node_t *statements) { pm_arguments_node_t *arguments = pm_arguments_node_create(parser); pm_arguments_node_arguments_append( + parser->arena, arguments, UP(pm_global_variable_read_node_synthesized_create(parser, pm_parser_constant_id_constant(parser, "$_", 2))) ); @@ -21729,6 +22086,7 @@ wrap_statements(pm_parser_t *parser, pm_statements_node_t *statements) { pm_arguments_node_t *arguments = pm_arguments_node_create(parser); pm_arguments_node_arguments_append( + parser->arena, arguments, UP(pm_global_variable_read_node_synthesized_create(parser, pm_parser_constant_id_constant(parser, "$;", 2))) ); @@ -21742,25 +22100,26 @@ wrap_statements(pm_parser_t *parser, pm_statements_node_t *statements) { UP(call) ); - pm_statements_node_body_prepend(statements, UP(write)); + pm_statements_node_body_prepend(parser->arena, statements, UP(write)); } pm_arguments_node_t *arguments = pm_arguments_node_create(parser); pm_arguments_node_arguments_append( + parser->arena, 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( + pm_keyword_hash_node_elements_append(parser->arena, keywords, UP(pm_assoc_node_create( parser, UP(pm_symbol_node_synthesized_create(parser, "chomp")), NULL, UP(pm_true_node_synthesized_create(parser)) ))); - pm_arguments_node_arguments_append(arguments, UP(keywords)); + pm_arguments_node_arguments_append(parser->arena, arguments, UP(keywords)); pm_node_flag_set(UP(arguments), PM_ARGUMENTS_NODE_FLAGS_CONTAINS_KEYWORDS); } @@ -21815,8 +22174,6 @@ parse_program(pm_parser_t *parser) { 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. @@ -21898,11 +22255,14 @@ pm_parser_init_shebang(pm_parser_t *parser, const pm_options_t *options, const c /** * 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) { +void +pm_parser_init(pm_arena_t *arena, pm_parser_t *parser, const uint8_t *source, size_t size, const pm_options_t *options) { + assert(arena != NULL); assert(source != NULL); *parser = (pm_parser_t) { + .arena = arena, + .metadata_arena = { 0 }, .node_id = 0, .lex_state = PM_LEX_STATE_BEG, .enclosure_nesting = 0, @@ -21931,11 +22291,11 @@ pm_parser_init(pm_parser_t *parser, const uint8_t *source, size_t size, const pm .encoding = PM_ENCODING_UTF_8_ENTRY, .encoding_changed_callback = NULL, .encoding_comment_start = source, - .lex_callback = NULL, + .lex_callback = { 0 }, .filepath = { 0 }, .constant_pool = { 0 }, - .newline_list = { 0 }, - .integer_base = 0, + .line_offsets = { 0 }, + .integer = { 0 }, .current_string = PM_STRING_EMPTY, .start_line = 1, .explicit_encoding = NULL, @@ -21944,6 +22304,7 @@ pm_parser_init(pm_parser_t *parser, const uint8_t *source, size_t size, const pm .partial_script = false, .command_start = true, .recovering = false, + .continuable = true, .encoding_locked = false, .encoding_changed = false, .pattern_matching_newlines = false, @@ -21951,32 +22312,30 @@ pm_parser_init(pm_parser_t *parser, const uint8_t *source, size_t size, const pm .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. + /* Pre-size the arenas based on input size to reduce the number of block + * allocations (and the kernel page zeroing they trigger). The ratios were + * measured empirically: AST arena ~3.3x input, metadata arena ~1.1x input. + * The reserve call is a no-op when the capacity is at or below the default + * arena block size, so small inputs don't waste an extra allocation. */ + if (size <= SIZE_MAX / 4) pm_arena_reserve(arena, size * 4); + if (size <= SIZE_MAX / 5 * 4) pm_arena_reserve(&parser->metadata_arena, size + size / 4); + + /* Initialize the constant pool. Measured across 1532 Ruby stdlib files, the + * bytes/constant ratio has a median of ~56 and a 90th percentile of ~135. + * We use 120 as a balance between over-allocation waste and resize + * frequency. Resizes are cheap with arena allocation, so we lean toward + * under-estimating. */ + uint32_t constant_size = ((uint32_t) size) / 120; + pm_constant_pool_init(&parser->metadata_arena, &parser->constant_pool, constant_size < 4 ? 4 : constant_size); + + /* Initialize the line offset list. Similar to the constant pool, we are + * going to estimate the number of newlines that we will need based on the + * size of the input. */ size_t newline_size = size / 22; - pm_newline_list_init(&parser->newline_list, newline_size < 4 ? 4 : newline_size); + pm_line_offset_list_init(&parser->metadata_arena, &parser->line_offsets, newline_size < 4 ? 4 : newline_size); // If options were provided to this parse, establish them here. if (options != NULL) { @@ -22013,7 +22372,7 @@ pm_parser_init(pm_parser_t *parser, const uint8_t *source, size_t size, const pm 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); + const pm_options_scope_t *scope = pm_options_scope(options, scope_index); pm_parser_scope_push(parser, scope_index == 0); // Scopes given from the outside are not allowed to have numbered @@ -22021,16 +22380,14 @@ pm_parser_init(pm_parser_t *parser, const uint8_t *source, size_t size, const pm 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 pm_string_t *local = pm_options_scope_local(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; - + uint8_t *allocated = (uint8_t *) pm_arena_alloc(&parser->metadata_arena, length, 1); memcpy(allocated, source, length); - pm_parser_local_add_owned(parser, (uint8_t *) allocated, length); + pm_parser_local_add_owned(parser, allocated, length); } } } @@ -22115,7 +22472,7 @@ pm_parser_init(pm_parser_t *parser, const uint8_t *source, size_t size, const pm const uint8_t *newline = next_newline(cursor, parser->end - cursor); while (newline != NULL) { - pm_newline_list_append(&parser->newline_list, U32(newline - parser->start + 1)); + pm_line_offset_list_append(&parser->metadata_arena, &parser->line_offsets, U32(newline - parser->start + 1)); cursor = newline + 1; newline = next_newline(cursor, parser->end - cursor); @@ -22145,7 +22502,7 @@ pm_parser_init(pm_parser_t *parser, const uint8_t *source, size_t size, const pm parser->current = (pm_token_t) { .type = PM_TOKEN_EOF, .start = cursor, .end = cursor }; } else { pm_parser_err(parser, 0, 0, PM_ERR_SCRIPT_NOT_FOUND); - pm_newline_list_clear(&parser->newline_list); + pm_line_offset_list_clear(&parser->line_offsets); } } @@ -22156,56 +22513,28 @@ pm_parser_init(pm_parser_t *parser, const uint8_t *source, size_t size, const pm } /** - * 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. + * Allocate and initialize a parser with the given start and end pointers. + * + * The resulting parser must eventually be freed with `pm_parser_free()`. The + * arena is caller-owned and must outlive the parser — `pm_parser_cleanup()` + * does not free the arena. */ -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_parser_t * +pm_parser_new(pm_arena_t *arena, const uint8_t *source, size_t size, const pm_options_t *options) { + pm_parser_t *parser = (pm_parser_t *) xmalloc(sizeof(pm_parser_t)); + if (parser == NULL) abort(); - pm_magic_comment_t *magic_comment = (pm_magic_comment_t *) node; - xfree(magic_comment); - } + pm_parser_init(arena, parser, source, size, options); + return parser; } /** * 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); +void +pm_parser_cleanup(pm_parser_t *parser) { + pm_string_cleanup(&parser->filepath); + pm_arena_cleanup(&parser->metadata_arena); while (parser->current_scope != NULL) { // Normally, popping the scope doesn't free the locals since it is @@ -22221,135 +22550,211 @@ pm_parser_free(pm_parser_t *parser) { } /** - * Parse the Ruby source associated with the given parser and return the tree. + * Free both the memory held by the given parser and the parser itself. */ -PRISM_EXPORTED_FUNCTION pm_node_t * -pm_parse(pm_parser_t *parser) { - return parse_program(parser); +void +pm_parser_free(pm_parser_t *parser) { + pm_parser_cleanup(parser); + xfree_sized(parser, sizeof(pm_parser_t)); } /** - * 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. + * Returns true if the given diagnostic ID represents an error that cannot be + * fixed by appending more input. These are errors where the existing source + * contains definitively invalid syntax (as opposed to merely incomplete input). */ 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; - } +pm_parse_err_is_fatal(pm_diagnostic_id_t diag_id) { + switch (diag_id) { + case PM_ERR_ARRAY_EXPRESSION_AFTER_STAR: + case PM_ERR_BEGIN_UPCASE_BRACE: + case PM_ERR_CLASS_VARIABLE_BARE: + case PM_ERR_END_UPCASE_BRACE: + case PM_ERR_ESCAPE_INVALID_HEXADECIMAL: + case PM_ERR_ESCAPE_INVALID_UNICODE_LIST: + case PM_ERR_ESCAPE_INVALID_UNICODE_SHORT: + case PM_ERR_EXPRESSION_NOT_WRITABLE: + case PM_ERR_EXPRESSION_NOT_WRITABLE_SELF: + case PM_ERR_FLOAT_PARSE: + case PM_ERR_GLOBAL_VARIABLE_BARE: + case PM_ERR_HASH_KEY: + case PM_ERR_HEREDOC_IDENTIFIER: + case PM_ERR_INSTANCE_VARIABLE_BARE: + case PM_ERR_INVALID_BLOCK_EXIT: + case PM_ERR_INVALID_ENCODING_MAGIC_COMMENT: + case PM_ERR_INVALID_FLOAT_EXPONENT: + case PM_ERR_INVALID_NUMBER_BINARY: + case PM_ERR_INVALID_NUMBER_DECIMAL: + case PM_ERR_INVALID_NUMBER_HEXADECIMAL: + case PM_ERR_INVALID_NUMBER_OCTAL: + case PM_ERR_INVALID_NUMBER_UNDERSCORE_TRAILING: + case PM_ERR_NO_LOCAL_VARIABLE: + case PM_ERR_PARAMETER_ORDER: + case PM_ERR_STATEMENT_UNDEF: + case PM_ERR_VOID_EXPRESSION: + return true; + default: + return false; + } +} + +/** + * Determine whether the source parsed by the given parser could become valid if + * more input were appended. This is used by tools like IRB to decide whether to + * prompt for continuation or to display an error. + * + * The parser starts with continuable=true. This function scans all errors to + * detect two categories of non-continuable errors: + * + * 1. Fatal errors: errors like invalid number literals or bare global variables + * that indicate definitively invalid syntax. These are only considered fatal + * if they occur before EOF (at EOF they could be from truncated input, e.g. + * `"\x` is an incomplete hex escape). + * + * 2. Stray tokens: unexpected_token_ignore and unexpected_token_close_context + * errors indicate tokens that don't belong. A stray token is a cascade + * effect (and does not prevent continuability) if: + * + * a. A non-stray, non-fatal error appeared earlier in the error list at a + * strictly earlier source position (the stray was caused by a preceding + * parse failure, e.g. a truncated heredoc), OR + * b. The stray token is at EOF, starts after position 0 (there is valid + * code before it), and either is a single byte (likely a truncated + * token like `\`) or there are non-stray errors elsewhere. + * + * Closing delimiters (`)`, `]`, `}`) at EOF are always genuinely stray — + * they are complete tokens and cannot become part of a longer valid + * construct by appending more input. + * + * c. The stray token is `=` at the start of a line, which could be the + * beginning of `=begin` (an embedded document). The remaining bytes + * after `=` may parse as an identifier, so the error is not at EOF, + * but the construct is genuinely incomplete. + */ +static void +pm_parse_continuable(pm_parser_t *parser) { + // If there are no errors then there is nothing to continue. + if (parser->error_list.size == 0) { + parser->continuable = false; + return; + } - // Append the line to the buffer. - length--; - pm_buffer_append_string(buffer, line, length); + if (!parser->continuable) return; - // 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; - } + size_t source_length = (size_t) (parser->end - parser->start); - // 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)) { + // First pass: check if there are any non-stray, non-fatal errors. + bool has_non_stray_error = false; + for (pm_diagnostic_t *error = (pm_diagnostic_t *) parser->error_list.head; error != NULL; error = (pm_diagnostic_t *) error->node.next) { + if (error->diag_id != PM_ERR_UNEXPECTED_TOKEN_IGNORE && error->diag_id != PM_ERR_UNEXPECTED_TOKEN_CLOSE_CONTEXT && !pm_parse_err_is_fatal(error->diag_id)) { + has_non_stray_error = true; break; } } - return true; -#undef LINE_SIZE -} + // Second pass: check each error. We track the minimum source position + // among non-stray, non-fatal errors seen so far in list order, which + // lets us detect cascade stray tokens. + size_t non_stray_min_start = SIZE_MAX; -/** - * 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 (pm_diagnostic_t *error = (pm_diagnostic_t *) parser->error_list.head; error != NULL; error = (pm_diagnostic_t *) error->node.next) { + size_t error_start = (size_t) error->location.start; + size_t error_end = error_start + (size_t) error->location.length; + bool at_eof = error_end >= source_length; - for (; diagnostic != NULL; diagnostic = (pm_diagnostic_t *) diagnostic->node.next) { - if (diagnostic->diag_id == PM_ERR_HEREDOC_TERM) { - return true; + // Fatal errors are non-continuable unless they occur at EOF. + if (pm_parse_err_is_fatal(error->diag_id) && !at_eof) { + parser->continuable = false; + return; } - } - return false; -} + // Track non-stray, non-fatal error positions in list order. + if (error->diag_id != PM_ERR_UNEXPECTED_TOKEN_IGNORE && + error->diag_id != PM_ERR_UNEXPECTED_TOKEN_CLOSE_CONTEXT) { + if (error_start < non_stray_min_start) non_stray_min_start = error_start; + continue; + } -/** - * 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); + // This is a stray token. Determine if it is a cascade effect + // of a preceding error or genuinely stray. + + // Rule (a): a non-stray error was seen earlier in the list at a + // strictly earlier position — this stray is a cascade effect. + if (non_stray_min_start < error_start) continue; + + // Rule (b): this stray is at EOF with valid code before it. + // Single-byte stray tokens at EOF (like `\` for line continuation) + // are likely truncated tokens. Multi-byte stray tokens (like the + // keyword `end`) need additional evidence that they are cascade + // effects (i.e. non-stray errors exist elsewhere). + if (at_eof && error_start > 0) { + // Exception: closing delimiters at EOF are genuinely stray. + if (error->location.length == 1) { + const uint8_t *byte = parser->start + error_start; + if (*byte == ')' || *byte == ']' || *byte == '}') { + parser->continuable = false; + return; + } - bool eof = pm_parse_stream_read(buffer, stream, stream_fgets, stream_feof); + // Single-byte non-delimiter stray at EOF: cascade. + continue; + } - pm_parser_init(parser, (const uint8_t *) pm_buffer_value(buffer), pm_buffer_length(buffer), options); - pm_node_t *node = pm_parse(parser); + // Multi-byte stray at EOF: cascade only if there are + // non-stray errors (evidence of a preceding parse failure). + if (has_non_stray_error) continue; + } - 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); + // Rule (c): a stray `=` at the start of a line could be the + // beginning of an embedded document (`=begin`). The remaining + // bytes after `=` parse as an identifier, so the error is not + // at EOF, but the construct is genuinely incomplete. + if (error->location.length == 1) { + const uint8_t *byte = parser->start + error_start; + if (*byte == '=' && (error_start == 0 || *(byte - 1) == '\n')) continue; + } - pm_parser_free(parser); - pm_parser_init(parser, (const uint8_t *) pm_buffer_value(buffer), pm_buffer_length(buffer), options); - node = pm_parse(parser); + // This stray token is genuinely non-continuable. + parser->continuable = false; + return; } +} +/** + * Parse the Ruby source associated with the given parser and return the tree. + */ +pm_node_t * +pm_parse(pm_parser_t *parser) { + pm_node_t *node = parse_program(parser); + pm_parse_continuable(parser); return node; } /** - * Parse the source and return true if it parses without errors or warnings. + * 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 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_node_t * +pm_parse_stream(pm_parser_t **parser, pm_arena_t *arena, pm_source_t *source, const pm_options_t *options) { + bool eof = pm_source_stream_read(source); - pm_parser_t parser; - pm_parser_init(&parser, source, size, &options); + pm_parser_t *tmp = pm_parser_new(arena, pm_source_source(source), pm_source_length(source), options); + pm_node_t *node = pm_parse(tmp); - pm_node_t *node = pm_parse(&parser); - pm_node_destroy(&parser, node); + while (!eof && tmp->error_list.size > 0) { + eof = pm_source_stream_read(source); - bool result = parser.error_list.size == 0; - pm_parser_free(&parser); - pm_options_free(&options); + pm_parser_free(tmp); + pm_arena_cleanup(arena); - return result; + tmp = pm_parser_new(arena, pm_source_source(source), pm_source_length(source), options); + node = pm_parse(tmp); + } + + *parser = tmp; + return node; } #undef PM_CASE_KEYWORD @@ -22362,7 +22767,7 @@ pm_parse_success_p(const uint8_t *source, size_t size, const char *data) { // PRISM_EXCLUDE_SERIALIZATION define. #ifndef PRISM_EXCLUDE_SERIALIZATION -static inline void +static PRISM_INLINE void pm_serialize_header(pm_buffer_t *buffer) { pm_buffer_append_string(buffer, "PRISM", 5); pm_buffer_append_byte(buffer, PRISM_VERSION_MAJOR); @@ -22374,7 +22779,7 @@ pm_serialize_header(pm_buffer_t *buffer) { /** * Serialize the AST represented by the given node to the given buffer. */ -PRISM_EXPORTED_FUNCTION void +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); @@ -22385,13 +22790,14 @@ pm_serialize(pm_parser_t *parser, pm_node_t *node, pm_buffer_t *buffer) { * Parse and serialize the AST represented by the given source to the given * buffer. */ -PRISM_EXPORTED_FUNCTION void +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_arena_t arena = { 0 }; pm_parser_t parser; - pm_parser_init(&parser, source, size, &options); + pm_parser_init(&arena, &parser, source, size, &options); pm_node_t *node = pm_parse(&parser); @@ -22399,216 +22805,53 @@ pm_serialize_parse(pm_buffer_t *buffer, const uint8_t *source, size_t size, cons 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); + pm_parser_cleanup(&parser); + pm_arena_cleanup(&arena); + pm_options_cleanup(&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; +void +pm_serialize_parse_stream(pm_buffer_t *buffer, pm_source_t *source, const char *data) { + pm_arena_t arena = { 0 }; + 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_node_t *node = pm_parse_stream(&parser, &arena, source, &options); pm_serialize_header(buffer); - pm_serialize_content(&parser, node, 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); + pm_parser_free(parser); + pm_arena_cleanup(&arena); + pm_options_cleanup(&options); } /** * Parse and serialize the comments in the given source to the given buffer. */ -PRISM_EXPORTED_FUNCTION void +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_arena_t arena = { 0 }; pm_parser_t parser; - pm_parser_init(&parser, source, size, &options); + pm_parser_init(&arena, &parser, source, size, &options); - pm_node_t *node = pm_parse(&parser); + 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.comment_list, buffer); - pm_node_destroy(&parser, node); - pm_parser_free(&parser); - pm_options_free(&options); + pm_parser_cleanup(&parser); + pm_arena_cleanup(&arena); + pm_options_cleanup(&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/src/regexp.c b/src/regexp.c index dcc7476244..cc17aa4d09 100644 --- a/src/regexp.c +++ b/src/regexp.c @@ -1,5 +1,20 @@ -#include "prism/regexp.h" - +#include "prism/internal/regexp.h" + +#include "prism/compiler/inline.h" +#include "prism/compiler/fallthrough.h" +#include "prism/internal/buffer.h" +#include "prism/internal/char.h" +#include "prism/internal/diagnostic.h" +#include "prism/internal/encoding.h" +#include "prism/internal/memchr.h" +#include "prism/internal/parser.h" +#include "prism/internal/stringy.h" +#include "prism/internal/strncasecmp.h" + +#include +#include + +/** The maximum depth of nested groups allowed in a regular expression. */ #define PM_REGEXP_PARSE_DEPTH_MAX 4096 /** @@ -18,6 +33,54 @@ typedef struct { /** A pointer to the end of the source that we are parsing. */ const uint8_t *end; + /** 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. */ + pm_regexp_name_data_t *name_data; + + /** The start of the regexp node (for error locations). */ + const uint8_t *node_start; + + /** The end of the regexp node (for error locations). */ + const uint8_t *node_end; + + /** + * The explicit encoding determined by escape sequences. NULL if no + * encoding-setting escape has been seen, UTF-8 for `\u` escapes, or the + * source encoding for `\x` escapes. + */ + const pm_encoding_t *explicit_encoding; + + /** + * Pointer to the first non-POSIX property name (for /n error messages). + * POSIX properties (Alnum, Alpha, etc.) work in all encodings. + * Script properties (Hiragana, Katakana, etc.) work in /e, /s, /u. + * Unicode-only properties (L, Ll, etc.) work only in /u. + */ + const uint8_t *property_name; + + /** Length of the first non-POSIX property name found. */ + size_t property_name_length; + + /** + * Pointer to the first Unicode-only property name (for /e, /s error + * messages). NULL if only POSIX or script properties have been seen. + */ + const uint8_t *unicode_property_name; + + /** Length of the first Unicode-only property name found. */ + size_t unicode_property_name_length; + + /** Buffer of hex escape byte values >= 0x80, separated by 0x00 sentinels. */ + pm_buffer_t hex_escape_buffer; + + /** Count of non-ASCII literal bytes (not from escapes). */ + uint32_t non_ascii_literal_count; + /** * Whether or not the regular expression currently being parsed is in * extended mode, wherein whitespace is ignored and comments are allowed. @@ -27,30 +90,76 @@ typedef struct { /** Whether the encoding has changed from the default. */ bool encoding_changed; - /** The encoding of the source. */ - const pm_encoding_t *encoding; + /** Whether the source content is shared (for named capture callback). */ + bool shared; - /** The callback to call when a named capture group is found. */ - pm_regexp_name_callback_t name_callback; + /** Whether a `\u{...}` escape with value >= 0x80 was seen. */ + bool has_unicode_escape; - /** The data to pass to the name callback. */ - void *name_data; + /** Whether a `\xNN` escape (or `\M-x`, etc.) with value >= 0x80 was seen. */ + bool has_hex_escape; + + /** + * Tracks whether the last encoding-setting escape was `\u` (true) or `\x` + * (false). This matters for error messages when both types are mixed. + */ + bool last_escape_was_unicode; + + /** Whether any `\p{...}` or `\P{...}` property escape was found. */ + bool has_property_escape; + + /** Whether a Unicode-only property escape was found (not POSIX or script). */ + bool has_unicode_property_escape; - /** The callback to call when a parse error is found. */ - pm_regexp_error_callback_t error_callback; + /** Whether a `\u` escape with invalid range (surrogate or > 0x10FFFF) was seen. */ + bool invalid_unicode_range; - /** The data to pass to the error callback. */ - void *error_data; + /** Whether we are accumulating consecutive hex escape bytes. */ + bool hex_group_active; + + /** Whether an invalid multibyte character was found during parsing. */ + bool has_invalid_multibyte; } pm_regexp_parser_t; /** - * Append an error to the parser. + * Append a syntax error to the parser's error list. If the source is shared + * (points into the original source), we can point to the exact error location. + * Otherwise, we point to the whole regexp node. */ -static inline void +static PRISM_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); + pm_parser_t *pm = parser->parser; + uint32_t loc_start, loc_length; + + if (parser->shared) { + loc_start = (uint32_t) (start - pm->start); + loc_length = (uint32_t) (end - start); + } else { + loc_start = (uint32_t) (parser->node_start - pm->start); + loc_length = (uint32_t) (parser->node_end - parser->node_start); + } + + pm_diagnostic_list_append_format(&pm->metadata_arena, &pm->error_list, loc_start, loc_length, PM_ERR_REGEXP_PARSE_ERROR, message); } +/** + * Append a formatted diagnostic error with proper shared/non-shared location + * handling. This is a macro because we need variadic args for the format string. + */ +#define pm_regexp_parse_error_format(parser_, err_start_, err_end_, diag_id, ...) \ + do { \ + pm_parser_t *pm__ = (parser_)->parser; \ + uint32_t loc_start__, loc_length__; \ + if ((parser_)->shared) { \ + loc_start__ = (uint32_t) ((err_start_) - pm__->start); \ + loc_length__ = (uint32_t) ((err_end_) - (err_start_)); \ + } else { \ + loc_start__ = (uint32_t) ((parser_)->node_start - pm__->start); \ + loc_length__ = (uint32_t) ((parser_)->node_end - (parser_)->node_start); \ + } \ + pm_diagnostic_list_append_format(&pm__->metadata_arena, &pm__->error_list, loc_start__, loc_length__, diag_id, __VA_ARGS__); \ + } while (0) + /** * 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. @@ -59,14 +168,14 @@ 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); + parser->name_callback(parser->parser, &string, parser->shared, parser->name_data); + pm_string_cleanup(&string); } /** * Returns true if the next character is the end of the source. */ -static inline bool +static PRISM_INLINE bool pm_regexp_char_is_eof(pm_regexp_parser_t *parser) { return parser->cursor >= parser->end; } @@ -74,7 +183,7 @@ pm_regexp_char_is_eof(pm_regexp_parser_t *parser) { /** * Optionally accept a char and consume it if it exists. */ -static inline bool +static PRISM_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++; @@ -86,7 +195,7 @@ pm_regexp_char_accept(pm_regexp_parser_t *parser, uint8_t value) { /** * Expect a character to be present and consume it. */ -static inline bool +static PRISM_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++; @@ -113,6 +222,47 @@ pm_regexp_char_find(pm_regexp_parser_t *parser, uint8_t value) { return true; } +/** + * Mark a group boundary in the hex escape byte buffer. When consecutive hex + * escape bytes >= 0x80 are followed by a non-hex-escape, this appends a 0x00 + * sentinel to separate the groups for later multibyte validation. + */ +static PRISM_INLINE void +pm_regexp_hex_group_boundary(pm_regexp_parser_t *parser) { + if (parser->hex_group_active) { + pm_buffer_append_byte(&parser->hex_escape_buffer, 0x00); + parser->hex_group_active = false; + } +} + +/** + * Track a hex escape byte value >= 0x80 for multibyte validation. + */ +static PRISM_INLINE void +pm_regexp_track_hex_escape(pm_regexp_parser_t *parser, uint8_t byte) { + if (byte >= 0x80) { + pm_buffer_append_byte(&parser->hex_escape_buffer, byte); + parser->hex_group_active = true; + parser->has_hex_escape = true; + + parser->explicit_encoding = parser->encoding; + parser->last_escape_was_unicode = false; + } else { + pm_regexp_hex_group_boundary(parser); + } +} + +/** + * Parse a hex digit character and return its value, or -1 if not a hex digit. + */ +static PRISM_INLINE int +pm_regexp_hex_digit_value(uint8_t byte) { + if (byte >= '0' && byte <= '9') return byte - '0'; + if (byte >= 'a' && byte <= 'f') return byte - 'a' + 10; + if (byte >= 'A' && byte <= 'F') return byte - 'A' + 10; + return -1; +} + /** * Range quantifiers are a special class of quantifiers that look like * @@ -121,13 +271,12 @@ pm_regexp_char_find(pm_regexp_parser_t *parser, uint8_t value) { * * {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. + * 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. + * To properly track everything, we're going to build a little state machine. * It looks something like the following: * * +-------+ +---------+ ------------+ @@ -275,10 +424,392 @@ pm_regexp_parse_posix_class(pm_regexp_parser_t *parser) { ); } +/** + * Property escape classification. Onigmo supports three tiers of property + * names depending on the encoding: + * + * - POSIX properties (Alnum, Alpha, ASCII, Blank, Cntrl, Digit, Graph, Lower, + * Print, Punct, Space, Upper, XDigit, Word): valid in all encodings. + * - Script properties (Hiragana, Katakana, Han, Latin, Greek, Cyrillic): valid + * in EUC-JP (/e), Windows-31J (/s), and UTF-8 (/u), but not ASCII-8BIT (/n). + * - Unicode-only properties (general categories like L, Ll, Lu, etc., plus + * Any, Assigned): valid only in UTF-8 (/u). + */ +typedef enum { + PM_REGEXP_PROPERTY_POSIX, + PM_REGEXP_PROPERTY_SCRIPT, + PM_REGEXP_PROPERTY_UNICODE +} pm_regexp_property_type_t; + +/** + * Classify a property name. The name may start with '^' for negation, which + * is skipped before matching. + */ +static pm_regexp_property_type_t +pm_regexp_classify_property(const uint8_t *name, size_t length) { + // Skip leading '^' for negated properties like \p{^Hiragana}. + if (length > 0 && name[0] == '^') { + name++; + length--; + } + +#define PM_REGEXP_CASECMP(str_) (pm_strncasecmp(name, (const uint8_t *) (str_), length) == 0) + + switch (length) { + case 3: + if (PM_REGEXP_CASECMP("Han")) return PM_REGEXP_PROPERTY_SCRIPT; + break; + case 4: + if (PM_REGEXP_CASECMP("Word")) return PM_REGEXP_PROPERTY_POSIX; + break; + case 5: + /* Most properties are length 5, so dispatch on first character. */ + switch (name[0] | 0x20) { + case 'a': + if (PM_REGEXP_CASECMP("Alnum")) return PM_REGEXP_PROPERTY_POSIX; + if (PM_REGEXP_CASECMP("Alpha")) return PM_REGEXP_PROPERTY_POSIX; + if (PM_REGEXP_CASECMP("ASCII")) return PM_REGEXP_PROPERTY_POSIX; + break; + case 'b': + if (PM_REGEXP_CASECMP("Blank")) return PM_REGEXP_PROPERTY_POSIX; + break; + case 'c': + if (PM_REGEXP_CASECMP("Cntrl")) return PM_REGEXP_PROPERTY_POSIX; + break; + case 'd': + if (PM_REGEXP_CASECMP("Digit")) return PM_REGEXP_PROPERTY_POSIX; + break; + case 'g': + if (PM_REGEXP_CASECMP("Graph")) return PM_REGEXP_PROPERTY_POSIX; + if (PM_REGEXP_CASECMP("Greek")) return PM_REGEXP_PROPERTY_SCRIPT; + break; + case 'l': + if (PM_REGEXP_CASECMP("Lower")) return PM_REGEXP_PROPERTY_POSIX; + if (PM_REGEXP_CASECMP("Latin")) return PM_REGEXP_PROPERTY_SCRIPT; + break; + case 'p': + if (PM_REGEXP_CASECMP("Print")) return PM_REGEXP_PROPERTY_POSIX; + if (PM_REGEXP_CASECMP("Punct")) return PM_REGEXP_PROPERTY_POSIX; + break; + case 's': + if (PM_REGEXP_CASECMP("Space")) return PM_REGEXP_PROPERTY_POSIX; + break; + case 'u': + if (PM_REGEXP_CASECMP("Upper")) return PM_REGEXP_PROPERTY_POSIX; + break; + } + break; + case 6: + if (PM_REGEXP_CASECMP("XDigit")) return PM_REGEXP_PROPERTY_POSIX; + break; + case 8: + if (PM_REGEXP_CASECMP("Hiragana")) return PM_REGEXP_PROPERTY_SCRIPT; + if (PM_REGEXP_CASECMP("Katakana")) return PM_REGEXP_PROPERTY_SCRIPT; + if (PM_REGEXP_CASECMP("Cyrillic")) return PM_REGEXP_PROPERTY_SCRIPT; + break; + } + +#undef PM_REGEXP_CASECMP + + // Everything else is Unicode-only (general categories, other scripts, etc.). + return PM_REGEXP_PROPERTY_UNICODE; +} + +/** + * Check for and skip a `\p{...}` or `\P{...}` Unicode property escape. The + * cursor should be pointing at 'p' or 'P' when this is called. If a property + * escape is found, record it on the regexp parser and advance past the closing + * '}'. + * + * Properties are classified into three tiers (POSIX, script, Unicode-only) to + * determine which encoding modifiers they are valid with. + */ +static bool +pm_regexp_parse_property_escape(pm_regexp_parser_t *parser) { + assert(*parser->cursor == 'p' || *parser->cursor == 'P'); + + if (parser->cursor + 1 < parser->end && parser->cursor[1] == '{') { + const uint8_t *name_start = parser->cursor + 2; + const uint8_t *search = name_start; + + while (search < parser->end && *search != '}') search++; + + if (search < parser->end) { + size_t name_length = (size_t) (search - name_start); + parser->has_property_escape = true; + + pm_regexp_property_type_t type = pm_regexp_classify_property(name_start, name_length); + + // Track the first non-POSIX property name (for /n error messages). + if (type >= PM_REGEXP_PROPERTY_SCRIPT && parser->property_name == NULL) { + parser->property_name = name_start; + parser->property_name_length = name_length; + } + + // Track the first Unicode-only property name (for /e, /s error messages). + if (type == PM_REGEXP_PROPERTY_UNICODE) { + parser->has_unicode_property_escape = true; + if (parser->unicode_property_name == NULL) { + parser->unicode_property_name = name_start; + parser->unicode_property_name_length = name_length; + } + } + + parser->cursor = search + 1; // skip past '}' + return true; + } + } + + // Not a property escape, just skip the single character after '\'. + parser->cursor++; + return false; +} + +/** + * Validate and skip a \u escape sequence in a regular expression. The cursor + * should be pointing at the character after 'u' when this is called. This + * handles both the \u{NNNN MMMM} and \uNNNN forms. Also tracks encoding + * state for validation. + */ +static void +pm_regexp_parse_unicode_escape(pm_regexp_parser_t *parser) { + const uint8_t *escape_start = parser->cursor - 2; // points to '\' + + if (pm_regexp_char_is_eof(parser)) { + pm_regexp_parse_error(parser, escape_start, parser->cursor, "invalid Unicode escape"); + return; + } + + if (*parser->cursor == '{') { + parser->cursor++; // skip '{' + + // Skip leading whitespace. + while (!pm_regexp_char_is_eof(parser) && pm_char_is_whitespace(*parser->cursor)) { + parser->cursor++; + } + + bool has_codepoint = false; + + while (!pm_regexp_char_is_eof(parser) && *parser->cursor != '}') { + // Parse the hex digits to compute the codepoint value. + uint32_t value = 0; + size_t hex_count = 0; + + int digit; + while (!pm_regexp_char_is_eof(parser) && (digit = pm_regexp_hex_digit_value(*parser->cursor)) >= 0) { + value = (value << 4) | (uint32_t) digit; + hex_count++; + parser->cursor++; + } + + if (hex_count == 0) { + // Skip to '}' or end of regexp to find the full extent. + while (!pm_regexp_char_is_eof(parser) && *parser->cursor != '}') { + parser->cursor++; + } + + const uint8_t *escape_end = parser->cursor; + if (!pm_regexp_char_is_eof(parser)) { + escape_end++; + parser->cursor++; // skip '}' + } + + pm_regexp_parse_error_format(parser, escape_start, escape_end, PM_ERR_ESCAPE_INVALID_UNICODE_LIST, (int) (escape_end - escape_start), (const char *) escape_start); + return; + } + + if (hex_count > 6) { + pm_regexp_parse_error(parser, escape_start, parser->cursor, "invalid Unicode range"); + } + + // Track encoding state for this codepoint. + if (value >= 0x80) { + parser->has_unicode_escape = true; + parser->explicit_encoding = PM_ENCODING_UTF_8_ENTRY; + parser->last_escape_was_unicode = true; + pm_regexp_hex_group_boundary(parser); + } + + // Check for invalid Unicode range (surrogates or > 0x10FFFF). + if (value > 0x10FFFF || (value >= 0xD800 && value <= 0xDFFF)) { + parser->invalid_unicode_range = true; + } + + has_codepoint = true; + + // Skip whitespace between codepoints. + while (!pm_regexp_char_is_eof(parser) && pm_char_is_whitespace(*parser->cursor)) { + parser->cursor++; + } + } + + if (pm_regexp_char_is_eof(parser)) { + pm_regexp_parse_error(parser, escape_start, parser->cursor, "unterminated Unicode escape"); + } else { + if (!has_codepoint) { + pm_regexp_parse_error_format(parser, escape_start, parser->cursor + 1, PM_ERR_ESCAPE_INVALID_UNICODE_LIST, (int) (parser->cursor + 1 - escape_start), (const char *) escape_start); + } + parser->cursor++; // skip '}' + } + } else { + // \uNNNN form — need exactly 4 hex digits. + uint32_t value = 0; + size_t hex_count = 0; + + int digit; + while (hex_count < 4 && !pm_regexp_char_is_eof(parser) && (digit = pm_regexp_hex_digit_value(*parser->cursor)) >= 0) { + value = (value << 4) | (uint32_t) digit; + hex_count++; + parser->cursor++; + } + + if (hex_count < 4) { + pm_regexp_parse_error(parser, escape_start, parser->cursor, "invalid Unicode escape"); + } else if (value >= 0x80) { + parser->has_unicode_escape = true; + parser->explicit_encoding = PM_ENCODING_UTF_8_ENTRY; + parser->last_escape_was_unicode = true; + pm_regexp_hex_group_boundary(parser); + } + + // Check for invalid Unicode range. + if (hex_count == 4 && (value > 0x10FFFF || (value >= 0xD800 && value <= 0xDFFF))) { + parser->invalid_unicode_range = true; + } + } +} + // Forward declaration because character sets can be nested. static bool pm_regexp_parse_lbracket(pm_regexp_parser_t *parser, uint16_t depth); +/** + * Parse a \x escape and return the byte value. The cursor should be pointing + * at the character after 'x'. Returns -1 if no hex digits follow. + */ +static int +pm_regexp_parse_hex_escape(pm_regexp_parser_t *parser) { + int value = -1; + + if (!pm_regexp_char_is_eof(parser)) { + int digit = pm_regexp_hex_digit_value(*parser->cursor); + if (digit >= 0) { + value = digit; + parser->cursor++; + + if (!pm_regexp_char_is_eof(parser)) { + digit = pm_regexp_hex_digit_value(*parser->cursor); + if (digit >= 0) { + value = (value << 4) | digit; + parser->cursor++; + } + } + } + } + + if (value >= 0) { + pm_regexp_track_hex_escape(parser, (uint8_t) value); + } + + return value; +} + +/** + * Parse a backslash escape sequence in a regexp, handling \u (unicode), + * \p/\P (property), \x (hex), and other single-character escapes. Also + * tracks encoding state for \M-x and \C-\M-x escapes. + */ +static void +pm_regexp_parse_backslash_escape(pm_regexp_parser_t *parser) { + if (pm_regexp_char_is_eof(parser)) return; + + switch (*parser->cursor) { + case 'u': + parser->cursor++; // skip 'u' + pm_regexp_parse_unicode_escape(parser); + break; + case 'p': + case 'P': + pm_regexp_parse_property_escape(parser); + break; + case 'x': + parser->cursor++; // skip 'x' + pm_regexp_parse_hex_escape(parser); + break; + case 'M': + // \M-x produces (x | 0x80), always >= 0x80 + if (parser->cursor + 2 < parser->end && parser->cursor[1] == '-') { + parser->cursor += 2; // skip 'M-' + if (!pm_regexp_char_is_eof(parser)) { + if (*parser->cursor == '\\') { + parser->cursor++; + // \M-\C-x or \M-\cx — the resulting byte is always >= 0x80 + // We just need to track it as a hex escape >= 0x80. + pm_regexp_parse_backslash_escape(parser); + } else { + parser->cursor++; + } + // \M-x always produces a byte >= 0x80 + pm_regexp_track_hex_escape(parser, 0x80); + } + } else { + parser->cursor++; + } + break; + case 'C': + // \C-x produces (x & 0x1F) + if (parser->cursor + 2 < parser->end && parser->cursor[1] == '-') { + parser->cursor += 2; // skip 'C-' + if (!pm_regexp_char_is_eof(parser)) { + if (*parser->cursor == '\\') { + parser->cursor++; + pm_regexp_parse_backslash_escape(parser); + } else { + parser->cursor++; + } + } + } else { + parser->cursor++; + } + break; + case 'c': + // \cx produces (x & 0x1F) + parser->cursor++; // skip 'c' + if (!pm_regexp_char_is_eof(parser)) { + if (*parser->cursor == '\\') { + parser->cursor++; + pm_regexp_parse_backslash_escape(parser); + } else { + parser->cursor++; + } + } + break; + default: + pm_regexp_hex_group_boundary(parser); + parser->cursor++; + break; + } +} + +/** + * Check if a byte at the current position is a non-ASCII byte in a multibyte + * encoding that produces an invalid character. If so, emit an error at the + * byte location immediately. + */ +static void +pm_regexp_parse_invalid_multibyte(pm_regexp_parser_t *parser, const uint8_t *cursor) { + uint8_t byte = *cursor; + if (byte >= 0x80 && parser->encoding_changed && parser->encoding->multibyte) { + size_t width = parser->encoding->char_width(cursor, (ptrdiff_t) (parser->end - cursor)); + if (width > 1) { + parser->cursor += width - 1; + } else if (width == 0) { + parser->has_invalid_multibyte = true; + pm_regexp_parse_error_format(parser, cursor, cursor + 1, PM_ERR_INVALID_MULTIBYTE_CHAR, parser->encoding->name); + } + } +} + /** * match-char-set : '[' '^'? (match-range | match-char)* ']' * ; @@ -293,12 +824,16 @@ pm_regexp_parse_character_set(pm_regexp_parser_t *parser, uint16_t depth) { pm_regexp_parse_lbracket(parser, (uint16_t) (depth + 1)); break; case '\\': - if (!pm_regexp_char_is_eof(parser)) { - parser->cursor++; - } + pm_regexp_parse_backslash_escape(parser); break; default: - // do nothing, we've already advanced the cursor + // We've already advanced the cursor by one byte. If the byte + // was >= 0x80 in a multibyte encoding, we may need to consume + // additional continuation bytes and validate the character. + if (*(parser->cursor - 1) >= 0x80) { + parser->non_ascii_literal_count++; + } + pm_regexp_parse_invalid_multibyte(parser, parser->cursor - 1); break; } } @@ -354,8 +889,13 @@ typedef enum { // These are the options that are configurable on the regular expression (or // from within a group). +/** The minimum character value for a regexp option slot. */ #define PRISM_REGEXP_OPTION_STATE_SLOT_MINIMUM 'a' + +/** The maximum character value for a regexp option slot. */ #define PRISM_REGEXP_OPTION_STATE_SLOT_MAXIMUM 'x' + +/** The number of regexp option slots. */ #define PRISM_REGEXP_OPTION_STATE_SLOTS (PRISM_REGEXP_OPTION_STATE_SLOT_MAXIMUM - PRISM_REGEXP_OPTION_STATE_SLOT_MINIMUM + 1) /** @@ -498,7 +1038,15 @@ pm_regexp_parse_group(pm_regexp_parser_t *parser, uint16_t depth) { } size_t width = parser->encoding->char_width(parser->cursor, (ptrdiff_t) (parser->end - parser->cursor)); - if (width == 0) return false; + if (width == 0) { + if (*parser->cursor >= 0x80) { + parser->has_invalid_multibyte = true; + pm_regexp_parse_error_format(parser, parser->cursor, parser->cursor + 1, PM_ERR_INVALID_MULTIBYTE_CHAR, parser->encoding->name); + parser->cursor++; + continue; + } + return false; + } escaped = (width == 1) && (*parser->cursor == '\\'); parser->cursor += width; @@ -686,9 +1234,7 @@ pm_regexp_parse_item(pm_regexp_parser_t *parser, uint16_t depth) { return pm_regexp_parse_quantifier(parser); case '\\': parser->cursor++; - if (!pm_regexp_char_is_eof(parser)) { - parser->cursor++; - } + pm_regexp_parse_backslash_escape(parser); return pm_regexp_parse_quantifier(parser); case '(': parser->cursor++; @@ -720,9 +1266,30 @@ pm_regexp_parse_item(pm_regexp_parser_t *parser, uint16_t depth) { 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; + if (width == 0) { + if (*parser->cursor >= 0x80 && parser->encoding_changed) { + if (parser->encoding->multibyte) { + // Invalid multibyte character in a multibyte encoding. + // Emit the error at the byte location immediately. + parser->has_invalid_multibyte = true; + pm_regexp_parse_error_format(parser, parser->cursor, parser->cursor + 1, PM_ERR_INVALID_MULTIBYTE_CHAR, parser->encoding->name); + } else { + // Non-ASCII byte in a single-byte encoding (e.g., + // US-ASCII). Count it for later error reporting. + parser->non_ascii_literal_count++; + } + parser->cursor++; + return pm_regexp_parse_quantifier(parser); + } + return false; + } + + // Count non-ASCII literal bytes. + for (size_t i = 0; i < width; i++) { + if (parser->cursor[i] >= 0x80) parser->non_ascii_literal_count++; + } + parser->cursor += width; return pm_regexp_parse_quantifier(parser); } } @@ -768,13 +1335,354 @@ pm_regexp_parse_pattern(pm_regexp_parser_t *parser) { return pm_regexp_char_is_eof(parser); } +// --------------------------------------------------------------------------- +// Encoding validation +// --------------------------------------------------------------------------- + /** - * Parse a regular expression and extract the names of all of the named capture - * groups. + * Validate that groups of hex escape bytes in the buffer form valid multibyte + * characters in the given encoding. Groups are separated by 0x00 sentinels. + */ +static bool +pm_regexp_validate_hex_escapes(const pm_encoding_t *encoding, const pm_buffer_t *buffer) { + const uint8_t *data = (const uint8_t *) pm_buffer_value(buffer); + size_t len = pm_buffer_length(buffer); + size_t i = 0; + + while (i < len) { + size_t group_start = i; + while (i < len && data[i] != 0x00) i++; + + for (size_t j = group_start; j < i; ) { + size_t width = encoding->char_width(data + j, (ptrdiff_t) (i - j)); + if (width == 0) return false; + j += width; + } + + if (i < len) i++; // skip sentinel + } + + return true; +} + +/** + * Format regexp source content for use in error messages, hex-escaping + * non-ASCII bytes. + */ +static void +pm_regexp_format_for_error(pm_buffer_t *buffer, const pm_encoding_t *encoding, const uint8_t *source, size_t length) { + size_t index = 0; + + if (encoding == PM_ENCODING_UTF_8_ENTRY) { + pm_buffer_append_string(buffer, (const char *) source, length); + return; + } + + while (index < length) { + if (source[index] < 0x80) { + pm_buffer_append_byte(buffer, source[index]); + index++; + } else if (encoding->multibyte) { + size_t width = encoding->char_width(source + index, (ptrdiff_t) (length - index)); + + if (width > 1) { + pm_buffer_append_string(buffer, "\\x{", 3); + for (size_t i = 0; i < width; i++) { + pm_buffer_append_format(buffer, "%02X", source[index + i]); + } + pm_buffer_append_byte(buffer, '}'); + index += width; + } else { + pm_buffer_append_format(buffer, "\\x%02X", source[index]); + index++; + } + } else { + pm_buffer_append_format(buffer, "\\x%02X", source[index]); + index++; + } + } +} + +/** + * Emit an encoding validation error on the regexp node. + */ +#define PM_REGEXP_ENCODING_ERROR(parser, diag_id, ...) \ + pm_diagnostic_list_append_format( \ + &(parser)->parser->metadata_arena, \ + &(parser)->parser->error_list, \ + (uint32_t) ((parser)->node_start - (parser)->parser->start), \ + (uint32_t) ((parser)->node_end - (parser)->node_start), \ + diag_id, __VA_ARGS__) + +/** + * Validate encoding for a regexp with an encoding modifier (/e, /s, /u, /n). + * + * The decision tree is: + * + * 1. No escape-set encoding (explicit_encoding == NULL): + * a. ASCII-only content: validate property escapes, return forced US-ASCII + * for /n or the modifier flags for others. + * b. US-ASCII source with non-ASCII literals: emit per-byte errors. + * c. Source encoding differs from modifier encoding: emit mismatch error. + * + * 2. Mixed \u and \x escapes: emit the appropriate conflict error depending + * on the modifier and which escape type was last. + * + * 3. \u escape with non-/u modifier: incompatible encoding error. + * + * 4. Validate that hex escape byte sequences form valid multibyte characters + * in the modifier's encoding. + */ +static pm_node_flags_t +pm_regexp_validate_encoding_modifier(pm_regexp_parser_t *parser, bool ascii_only, pm_node_flags_t flags, char modifier, const pm_encoding_t *modifier_encoding, const char *source_start, int source_length) { + + if (parser->explicit_encoding == NULL) { + if (ascii_only) { + // Check property escapes against the modifier's encoding tier. + // /n (ASCII-8BIT): only POSIX properties are valid. + // /e, /s: POSIX and script properties are valid. + // /u: all properties are valid. + if (modifier == 'n' && parser->property_name != NULL) { + PM_REGEXP_ENCODING_ERROR(parser, PM_ERR_REGEXP_INVALID_CHAR_PROPERTY, + (int) parser->property_name_length, (const char *) parser->property_name, + source_length, source_start); + } else if (modifier != 'u' && parser->has_unicode_property_escape) { + PM_REGEXP_ENCODING_ERROR(parser, PM_ERR_REGEXP_INVALID_CHAR_PROPERTY, + (int) parser->unicode_property_name_length, (const char *) parser->unicode_property_name, + source_length, source_start); + } + return modifier == 'n' ? PM_REGULAR_EXPRESSION_FLAGS_FORCED_US_ASCII_ENCODING : flags; + } + + if (parser->encoding == PM_ENCODING_US_ASCII_ENTRY) { + for (uint32_t i = 0; i < parser->non_ascii_literal_count; i++) { + PM_REGEXP_ENCODING_ERROR(parser, PM_ERR_INVALID_MULTIBYTE_CHAR, parser->encoding->name); + } + } else if (parser->encoding != modifier_encoding) { + PM_REGEXP_ENCODING_ERROR(parser, PM_ERR_REGEXP_ENCODING_OPTION_MISMATCH, modifier, parser->encoding->name); + + if (modifier == 'n' && !ascii_only) { + pm_buffer_t formatted = { 0 }; + pm_regexp_format_for_error(&formatted, parser->encoding, (const uint8_t *) source_start, (size_t) source_length); + PM_REGEXP_ENCODING_ERROR(parser, PM_ERR_REGEXP_NON_ESCAPED_MBC, (int) formatted.length, (const char *) formatted.value); + pm_buffer_cleanup(&formatted); + } + } + + return flags; + } + + // Mixed unicode + hex escapes. + if (parser->has_unicode_escape && parser->has_hex_escape) { + if (modifier == 'n') { + if (parser->last_escape_was_unicode) { + PM_REGEXP_ENCODING_ERROR(parser, PM_ERR_REGEXP_UTF8_CHAR_NON_UTF8_REGEXP, source_length, source_start); + } else { + PM_REGEXP_ENCODING_ERROR(parser, PM_ERR_REGEXP_ESCAPED_NON_ASCII_IN_UTF8, source_length, source_start); + } + } else { + if (!pm_regexp_validate_hex_escapes(modifier_encoding, &parser->hex_escape_buffer)) { + PM_REGEXP_ENCODING_ERROR(parser, PM_ERR_INVALID_MULTIBYTE_ESCAPE, source_length, source_start); + } + } + + return flags; + } + + if (modifier != 'u' && parser->explicit_encoding == PM_ENCODING_UTF_8_ENTRY) { + if (parser->last_escape_was_unicode) { + PM_REGEXP_ENCODING_ERROR(parser, PM_ERR_REGEXP_INCOMPAT_CHAR_ENCODING, source_length, source_start); + } else if (parser->encoding != PM_ENCODING_UTF_8_ENTRY) { + PM_REGEXP_ENCODING_ERROR(parser, PM_ERR_REGEXP_INCOMPAT_CHAR_ENCODING, source_length, source_start); + } + } + + if (modifier != 'n' && !pm_regexp_validate_hex_escapes(modifier_encoding, &parser->hex_escape_buffer)) { + PM_REGEXP_ENCODING_ERROR(parser, PM_ERR_INVALID_MULTIBYTE_ESCAPE, source_length, source_start); + } + + return flags; +} + +/** + * Validate encoding for a regexp without a modifier and compute the encoding + * flags to set on the node. + * + * The decision tree is: + * + * 1. If a modifier (/n, /u, /e, /s) is present, delegate to + * pm_regexp_validate_encoding_modifier. + * 2. Invalid multibyte chars or unicode ranges: suppress further checks (errors + * were already emitted during parsing). + * 3. US-ASCII source with non-ASCII literals: emit per-byte errors. + * 4. ASCII-only content: return forced US-ASCII (or forced UTF-8 if \p{...}). + * 5. Escape-set encoding present: validate hex escapes against the target + * encoding, handle mixed \u + \x conflicts, and return the appropriate + * forced encoding flag. + */ +static pm_node_flags_t +pm_regexp_validate_encoding(pm_regexp_parser_t *parser, bool ascii_only, pm_node_flags_t flags, const char *source_start, int source_length) { + + // Invalid multibyte characters suppress further validation. + // Errors were already emitted at the byte locations during parsing. + if (parser->has_invalid_multibyte) { + return flags; + } + + if (parser->invalid_unicode_range) { + PM_REGEXP_ENCODING_ERROR(parser, PM_ERR_REGEXP_INVALID_UNICODE_RANGE, source_length, source_start); + return flags; + } + + // Check modifier flags first. + if (flags & PM_REGULAR_EXPRESSION_FLAGS_ASCII_8BIT) { + return pm_regexp_validate_encoding_modifier(parser, ascii_only, flags, 'n', PM_ENCODING_ASCII_8BIT_ENTRY, source_start, source_length); + } + if (flags & PM_REGULAR_EXPRESSION_FLAGS_UTF_8) { + return pm_regexp_validate_encoding_modifier(parser, ascii_only, flags, 'u', PM_ENCODING_UTF_8_ENTRY, source_start, source_length); + } + if (flags & PM_REGULAR_EXPRESSION_FLAGS_EUC_JP) { + return pm_regexp_validate_encoding_modifier(parser, ascii_only, flags, 'e', PM_ENCODING_EUC_JP_ENTRY, source_start, source_length); + } + if (flags & PM_REGULAR_EXPRESSION_FLAGS_WINDOWS_31J) { + return pm_regexp_validate_encoding_modifier(parser, ascii_only, flags, 's', PM_ENCODING_WINDOWS_31J_ENTRY, source_start, source_length); + } + + // No modifier — check for non-ASCII literals in US-ASCII encoding. + if (parser->encoding == PM_ENCODING_US_ASCII_ENTRY && parser->explicit_encoding == NULL && !ascii_only) { + for (uint32_t i = 0; i < parser->non_ascii_literal_count; i++) { + PM_REGEXP_ENCODING_ERROR(parser, PM_ERR_INVALID_MULTIBYTE_CHAR, parser->encoding->name); + } + } + + // ASCII-only regexps get downgraded to US-ASCII, unless property escapes + // force UTF-8. + if (ascii_only) { + if (parser->has_property_escape) { + return PM_REGULAR_EXPRESSION_FLAGS_FORCED_UTF8_ENCODING; + } + return PM_REGULAR_EXPRESSION_FLAGS_FORCED_US_ASCII_ENCODING; + } + + // Check explicit encoding from escape sequences. + if (parser->explicit_encoding != NULL) { + // Mixed unicode + hex escapes without modifier. + if (parser->has_unicode_escape && parser->has_hex_escape && parser->encoding != PM_ENCODING_UTF_8_ENTRY) { + if (parser->encoding != PM_ENCODING_US_ASCII_ENTRY && + parser->encoding != PM_ENCODING_ASCII_8BIT_ENTRY && + !pm_regexp_validate_hex_escapes(parser->encoding, &parser->hex_escape_buffer)) { + PM_REGEXP_ENCODING_ERROR(parser, PM_ERR_INVALID_MULTIBYTE_ESCAPE, source_length, source_start); + } else if (parser->last_escape_was_unicode) { + PM_REGEXP_ENCODING_ERROR(parser, PM_ERR_REGEXP_UTF8_CHAR_NON_UTF8_REGEXP, source_length, source_start); + } else { + PM_REGEXP_ENCODING_ERROR(parser, PM_ERR_REGEXP_ESCAPED_NON_ASCII_IN_UTF8, source_length, source_start); + } + + return 0; + } + + if (parser->explicit_encoding == PM_ENCODING_UTF_8_ENTRY) { + if (!pm_regexp_validate_hex_escapes(parser->explicit_encoding, &parser->hex_escape_buffer)) { + PM_REGEXP_ENCODING_ERROR(parser, PM_ERR_INVALID_MULTIBYTE_ESCAPE, source_length, source_start); + } + + 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; + } else { + if (!pm_regexp_validate_hex_escapes(parser->explicit_encoding, &parser->hex_escape_buffer)) { + PM_REGEXP_ENCODING_ERROR(parser, PM_ERR_INVALID_MULTIBYTE_ESCAPE, source_length, source_start); + } + } + } + + return 0; +} + +/** + * Parse a regular expression, validate its encoding, and optionally extract + * named capture groups. Encoding validation walks the raw source (content_loc) + * to distinguish escape-produced bytes from literal bytes. Named capture + * extraction walks the unescaped content since escape sequences in group names + * (e.g., line continuations) have already been processed by the lexer. + */ +pm_node_flags_t +pm_regexp_parse(pm_parser_t *parser, pm_regular_expression_node_t *node, pm_regexp_name_callback_t name_callback, pm_regexp_name_data_t *name_data) { + const uint8_t *source = parser->start + node->content_loc.start; + size_t size = node->content_loc.length; + bool extended_mode = PM_NODE_FLAG_P(node, PM_REGULAR_EXPRESSION_FLAGS_EXTENDED); + pm_node_flags_t flags = PM_NODE_FLAGS(node); + + const uint8_t *node_start = parser->start + node->base.location.start; + const uint8_t *node_end = parser->start + node->base.location.start + node->base.location.length; + + // First pass: walk raw source for encoding validation (no name extraction). + pm_regexp_parser_t regexp_parser = { + .parser = parser, + .start = source, + .cursor = source, + .end = source + size, + .extended_mode = extended_mode, + .encoding_changed = parser->encoding_changed, + .encoding = parser->encoding, + .name_callback = NULL, + .name_data = NULL, + .shared = true, + .node_start = node_start, + .node_end = node_end, + .has_unicode_escape = false, + .has_hex_escape = false, + .last_escape_was_unicode = false, + .explicit_encoding = NULL, + .has_property_escape = false, + .has_unicode_property_escape = false, + .property_name = NULL, + .property_name_length = 0, + .unicode_property_name = NULL, + .unicode_property_name_length = 0, + .non_ascii_literal_count = 0, + .invalid_unicode_range = false, + .hex_escape_buffer = { 0 }, + .hex_group_active = false, + .has_invalid_multibyte = false, + }; + + pm_regexp_parse_pattern(®exp_parser); + + // Compute ascii_only from the regexp parser's tracked state. We cannot + // use node->unescaped for this because regexp unescaped content preserves + // escape text (e.g., \x80 is 4 ASCII chars), not the binary values. + bool ascii_only = !regexp_parser.has_hex_escape && !regexp_parser.has_unicode_escape && regexp_parser.non_ascii_literal_count == 0; + // Use the unescaped content for error messages to match CRuby's format, + // where Ruby escapes like \M-\C-? are resolved to bytes but regexp escapes + // like \u{80} are preserved as text. + const char *error_source = (const char *) pm_string_source(&node->unescaped); + int error_source_length = (int) pm_string_length(&node->unescaped); + pm_node_flags_t encoding_flags = pm_regexp_validate_encoding(®exp_parser, ascii_only, flags, error_source, error_source_length); + pm_buffer_cleanup(®exp_parser.hex_escape_buffer); + + // Second pass: walk unescaped content for named capture extraction. + if (name_callback != NULL) { + bool shared = node->unescaped.type == PM_STRING_SHARED; + pm_regexp_parse_named_captures(parser, pm_string_source(&node->unescaped), pm_string_length(&node->unescaped), shared, extended_mode, name_callback, name_data); + } + + return encoding_flags; +} + +/** + * Parse an interpolated regular expression for named capture groups only. + * This is used for the =~ operator with interpolated regexps where we don't + * have a pm_regular_expression_node_t. No encoding validation is performed. + * + * Note: The encoding-tracking fields (has_unicode_escape, has_hex_escape, etc.) + * are initialized but not used for the result. They exist because the parsing + * functions (pm_regexp_parse_backslash_escape, etc.) unconditionally update + * them as they walk through the content. */ -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) { +void +pm_regexp_parse_named_captures(pm_parser_t *parser, const uint8_t *source, size_t size, bool shared, bool extended_mode, pm_regexp_name_callback_t name_callback, pm_regexp_name_data_t *name_data) { + pm_regexp_parser_t regexp_parser = { .parser = parser, .start = source, .cursor = source, @@ -784,7 +1692,26 @@ pm_regexp_parse(pm_parser_t *parser, const uint8_t *source, size_t size, bool ex .encoding = parser->encoding, .name_callback = name_callback, .name_data = name_data, - .error_callback = error_callback, - .error_data = error_data - }); + .shared = shared, + .node_start = source, + .node_end = source + size, + .has_unicode_escape = false, + .has_hex_escape = false, + .last_escape_was_unicode = false, + .explicit_encoding = NULL, + .has_property_escape = false, + .has_unicode_property_escape = false, + .property_name = NULL, + .property_name_length = 0, + .unicode_property_name = NULL, + .unicode_property_name_length = 0, + .non_ascii_literal_count = 0, + .invalid_unicode_range = false, + .hex_escape_buffer = { 0 }, + .hex_group_active = false, + .has_invalid_multibyte = false, + }; + + pm_regexp_parse_pattern(®exp_parser); + pm_buffer_cleanup(®exp_parser.hex_escape_buffer); } diff --git a/src/source.c b/src/source.c new file mode 100644 index 0000000000..f61cb19c1b --- /dev/null +++ b/src/source.c @@ -0,0 +1,491 @@ +#include "prism/internal/source.h" + +#include "prism/internal/allocator.h" +#include "prism/internal/buffer.h" + +#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 + +static const uint8_t empty_source[] = ""; + +/** + * Allocate and initialize a pm_source_t with the given fields. + */ +static pm_source_t * +pm_source_alloc(const uint8_t *source, size_t length, pm_source_type_t type) { + pm_source_t *result = xmalloc(sizeof(pm_source_t)); + if (result == NULL) abort(); + + *result = (struct pm_source_t) { + .source = source, + .length = length, + .type = type + }; + + return result; +} + +/** + * Create a new source that wraps existing constant memory. + */ +pm_source_t * +pm_source_constant_new(const uint8_t *data, size_t length) { + return pm_source_alloc(data, length, PM_SOURCE_CONSTANT); +} + +/** + * Create a new source that wraps existing shared memory. + */ +pm_source_t * +pm_source_shared_new(const uint8_t *data, size_t length) { + return pm_source_alloc(data, length, PM_SOURCE_SHARED); +} + +/** + * Create a new source that owns its memory. + */ +pm_source_t * +pm_source_owned_new(uint8_t *data, size_t length) { + return pm_source_alloc(data, length, PM_SOURCE_OWNED); +} + +#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 size of the allocated path in bytes. */ + size_t path_size; + + /** The handle to the file, which will start as uninitialized memory. */ + HANDLE file; +} pm_source_file_handle_t; + +/** + * Open the file indicated by the filepath parameter for reading on Windows. + */ +static pm_source_init_result_t +pm_source_file_handle_open(pm_source_file_handle_t *handle, const char *filepath) { + int length = MultiByteToWideChar(CP_UTF8, 0, filepath, -1, NULL, 0); + if (length == 0) return PM_SOURCE_INIT_ERROR_GENERIC; + + handle->path_size = sizeof(WCHAR) * ((size_t) length); + handle->path = xmalloc(handle->path_size); + if ((handle->path == NULL) || (MultiByteToWideChar(CP_UTF8, 0, filepath, -1, handle->path, length) == 0)) { + xfree_sized(handle->path, handle->path_size); + return PM_SOURCE_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_source_init_result_t result = PM_SOURCE_INIT_ERROR_GENERIC; + + if (GetLastError() == ERROR_ACCESS_DENIED) { + DWORD attributes = GetFileAttributesW(handle->path); + if ((attributes != INVALID_FILE_ATTRIBUTES) && (attributes & FILE_ATTRIBUTE_DIRECTORY)) { + result = PM_SOURCE_INIT_ERROR_DIRECTORY; + } + } + + xfree_sized(handle->path, handle->path_size); + return result; + } + + return PM_SOURCE_INIT_SUCCESS; +} + +/** + * Close the file handle and free the path. + */ +static void +pm_source_file_handle_close(pm_source_file_handle_t *handle) { + xfree_sized(handle->path, handle->path_size); + CloseHandle(handle->file); +} +#endif + +/** + * Create a new source by memory-mapping a file. + */ +pm_source_t * +pm_source_mapped_new(const char *filepath, int open_flags, pm_source_init_result_t *result) { +#ifdef _WIN32 + (void) open_flags; + + /* Open the file for reading. */ + pm_source_file_handle_t handle; + *result = pm_source_file_handle_open(&handle, filepath); + if (*result != PM_SOURCE_INIT_SUCCESS) return NULL; + + /* Get the file size. */ + DWORD file_size = GetFileSize(handle.file, NULL); + if (file_size == INVALID_FILE_SIZE) { + pm_source_file_handle_close(&handle); + *result = PM_SOURCE_INIT_ERROR_GENERIC; + return NULL; + } + + /* If the file is empty, then return a constant source. */ + if (file_size == 0) { + pm_source_file_handle_close(&handle); + *result = PM_SOURCE_INIT_SUCCESS; + return pm_source_alloc(empty_source, 0, PM_SOURCE_CONSTANT); + } + + /* Create a mapping of the file. */ + HANDLE mapping = CreateFileMapping(handle.file, NULL, PAGE_READONLY, 0, 0, NULL); + if (mapping == NULL) { + pm_source_file_handle_close(&handle); + *result = PM_SOURCE_INIT_ERROR_GENERIC; + return NULL; + } + + /* Map the file into memory. */ + uint8_t *source = (uint8_t *) MapViewOfFile(mapping, FILE_MAP_READ, 0, 0, 0); + CloseHandle(mapping); + pm_source_file_handle_close(&handle); + + if (source == NULL) { + *result = PM_SOURCE_INIT_ERROR_GENERIC; + return NULL; + } + + *result = PM_SOURCE_INIT_SUCCESS; + return pm_source_alloc(source, (size_t) file_size, PM_SOURCE_MAPPED); +#elif defined(_POSIX_MAPPED_FILES) + /* Open the file for reading. */ + int fd = open(filepath, O_RDONLY | open_flags); + if (fd == -1) { + *result = PM_SOURCE_INIT_ERROR_GENERIC; + return NULL; + } + + /* Stat the file to get the file size. */ + struct stat sb; + if (fstat(fd, &sb) == -1) { + close(fd); + *result = PM_SOURCE_INIT_ERROR_GENERIC; + return NULL; + } + + /* Ensure it is a file and not a directory. */ + if (S_ISDIR(sb.st_mode)) { + close(fd); + *result = PM_SOURCE_INIT_ERROR_DIRECTORY; + return NULL; + } + + /* + * For non-regular files (pipes, character devices), return a specific + * error so the caller can handle reading through their own I/O layer. + */ + if (!S_ISREG(sb.st_mode)) { + close(fd); + *result = PM_SOURCE_INIT_ERROR_NON_REGULAR; + return NULL; + } + + /* mmap the file descriptor to virtually get the contents. */ + size_t size = (size_t) sb.st_size; + + if (size == 0) { + close(fd); + *result = PM_SOURCE_INIT_SUCCESS; + return pm_source_alloc(empty_source, 0, PM_SOURCE_CONSTANT); + } + + uint8_t *source = mmap(NULL, size, PROT_READ, MAP_PRIVATE, fd, 0); + if (source == MAP_FAILED) { + close(fd); + *result = PM_SOURCE_INIT_ERROR_GENERIC; + return NULL; + } + + close(fd); + *result = PM_SOURCE_INIT_SUCCESS; + return pm_source_alloc(source, size, PM_SOURCE_MAPPED); +#else + (void) open_flags; + return pm_source_file_new(filepath, result); +#endif +} + +/** + * Create a new source by reading a file into a heap-allocated buffer. + */ +pm_source_t * +pm_source_file_new(const char *filepath, pm_source_init_result_t *result) { +#ifdef _WIN32 + /* Open the file for reading. */ + pm_source_file_handle_t handle; + *result = pm_source_file_handle_open(&handle, filepath); + if (*result != PM_SOURCE_INIT_SUCCESS) return NULL; + + /* Get the file size. */ + const DWORD file_size = GetFileSize(handle.file, NULL); + if (file_size == INVALID_FILE_SIZE) { + pm_source_file_handle_close(&handle); + *result = PM_SOURCE_INIT_ERROR_GENERIC; + return NULL; + } + + /* If the file is empty, return a constant source. */ + if (file_size == 0) { + pm_source_file_handle_close(&handle); + *result = PM_SOURCE_INIT_SUCCESS; + return pm_source_alloc(empty_source, 0, PM_SOURCE_CONSTANT); + } + + /* Create a buffer to read the file into. */ + uint8_t *source = xmalloc(file_size); + if (source == NULL) { + pm_source_file_handle_close(&handle); + *result = PM_SOURCE_INIT_ERROR_GENERIC; + return NULL; + } + + /* Read the contents of the file. */ + DWORD bytes_read; + if (!ReadFile(handle.file, source, file_size, &bytes_read, NULL)) { + xfree_sized(source, file_size); + pm_source_file_handle_close(&handle); + *result = PM_SOURCE_INIT_ERROR_GENERIC; + return NULL; + } + + /* Check the number of bytes read. */ + if (bytes_read != file_size) { + xfree_sized(source, file_size); + pm_source_file_handle_close(&handle); + *result = PM_SOURCE_INIT_ERROR_GENERIC; + return NULL; + } + + pm_source_file_handle_close(&handle); + *result = PM_SOURCE_INIT_SUCCESS; + return pm_source_alloc(source, (size_t) file_size, PM_SOURCE_OWNED); +#elif defined(PRISM_HAS_FILESYSTEM) + /* Open the file for reading. */ + int fd = open(filepath, O_RDONLY); + if (fd == -1) { + *result = PM_SOURCE_INIT_ERROR_GENERIC; + return NULL; + } + + /* Stat the file to get the file size. */ + struct stat sb; + if (fstat(fd, &sb) == -1) { + close(fd); + *result = PM_SOURCE_INIT_ERROR_GENERIC; + return NULL; + } + + /* Ensure it is a file and not a directory. */ + if (S_ISDIR(sb.st_mode)) { + close(fd); + *result = PM_SOURCE_INIT_ERROR_DIRECTORY; + return NULL; + } + + /* Check the size to see if it's empty. */ + size_t size = (size_t) sb.st_size; + if (size == 0) { + close(fd); + *result = PM_SOURCE_INIT_SUCCESS; + return pm_source_alloc(empty_source, 0, PM_SOURCE_CONSTANT); + } + + const size_t length = (size_t) size; + uint8_t *source = xmalloc(length); + if (source == NULL) { + close(fd); + *result = PM_SOURCE_INIT_ERROR_GENERIC; + return NULL; + } + + ssize_t bytes_read = read(fd, source, length); + close(fd); + + if (bytes_read == -1 || (size_t) bytes_read != length) { + xfree_sized(source, length); + *result = PM_SOURCE_INIT_ERROR_GENERIC; + return NULL; + } + + *result = PM_SOURCE_INIT_SUCCESS; + return pm_source_alloc(source, length, PM_SOURCE_OWNED); +#else + (void) filepath; + *result = PM_SOURCE_INIT_ERROR_GENERIC; + perror("pm_source_file_new is not implemented for this platform"); + return NULL; +#endif +} + +/** + * Create a new source by reading from a stream. This allocates the source + * but does not read from the stream yet. Use pm_source_stream_read to read + * data. + */ +pm_source_t * +pm_source_stream_new(void *stream, pm_source_stream_fgets_t *fgets, pm_source_stream_feof_t *feof) { + pm_source_t *source = pm_source_alloc(NULL, 0, PM_SOURCE_STREAM); + source->stream.buffer = pm_buffer_new(); + source->stream.stream = stream; + source->stream.fgets = fgets; + source->stream.feof = feof; + source->stream.eof = false; + + return source; +} + +/** + * Read from the stream into the source's internal buffer until __END__ is + * encountered or EOF is reached. Updates the source pointer and length. + * + * Returns true if EOF was reached, false if __END__ was encountered. + */ +bool +pm_source_stream_read(pm_source_t *source) { + pm_buffer_t *buffer = source->stream.buffer; + +#define LINE_SIZE 4096 + char line[LINE_SIZE]; + + while (memset(line, '\n', LINE_SIZE), source->stream.fgets(line, LINE_SIZE, source->stream.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) { + source->source = (const uint8_t *) pm_buffer_value(buffer); + source->length = pm_buffer_length(buffer); + return false; + } + break; + case 8: + if (strncmp(line, "__END__\n", 8) == 0) { + source->source = (const uint8_t *) pm_buffer_value(buffer); + source->length = pm_buffer_length(buffer); + return false; + } + break; + case 9: + if (strncmp(line, "__END__\r\n", 9) == 0) { + source->source = (const uint8_t *) pm_buffer_value(buffer); + source->length = pm_buffer_length(buffer); + 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 (source->stream.feof(source->stream.stream)) { + break; + } + } + +#undef LINE_SIZE + + source->stream.eof = true; + source->source = (const uint8_t *) pm_buffer_value(buffer); + source->length = pm_buffer_length(buffer); + return true; +} + +/** + * Returns whether the stream source has reached EOF. + */ +bool +pm_source_stream_eof(const pm_source_t *source) { + return source->stream.eof; +} + +/** + * Free the given source and any memory it owns. + */ +void +pm_source_free(pm_source_t *source) { + switch (source->type) { + case PM_SOURCE_CONSTANT: + case PM_SOURCE_SHARED: + /* No cleanup needed for the data. */ + break; + case PM_SOURCE_OWNED: + xfree_sized((void *) source->source, source->length); + break; + case PM_SOURCE_MAPPED: +#if defined(_WIN32) + if (source->length > 0) { + UnmapViewOfFile((void *) source->source); + } +#elif defined(_POSIX_MAPPED_FILES) + if (source->length > 0) { + munmap((void *) source->source, source->length); + } +#endif + break; + case PM_SOURCE_STREAM: + pm_buffer_free(source->stream.buffer); + break; + } + + xfree_sized(source, sizeof(pm_source_t)); +} + +/** + * Returns the length of the source data in bytes. + */ +size_t +pm_source_length(const pm_source_t *source) { + return source->length; +} + +/** + * Returns a pointer to the source data. + */ +const uint8_t * +pm_source_source(const pm_source_t *source) { + return source->source; +} diff --git a/src/static_literals.c b/src/static_literals.c index 13a52378dd..9af1eadf5d 100644 --- a/src/static_literals.c +++ b/src/static_literals.c @@ -1,4 +1,18 @@ -#include "prism/static_literals.h" +#include "prism/internal/static_literals.h" + +#include "prism/compiler/inline.h" +#include "prism/compiler/unused.h" + +#include "prism/internal/allocator.h" +#include "prism/internal/buffer.h" +#include "prism/internal/integer.h" +#include "prism/internal/isinf.h" +#include "prism/internal/stringy.h" + +#include +#include +#include +#include /** * A small struct used for passing around a subset of the information that is @@ -7,7 +21,7 @@ */ typedef struct { /** The list of newline offsets to use to calculate line numbers. */ - const pm_newline_list_t *newline_list; + const pm_line_offset_list_t *line_offsets; /** The start of the source being parsed. */ const uint8_t *start; @@ -19,7 +33,7 @@ typedef struct { const char *encoding_name; } pm_static_literals_metadata_t; -static inline uint32_t +static PRISM_INLINE uint32_t murmur_scramble(uint32_t value) { value *= 0xcc9e2d51; value = (value << 15) | (value >> 17); @@ -95,7 +109,7 @@ node_hash(const pm_static_literals_metadata_t *metadata, const pm_node_t *node) } 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 pm_line_column_t line_column = pm_line_offset_list_line_column(metadata->line_offsets, node->location.start, metadata->start_line); const int32_t *value = &line_column.line; return murmur_hash((const uint8_t *) value, sizeof(int32_t)); } @@ -183,7 +197,7 @@ pm_node_hash_insert(pm_node_hash_t *hash, const pm_static_literals_metadata_t *m } // Finally, free the old node list and update the hash. - xfree(hash->nodes); + xfree_sized(hash->nodes, hash->capacity * sizeof(pm_node_t *)); hash->nodes = new_nodes; hash->capacity = new_capacity; } @@ -221,7 +235,7 @@ pm_node_hash_insert(pm_node_hash_t *hash, const pm_static_literals_metadata_t *m */ static void pm_node_hash_free(pm_node_hash_t *hash) { - if (hash->capacity > 0) xfree(hash->nodes); + if (hash->capacity > 0) xfree_sized(hash->nodes, hash->capacity * sizeof(pm_node_t *)); } /** @@ -243,7 +257,7 @@ pm_int64_value(const pm_static_literals_metadata_t *metadata, const pm_node_t *n 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; + return (int64_t) pm_line_offset_list_line_column(metadata->line_offsets, node->location.start, metadata->start_line).line; default: assert(false && "unreachable"); return 0; @@ -271,7 +285,7 @@ pm_compare_integer_nodes(const pm_static_literals_metadata_t *metadata, const pm * 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) { +pm_compare_float_nodes(PRISM_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); @@ -330,7 +344,7 @@ pm_string_value(const pm_node_t *node) { * 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) { +pm_compare_string_nodes(PRISM_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); @@ -340,7 +354,7 @@ pm_compare_string_nodes(PRISM_ATTRIBUTE_UNUSED const pm_static_literals_metadata * 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) { +pm_compare_regular_expression_nodes(PRISM_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; @@ -356,14 +370,14 @@ pm_compare_regular_expression_nodes(PRISM_ATTRIBUTE_UNUSED const pm_static_liter * Add a node to the set of static literals. */ pm_node_t * -pm_static_literals_add(const pm_newline_list_t *newline_list, const uint8_t *start, int32_t start_line, pm_static_literals_t *literals, pm_node_t *node, bool replace) { +pm_static_literals_add(const pm_line_offset_list_t *line_offsets, const uint8_t *start, 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, + .line_offsets = line_offsets, .start = start, .start_line = start_line, .encoding_name = NULL @@ -376,7 +390,7 @@ pm_static_literals_add(const pm_newline_list_t *newline_list, const uint8_t *sta return pm_node_hash_insert( &literals->float_nodes, &(pm_static_literals_metadata_t) { - .newline_list = newline_list, + .line_offsets = line_offsets, .start = start, .start_line = start_line, .encoding_name = NULL @@ -390,7 +404,7 @@ pm_static_literals_add(const pm_newline_list_t *newline_list, const uint8_t *sta return pm_node_hash_insert( &literals->number_nodes, &(pm_static_literals_metadata_t) { - .newline_list = newline_list, + .line_offsets = line_offsets, .start = start, .start_line = start_line, .encoding_name = NULL @@ -404,7 +418,7 @@ pm_static_literals_add(const pm_newline_list_t *newline_list, const uint8_t *sta return pm_node_hash_insert( &literals->string_nodes, &(pm_static_literals_metadata_t) { - .newline_list = newline_list, + .line_offsets = line_offsets, .start = start, .start_line = start_line, .encoding_name = NULL @@ -417,7 +431,7 @@ pm_static_literals_add(const pm_newline_list_t *newline_list, const uint8_t *sta return pm_node_hash_insert( &literals->regexp_nodes, &(pm_static_literals_metadata_t) { - .newline_list = newline_list, + .line_offsets = line_offsets, .start = start, .start_line = start_line, .encoding_name = NULL @@ -430,7 +444,7 @@ pm_static_literals_add(const pm_newline_list_t *newline_list, const uint8_t *sta return pm_node_hash_insert( &literals->symbol_nodes, &(pm_static_literals_metadata_t) { - .newline_list = newline_list, + .line_offsets = line_offsets, .start = start, .start_line = start_line, .encoding_name = NULL @@ -501,7 +515,7 @@ pm_static_literal_positive_p(const pm_node_t *node) { /** * Create a string-based representation of the given static literal. */ -static inline void +static PRISM_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: @@ -585,7 +599,7 @@ pm_static_literal_inspect_node(pm_buffer_t *buffer, const pm_static_literals_met 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); + pm_buffer_append_format(buffer, "%d", pm_line_offset_list_line_column(metadata->line_offsets, node->location.start, metadata->start_line).line); break; case PM_STRING_NODE: { const pm_string_t *unescaped = &((const pm_string_node_t *) node)->unescaped; @@ -613,11 +627,11 @@ pm_static_literal_inspect_node(pm_buffer_t *buffer, const pm_static_literals_met * 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, const uint8_t *start, int32_t start_line, const char *encoding_name, const pm_node_t *node) { +pm_static_literal_inspect(pm_buffer_t *buffer, const pm_line_offset_list_t *line_offsets, const uint8_t *start, 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, + .line_offsets = line_offsets, .start = start, .start_line = start_line, .encoding_name = encoding_name diff --git a/src/string_query.c b/src/string_query.c new file mode 100644 index 0000000000..ccedaf9c00 --- /dev/null +++ b/src/string_query.c @@ -0,0 +1,166 @@ +#include "prism/string_query.h" + +#include "prism/internal/char.h" +#include "prism/internal/encoding.h" + +#include +#include + +/** 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. + */ +static 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. + */ +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. + */ +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. + */ +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/src/stringy.c b/src/stringy.c new file mode 100644 index 0000000000..d6f4c4a777 --- /dev/null +++ b/src/stringy.c @@ -0,0 +1,91 @@ +#include "prism/internal/stringy.h" + +#include "prism/internal/allocator.h" + +#include +#include +#include + +/** + * 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 + }; +} + +/** + * 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. + */ +size_t +pm_string_length(const pm_string_t *string) { + return string->length; +} + +/** + * Returns the start pointer associated with the string. + */ +const uint8_t * +pm_string_source(const pm_string_t *string) { + return string->source; +} + +/** + * Free the associated memory of the given string. + */ +void +pm_string_cleanup(pm_string_t *string) { + if (string->type == PM_STRING_OWNED) { + xfree_sized((void *) string->source, string->length); + } +} diff --git a/src/util/pm_strncasecmp.c b/src/strncasecmp.c similarity index 88% rename from src/util/pm_strncasecmp.c rename to src/strncasecmp.c index 3f58421554..a373cad6d7 100644 --- a/src/util/pm_strncasecmp.c +++ b/src/strncasecmp.c @@ -1,11 +1,12 @@ -#include "prism/util/pm_strncasecmp.h" +#include "prism/internal/strncasecmp.h" + +#include "prism/compiler/inline.h" /** * A locale-insensitive version of `tolower(3)` */ -static inline int -pm_tolower(int c) -{ +static PRISM_INLINE int +pm_tolower(int c) { if ('A' <= c && c <= 'Z') { return c | 0x20; } diff --git a/src/strpbrk.c b/src/strpbrk.c new file mode 100644 index 0000000000..383707eb72 --- /dev/null +++ b/src/strpbrk.c @@ -0,0 +1,439 @@ +#include "prism/internal/strpbrk.h" + +#include "prism/compiler/accel.h" +#include "prism/compiler/inline.h" +#include "prism/compiler/unused.h" + +#include "prism/internal/bit.h" +#include "prism/internal/diagnostic.h" +#include "prism/internal/encoding.h" +#include "prism/internal/parser.h" + +#include +#include +#include + +/** + * Add an invalid multibyte character error to the parser. + */ +static PRISM_INLINE void +pm_strpbrk_invalid_multibyte_character(pm_parser_t *parser, uint32_t start, uint32_t length) { + pm_diagnostic_list_append_format(&parser->metadata_arena, &parser->error_list, start, length, PM_ERR_INVALID_MULTIBYTE_CHARACTER, parser->start[start]); +} + +/** + * Set the explicit encoding for the parser to the current encoding. + */ +static PRISM_INLINE void +pm_strpbrk_explicit_encoding_set(pm_parser_t *parser, uint32_t start, uint32_t length) { + 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->metadata_arena, &parser->error_list, start, length, PM_ERR_MIXED_ENCODING, parser->encoding->name); + } else { + // Should not be anything else. + assert(false && "unreachable"); + } + } + + parser->explicit_encoding = parser->encoding; +} + +/** + * Scan forward through ASCII bytes looking for a byte that is in the given + * charset. Returns true if a match was found, storing its offset in *index. + * Returns false if no match was found, storing the number of ASCII bytes + * consumed in *index (so the caller can skip past them). + * + * All charset characters must be ASCII (< 0x80). The scanner stops at non-ASCII + * bytes, returning control to the caller's encoding-aware loop. + * + * Up to three optimized implementations are selected at compile time, with a + * no-op fallback for unsupported platforms: + * 1. NEON — processes 16 bytes per iteration on aarch64. + * 2. SSSE3 — processes 16 bytes per iteration on x86-64. + * 3. SWAR — little-endian fallback, processes 8 bytes per iteration. + */ + +#if defined(PRISM_HAS_NEON) || defined(PRISM_HAS_SSSE3) || defined(PRISM_HAS_SWAR) + +/** + * Update the cached strpbrk lookup tables if the charset has changed. The + * parser caches the last charset's precomputed tables so that repeated calls + * with the same breakpoints (the common case during string/regex/list lexing) + * skip table construction entirely. + * + * Builds three structures: + * - low_lut/high_lut: nibble-based lookup tables for SIMD matching (NEON/SSSE3) + * - table: 256-bit bitmap for scalar fallback matching (all platforms) + */ +static PRISM_INLINE void +pm_strpbrk_cache_update(pm_parser_t *parser, const uint8_t *charset) { + // The cache key is the full charset buffer (PM_STRPBRK_CACHE_SIZE bytes). + // Since it is always NUL-padded, a fixed-size comparison covers both + // content and length. + if (memcmp(parser->strpbrk_cache.charset, charset, sizeof(parser->strpbrk_cache.charset)) == 0) return; + + memset(parser->strpbrk_cache.low_lut, 0, sizeof(parser->strpbrk_cache.low_lut)); + memset(parser->strpbrk_cache.high_lut, 0, sizeof(parser->strpbrk_cache.high_lut)); + memset(parser->strpbrk_cache.table, 0, sizeof(parser->strpbrk_cache.table)); + + // Always include NUL in the tables. The slow path uses strchr, which + // always matches NUL (it finds the C string terminator), so NUL is + // effectively always a breakpoint. Replicating that here lets the fast + // scanner handle NUL at full speed instead of bailing to the slow path. + parser->strpbrk_cache.low_lut[0x00] |= (uint8_t) (1 << 0); + parser->strpbrk_cache.high_lut[0x00] = (uint8_t) (1 << 0); + parser->strpbrk_cache.table[0] |= (uint64_t) 1; + + size_t charset_len = 0; + for (const uint8_t *c = charset; *c != '\0'; c++) { + parser->strpbrk_cache.low_lut[*c & 0x0F] |= (uint8_t) (1 << (*c >> 4)); + parser->strpbrk_cache.high_lut[*c >> 4] = (uint8_t) (1 << (*c >> 4)); + parser->strpbrk_cache.table[*c >> 6] |= (uint64_t) 1 << (*c & 0x3F); + charset_len++; + } + + // Store the new charset key, NUL-padded to the full buffer size. + memcpy(parser->strpbrk_cache.charset, charset, charset_len + 1); + memset(parser->strpbrk_cache.charset + charset_len + 1, 0, sizeof(parser->strpbrk_cache.charset) - charset_len - 1); +} + +#endif + +#if defined(PRISM_HAS_NEON) +#include + +static PRISM_INLINE bool +scan_strpbrk_ascii(pm_parser_t *parser, const uint8_t *source, size_t maximum, const uint8_t *charset, size_t *index) { + pm_strpbrk_cache_update(parser, charset); + + uint8x16_t low_lut = vld1q_u8(parser->strpbrk_cache.low_lut); + uint8x16_t high_lut = vld1q_u8(parser->strpbrk_cache.high_lut); + uint8x16_t mask_0f = vdupq_n_u8(0x0F); + uint8x16_t mask_80 = vdupq_n_u8(0x80); + + size_t idx = 0; + + while (idx + 16 <= maximum) { + uint8x16_t v = vld1q_u8(source + idx); + + // If any byte has the high bit set, we have non-ASCII data. + // Return to let the caller's encoding-aware loop handle it. + if (vmaxvq_u8(vandq_u8(v, mask_80)) != 0) break; + + uint8x16_t lo_class = vqtbl1q_u8(low_lut, vandq_u8(v, mask_0f)); + uint8x16_t hi_class = vqtbl1q_u8(high_lut, vshrq_n_u8(v, 4)); + uint8x16_t matched = vtstq_u8(lo_class, hi_class); + + if (vmaxvq_u8(matched) == 0) { + idx += 16; + continue; + } + + // Find the position of the first matching byte. + uint64_t lo64 = vgetq_lane_u64(vreinterpretq_u64_u8(matched), 0); + if (lo64 != 0) { + *index = idx + pm_ctzll(lo64) / 8; + return true; + } + uint64_t hi64 = vgetq_lane_u64(vreinterpretq_u64_u8(matched), 1); + *index = idx + 8 + pm_ctzll(hi64) / 8; + return true; + } + + // Scalar tail for remaining < 16 ASCII bytes. + while (idx < maximum && source[idx] < 0x80) { + uint8_t byte = source[idx]; + if (parser->strpbrk_cache.table[byte >> 6] & ((uint64_t) 1 << (byte & 0x3F))) { + *index = idx; + return true; + } + idx++; + } + + *index = idx; + return false; +} + +#elif defined(PRISM_HAS_SSSE3) +#include + +static PRISM_INLINE bool +scan_strpbrk_ascii(pm_parser_t *parser, const uint8_t *source, size_t maximum, const uint8_t *charset, size_t *index) { + pm_strpbrk_cache_update(parser, charset); + + __m128i low_lut = _mm_loadu_si128((const __m128i *) parser->strpbrk_cache.low_lut); + __m128i high_lut = _mm_loadu_si128((const __m128i *) parser->strpbrk_cache.high_lut); + __m128i mask_0f = _mm_set1_epi8(0x0F); + + size_t idx = 0; + + while (idx + 16 <= maximum) { + __m128i v = _mm_loadu_si128((const __m128i *) (source + idx)); + + // If any byte has the high bit set, stop. + if (_mm_movemask_epi8(v) != 0) break; + + // Nibble-based classification using pshufb (SSSE3), same as NEON + // vqtbl1q_u8. A byte matches iff (low_lut[lo_nib] & high_lut[hi_nib]) != 0. + __m128i lo_class = _mm_shuffle_epi8(low_lut, _mm_and_si128(v, mask_0f)); + __m128i hi_class = _mm_shuffle_epi8(high_lut, _mm_and_si128(_mm_srli_epi16(v, 4), mask_0f)); + __m128i matched = _mm_and_si128(lo_class, hi_class); + + // Check if any byte matched. + int mask = _mm_movemask_epi8(_mm_cmpeq_epi8(matched, _mm_setzero_si128())); + + if (mask == 0xFFFF) { + // All bytes were zero — no match in this chunk. + idx += 16; + continue; + } + + // Find the first matching byte (first non-zero in matched). + *index = idx + pm_ctzll((uint64_t) (~mask & 0xFFFF)); + return true; + } + + // Scalar tail. + while (idx < maximum && source[idx] < 0x80) { + uint8_t byte = source[idx]; + if (parser->strpbrk_cache.table[byte >> 6] & ((uint64_t) 1 << (byte & 0x3F))) { + *index = idx; + return true; + } + idx++; + } + + *index = idx; + return false; +} + +#elif defined(PRISM_HAS_SWAR) + +static PRISM_INLINE bool +scan_strpbrk_ascii(pm_parser_t *parser, const uint8_t *source, size_t maximum, const uint8_t *charset, size_t *index) { + pm_strpbrk_cache_update(parser, charset); + + static const uint64_t highs = 0x8080808080808080ULL; + size_t idx = 0; + + while (idx + 8 <= maximum) { + uint64_t word; + memcpy(&word, source + idx, 8); + + // Bail on any non-ASCII byte. + if (word & highs) break; + + // Check each byte against the charset table. + for (size_t j = 0; j < 8; j++) { + uint8_t byte = source[idx + j]; + if (parser->strpbrk_cache.table[byte >> 6] & ((uint64_t) 1 << (byte & 0x3F))) { + *index = idx + j; + return true; + } + } + + idx += 8; + } + + // Scalar tail. + while (idx < maximum && source[idx] < 0x80) { + uint8_t byte = source[idx]; + if (parser->strpbrk_cache.table[byte >> 6] & ((uint64_t) 1 << (byte & 0x3F))) { + *index = idx; + return true; + } + idx++; + } + + *index = idx; + return false; +} + +#else + +static PRISM_INLINE bool +scan_strpbrk_ascii(PRISM_UNUSED pm_parser_t *parser, PRISM_UNUSED const uint8_t *source, PRISM_UNUSED size_t maximum, PRISM_UNUSED const uint8_t *charset, size_t *index) { + *index = 0; + return false; +} + +#endif + +/** + * This is the default path. + */ +static PRISM_INLINE const uint8_t * +pm_strpbrk_utf8(pm_parser_t *parser, const uint8_t *source, const uint8_t *charset, size_t index, size_t maximum, bool validate) { + 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, (uint32_t) ((source + start) - parser->start), (uint32_t) (index - start)); + } + } + } + + return NULL; +} + +/** + * This is the path when the encoding is ASCII-8BIT. + */ +static PRISM_INLINE const uint8_t * +pm_strpbrk_ascii_8bit(pm_parser_t *parser, const uint8_t *source, const uint8_t *charset, size_t index, size_t maximum, bool validate) { + 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, (uint32_t) (source - parser->start), 1); + index++; + } + + return NULL; +} + +/** + * This is the slow path that does care about the encoding. + */ +static PRISM_INLINE const uint8_t * +pm_strpbrk_multi_byte(pm_parser_t *parser, const uint8_t *source, const uint8_t *charset, size_t index, size_t maximum, bool validate) { + 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, (uint32_t) (source - parser->start), (uint32_t) 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, (uint32_t) ((source + start) - parser->start), (uint32_t) (index - start)); + } + } + } + + 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 PRISM_INLINE const uint8_t * +pm_strpbrk_single_byte(pm_parser_t *parser, const uint8_t *source, const uint8_t *charset, size_t index, size_t maximum, bool validate) { + 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, (uint32_t) (source - parser->start), (uint32_t) 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, (uint32_t) ((source + start) - parser->start), (uint32_t) (index - start)); + } + } + } + + 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; + + size_t maximum = (size_t) length; + size_t index = 0; + if (scan_strpbrk_ascii(parser, source, maximum, charset, &index)) return source + index; + + if (!parser->encoding_changed) { + return pm_strpbrk_utf8(parser, source, charset, index, maximum, validate); + } else if (parser->encoding == PM_ENCODING_ASCII_8BIT_ENTRY) { + return pm_strpbrk_ascii_8bit(parser, source, charset, index, maximum, validate); + } else if (parser->encoding->multibyte) { + return pm_strpbrk_multi_byte(parser, source, charset, index, maximum, validate); + } else { + return pm_strpbrk_single_byte(parser, source, charset, index, maximum, validate); + } +} diff --git a/src/util/pm_list.c b/src/util/pm_list.c deleted file mode 100644 index ad2294cd60..0000000000 --- a/src/util/pm_list.c +++ /dev/null @@ -1,49 +0,0 @@ -#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/src/util/pm_string.c b/src/util/pm_string.c deleted file mode 100644 index a7493c468b..0000000000 --- a/src/util/pm_string.c +++ /dev/null @@ -1,381 +0,0 @@ -#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/src/util/pm_strpbrk.c b/src/util/pm_strpbrk.c deleted file mode 100644 index 60c67b2983..0000000000 --- a/src/util/pm_strpbrk.c +++ /dev/null @@ -1,206 +0,0 @@ -#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, uint32_t start, uint32_t length) { - pm_diagnostic_list_append_format(&parser->error_list, start, length, PM_ERR_INVALID_MULTIBYTE_CHARACTER, parser->start[start]); -} - -/** - * Set the explicit encoding for the parser to the current encoding. - */ -static inline void -pm_strpbrk_explicit_encoding_set(pm_parser_t *parser, uint32_t start, uint32_t length) { - 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, start, length, 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, (uint32_t) ((source + start) - parser->start), (uint32_t) (index - start)); - } - } - } - - 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, (uint32_t) (source - parser->start), 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, (uint32_t) (source - parser->start), (uint32_t) 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, (uint32_t) ((source + start) - parser->start), (uint32_t) (index - start)); - } - } - } - - 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, (uint32_t) (source - parser->start), (uint32_t) 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, (uint32_t) ((source + start) - parser->start), (uint32_t) (index - start)); - } - } - } - - 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/templates/ext/prism/api_node.c.erb b/templates/ext/prism/api_node.c.erb index e9c3742085..71f7fe273e 100644 --- a/templates/ext/prism/api_node.c.erb +++ b/templates/ext/prism/api_node.c.erb @@ -1,5 +1,9 @@ #line <%= __LINE__ + 1 %> "prism/templates/ext/prism/<%= File.basename(__FILE__) %>" #include "prism/extension.h" +#include "prism/internal/allocator.h" +#include "prism/internal/arena.h" + +#include extern VALUE rb_cPrism; extern VALUE rb_cPrismNode; @@ -24,8 +28,8 @@ pm_location_new(const uint32_t start, const uint32_t length, VALUE source, bool 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((uint32_t) (token->start - parser->start), (uint32_t) (token->end - token->start), source, freeze); + ID type = rb_intern(pm_token_type(token->type)); + VALUE location = pm_location_new((uint32_t) (token->start - pm_parser_start(parser)), (uint32_t) (token->end - token->start), source, freeze); VALUE slice = rb_enc_str_new((const char *) token->start, token->end - token->start, encoding); if (freeze) rb_obj_freeze(slice); @@ -74,19 +78,18 @@ pm_integer_new(const pm_integer_t *integer) { // 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); + const uint8_t *start = pm_parser_start(parser); + VALUE source_string = rb_enc_str_new((const char *) start, pm_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])); - } + const pm_line_offset_list_t *line_offsets = pm_parser_line_offsets(parser); + VALUE offsets = rb_str_new((const char *) line_offsets->offsets, line_offsets->size * sizeof(uint32_t)); 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); + VALUE source = rb_funcall(rb_cPrismSource, rb_intern("for"), 3, source_string, LONG2NUM(pm_parser_start_line(parser)), offsets); if (freeze) rb_obj_freeze(source); return source; @@ -99,8 +102,8 @@ typedef struct pm_node_stack_node { } 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)); +pm_node_stack_push(pm_arena_t *arena, pm_node_stack_node_t **stack, const pm_node_t *visit) { + pm_node_stack_node_t *node = (pm_node_stack_node_t *) pm_arena_alloc(arena, sizeof(pm_node_stack_node_t), PRISM_ALIGNOF(pm_node_stack_node_t)); node->prev = *stack; node->visit = visit; node->visited = false; @@ -113,32 +116,40 @@ pm_node_stack_pop(pm_node_stack_node_t **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; +typedef struct { + VALUE constants; + rb_encoding *encoding; +} pm_ast_constants_each_data_t; - VALUE string = rb_enc_str_new((const char *) constant->start, constant->length, encoding); - VALUE value = rb_protect(rb_str_intern, string, &state); +static void +pm_ast_constants_each(const pm_constant_t *constant, void *data) { + pm_ast_constants_each_data_t *constants_data = (pm_ast_constants_each_data_t *) data; + int state = 0; - if (state != 0) { - value = ID2SYM(rb_intern_const("?")); - rb_set_errinfo(Qnil); - } + VALUE string = rb_enc_str_new((const char *) pm_constant_start(constant), pm_constant_length(constant), constants_data->encoding); + VALUE value = rb_protect(rb_str_intern, string, &state); - rb_ary_push(constants, value); + if (state != 0) { + value = ID2SYM(rb_intern_const("?")); + rb_set_errinfo(Qnil); } + rb_ary_push(constants_data->constants, value); +} + +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(pm_parser_constants_size(parser)); + pm_ast_constants_each_data_t constants_data = { .constants = constants, .encoding = encoding }; + pm_parser_constants_each(parser, pm_ast_constants_each, &constants_data); + + pm_arena_t *node_arena = pm_arena_new(); pm_node_stack_node_t *node_stack = NULL; - pm_node_stack_push(&node_stack, node); + pm_node_stack_push(node_arena, &node_stack, node); VALUE value_stack = rb_ary_new(); while (node_stack != NULL) { @@ -161,10 +172,10 @@ pm_ast_new(const pm_parser_t *parser, const pm_node_t *node, rb_encoding *encodi <%- node.fields.each do |field| -%> <%- case field -%> <%- when Prism::Template::NodeField, Prism::Template::OptionalNodeField -%> - pm_node_stack_push(&node_stack, (pm_node_t *) cast-><%= field.name %>); + pm_node_stack_push(node_arena, &node_stack, (pm_node_t *) cast-><%= field.name %>); <%- when Prism::Template::NodeListField -%> for (size_t index = 0; index < cast-><%= field.name %>.size; index++) { - pm_node_stack_push(&node_stack, (pm_node_t *) cast-><%= field.name %>.nodes[index]); + pm_node_stack_push(node_arena, &node_stack, (pm_node_t *) cast-><%= field.name %>.nodes[index]); } <%- end -%> <%- end -%> @@ -266,6 +277,7 @@ pm_ast_new(const pm_parser_t *parser, const pm_node_t *node, rb_encoding *encodi } } + pm_arena_free(node_arena); return rb_ary_pop(value_stack); } diff --git a/templates/include/prism/ast.h.erb b/templates/include/prism/ast.h.erb index 0612341772..3b3be25e76 100644 --- a/templates/include/prism/ast.h.erb +++ b/templates/include/prism/ast.h.erb @@ -8,12 +8,14 @@ #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 "prism/compiler/align.h" +#include "prism/compiler/exported.h" + +#include "prism/arena.h" +#include "prism/constant_pool.h" +#include "prism/integer.h" +#include "prism/stringy.h" -#include #include #include @@ -45,6 +47,15 @@ typedef struct { const uint8_t *end; } pm_token_t; +/** + * Returns a string representation of the given token type. + * + * @param token_type The type of the token to get the string representation of. + * @returns A string representation of the given token type. This is meant for + * debugging purposes and is not guaranteed to be stable across versions. + */ +PRISM_EXPORTED_FUNCTION const char * pm_token_type(pm_token_type_t token_type); + /** * This struct represents a slice in the source code, defined by an offset and * a length. Note that we have confirmation that we can represent all locations @@ -238,6 +249,23 @@ typedef enum pm_<%= flag.human %> { PM_<%= flag.human.upcase %>_LAST, } pm_<%= flag.human %>_t; <%- end -%> +<%- nodes.each do |node| -%> + +<%- params = node.fields.map(&:c_param) -%> +/** + * Allocate and initialize a new <%= node.name %> node. + * + * @param arena The arena to allocate from. + * @param node_id The unique identifier for this node. + * @param flags The flags for this node. + * @param location The location of this node in the source. +<%- node.fields.each do |field| -%> + * @param <%= field.name %> <%= field.comment ? Prism::Template::Doxygen.verbatim(field.comment.lines.first.strip) : "The #{field.name} field." %> +<%- end -%> + * @returns The newly allocated and initialized node. + */ +PRISM_EXPORTED_FUNCTION pm_<%= node.human %>_t * pm_<%= node.human %>_new(pm_arena_t *arena, uint32_t node_id, pm_node_flags_t flags, pm_location_t location<%= params.empty? ? "" : ", #{params.join(", ")}" %>); +<%- end -%> /** * When we're serializing to Java, we want to skip serializing the location diff --git a/templates/include/prism/diagnostic.h.erb b/templates/include/prism/diagnostic.h.erb deleted file mode 100644 index c1864e6021..0000000000 --- a/templates/include/prism/diagnostic.h.erb +++ /dev/null @@ -1,130 +0,0 @@ -/** - * @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. - <%- errors.each do |error| -%> - PM_ERR_<%= error.name %>, - <%- end -%> - - // These are the warning diagnostics. - <%- warnings.each do |warning| -%> - PM_WARN_<%= warning.name %>, - <%- end -%> -} pm_diagnostic_id_t; - -/** - * This struct represents a diagnostic generated during parsing. - * - * @extends pm_list_node_t - */ -typedef struct { - /** The embedded base node. */ - pm_list_node_t node; - - /** The location of the diagnostic in the source. */ - pm_location_t location; - - /** The ID of the diagnostic. */ - pm_diagnostic_id_t diag_id; - - /** The message associated with the diagnostic. */ - const char *message; - - /** - * Whether or not the memory related to the message of this diagnostic is - * owned by this diagnostic. If it is, it needs to be freed when the - * diagnostic is freed. - */ - bool owned; - - /** - * The level of the diagnostic, see `pm_error_level_t` and - * `pm_warning_level_t` for possible values. - */ - uint8_t level; -} pm_diagnostic_t; - -/** - * The levels of errors generated during parsing. - */ -typedef enum { - /** For errors that should raise a syntax error. */ - PM_ERROR_LEVEL_SYNTAX = 0, - - /** For errors that should raise an argument error. */ - PM_ERROR_LEVEL_ARGUMENT = 1, - - /** For errors that should raise a load error. */ - PM_ERROR_LEVEL_LOAD = 2 -} pm_error_level_t; - -/** - * The levels of warnings generated during parsing. - */ -typedef enum { - /** For warnings which should be emitted if $VERBOSE != nil. */ - PM_WARNING_LEVEL_DEFAULT = 0, - - /** For warnings which should be emitted if $VERBOSE == true. */ - PM_WARNING_LEVEL_VERBOSE = 1 -} pm_warning_level_t; - -/** - * Get the human-readable name of the given diagnostic ID. - * - * @param diag_id The diagnostic ID. - * @return The human-readable name of the diagnostic ID. - */ -const char * pm_diagnostic_id_human(pm_diagnostic_id_t diag_id); - -/** - * Append a diagnostic to the given list of diagnostics that is using shared - * memory for its message. - * - * @param list The list to append to. - * @param start The source offset of the start of the diagnostic. - * @param length The length 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, uint32_t start, uint32_t length, 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 source offset of the start of the diagnostic. - * @param length The length 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, uint32_t start, uint32_t length, 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/templates/include/prism/internal/diagnostic.h.erb b/templates/include/prism/internal/diagnostic.h.erb new file mode 100644 index 0000000000..ee44ff5382 --- /dev/null +++ b/templates/include/prism/internal/diagnostic.h.erb @@ -0,0 +1,60 @@ +#ifndef PRISM_INTERNAL_DIAGNOSTIC_H +#define PRISM_INTERNAL_DIAGNOSTIC_H + +#include "prism/internal/list.h" + +#include "prism/arena.h" +#include "prism/diagnostic.h" + +/* + * The diagnostic IDs of all of the diagnostics, used to communicate the types + * of errors between the parser and the user. + */ +typedef enum { + /* These are the error diagnostics. */ + <%- errors.each do |error| -%> + PM_ERR_<%= error.name %>, + <%- end -%> + + /* These are the warning diagnostics. */ + <%- warnings.each do |warning| -%> + PM_WARN_<%= warning.name %>, + <%- end -%> +} pm_diagnostic_id_t; + +/* + * This struct represents a diagnostic generated during parsing. + */ +struct pm_diagnostic_t { + /* 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; + + /* + * The level of the diagnostic, see `pm_error_level_t` and + * `pm_warning_level_t` for possible values. + */ + uint8_t level; +}; + +/* + * Append a diagnostic to the given list of diagnostics that is using shared + * memory for its message. + */ +void pm_diagnostic_list_append(pm_arena_t *arena, pm_list_t *list, uint32_t start, uint32_t length, pm_diagnostic_id_t diag_id); + +/* + * Append a diagnostic to the given list of diagnostics that is using a format + * string for its message. + */ +void pm_diagnostic_list_append_format(pm_arena_t *arena, pm_list_t *list, uint32_t start, uint32_t length, pm_diagnostic_id_t diag_id, ...); + +#endif diff --git a/templates/java/org/prism/AbstractNodeVisitor.java.erb b/templates/java/org/ruby_lang/prism/AbstractNodeVisitor.java.erb similarity index 94% rename from templates/java/org/prism/AbstractNodeVisitor.java.erb rename to templates/java/org/ruby_lang/prism/AbstractNodeVisitor.java.erb index a66daedf63..58eabebae8 100644 --- a/templates/java/org/prism/AbstractNodeVisitor.java.erb +++ b/templates/java/org/ruby_lang/prism/AbstractNodeVisitor.java.erb @@ -1,4 +1,4 @@ -package org.prism; +package org.ruby_lang.prism; // GENERATED BY <%= File.basename(__FILE__) %> // @formatter:off diff --git a/templates/java/org/prism/Loader.java.erb b/templates/java/org/ruby_lang/prism/Loader.java.erb similarity index 77% rename from templates/java/org/prism/Loader.java.erb rename to templates/java/org/ruby_lang/prism/Loader.java.erb index aa8e1e30c9..6d1a77fd10 100644 --- a/templates/java/org/prism/Loader.java.erb +++ b/templates/java/org/ruby_lang/prism/Loader.java.erb @@ -1,7 +1,5 @@ -<%- string_type = Prism::Template::JAVA_STRING_TYPE -%> -package org.prism; - -import org.prism.Nodes; +<%- id_type = Prism::Template::JAVA_IDENTIFIER_TYPE -%> +package org.ruby_lang.prism; import java.lang.Short; import java.math.BigInteger; @@ -15,45 +13,39 @@ import java.util.Locale; // @formatter:off public class Loader { - public static ParseResult load(byte[] serialized, byte[] sourceBytes) { - return new Loader(serialized, sourceBytes).load(); + public static ParseResult load(byte[] serialized) { + return new Loader(serialized).load(); } // Overridable methods - public Charset getEncodingCharset(String encodingName) { - encodingName = encodingName.toLowerCase(Locale.ROOT); - if (encodingName.equals("ascii-8bit")) { - return StandardCharsets.US_ASCII; - } - return Charset.forName(encodingName); - } - - public <%= string_type %> bytesToName(byte[] bytes) { - <%- if string_type == "String" -%> - return new String(bytes, encodingCharset).intern(); + public <%= id_type %> bytesToName(byte[] bytes) { + <%- if id_type == "byte[]" -%> + return bytes; <%- else -%> - return null; // Must be implemented by subclassing Loader + throw new AbstractMethodError("Loader.bytesToName(<%= id_type %>) is not implemented"); <%- end -%> } private static final class ConstantPool { private final Loader loader; - private final byte[] source; private final int bufferOffset; - private final <%= string_type %>[] cache; + private final <%= id_type %>[] cache; - ConstantPool(Loader loader, byte[] source, int bufferOffset, int length) { + ConstantPool(Loader loader, int bufferOffset, int length) { this.loader = loader; - this.source = source; this.bufferOffset = bufferOffset; - cache = new <%= string_type %>[length]; + <%- if id_type == "String" -%> + cache = new <%= id_type %>[length]; + <%- else -%> + cache = new byte[length][]; + <%- end -%> } - <%= string_type %> get(ByteBuffer buffer, int oneBasedIndex) { + <%= id_type %> get(ByteBuffer buffer, int oneBasedIndex) { int index = oneBasedIndex - 1; - <%= string_type %> constant = cache[index]; + <%= id_type %> constant = cache[index]; if (constant == null) { int offset = bufferOffset + index * 8; @@ -61,15 +53,7 @@ public class Loader { int length = buffer.getInt(offset + 4); byte[] bytes = new byte[length]; - - if (Integer.compareUnsigned(start, 0x7FFFFFFF) <= 0) { - System.arraycopy(source, start, bytes, 0, length); - } else { - int position = buffer.position(); - buffer.position(start & 0x7FFFFFFF); - buffer.get(bytes, 0, length); - buffer.position(position); - } + buffer.get(start, bytes); constant = loader.bytesToName(bytes); cache[index] = constant; @@ -81,19 +65,17 @@ public class Loader { } private final ByteBuffer buffer; - private final Nodes.Source source; protected String encodingName; - <%- if string_type == "String" -%> - private Charset encodingCharset; - <%- end -%> private ConstantPool constantPool; + private Nodes.Source source = null; - protected Loader(byte[] serialized, byte[] sourceBytes) { + protected Loader(byte[] serialized) { this.buffer = ByteBuffer.wrap(serialized).order(ByteOrder.nativeOrder()); - this.source = new Nodes.Source(sourceBytes); } protected ParseResult load() { + this.source = new Nodes.Source(); + expect((byte) 'P', "incorrect prism header"); expect((byte) 'R', "incorrect prism header"); expect((byte) 'I', "incorrect prism header"); @@ -111,9 +93,6 @@ public class Loader { byte[] encodingNameBytes = new byte[encodingLength]; buffer.get(encodingNameBytes); this.encodingName = new String(encodingNameBytes, StandardCharsets.US_ASCII); - <%- if string_type == "String" -%> - this.encodingCharset = getEncodingCharset(this.encodingName); - <%- end -%> source.setStartLine(loadVarSInt()); source.setLineOffsets(loadLineOffsets()); @@ -122,10 +101,11 @@ public class Loader { Nodes.Location dataLocation = loadOptionalLocation(); ParseResult.Error[] errors = loadErrors(); ParseResult.Warning[] warnings = loadWarnings(); + boolean continuable = buffer.get() != 0; int constantPoolBufferOffset = buffer.getInt(); int constantPoolLength = loadVarUInt(); - this.constantPool = new ConstantPool(this, source.bytes, constantPoolBufferOffset, constantPoolLength); + this.constantPool = new ConstantPool(this, constantPoolBufferOffset, constantPoolLength); Nodes.Node node; if (errors.length == 0) { @@ -136,38 +116,22 @@ public class Loader { throw new Error("Expected to consume all bytes while deserializing but there were " + left + " bytes left"); } - boolean[] newlineMarked = new boolean[1 + source.getLineCount()]; - MarkNewlinesVisitor visitor = new MarkNewlinesVisitor(source, newlineMarked); + MarkNewlinesVisitor visitor = new MarkNewlinesVisitor(source); node.accept(visitor); } else { node = null; } - return new ParseResult(node, magicComments, dataLocation, errors, warnings, source); + return new ParseResult(node, magicComments, dataLocation, errors, warnings, continuable, source); } - private byte[] loadEmbeddedString() { + private byte[] loadString() { int length = loadVarUInt(); byte[] bytes = new byte[length]; buffer.get(bytes); return bytes; } - private byte[] loadString() { - switch (buffer.get()) { - case 1: - int start = loadVarUInt(); - int length = loadVarUInt(); - byte[] bytes = new byte[length]; - System.arraycopy(source.bytes, start, bytes, 0, length); - return bytes; - case 2: - return loadEmbeddedString(); - default: - throw new Error("Expected 0 or 1 but was " + buffer.get()); - } - } - private int[] loadLineOffsets() { int count = loadVarUInt(); int[] lineOffsets = new int[count]; @@ -199,7 +163,7 @@ public class Loader { // error messages only contain ASCII characters for (int i = 0; i < count; i++) { Nodes.ErrorType type = Nodes.ERROR_TYPES[loadVarUInt()]; - byte[] bytes = loadEmbeddedString(); + byte[] bytes = loadString(); String message = new String(bytes, StandardCharsets.US_ASCII); Nodes.Location location = loadLocation(); ParseResult.ErrorLevel level = ParseResult.ERROR_LEVELS[buffer.get()]; @@ -218,7 +182,7 @@ public class Loader { // warning messages only contain ASCII characters for (int i = 0; i < count; i++) { Nodes.WarningType type = Nodes.WARNING_TYPES[loadVarUInt() - <%= errors.length %>]; - byte[] bytes = loadEmbeddedString(); + byte[] bytes = loadString(); String message = new String(bytes, StandardCharsets.US_ASCII); Nodes.Location location = loadLocation(); ParseResult.WarningLevel level = ParseResult.WARNING_LEVELS[buffer.get()]; @@ -239,11 +203,11 @@ public class Loader { } } - private <%= string_type %> loadConstant() { + private <%= id_type %> loadConstant() { return constantPool.get(buffer, loadVarUInt()); } - private <%= string_type %> loadOptionalConstant() { + private <%= id_type %> loadOptionalConstant() { if (buffer.get(buffer.position()) != 0) { return loadConstant(); } else { @@ -252,12 +216,16 @@ public class Loader { } } - private <%= string_type %>[] loadConstants() { + private <%= id_type %>[] loadConstants() { int length = loadVarUInt(); if (length == 0) { - return Nodes.EMPTY_STRING_ARRAY; + return Nodes.EMPTY_IDENTIFIER_ARRAY; } - <%= string_type %>[] constants = new <%= string_type %>[length]; + <%- if id_type == "String" -%> + <%= id_type %>[] constants = new <%= id_type %>[length]; + <%- else -%> + <%= id_type %>[] constants = new byte[length][]; + <%- end -%> for (int i = 0; i < length; i++) { constants[i] = constantPool.get(buffer, loadVarUInt()); } @@ -356,6 +324,10 @@ public class Loader { return negative ? result.negate() : result; } + <%- + base_params = [*("nodeId" if Prism::Template::INCLUDE_NODE_ID), "startOffset", "length"] + base_params_sig = base_params.map { "int #{_1}" }.join(", ") + -%> private Nodes.Node loadNode() { int type = buffer.get() & 0xFF; <%- if Prism::Template::INCLUDE_NODE_ID -%> @@ -372,7 +344,7 @@ public class Loader { params = [] params << "nodeId" if Prism::Template::INCLUDE_NODE_ID params << "startOffset" << "length" - params << "buffer.getInt()" if node.needs_serialized_length? + params << "buffer.getInt()" << "null" if node.needs_serialized_length? params << "loadFlags()" if node.flags params.concat node.semantic_fields.map { |field| case field @@ -395,13 +367,50 @@ public class Loader { else raise end } + $DefNode_params = params if node.name == "DefNode" -%> + <%- if node.name == "DefNode" -%> + return loadDefNode(<%= base_params.join(", ") -%>); + <%- else -%> return new Nodes.<%= node.name %>(<%= params.join(", ") -%>); + <%- end -%> <%- end -%> default: throw new Error("Unknown node type: " + type); } } + + // Can be overridden to use createLazyDefNode instead + protected Nodes.DefNode loadDefNode(<%= base_params_sig -%>) { + return createDefNode(<%= base_params.join(", ") -%>); + } + + protected Nodes.DefNode createLazyDefNode(<%= base_params_sig -%>) { + int bufferPosition = buffer.position(); + int serializedLength = buffer.getInt(); + // Load everything except the body and locals, because the name, receiver, parameters are still needed for lazily defining the method + Nodes.DefNode lazyDefNode = new Nodes.DefNode(<%= base_params.join(", ") -%>, -bufferPosition, this, loadConstant(), loadOptionalNode(), (Nodes.ParametersNode) loadOptionalNode(), null, Nodes.EMPTY_IDENTIFIER_ARRAY); + buffer.position(bufferPosition + serializedLength); // skip past the serialized DefNode + return lazyDefNode; + } + + protected Nodes.DefNode createDefNode(<%= base_params_sig -%>) { + return new Nodes.DefNode(<%= $DefNode_params.join(", ") -%>); + } + + Nodes.DefNode createDefNodeFromSavedPosition(<%= base_params_sig -%>, int bufferPosition) { + Nodes.DefNode node; + // This method mutates the buffer position and may be called from different threads so we must synchronize + synchronized (this) { + buffer.position(bufferPosition); + node = createDefNode(<%= base_params.join(", ") -%>); + } + + MarkNewlinesVisitor visitor = new MarkNewlinesVisitor(source); + node.accept(visitor); + + return node; + } <%- array_types.uniq.each do |type| -%> private static final Nodes.<%= type %>[] EMPTY_<%= type %>_ARRAY = {}; diff --git a/templates/java/org/prism/Nodes.java.erb b/templates/java/org/ruby_lang/prism/Nodes.java.erb similarity index 87% rename from templates/java/org/prism/Nodes.java.erb rename to templates/java/org/ruby_lang/prism/Nodes.java.erb index 8b0a9ce20d..9ef03e99cd 100644 --- a/templates/java/org/prism/Nodes.java.erb +++ b/templates/java/org/ruby_lang/prism/Nodes.java.erb @@ -1,5 +1,5 @@ -<%- string_type = Prism::Template::JAVA_STRING_TYPE -%> -package org.prism; +<%- id_type = Prism::Template::JAVA_IDENTIFIER_TYPE -%> +package org.ruby_lang.prism; import java.lang.Override; import java.lang.String; @@ -16,7 +16,7 @@ import java.util.Arrays; // @formatter:off public abstract class Nodes { - public static final <%= string_type %>[] EMPTY_STRING_ARRAY = {}; + public static final <%= id_type %>[] EMPTY_IDENTIFIER_ARRAY = {}; @Target(ElementType.FIELD) @Retention(RetentionPolicy.SOURCE) @@ -47,12 +47,10 @@ public abstract class Nodes { } public static final class Source { - public final byte[] bytes; private int startLine = 1; private int[] lineOffsets = null; - Source(byte[] bytes) { - this.bytes = bytes; + Source() { } void setStartLine(int startLine) { @@ -70,7 +68,6 @@ public abstract class Nodes { // 0-based public int findLine(int byteOffset) { - if (byteOffset >= bytes.length) byteOffset = bytes.length - 1; assert byteOffset >= 0 : byteOffset; int index = Arrays.binarySearch(lineOffsets, byteOffset); int line; @@ -142,7 +139,23 @@ public abstract class Nodes { protected abstract String toString(String indent); } -<%# FLAGS -%> + + protected static String asString(Object value) { + return value.toString(); + } + + protected static String asString(byte[] value) { + StringBuilder buf = new StringBuilder(value.length); + for (byte b : value) { + if (b >= 0x20 && b <= 0x7e) { + buf.append((char) b); + } else { + buf.append(String.format("\\x%02x", Byte.toUnsignedInt(b))); + } + } + return buf.toString(); + } +<%-# FLAGS -%> <%- flags.each do |flag| -%> /** @@ -194,7 +207,7 @@ public abstract class Nodes { <%- end -%> } <%- end -%> -<%# NODES -%> +<%-# NODES -%> <%- nodes.each do |node| -%> /** @@ -207,6 +220,7 @@ public abstract class Nodes { public static final class <%= node.name -%> extends Node { <%- if node.needs_serialized_length? -%> public final int serializedLength; + public final Loader loader; <%- end -%> <%- if node.flags -%> public final short flags; @@ -234,7 +248,10 @@ public abstract class Nodes { params = [] params << "int nodeId" if Prism::Template::INCLUDE_NODE_ID params << "int startOffset" << "int length" - params << "int serializedLength" if node.needs_serialized_length? + if node.needs_serialized_length? + params << "int serializedLength" + params << "Loader loader" + end params << "short flags" if node.flags params.concat(node.semantic_fields.map { |field| "#{field.java_type} #{field.name}" }) -%> @@ -246,6 +263,7 @@ public abstract class Nodes { <%- end -%> <%- if node.needs_serialized_length? -%> this.serializedLength = serializedLength; + this.loader = loader; <%- end -%> <%- if node.flags -%> this.flags = flags; @@ -254,7 +272,22 @@ public abstract class Nodes { this.<%= field.name %> = <%= field.name %>; <%- end -%> } - <%# methods for flags -%> + <%-# extra methods for DefNode -%> + <%- if node.needs_serialized_length? -%> + + public boolean isLazy() { + return serializedLength < 0; + } + + public <%= node.name -%> getNonLazy() { + if (isLazy()) { + return loader.createDefNodeFromSavedPosition(<%= "nodeId, " if Prism::Template::INCLUDE_NODE_ID %>startOffset, length, -serializedLength); + } else { + return this; + } + } + <%- end -%> + <%-# methods for flags -%> <%- if (node_flags = node.flags) -%> <%- node_flags.values.each do |value| -%> @@ -263,7 +296,7 @@ public abstract class Nodes { } <%- end -%> <%- end -%> - <%# potential override of setNewLineFlag() -%> + <%-# potential override of setNewLineFlag() -%> <%- if node.newline == false -%> @Override @@ -356,18 +389,18 @@ public abstract class Nodes { builder.append(nextNextIndent).append(child.toString(nextNextIndent)); } <%- when Prism::Template::StringField -%> - builder.append('"' + new String(this.<%= field.name %>, StandardCharsets.UTF_8) + '"'); + builder.append('"' + asString(this.<%= field.name %>) + '"'); builder.append('\n'); <%- when Prism::Template::ConstantField -%> - builder.append('"').append(this.<%= field.name %>).append('"'); + builder.append('"').append(asString(this.<%= field.name %>)).append('"'); builder.append('\n'); <%- when Prism::Template::OptionalConstantField -%> - builder.append(this.<%= field.name %> == null ? "null" : "\"" + this.<%= field.name %> + "\""); + builder.append(this.<%= field.name %> == null ? "null" : "\"" + asString(this.<%= field.name %>) + "\""); builder.append('\n'); <%- when Prism::Template::ConstantListField -%> builder.append('\n'); - for (<%= string_type %> constant : this.<%= field.name %>) { - builder.append(nextNextIndent).append('"').append(constant).append('"').append('\n'); + for (<%= id_type %> constant : this.<%= field.name %>) { + builder.append(nextNextIndent).append('"').append(asString(constant)).append('"').append('\n'); } <%- when Prism::Template::Flags -%> builder.append(flags); diff --git a/templates/javascript/src/deserialize.js.erb b/templates/javascript/src/deserialize.js.erb index 7aebee750c..34ff1574da 100644 --- a/templates/javascript/src/deserialize.js.erb +++ b/templates/javascript/src/deserialize.js.erb @@ -29,8 +29,7 @@ class SerializationBuffer { ["ascii-8bit", "ascii"] ]); - constructor(source, array) { - this.source = source; + constructor(array) { this.array = array; this.index = 0; this.fileEncoding = "utf-8"; @@ -96,32 +95,15 @@ class SerializationBuffer { readStringField(flags) { if (flags === undefined) flags = 0; - const type = this.readByte(); - - switch (type) { - case 1: { - const startOffset = this.readVarInt(); - const length = this.readVarInt(); - return this.decodeString(this.source.slice(startOffset, startOffset + length), flags); - } - case 2: - return this.decodeString(this.readBytes(this.readVarInt()), flags); - default: - throw new Error(`Unknown serialized string type: ${type}`); - } + return this.decodeString(this.readBytes(this.readVarInt()), flags); } scanConstant(constantPoolOffset, constantIndex) { const offset = constantPoolOffset + constantIndex * 8; - let startOffset = this.scanUint32(offset); + const startOffset = this.scanUint32(offset); const length = this.scanUint32(offset + 4); - if (startOffset & (1 << 31)) { - startOffset &= (1 << 31) - 1; - return new TextDecoder().decode(this.array.slice(startOffset, startOffset + length)); - } else { - return new TextDecoder().decode(this.source.slice(startOffset, startOffset + length)); - } + return this.getDecoder(this.fileEncoding).decode(this.array.slice(startOffset, startOffset + length)); } readDouble() { @@ -293,13 +275,12 @@ const warningTypes = [ * Accept two Uint8Arrays, one for the source and one for the serialized format. * Return the AST corresponding to the serialized form. * - * @param {Uint8Array} source * @param {Uint8Array} array * @returns {ParseResult} * @throws {Error} */ -export function deserialize(source, array) { - const buffer = new SerializationBuffer(source, array); +export function deserialize(array) { + const buffer = new SerializationBuffer(array); if (buffer.readString(5) !== "PRISM") { throw new Error("Invalid serialization"); @@ -353,10 +334,12 @@ export function deserialize(source, array) { level: warningLevels[buffer.readByte()] })); + const continuable = buffer.readByte() !== 0; + const constantPoolOffset = buffer.readUint32(); const constants = Array.from({ length: buffer.readVarInt() }, () => null); - return new ParseResult(readRequiredNode(), comments, magicComments, dataLoc, errors, warnings); + return new ParseResult(readRequiredNode(), comments, magicComments, dataLoc, errors, warnings, continuable); function readRequiredNode() { const type = buffer.readByte(); diff --git a/templates/lib/prism/compiler.rb.erb b/templates/lib/prism/compiler.rb.erb index 66dbe666b9..13317cac04 100644 --- a/templates/lib/prism/compiler.rb.erb +++ b/templates/lib/prism/compiler.rb.erb @@ -1,3 +1,6 @@ +#-- +# rbs_inline: enabled + 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 @@ -18,24 +21,30 @@ module Prism # class Compiler < Visitor # Visit an individual node. - def visit(node) + #-- + #: (node?) -> untyped + def visit(node) # :nodoc: node&.accept(self) end # Visit a list of nodes. - def visit_all(nodes) + #-- + #: (Array[node?]) -> untyped + def visit_all(nodes) # :nodoc: nodes.map { |node| node&.accept(self) } end # Visit the child nodes of the given node. - def visit_child_nodes(node) + #-- + #: (node) -> Array[untyped] + def visit_child_nodes(node) # :nodoc: node.each_child_node.map { |node| node.accept(self) } end <%- nodes.each_with_index do |node, index| -%> <%= "\n" if index != 0 -%> - # Compile a <%= node.name %> node - def visit_<%= node.human %>(node) + #: (<%= node.name %>) -> Array[untyped] + def visit_<%= node.human %>(node) # :nodoc: node.each_child_node.map { |node| node.accept(self) } end <%- end -%> diff --git a/templates/lib/prism/dispatcher.rb.erb b/templates/lib/prism/dispatcher.rb.erb index 52478451c9..5991b0c904 100644 --- a/templates/lib/prism/dispatcher.rb.erb +++ b/templates/lib/prism/dispatcher.rb.erb @@ -1,3 +1,6 @@ +#-- +# rbs_inline: enabled + 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 @@ -32,50 +35,52 @@ module Prism # dispatcher.dispatch_once(integer) # class Dispatcher < Visitor - # attr_reader listeners: Hash[Symbol, Array[Listener]] - attr_reader :listeners + # A hash mapping event names to arrays of listeners that should be notified + # when that event is fired. + attr_reader :listeners #: Hash[Symbol, Array[untyped]] # Initialize a new dispatcher. + #-- + #: () -> void def initialize @listeners = {} end # Register a listener for one or more events. - # - # def register: (Listener, *Symbol) -> void + #-- + #: (untyped, *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 + #-- + #: (untyped) -> 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) + #-- + #: (untyped, Array[Symbol]) -> void + private def register_events(listener, events) # :nodoc: 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 + #-- + #: (node node) -> void def dispatch_once(node) node.accept(DispatchOnce.new(listeners)) end <%- nodes.each do |node| -%> - # Dispatch enter and leave events for <%= node.name %> nodes and continue - # walking the tree. - def visit_<%= node.human %>(node) + #: (<%= node.name %> node) -> void + def visit_<%= node.human %>(node) # :nodoc: listeners[:on_<%= node.human %>_enter]&.each { |listener| listener.on_<%= node.human %>_enter(node) } super listeners[:on_<%= node.human %>_leave]&.each { |listener| listener.on_<%= node.human %>_leave(node) } @@ -83,14 +88,17 @@ module Prism <%- end -%> class DispatchOnce < Visitor # :nodoc: - attr_reader :listeners + attr_reader :listeners #: Hash[Symbol, Array[untyped]] + #: (Hash[Symbol, Array[untyped]] listeners) -> void def initialize(listeners) @listeners = listeners end <%- nodes.each do |node| -%> # Dispatch enter and leave events for <%= node.name %> nodes. + #-- + #: (<%= node.name %> node) -> void def visit_<%= node.human %>(node) listeners[:on_<%= node.human %>_enter]&.each { |listener| listener.on_<%= node.human %>_enter(node) } listeners[:on_<%= node.human %>_leave]&.each { |listener| listener.on_<%= node.human %>_leave(node) } diff --git a/templates/lib/prism/dot_visitor.rb.erb b/templates/lib/prism/dot_visitor.rb.erb index 87de1965b0..88ef1e1f36 100644 --- a/templates/lib/prism/dot_visitor.rb.erb +++ b/templates/lib/prism/dot_visitor.rb.erb @@ -1,3 +1,6 @@ +#-- +# rbs_inline: enabled + require "cgi/escape" require "cgi/util" unless defined?(CGI::EscapeExt) @@ -6,14 +9,18 @@ module Prism # subtree into a graphviz dot graph. class DotVisitor < Visitor class Field # :nodoc: - attr_reader :name, :value, :port + attr_reader :name #: String + attr_reader :value #: String? + attr_reader :port #: bool + #: (String name, String? value, bool port) -> void def initialize(name, value, port) @name = name @value = value @port = port end + #: () -> String def to_dot if port "#{name}" @@ -24,17 +31,21 @@ module Prism end class Table # :nodoc: - attr_reader :name, :fields + attr_reader :name #: String + attr_reader :fields #: Array[Field] + #: (String name) -> void def initialize(name) @name = name @fields = [] end + #: (String name, ?String? value, ?port: bool) -> void def field(name, value = nil, port: false) fields << Field.new(name, value, port) end + #: () -> String def to_dot dot = <<~DOT @@ -50,26 +61,31 @@ module Prism end class Digraph # :nodoc: - attr_reader :nodes, :waypoints, :edges + attr_reader :nodes, :waypoints, :edges #: Array[String] + #: () -> void def initialize @nodes = [] @waypoints = [] @edges = [] end + #: (String value) -> void def node(value) nodes << value end + #: (String value) -> void def waypoint(value) waypoints << value end + #: (String value) -> void def edge(value) edges << value end + #: () -> String def to_dot <<~DOT digraph "Prism" { @@ -93,21 +109,25 @@ module Prism private_constant :Field, :Table, :Digraph # The digraph that is being built. - attr_reader :digraph + attr_reader :digraph #: Digraph # Initialize a new dot visitor. + #-- + #: () -> void def initialize @digraph = Digraph.new end # Convert this visitor into a graphviz dot graph string. + #-- + #: () -> String def to_dot digraph.to_dot end <%- nodes.each do |node| -%> - # Visit a <%= node.name %> node. - def visit_<%= node.human %>(node) + #: (<%= node.name %>) -> void + def visit_<%= node.human %>(node) # :nodoc: table = Table.new("<%= node.name %>") id = node_id(node) <%- if (node_flags = node.flags) -%> @@ -152,7 +172,7 @@ module Prism <%- end -%> <%- end -%> - digraph.nodes << <<~DOT + digraph.node(<<~DOT) #{id} [ label=<#{table.to_dot.gsub(/\n/, "\n ")}> ]; @@ -165,19 +185,25 @@ module Prism private # Generate a unique node ID for a node throughout the digraph. - def node_id(node) + #-- + #: (node) -> String + def node_id(node) # :nodoc: "Node_#{node.object_id}" end # Inspect a location to display the start and end line and columns in bytes. - def location_inspect(location) + #-- + #: (Location) -> String + def location_inspect(location) # :nodoc: "(#{location.start_line},#{location.start_column})-(#{location.end_line},#{location.end_column})" end <%- flags.each do |flag| -%> # Inspect a node that has <%= flag.human %> flags to display the flags as a # comma-separated list. - def <%= flag.human %>_inspect(node) + #-- + #: (<%= nodes.filter_map { |node| node.name if node.flags == flag }.join(" | ") %> node) -> String + def <%= flag.human %>_inspect(node) # :nodoc: flags = [] #: Array[String] <%- flag.values.each do |value| -%> flags << "<%= value.name.downcase %>" if node.<%= value.name.downcase %>? diff --git a/templates/lib/prism/dsl.rb.erb b/templates/lib/prism/dsl.rb.erb index e16ebb7110..95c4dac71a 100644 --- a/templates/lib/prism/dsl.rb.erb +++ b/templates/lib/prism/dsl.rb.erb @@ -1,8 +1,11 @@ +#-- +# rbs_inline: enabled + 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]") + # source = Prism::Source.for("[1]", 1, [0]) # # Prism::ArrayNode.new( # source, @@ -56,17 +59,31 @@ module Prism extend self # Create a new Source object. + #-- + #: (String string) -> Source def source(string) - Source.for(string) + Source.for(string, 1, build_offsets(string)) end # Create a new Location object. + #-- + #: (?source: Source, ?start_offset: Integer, ?length: Integer) -> Location def location(source: default_source, start_offset: 0, length: 0) Location.new(source, start_offset, length) end <%- nodes.each do |node| -%> + <%- + params = [ + ["source", "Source"], + ["node_id", "Integer"], + ["location", "Location"], + ["flags", "Integer"] + ].concat(node.fields.map { |field| [field.name, field.rbs_class] }) + -%> # Create a new <%= node.name %> node. + #-- + #: (<%= params.map { |(name, type)| "?#{name}: #{type}" }.join(", ") %>) -> <%= node.name %> def <%= node.human %>(<%= ["source: default_source", "node_id: 0", "location: default_location", "flags: 0", *node.fields.map { |field| case field when Prism::Template::NodeField @@ -100,6 +117,8 @@ module Prism <%- flags.each do |flag| -%> # Retrieve the value of one of the <%= flag.name %> flags. + #-- + #: (Symbol name) -> Integer def <%= flag.human.chomp("s") %>(name) case name <%- flag.values.each do |value| -%> @@ -114,20 +133,40 @@ module Prism # The default source object that gets attached to nodes and locations if no # source is specified. + #-- + #: () -> Source def default_source - Source.for("") + Source.for("", 1, [0]) end # The default location object that gets attached to nodes if no location is # specified, which uses the given source. + #-- + #: () -> Location 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. + #-- + #: (Source source, Location location) -> node def default_node(source, location) MissingNode.new(source, -1, location, 0) end + + private + + # Build the newline byte offset array for the given source string. + #-- + #: (String source) -> Array[Integer] + def build_offsets(source) + offsets = [0] + start = 0 + while (index = source.byteindex("\n", start)) + offsets << (start = index + 1) + end + offsets + end end end diff --git a/templates/lib/prism/inspect_visitor.rb.erb b/templates/lib/prism/inspect_visitor.rb.erb index 3cfe615d85..820f5ae75f 100644 --- a/templates/lib/prism/inspect_visitor.rb.erb +++ b/templates/lib/prism/inspect_visitor.rb.erb @@ -1,3 +1,6 @@ +#-- +# rbs_inline: enabled + module Prism # This visitor is responsible for composing the strings that get returned by # the various #inspect methods defined on each of the nodes. @@ -7,8 +10,9 @@ module Prism # 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 + attr_reader :value #: String + #: (String value) -> void def initialize(value) @value = value end @@ -17,19 +21,25 @@ module Prism private_constant :Replace # The current prefix string. - attr_reader :indent + # :stopdoc: + attr_reader :indent #: String + # :startdoc: # The list of commands that we need to execute in order to compose the # final string. - attr_reader :commands + #: stopdoc: + attr_reader :commands #: Array[[String | node | Replace, String]] + # :startdoc: - # Initializes a new instance of the InspectVisitor. - def initialize(indent = +"") + #: (?String indent) -> void + def initialize(indent = +"") # :nodoc: @indent = indent @commands = [] end # Compose an inspect string for the given node. + #-- + #: (node node) -> String def self.compose(node) visitor = new node.accept(visitor) @@ -37,7 +47,9 @@ module Prism end # Compose the final string. - def compose + #-- + #: () -> String + def compose # :nodoc: buffer = +"" replace = nil @@ -66,8 +78,8 @@ module Prism end <%- nodes.each do |node| -%> - # Inspect a <%= node.name %> node. - def visit_<%= node.human %>(node) + #: (<%= node.name %> node) -> void + def visit_<%= node.human %>(node) # :nodoc: commands << [inspect_node(<%= node.name.inspect %>, node), indent] <%- (fields = [node.flags || Prism::Template::Flags.empty, *node.fields]).each_with_index do |field, index| -%> <%- pointer = index == fields.length - 1 ? "└── " : "├── " -%> @@ -114,13 +126,17 @@ module Prism private # Compose a header for the given node. - def inspect_node(name, node) + #-- + #: (String name, node node) -> String + def inspect_node(name, node) # :nodoc: 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) + #-- + #: (Location? location) -> String + def inspect_location(location) # :nodoc: if location "(#{location.start_line},#{location.start_column})-(#{location.end_line},#{location.end_column}) = #{location.slice.inspect}" else diff --git a/templates/lib/prism/mutation_compiler.rb.erb b/templates/lib/prism/mutation_compiler.rb.erb index 565ee4e315..2d555048d2 100644 --- a/templates/lib/prism/mutation_compiler.rb.erb +++ b/templates/lib/prism/mutation_compiler.rb.erb @@ -1,3 +1,6 @@ +#-- +# rbs_inline: enabled + 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 @@ -5,8 +8,8 @@ module Prism class MutationCompiler < Compiler <%- nodes.each_with_index do |node, index| -%> <%= "\n" if index != 0 -%> - # Copy a <%= node.name %> node - def visit_<%= node.human %>(node) + #: (<%= node.name %>) -> node? + def visit_<%= node.human %>(node) # :nodoc: <%- fields = node.fields.select { |field| [Prism::Template::NodeField, Prism::Template::OptionalNodeField, Prism::Template::NodeListField].include?(field.class) } -%> <%- if fields.any? -%> node.copy(<%= fields.map { |field| "#{field.name}: #{field.is_a?(Prism::Template::NodeListField) ? "visit_all" : "visit"}(node.#{field.name})" }.join(", ") %>) diff --git a/templates/lib/prism/node.rb.erb b/templates/lib/prism/node.rb.erb index d14a06961a..fb13051aba 100644 --- a/templates/lib/prism/node.rb.erb +++ b/templates/lib/prism/node.rb.erb @@ -1,24 +1,49 @@ +#-- +# rbs_inline: enabled + module Prism + # @rbs! + # interface _Repository + # def enter: (Integer node_id, Symbol field_name) -> Relocation::Entry + # end + # + # interface _Node + # def deconstruct: () -> Array[Prism::node?] + # def inspect: () -> String + # end + # + # type node = Node & _Node + # 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 + # :stopdoc: + attr_reader :source #: Source private :source + # :startdoc: # 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 + attr_reader :node_id #: Integer + + # The location associated with this node. For lazily loading Location + # objects, we keep it as a packed integer until it is accessed. + # @rbs @location: Location | Integer # Save this node using a saved source so that it can be retrieved later. + #-- + #: (_Repository repository) -> Relocation::Entry def save(repository) repository.enter(node_id, :itself) end # A Location instance that represents the location of this node in the # source. + #-- + #: () -> Location def location location = @location return location if location.is_a?(Location) @@ -26,104 +51,151 @@ module Prism end # Save the location using a saved source so that it can be retrieved later. + #-- + #: (_Repository repository) -> Relocation::Entry def save_location(repository) repository.enter(node_id, :location) end - # Delegates to the start_line of the associated location object. + # -------------------------------------------------------------------------- + # :section: Location Delegators + # These methods provide convenient access to the underlying Location object. + # -------------------------------------------------------------------------- + + # Delegates to [`start_line`](rdoc-ref:Location#start_line) of the associated location object. + #-- + #: () -> Integer def start_line location.start_line end - # Delegates to the end_line of the associated location object. + # Delegates to [`end_line`](rdoc-ref:Location#end_line) of the associated location object. + #-- + #: () -> Integer 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. + # Delegates to [`start_offset`](rdoc-ref:Location#start_offset) of the associated location object. + #-- + #: () -> Integer 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. + # Delegates to [`end_offset`](rdoc-ref:Location#end_offset) of the associated location object. + #-- + #: () -> Integer 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. + # Delegates to [`start_character_offset`](rdoc-ref:Location#start_character_offset) + # of the associated location object. + #-- + #: () -> Integer def start_character_offset location.start_character_offset end - # Delegates to the end_character_offset of the associated location object. + # Delegates to [`end_character_offset`](rdoc-ref:Location#end_character_offset) + # of the associated location object. + #-- + #: () -> Integer def end_character_offset location.end_character_offset end - # Delegates to the cached_start_code_units_offset of the associated location - # object. + # Delegates to [`cached_start_code_units_offset`](rdoc-ref:Location#cached_start_code_units_offset) + # of the associated location object. + #-- + #: (_CodeUnitsCache cache) -> Integer 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. + # Delegates to [`cached_end_code_units_offset`](rdoc-ref:Location#cached_end_code_units_offset) + # of the associated location object. + #-- + #: (_CodeUnitsCache cache) -> Integer 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. + # Delegates to [`start_column`](rdoc-ref:Location#start_column) of the associated location object. + #-- + #: () -> Integer def start_column location.start_column end - # Delegates to the end_column of the associated location object. + # Delegates to [`end_column`](rdoc-ref:Location#end_column) of the associated location object. + #-- + #: () -> Integer def end_column location.end_column end - # Delegates to the start_character_column of the associated location object. + # Delegates to [`start_character_column`](rdoc-ref:Location#start_character_column) + # of the associated location object. + #-- + #: () -> Integer def start_character_column location.start_character_column end - # Delegates to the end_character_column of the associated location object. + # Delegates to [`end_character_column`](rdoc-ref:Location#end_character_column) + # of the associated location object. + #-- + #: () -> Integer def end_character_column location.end_character_column end - # Delegates to the cached_start_code_units_column of the associated location - # object. + # Delegates to [`cached_start_code_units_column`](rdoc-ref:Location#cached_start_code_units_column) + # of the associated location object. + #-- + #: (_CodeUnitsCache cache) -> Integer 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. + # Delegates to [`cached_end_code_units_column`](rdoc-ref:Location#cached_end_code_units_column) + # of the associated location object. + #-- + #: (_CodeUnitsCache cache) -> Integer 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. + # Delegates to [`leading_comments`](rdoc-ref:Location#leading_comments) of the associated location object. + #-- + #: () -> Array[Comment] def leading_comments location.leading_comments end - # Delegates to the trailing_comments of the associated location object. + # Delegates to [`trailing_comments`](rdoc-ref:Location#trailing_comments) of the associated location object. + #-- + #: () -> Array[Comment] def trailing_comments location.trailing_comments end - # Delegates to the comments of the associated location object. + # Delegates to [`comments`](rdoc-ref:Location#comments) of the associated location object. + #-- + #: () -> Array[Comment] def comments location.comments end + # :section: + # Returns all of the lines of the source code associated with this node. + #-- + #: () -> Array[String] def source_lines location.source_lines end @@ -133,6 +205,8 @@ module Prism alias script_lines source_lines # Slice the location of the node from the source. + #-- + #: () -> String def slice location.slice end @@ -140,28 +214,38 @@ module Prism # 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. + #-- + #: () -> String 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 + # :stopdoc: + attr_reader :flags #: Integer protected :flags + # :startdoc: # Returns true if the node has the newline flag set. + #-- + #: () -> bool def newline? flags.anybits?(NodeFlags::NEWLINE) end # Returns true if the node has the static literal flag set. + #-- + #: () -> bool 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) + #-- + #: (PP q) -> void + def pretty_print(q) # :nodoc: q.seplist(inspect.chomp.each_line, -> { q.breakable }) do |line| q.text(line.chomp) end @@ -169,6 +253,8 @@ module Prism end # Convert this node into a graphviz dot graph string. + #-- + #: () -> String def to_dot # @type self: node DotVisitor.new.tap { |visitor| accept(visitor) }.to_dot @@ -180,9 +266,11 @@ module Prism # # Important to note is that the column given to this method should be in # bytes, as opposed to characters or code units. + #-- + #: (Integer line, Integer column) -> Array[node] def tunnel(line, column) - queue = [self] #: Array[Prism::node] - result = [] #: Array[Prism::node] + queue = [self] #: Array[node] + result = [] #: Array[node] offset = source.byte_offset(line, column) while (node = queue.shift) @@ -204,9 +292,10 @@ module Prism # particular condition. # # node.breadth_first_search { |node| node.node_id == node_id } - # - def breadth_first_search(&block) - queue = [self] #: Array[Prism::node] + #-- + #: () { (node) -> bool } -> node? + def breadth_first_search(&blk) + queue = [self] #: Array[node] while (node = queue.shift) return node if yield node @@ -222,8 +311,9 @@ module Prism # particular condition. # # node.breadth_first_search_all { |node| node.is_a?(Prism::CallNode) } - # - def breadth_first_search_all(&block) + #-- + #: () { (node) -> bool } -> Array[node] + def breadth_first_search_all(&blk) queue = [self] #: Array[Prism::node] results = [] #: Array[Prism::node] @@ -239,6 +329,8 @@ module Prism # 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. + #-- + #: () -> Array[Reflection::Field] def self.fields # This method should only be called on subclasses of Node, not Node # itself. @@ -248,19 +340,22 @@ module Prism 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. + # :section: Node Interface + # These methods are effectively abstract methods that are implemented by + # the various subclasses of Node. # -------------------------------------------------------------------------- # Accepts a visitor and calls back into the specialized visit function. + #-- + #: (_Visitor visitor) -> untyped 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. + #-- + #: () -> Array[node?] def child_nodes raise NoMethodError, "undefined method `child_nodes' for #{inspect}" end @@ -270,23 +365,32 @@ module Prism # 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 + #-- + #: () -> Enumerator[node, void] + #: () { (node) -> void } -> void + def each_child_node(&blk) 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. + #-- + #: () -> Array[node] 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. + #-- + #: () -> Array[node | Location] def comment_targets raise NoMethodError, "undefined method `comment_targets' for #{inspect}" end # Returns a string representation of the node. + #-- + #: () -> String def inspect raise NoMethodError, "undefined method `inspect' for #{inspect}" end @@ -303,6 +407,8 @@ module Prism # 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. + #-- + #: () -> Symbol def type raise NoMethodError, "undefined method `type' for #{inspect}" end @@ -311,6 +417,8 @@ module Prism # 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. + #-- + #: () -> Symbol def self.type raise NoMethodError, "undefined method `type' for #{inspect}" end @@ -321,7 +429,13 @@ module Prism #<%= line %> <%- end -%> class <%= node.name -%> < Node + <%- node.fields.each do |field| -%> + # @rbs @<%= field.name %>: <%= field.rbs_class %> + <%- end -%> + # Initialize a new <%= node.name %> node. + #-- + #: (Source source, Integer node_id, Location location, Integer flags, <%= node.fields.map { |field| "#{field.rbs_class} #{field.name}" }.join(", ") %>) -> void def initialize(<%= ["source", "node_id", "location", "flags", *node.fields.map(&:name)].join(", ") %>) @source = source @node_id = node_id @@ -335,12 +449,27 @@ module Prism <%- end -%> end - # def accept: (Visitor visitor) -> void + # --------- + # :section: Repository + # Methods related to Relocation. + # --------- + + # ---------------------------------------------------------------------------------- + # :section: Node Interface + # These methods are present on all subclasses of Node. + # Read the [node interface docs](Node.html#node-interface) for more information. + # ---------------------------------------------------------------------------------- + + # See Node.accept. + #-- + #: (_Visitor visitor) -> untyped def accept(visitor) visitor.visit_<%= node.human %>(self) end - # def child_nodes: () -> Array[Node?] + # See Node.child_nodes. + #-- + #: () -> Array[node?] def child_nodes [<%= node.fields.map { |field| case field @@ -350,8 +479,11 @@ module Prism }.compact.join(", ") %>] end - # def each_child_node: () { (Prism::node) -> void } -> void | () -> Enumerator[Prism::node] - def each_child_node + # See Node.each_child_node. + #-- + #: () -> Enumerator[node, void] + #: () { (node) -> void } -> void + def each_child_node(&blk) return to_enum(:each_child_node) unless block_given? <%- node.fields.each do |field| -%> @@ -359,14 +491,16 @@ module Prism <%- when Prism::Template::NodeField -%> yield <%= field.name %> <%- when Prism::Template::OptionalNodeField -%> - yield <%= field.name %> if <%= field.name %> + if (<%= field.name %> = self.<%= field.name %>); yield <%= field.name %>; end <%- when Prism::Template::NodeListField -%> <%= field.name %>.each { |node| yield node } <%- end -%> <%- end -%> end - # def compact_child_nodes: () -> Array[Node] + # See Node.compact_child_nodes. + #-- + #: () -> Array[node] def compact_child_nodes <%- if node.fields.any? { |field| field.is_a?(Prism::Template::OptionalNodeField) } -%> compact = [] #: Array[Prism::node] @@ -375,7 +509,7 @@ module Prism <%- when Prism::Template::NodeField -%> compact << <%= field.name %> <%- when Prism::Template::OptionalNodeField -%> - compact << <%= field.name %> if <%= field.name %> + if (<%= field.name %> = self.<%= field.name %>); compact << <%= field.name %>; end <%- when Prism::Template::NodeListField -%> compact.concat(<%= field.name %>) <%- end -%> @@ -391,7 +525,9 @@ module Prism <%- end -%> end - # def comment_targets: () -> Array[Node | Location] + # See Node.comment_targets. + #-- + #: () -> Array[node | Location] def comment_targets [<%= node.fields.map { |field| case field @@ -401,50 +537,101 @@ module Prism }.compact.join(", ") %>] #: Array[Prism::node | Location] end - # def copy: (<%= (["?node_id: Integer", "?location: Location", "?flags: Integer"] + node.fields.map { |field| "?#{field.name}: #{field.rbs_class}" }).join(", ") %>) -> <%= node.name %> + # :call-seq: + # copy(**fields) -> <%= node.name %> + # + # Creates a copy of self with the given fields, using self as the template. + #-- + #: (?node_id: Integer, ?location: Location, ?flags: Integer, <%= node.fields.map { |field| "?#{field.name}: #{field.rbs_class}" }.join(", ") %>) -> <%= node.name %> def copy(<%= (["node_id", "location", "flags"] + node.fields.map(&:name)).map { |field| "#{field}: self.#{field}" }.join(", ") %>) <%= node.name %>.new(<%= ["source", "node_id", "location", "flags", *node.fields.map(&:name)].join(", ") %>) end - # def deconstruct: () -> Array[Node?] alias deconstruct child_nodes - # def deconstruct_keys: (Array[Symbol] keys) -> { <%= (["node_id: Integer", "location: Location"] + node.fields.map { |field| "#{field.name}: #{field.rbs_class}" }).join(", ") %> } - def deconstruct_keys(keys) + #: (Array[Symbol]? keys) -> Hash[Symbol, untyped] + def deconstruct_keys(keys) # :nodoc: { <%= (["node_id: node_id", "location: location"] + node.fields.map { |field| "#{field.name}: #{field.name}" }).join(", ") %> } end + + # See `Node#type`. + #-- + #: () -> :<%= node.human %> + def type + :<%= node.human %> + end + + # See `Node.type`. + #-- + #: () -> :<%= node.human %> + def self.type + :<%= node.human %> + end + + #: () -> String + def inspect # :nodoc: + InspectVisitor.compose(self) + end + + # :section: + <%- if (node_flags = node.flags) -%> <%- node_flags.values.each do |value| -%> - - # def <%= value.name.downcase %>?: () -> bool + # :category: Flags + # <%= value.comment %> + #-- + #: () -> bool def <%= value.name.downcase %>? flags.anybits?(<%= node_flags.name %>::<%= value.name %>) end + <%- end -%> <%- end -%> <%- node.fields.each do |field| -%> - + <%- case field -%> + <%- when Prism::Template::LocationField -%> + # :category: Locations + # :call-seq: + # <%= field.name %> -> <%= field.call_seq_type %> + # <%- if field.comment.nil? -%> - # attr_reader <%= field.name %>: <%= field.rbs_class %> + # Returns the Location represented by `<%= field.name %>`. <%- else -%> <%- field.each_comment_line do |line| -%> #<%= line %> <%- end -%> <%- end -%> - <%- case field -%> - <%- when Prism::Template::LocationField -%> + #-- + #: () -> Location def <%= field.name %> location = @<%= field.name %> return location if location.is_a?(Location) @<%= field.name %> = Location.new(source, location >> 32, location & 0xFFFFFFFF) end + # :category: Repository # Save the <%= field.name %> location using the given saved source so that # it can be retrieved later. + #-- + #: (_Repository repository) -> Relocation::Entry def save_<%= field.name %>(repository) repository.enter(node_id, :<%= field.name %>) end + <%- when Prism::Template::OptionalLocationField -%> + # :category: Locations + # :call-seq: + # <%= field.name %> -> <%= field.call_seq_type %> + # + <%- if field.comment.nil? -%> + # Returns the Location represented by `<%= field.name %>`. + <%- else -%> + <%- field.each_comment_line do |line| -%> + #<%= line %> + <%- end -%> + <%- end -%> + #-- + #: () -> Location? def <%= field.name %> location = @<%= field.name %> case location @@ -457,54 +644,69 @@ module Prism end end + # :category: Repository # Save the <%= field.name %> location using the given saved source so that # it can be retrieved later. + #-- + #: (_Repository repository) -> Relocation::Entry? def save_<%= field.name %>(repository) repository.enter(node_id, :<%= field.name %>) unless @<%= field.name %>.nil? end <%- else -%> - attr_reader :<%= field.name %> + # :call-seq: + # <%= field.name %> -> <%= field.call_seq_type %> + # + <%- if field.comment.nil? -%> + # Returns the `<%= field.name %>` attribute. + <%- else -%> + <%- field.each_comment_line do |line| -%> + #<%= line %> + <%- end -%> + <%- end -%> + #-- + #: () -> <%= field.rbs_class %> + def <%= field.name %> + @<%= field.name %> + end + <%- end -%> <%- end -%> + # :section: Slicing + <%- node.fields.each do |field| -%> <%- case field -%> <%- when Prism::Template::LocationField -%> <%- raise unless field.name.end_with?("_loc") -%> <%- next if node.fields.any? { |other| other.name == field.name.delete_suffix("_loc") } -%> - - # def <%= field.name.delete_suffix("_loc") %>: () -> String + # :call-seq: + # <%= field.name.delete_suffix("_loc") %> -> String + # + # Slice the location of <%= field.name %> from the source. + #-- + #: () -> String def <%= field.name.delete_suffix("_loc") %> <%= field.name %>.slice end + <%- when Prism::Template::OptionalLocationField -%> <%- raise unless field.name.end_with?("_loc") -%> <%- next if node.fields.any? { |other| other.name == field.name.delete_suffix("_loc") } -%> - - # def <%= field.name.delete_suffix("_loc") %>: () -> String? + # :call-seq: + # <%= field.name.delete_suffix("_loc") %> -> String | nil + # + # Slice the location of <%= field.name %> from the source. + #-- + #: () -> String? def <%= field.name.delete_suffix("_loc") %> <%= field.name %>&.slice end + <%- end -%> <%- end -%> + # :section: - # def inspect -> String - def inspect - InspectVisitor.compose(self) - end - - # Return a symbol representation of this node type. See `Node#type`. - def type - :<%= node.human %> - end - - # Return a symbol representation of this node type. See `Node::type`. - def self.type - :<%= node.human %> - end - - # Implements case-equality for the node. This is effectively == but without - # comparing the value of locations. Locations are checked only for presence. - def ===(other) + #: (untyped other) -> boolish + def ===(other) # :nodoc: other.is_a?(<%= node.name %>)<%= " &&" if (fields = [*node.flags, *node.fields]).any? %> <%- fields.each_with_index do |field, index| -%> <%- if field.is_a?(Prism::Template::LocationField) || field.is_a?(Prism::Template::OptionalLocationField) -%> diff --git a/templates/lib/prism/reflection.rb.erb b/templates/lib/prism/reflection.rb.erb index 6c8b2f4d25..0012f120b2 100644 --- a/templates/lib/prism/reflection.rb.erb +++ b/templates/lib/prism/reflection.rb.erb @@ -1,3 +1,6 @@ +#-- +# rbs_inline: enabled + 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 @@ -7,9 +10,11 @@ module Prism # for all other field types. class Field # The name of the field. - attr_reader :name + attr_reader :name #: Symbol # Initializes the field with the given name. + #-- + #: (Symbol name) -> void def initialize(name) @name = name end @@ -83,9 +88,11 @@ module Prism # the bitset should be accessed through their query methods. class FlagsField < Field # The names of the flags in the bitset. - attr_reader :flags + attr_reader :flags #: Array[Symbol] # Initializes the flags field with the given name and flags. + #-- + #: (Symbol name, Array[Symbol] flags) -> void def initialize(name, flags) super(name) @flags = flags @@ -93,6 +100,8 @@ module Prism end # Returns the fields for the given node. + #-- + #: (singleton(Node) node) -> Array[Field] def self.fields_for(node) case node.type <%- nodes.each do |node| -%> diff --git a/templates/lib/prism/serialize.rb.erb b/templates/lib/prism/serialize.rb.erb index 2275d685ca..a676f957af 100644 --- a/templates/lib/prism/serialize.rb.erb +++ b/templates/lib/prism/serialize.rb.erb @@ -1,9 +1,12 @@ +#-- +# rbs_inline: enabled + require "stringio" require_relative "polyfill/unpack1" module Prism # A module responsible for deserializing parse results. - module Serialize + module Serialize # :nodoc: # The major version of prism that we are expecting to find in the serialized # strings. MAJOR_VERSION = 1 @@ -20,9 +23,11 @@ module Prism # # The formatting of the source of this method is purposeful to illustrate # the structure of the serialized data. + #-- + #: (String input, String serialized, bool freeze) -> ParseResult def self.load_parse(input, serialized, freeze) input = input.dup - source = Source.for(input) + source = Source.for(input, 1, []) loader = Loader.new(source, serialized) loader.load_header @@ -38,16 +43,17 @@ module Prism data_loc = loader.load_optional_location_object(freeze) errors = loader.load_errors(encoding, freeze) warnings = loader.load_warnings(encoding, freeze) + continuable = loader.load_bool cpool_base = loader.load_uint32 cpool_size = loader.load_varuint - constant_pool = ConstantPool.new(input, serialized, cpool_base, cpool_size) + constant_pool = ConstantPool.new(serialized, cpool_base, cpool_size) - node = loader.load_node(constant_pool, encoding, freeze) + node = loader.load_node(constant_pool, encoding, freeze) #: ProgramNode loader.load_constant_pool(constant_pool) raise unless loader.eof? - result = ParseResult.new(node, comments, magic_comments, data_loc, errors, warnings, source) + result = ParseResult.new(node, comments, magic_comments, data_loc, errors, warnings, continuable, source) result.freeze if freeze input.force_encoding(encoding) @@ -73,8 +79,10 @@ module Prism # # The formatting of the source of this method is purposeful to illustrate # the structure of the serialized data. + #-- + #: (String input, String serialized, bool freeze) -> LexResult def self.load_lex(input, serialized, freeze) - source = Source.for(input) + source = Source.for(input, 1, []) loader = Loader.new(source, serialized) tokens = loader.load_tokens @@ -90,9 +98,10 @@ module Prism data_loc = loader.load_optional_location_object(freeze) errors = loader.load_errors(encoding, freeze) warnings = loader.load_warnings(encoding, freeze) + continuable = loader.load_bool raise unless loader.eof? - result = LexResult.new(tokens, comments, magic_comments, data_loc, errors, warnings, source) + result = LexResult.new(tokens, comments, magic_comments, data_loc, errors, warnings, continuable, source) tokens.each do |token| token[0].value.force_encoding(encoding) @@ -117,8 +126,10 @@ module Prism # # The formatting of the source of this method is purposeful to illustrate # the structure of the serialized data. + #-- + #: (String input, String serialized, bool freeze) -> Array[Comment] def self.load_parse_comments(input, serialized, freeze) - source = Source.for(input) + source = Source.for(input, 1, []) loader = Loader.new(source, serialized) loader.load_header @@ -139,8 +150,10 @@ module Prism # # The formatting of the source of this method is purposeful to illustrate # the structure of the serialized data. + #-- + #: (String input, String serialized, bool freeze) -> ParseLexResult def self.load_parse_lex(input, serialized, freeze) - source = Source.for(input) + source = Source.for(input, 1, []) loader = Loader.new(source, serialized) tokens = loader.load_tokens @@ -157,17 +170,18 @@ module Prism data_loc = loader.load_optional_location_object(freeze) errors = loader.load_errors(encoding, freeze) warnings = loader.load_warnings(encoding, freeze) + continuable = loader.load_bool cpool_base = loader.load_uint32 cpool_size = loader.load_varuint - constant_pool = ConstantPool.new(input, serialized, cpool_base, cpool_size) + constant_pool = ConstantPool.new(serialized, cpool_base, cpool_size) - node = loader.load_node(constant_pool, encoding, freeze) + node = loader.load_node(constant_pool, encoding, freeze) #: ProgramNode 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) + value = [node, tokens] #: [ProgramNode, Array[[Token, Integer]]] + result = ParseLexResult.new(value, comments, magic_comments, data_loc, errors, warnings, continuable, source) tokens.each do |token| token[0].value.force_encoding(encoding) @@ -189,34 +203,36 @@ module Prism end class ConstantPool # :nodoc: - attr_reader :size + attr_reader :size #: Integer + + # @rbs @serialized: String + # @rbs @base: Integer + # @rbs @pool: Array[Symbol?] - def initialize(input, serialized, base, size) - @input = input + #: (String serialized, Integer base, Integer size) -> void + def initialize(serialized, base, size) @serialized = serialized @base = base @size = size @pool = Array.new(size, nil) end + #: (Integer index, Encoding encoding) -> Symbol def get(index, encoding) @pool[index] ||= begin offset = @base + index * 8 - start = @serialized.unpack1("L", offset: offset) - length = @serialized.unpack1("L", offset: offset + 4) + start = @serialized.unpack1("L", offset: offset) #: Integer + length = @serialized.unpack1("L", offset: offset + 4) #: Integer - 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 + (@serialized.byteslice(start, length) or raise).force_encoding(encoding).to_sym end end end if RUBY_ENGINE == "truffleruby" # StringIO is synchronized and that adds a high overhead on TruffleRuby. + # @rbs skip class FastStringIO # :nodoc: attr_accessor :pos @@ -246,8 +262,11 @@ module Prism end class Loader # :nodoc: - attr_reader :input, :io, :source + attr_reader :input #: String + attr_reader :io #: StringIO + attr_reader :source #: Source + #: (Source source, String serialized) -> void def initialize(source, serialized) @input = source.source.dup raise unless serialized.encoding == Encoding::BINARY @@ -256,40 +275,46 @@ module Prism define_load_node_lambdas if RUBY_ENGINE != "ruby" end + #: () -> bool def eof? io.getbyte io.eof? end + #: (ConstantPool constant_pool) -> void 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) + length = (io.read(8) or raise).unpack1("L", offset: 4) #: Integer + trailer += length end io.read(trailer) end + #: () -> void 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" if (io.read(3) or raise).unpack("C3") != [MAJOR_VERSION, MINOR_VERSION, PATCH_VERSION] raise "Invalid serialization (location fields must be included but are not)" if io.getbyte != 0 end + #: () -> Encoding def load_encoding - encoding = Encoding.find(io.read(load_varuint)) + encoding = Encoding.find((io.read(load_varuint) or raise)) or raise @input = input.force_encoding(encoding).freeze encoding end + #: (bool freeze) -> Array[Integer] def load_line_offsets(freeze) offsets = Array.new(load_varuint) { load_varuint } offsets.freeze if freeze offsets end + #: (bool freeze) -> Array[Comment] def load_comments(freeze) comments = Array.new(load_varuint) do @@ -297,6 +322,7 @@ module Prism case load_varuint when 0 then InlineComment.new(load_location_object(freeze)) when 1 then EmbDocComment.new(load_location_object(freeze)) + else raise end comment.freeze if freeze @@ -307,6 +333,7 @@ module Prism comments end + #: (bool freeze) -> Array[MagicComment] def load_magic_comments(freeze) magic_comments = Array.new(load_varuint) do @@ -331,10 +358,11 @@ module Prism <%- warnings.each do |warning| -%> <%= warning.name.downcase.to_sym.inspect %>, <%- end -%> - ].freeze + ].freeze #: Array[Symbol] private_constant :DIAGNOSTIC_TYPES + #: () -> Symbol def load_error_level level = io.getbyte @@ -350,13 +378,14 @@ module Prism end end + #: (Encoding encoding, bool freeze) -> Array[ParseError] def load_errors(encoding, freeze) errors = Array.new(load_varuint) do error = ParseError.new( DIAGNOSTIC_TYPES.fetch(load_varuint), - load_embedded_string(encoding), + load_string(encoding), load_location_object(freeze), load_error_level ) @@ -369,6 +398,7 @@ module Prism errors end + #: () -> Symbol def load_warning_level level = io.getbyte @@ -382,13 +412,14 @@ module Prism end end + #: (Encoding encoding, bool freeze) -> Array[ParseWarning] def load_warnings(encoding, freeze) warnings = Array.new(load_varuint) do warning = ParseWarning.new( DIAGNOSTIC_TYPES.fetch(load_varuint), - load_embedded_string(encoding), + load_string(encoding), load_location_object(freeze), load_warning_level ) @@ -401,15 +432,15 @@ module Prism warnings end + #: () -> Array[[Token, Integer]] def load_tokens - tokens = [] + tokens = [] #: Array[[Token, Integer]] while (type = TOKEN_TYPES.fetch(load_varuint)) - start = load_varuint - length = load_varuint + location = load_location_object(false) + lex_state = load_varuint - location = Location.new(@source, start, length) token = Token.new(@source, type, location.slice, location) tokens << [token, lex_state] @@ -420,25 +451,29 @@ module Prism # variable-length integer using https://en.wikipedia.org/wiki/LEB128 # This is also what protobuf uses: https://protobuf.dev/programming-guides/encoding/#varints + #-- + #: () -> Integer def load_varuint - n = io.getbyte + n = (io.getbyte or raise) if n < 128 n else n -= 128 shift = 0 - while (b = io.getbyte) >= 128 + while (b = (io.getbyte or raise)) >= 128 n += (b - 128) << (shift += 7) end n + (b << (shift + 7)) end end + #: () -> Integer def load_varsint n = load_varuint (n >> 1) ^ (-(n & 1)) end + #: () -> Integer def load_integer negative = io.getbyte != 0 length = load_varuint @@ -450,14 +485,22 @@ module Prism value end + #: () -> Float def load_double - io.read(8).unpack1("D") + (io.read(8) or raise).unpack1("D") #: Float end + #: () -> bool + def load_bool + (io.getbyte or raise) != 0 + end + + #: () -> Integer def load_uint32 - io.read(4).unpack1("L") + (io.read(4) or raise).unpack1("L") #: Integer end + #: (ConstantPool constant_pool, Encoding encoding, bool freeze) -> node? def load_optional_node(constant_pool, encoding, freeze) if io.getbyte != 0 io.pos -= 1 @@ -465,90 +508,121 @@ module Prism end end - def load_embedded_string(encoding) - io.read(load_varuint).force_encoding(encoding).freeze - end - + #: (Encoding encoding) -> String 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 + (io.read(load_varuint) or raise).force_encoding(encoding).freeze end + #: (bool freeze) -> Location def load_location_object(freeze) location = Location.new(source, load_varuint, load_varuint) location.freeze if freeze location end + # Load a location object from the serialized data. Note that we are lying + # about the signature a bit here, because we sometimes load it as a packed + # integer instead of an object. + #-- + #: (bool freeze) -> Location def load_location(freeze) return load_location_object(freeze) if freeze - (load_varuint << 32) | load_varuint + (load_varuint << 32) | load_varuint #: Location end + # Load an optional location object from the serialized data if it is + # present. Note that we are lying about the signature a bit here, because + # we sometimes load it as a packed integer instead of an object. + #-- + #: (bool freeze) -> Location? def load_optional_location(freeze) load_location(freeze) if io.getbyte != 0 end + #: (bool freeze) -> Location? def load_optional_location_object(freeze) load_location_object(freeze) if io.getbyte != 0 end + #: (ConstantPool constant_pool, Encoding encoding) -> Symbol def load_constant(constant_pool, encoding) index = load_varuint constant_pool.get(index - 1, encoding) end + #: (ConstantPool constant_pool, Encoding encoding) -> Symbol? def load_optional_constant(constant_pool, encoding) index = load_varuint constant_pool.get(index - 1, encoding) if index != 0 end if RUBY_ENGINE == "ruby" + #: (ConstantPool constant_pool, Encoding encoding, bool freeze) -> node def load_node(constant_pool, encoding, freeze) type = io.getbyte node_id = load_varuint - location = load_location(freeze) - value = case type - <%- nodes.each_with_index do |node, index| -%> - when <%= index + 1 %> then - <%- if node.needs_serialized_length? -%> - load_uint32 - <%- end -%> - <%= node.name %>.new(<%= ["source", "node_id", "location", "load_varuint", *node.fields.map { |field| - case field - when Prism::Template::NodeField then "load_node(constant_pool, encoding, freeze)" - when Prism::Template::OptionalNodeField then "load_optional_node(constant_pool, encoding, freeze)" - when Prism::Template::StringField then "load_string(encoding)" - when Prism::Template::NodeListField then "Array.new(load_varuint) { load_node(constant_pool, encoding, freeze) }.tap { |nodes| nodes.freeze if freeze }" - when Prism::Template::ConstantField then "load_constant(constant_pool, encoding)" - when Prism::Template::OptionalConstantField then "load_optional_constant(constant_pool, encoding)" - when Prism::Template::ConstantListField then "Array.new(load_varuint) { load_constant(constant_pool, encoding) }.tap { |constants| constants.freeze if freeze }" - when Prism::Template::LocationField then "load_location(freeze)" - when Prism::Template::OptionalLocationField then "load_optional_location(freeze)" - when Prism::Template::UInt8Field then "io.getbyte" - when Prism::Template::UInt32Field then "load_varuint" - when Prism::Template::IntegerField then "load_integer" - when Prism::Template::DoubleField then "load_double" - else raise - end - }].join(", ") -%>) + location = load_location(freeze) #: Location + value = + case type + <%- nodes.each_with_index do |node, index| -%> + when <%= index + 1 %> + <%- if node.needs_serialized_length? -%> + load_uint32 + <%- end -%> + <%= node.name %>.new( + source, + node_id, + location, + load_varuint, + <%- node.fields.each do |field| -%> + <%- case field -%> + <%- when Prism::Template::NodeField -%> + load_node(constant_pool, encoding, freeze), #: <%= field.rbs_class %> + <%- when Prism::Template::OptionalNodeField -%> + load_optional_node(constant_pool, encoding, freeze), #: <%= field.rbs_class %> + <%- when Prism::Template::StringField -%> + load_string(encoding), + <%- when Prism::Template::NodeListField -%> + Array.new(load_varuint) do + load_node(constant_pool, encoding, freeze) #: <%= field.element_rbs_class %> + end.tap { |nodes| nodes.freeze if freeze }, + <%- when Prism::Template::ConstantField -%> + load_constant(constant_pool, encoding), + <%- when Prism::Template::OptionalConstantField -%> + load_optional_constant(constant_pool, encoding), + <%- when Prism::Template::ConstantListField -%> + Array.new(load_varuint) { load_constant(constant_pool, encoding) }.tap { |constants| constants.freeze if freeze }, + <%- when Prism::Template::LocationField -%> + load_location(freeze), + <%- when Prism::Template::OptionalLocationField -%> + load_optional_location(freeze), + <%- when Prism::Template::UInt8Field -%> + (io.getbyte or raise), + <%- when Prism::Template::UInt32Field -%> + load_varuint, + <%- when Prism::Template::IntegerField -%> + load_integer, + <%- when Prism::Template::DoubleField -%> + load_double, + <%- else raise -%> + <%- end -%> + <%- end -%> + ) <%- end -%> - end + else + raise "Unknown node type: #{type}" + end value.freeze if freeze value end else + # @rbs skip def load_node(constant_pool, encoding, freeze) - @load_node_lambdas[io.getbyte].call(constant_pool, encoding, freeze) + @load_node_lambdas[(io.getbyte or raise)].call(constant_pool, encoding, freeze) end + # @rbs skip def define_load_node_lambdas @load_node_lambdas = [ nil, @@ -559,24 +633,46 @@ module Prism <%- if node.needs_serialized_length? -%> load_uint32 <%- end -%> - value = <%= node.name %>.new(<%= ["source", "node_id", "location", "load_varuint", *node.fields.map { |field| - case field - when Prism::Template::NodeField then "load_node(constant_pool, encoding, freeze)" - when Prism::Template::OptionalNodeField then "load_optional_node(constant_pool, encoding, freeze)" - when Prism::Template::StringField then "load_string(encoding)" - when Prism::Template::NodeListField then "Array.new(load_varuint) { load_node(constant_pool, encoding, freeze) }" - when Prism::Template::ConstantField then "load_constant(constant_pool, encoding)" - when Prism::Template::OptionalConstantField then "load_optional_constant(constant_pool, encoding)" - when Prism::Template::ConstantListField then "Array.new(load_varuint) { load_constant(constant_pool, encoding) }" - when Prism::Template::LocationField then "load_location(freeze)" - when Prism::Template::OptionalLocationField then "load_optional_location(freeze)" - when Prism::Template::UInt8Field then "io.getbyte" - when Prism::Template::UInt32Field then "load_varuint" - when Prism::Template::IntegerField then "load_integer" - when Prism::Template::DoubleField then "load_double" - else raise - end - }].join(", ") -%>) + value = + <%= node.name %>.new( + source, + node_id, + location, + load_varuint, + <%- node.fields.map do |field| -%> + <%- case field -%> + <%- when Prism::Template::NodeField -%> + load_node(constant_pool, encoding, freeze), #: <%= field.rbs_class %> + <%- when Prism::Template::OptionalNodeField -%> + load_optional_node(constant_pool, encoding, freeze), #: <%= field.rbs_class %> + <%- when Prism::Template::StringField -%> + load_string(encoding), + <%- when Prism::Template::NodeListField -%> + Array.new(load_varuint) do + load_node(constant_pool, encoding, freeze) #: <%= field.element_rbs_class %> + end, + <%- when Prism::Template::ConstantField -%> + load_constant(constant_pool, encoding), + <%- when Prism::Template::OptionalConstantField -%> + load_optional_constant(constant_pool, encoding), + <%- when Prism::Template::ConstantListField -%> + Array.new(load_varuint) { load_constant(constant_pool, encoding) }, + <%- when Prism::Template::LocationField -%> + load_location(freeze), + <%- when Prism::Template::OptionalLocationField -%> + load_optional_location(freeze), + <%- when Prism::Template::UInt8Field -%> + (io.getbyte or raise), + <%- when Prism::Template::UInt32Field -%> + load_varuint, + <%- when Prism::Template::IntegerField -%> + load_integer, + <%- when Prism::Template::DoubleField -%> + load_double, + <%- else raise -%> + <%- end -%> + <%- end -%> + ) value.freeze if freeze value }, @@ -584,6 +680,10 @@ module Prism ] end end + + # @rbs! + # @load_node_lambdas: Array[Proc] + # def define_load_node_lambdas: () -> void end # The token types that can be indexed by their enum values. @@ -592,7 +692,7 @@ module Prism <%- tokens.each do |token| -%> <%= token.name.to_sym.inspect %>, <%- end -%> - ].freeze + ].freeze #: Array[Symbol?] private_constant :MAJOR_VERSION, :MINOR_VERSION, :PATCH_VERSION private_constant :ConstantPool, :FastStringIO, :Loader, :TOKEN_TYPES diff --git a/templates/lib/prism/visitor.rb.erb b/templates/lib/prism/visitor.rb.erb index 76f907724f..f23e87d99e 100644 --- a/templates/lib/prism/visitor.rb.erb +++ b/templates/lib/prism/visitor.rb.erb @@ -1,4 +1,14 @@ +#-- +# rbs_inline: enabled + module Prism + # @rbs! + # interface _Visitor + # <% nodes.each do |node| %> + # def visit_<%= node.human %>: (<%= node.name %>) -> void + # <% end %> + # end + # 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 @@ -6,18 +16,24 @@ module Prism 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. + #-- + #: (node? node) -> void def visit(node) # @type self: _Visitor node&.accept(self) end # Visits each node in `nodes` by calling `accept` on each one. + #-- + #: (Array[node?] nodes) -> void 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. + #-- + #: (node node) -> void def visit_child_nodes(node) # @type self: _Visitor node.each_child_node { |node| node.accept(self) } @@ -47,6 +63,8 @@ module Prism <%- nodes.each_with_index do |node, index| -%> <%= "\n" if index != 0 -%> # Visit a <%= node.name %> node + #-- + #: (<%= node.name %> node) -> void def visit_<%= node.human %>(node) node.each_child_node { |node| node.accept(self) } end diff --git a/templates/rbi/prism/dsl.rbi.erb b/templates/rbi/prism/dsl.rbi.erb deleted file mode 100644 index 76a39c5051..0000000000 --- a/templates/rbi/prism/dsl.rbi.erb +++ /dev/null @@ -1,68 +0,0 @@ -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 - <%- nodes.each do |node| -%> - <%- - params = [ - ["source", "default_source", "Prism::Source"], - ["node_id", "0", "Integer"], - ["location", "default_location", "Prism::Location"], - ["flags", "0", "Integer"] - ].concat(node.fields.map { |field| - case field - when Prism::Template::NodeField - kind = field.specific_kind || field.union_kind&.first - if kind.nil? - [field.name, "default_node(source, location)", field.rbi_class] - else - [field.name, %Q{#{kind.gsub(/(?<=.)[A-Z]/, "_\\0").downcase}(source: source)}, field.rbi_class] - end - when Prism::Template::OptionalNodeField - [field.name, "nil", field.rbi_class] - when Prism::Template::NodeListField - [field.name, "[]", field.rbi_class] - when Prism::Template::ConstantField - [field.name, ":\"\"", field.rbi_class] - when Prism::Template::OptionalConstantField - [field.name, "nil", field.rbi_class] - when Prism::Template::ConstantListField - [field.name, "[]", field.rbi_class] - when Prism::Template::StringField - [field.name, "\"\"", field.rbi_class] - when Prism::Template::LocationField - [field.name, "location", field.rbi_class] - when Prism::Template::OptionalLocationField - [field.name, "nil", field.rbi_class] - when Prism::Template::UInt8Field, Prism::Template::UInt32Field, Prism::Template::IntegerField - [field.name, "0", field.rbi_class] - when Prism::Template::DoubleField - [field.name, "0.0", field.rbi_class] - else - raise - end - }) - -%> - - sig { params(<%= params.map { |(name, _, type)| "#{name}: #{type}" }.join(", ") %>).returns(Prism::<%= node.name %>) } - def <%= node.human %>(<%= params.map { |(name, default, _)| "#{name}: #{default}" }.join(", ") %>); end - <%- end -%> - <%- flags.each do |flag| -%> - - sig { params(name: Symbol).returns(Integer) } - def <%= flag.human.chomp("s") %>(name); end - <%- 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/templates/rbi/prism/node.rbi.erb b/templates/rbi/prism/node.rbi.erb deleted file mode 100644 index 0c89b4524c..0000000000 --- a/templates/rbi/prism/node.rbi.erb +++ /dev/null @@ -1,167 +0,0 @@ -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 -<%- nodes.each do |node| -%> - -<%- node.each_comment_line do |line| -%> -#<%= line %> -<%- end -%> -class Prism::<%= node.name -%> < Prism::Node - <%- if (node_flags = node.flags) -%> - <%- node_flags.values.each do |value| -%> - sig { returns(T::Boolean) } - def <%= value.name.downcase %>?; end - - <%- end -%> - <%- end -%> - <%- node.fields.each do |field| -%> - sig { returns(<%= field.rbi_class %>) } - def <%= field.name %>; end - - <%- end -%> - sig { params(<%= ["source: Prism::Source", "node_id: Integer", "location: Prism::Location", "flags: Integer", *node.fields.map { |field| "#{field.name}: #{field.rbi_class}" }].join(", ") %>).void } - def initialize(<%= ["source", "node_id", "location", "flags", *node.fields.map(&:name)].join(", ") %>); 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"] + node.fields.map { |field| "#{field.name}: #{field.rbi_class}" }).join(", ") %>).returns(Prism::<%= node.name %>) } - def copy(<%= (["node_id", "location", "flags"] + node.fields.map(&:name)).map { |field| "#{field}: self.#{field}" }.join(", ") %>); end - - sig { params(keys: T.nilable(T::Array[Symbol])).returns(T::Hash[Symbol, T.untyped]) } - def deconstruct_keys(keys); end - <%- node.fields.each do |field| -%> - <%- case field -%> - <%- when Prism::Template::LocationField -%> - <%- raise unless field.name.end_with?("_loc") -%> - <%- next if node.fields.any? { |other| other.name == field.name.delete_suffix("_loc") } -%> - - sig { returns(String) } - def <%= field.name.delete_suffix("_loc") %>; end - <%- when Prism::Template::OptionalLocationField -%> - <%- raise unless field.name.end_with?("_loc") -%> - <%- next if node.fields.any? { |other| other.name == field.name.delete_suffix("_loc") } -%> - - sig { returns(T.nilable(String)) } - def <%= field.name.delete_suffix("_loc") %>; end - <%- end -%> - <%- 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 -<%- end -%> -<%- flags.each do |flag| -%> - -# <%= flag.comment %> -module Prism::<%= flag.name %> - <%- flag.values.each_with_index do |value, index| -%> - # <%= value.comment %> - <%= value.name %> = T.let(1 << <%= index + Prism::Template::COMMON_FLAGS_COUNT %>, Integer) - <%- end -%> -end -<%- 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/templates/rbi/prism/visitor.rbi.erb b/templates/rbi/prism/visitor.rbi.erb deleted file mode 100644 index dd42432186..0000000000 --- a/templates/rbi/prism/visitor.rbi.erb +++ /dev/null @@ -1,18 +0,0 @@ -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 - <%- nodes.each_with_index do |node, index| -%> -<%= "\n" if index != 0 -%> - sig { params(node: Prism::<%= node.name %>).void } - def visit_<%= node.human %>(node); end - <%- end -%> -end diff --git a/templates/sig/prism.rbs.erb b/templates/sig/prism.rbs.erb deleted file mode 100644 index ee35feccb8..0000000000 --- a/templates/sig/prism.rbs.erb +++ /dev/null @@ -1,92 +0,0 @@ -module Prism - BACKEND: :CEXT | :FFI - VERSION: String - - class CurrentVersionError < ArgumentError - def initialize: (String version) -> void - end - - # Methods taking a Ruby source code string: - <%- - { - parse: "ParseResult", - profile: "nil", - lex: "LexResult", - lex_compat: "LexCompat::Result", - parse_lex: "ParseLexResult", - dump: "String", - parse_comments: "Array[comment]", - parse_success?: "bool", - parse_failure?: "bool", - }.each do |method, return_type| - -%> - - def self.<%= method %>: ( - 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 - ) -> <%= return_type %> - <%- end -%> - - def self.load: ( - String source, - String serialized, - ?bool freeze - ) -> ParseResult - - # Methods taking a path to a Ruby file: - <%- - { - parse_file: "ParseResult", - profile_file: "nil", - lex_file: "LexResult", - parse_lex_file: "ParseLexResult", - dump_file: "String", - parse_file_comments: "Array[comment]", - parse_file_success?: "bool", - parse_file_failure?: "bool", - }.each do |method, return_type| - -%> - - def self.<%= method %>: ( - 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 - ) -> <%= return_type %> - <%- end -%> - - 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/templates/sig/prism/_private/dot_visitor.rbs.erb b/templates/sig/prism/_private/dot_visitor.rbs.erb deleted file mode 100644 index 4b380ecc97..0000000000 --- a/templates/sig/prism/_private/dot_visitor.rbs.erb +++ /dev/null @@ -1,45 +0,0 @@ -module Prism - class DotVisitor < Visitor - class Field - attr_reader name: String - attr_reader value: String? - attr_reader port: bool - - def initialize: (String name, String? value, bool port) -> void - def to_dot: () -> String - end - - class Table - attr_reader name: String - attr_reader fields: Array[Field] - - def initialize: (String name) -> void - def field: (String name, ?String? value, ?port: bool) -> void - def to_dot: () -> String - end - - class Digraph - attr_reader nodes: Array[String] - attr_reader waypoints: Array[String] - attr_reader edges: Array[String] - - def initialize: () -> void - def node: (String value) -> void - def waypoint: (String value) -> void - def edge: (String value) -> void - - def to_dot: () -> String - end - - attr_reader digraph: Digraph - - private - - def node_id: (Prism::node node) -> String - def location_inspect: (Location location) -> String - - <%- flags.each do |flag| -%> - def <%= flag.human %>_inspect: (<%= nodes.filter_map { |node| node.name if node.flags == flag }.join(" | ") %> node) -> String - <%- end -%> - end -end diff --git a/templates/sig/prism/dsl.rbs.erb b/templates/sig/prism/dsl.rbs.erb deleted file mode 100644 index 401467c0d8..0000000000 --- a/templates/sig/prism/dsl.rbs.erb +++ /dev/null @@ -1,31 +0,0 @@ -module Prism - module DSL - def source: (String string) -> Source - - def location: (?source: Source, ?start_offset: Integer, ?length: Integer) -> Location - <%- nodes.each do |node| -%> - <%- - params = [ - ["source", "Source"], - ["node_id", "Integer"], - ["location", "Location"], - ["flags", "Integer"] - ].concat(node.fields.map { |field| [field.name, field.rbs_class] }) - -%> - - def <%= node.human %>: (<%= params.map { |(name, type)| "?#{name}: #{type}" }.join(", ") %>) -> <%= node.name %> - <%- end -%> - <%- flags.each do |flag| -%> - - def <%= flag.human.chomp("s") %>: (Symbol name) -> Integer - <%- end -%> - - private - - def default_source: () -> Source - - def default_location: () -> Location - - def default_node: (Source source, Location location) -> node - end -end diff --git a/templates/sig/prism/mutation_compiler.rbs.erb b/templates/sig/prism/mutation_compiler.rbs.erb deleted file mode 100644 index 303269ba49..0000000000 --- a/templates/sig/prism/mutation_compiler.rbs.erb +++ /dev/null @@ -1,7 +0,0 @@ -module Prism - class MutationCompiler < Compiler - <%- nodes.each do |node| -%> - def visit_<%= node.human %>: (<%= node.name %>) -> node? - <%- end -%> - end -end diff --git a/templates/sig/prism/node.rbs.erb b/templates/sig/prism/node.rbs.erb deleted file mode 100644 index 138edc11f4..0000000000 --- a/templates/sig/prism/node.rbs.erb +++ /dev/null @@ -1,136 +0,0 @@ -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 - - <%- nodes.each do |node| -%> - - <%- node.each_comment_line do |line| -%> - #<%= line %> - <%- end -%> - class <%= node.name -%> < Node - include _Node - <%- if (node_flags = node.flags) -%> - - <%- node_flags.values.each do |value| -%> - def <%= value.name.downcase %>?: () -> bool - <%- end -%> - <%- end -%> - - <%- node.fields.each do |field| -%> - attr_reader <%= field.name %>: <%= field.rbs_class %> - <%- end -%> - <%- if (locations = node.fields.select { |field| field.is_a?(Prism::Template::LocationField) || field.is_a?(Prism::Template::OptionalLocationField) }) -%> - - <%- locations.each do |field| -%> - def save_<%= field.name %>: (_Repository repository) -> void - <%- end -%> - <%- end -%> - - def initialize: (<%= ["Source source", "Integer node_id", "Location location", "Integer flags", *node.fields.map { |field| "#{field.rbs_class} #{field.name}" }].join(", ") %>) -> void - def copy: (<%= (["?node_id: Integer", "?location: Location", "?flags: Integer"] + node.fields.map { |field| "?#{field.name}: #{field.rbs_class}" }).join(", ") %>) -> <%= node.name %> - def deconstruct_keys: (Array[Symbol] keys) -> { <%= (["node_id: Integer", "location: Location"] + node.fields.map { |field| "#{field.name}: #{field.rbs_class}" }).join(", ") %> } - <%- node.fields.each do |field| -%> - <%- case field -%> - <%- when Prism::Template::LocationField -%> - <%- raise unless field.name.end_with?("_loc") -%> - <%- next if node.fields.any? { |other| other.name == field.name.delete_suffix("_loc") } -%> - def <%= field.name.delete_suffix("_loc") %>: () -> String - <%- when Prism::Template::OptionalLocationField -%> - <%- raise unless field.name.end_with?("_loc") -%> - <%- next if node.fields.any? { |other| other.name == field.name.delete_suffix("_loc") } -%> - def <%= field.name.delete_suffix("_loc") %>: () -> String? - <%- end -%> - <%- end -%> - def type: () -> :<%= node.human %> - | ... - def self.type: () -> :<%= node.human %> - end - <%- end -%> - <%- flags.each do |flag| -%> - - # <%= flag.comment %> - module <%= flag.name %> - <%- flag.values.each do |value| -%> - # <%= value.comment %> - <%= value.name %>: Integer - <%- end -%> - end - <%- end -%> - - # The flags that are common to all nodes. - module NodeFlags - NEWLINE: Integer - STATIC_LITERAL: Integer - end -end diff --git a/templates/sig/prism/visitor.rbs.erb b/templates/sig/prism/visitor.rbs.erb deleted file mode 100644 index 2e8de030f6..0000000000 --- a/templates/sig/prism/visitor.rbs.erb +++ /dev/null @@ -1,17 +0,0 @@ -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 - <%- nodes.each do |node| -%> - def visit_<%= node.human %>: (<%= node.name %>) -> void - <%- end -%> - end - - class Visitor < BasicVisitor - include _Visitor - end -end diff --git a/templates/src/diagnostic.c.erb b/templates/src/diagnostic.c.erb index 88f8525f80..0dea732869 100644 --- a/templates/src/diagnostic.c.erb +++ b/templates/src/diagnostic.c.erb @@ -1,4 +1,16 @@ -#include "prism/diagnostic.h" +#include "prism/internal/diagnostic.h" + +#include "prism/compiler/inline.h" + +#include "prism/internal/allocator.h" +#include "prism/internal/arena.h" +#include "prism/internal/list.h" + +#include +#include +#include +#include +#include #define PM_DIAGNOSTIC_ID_MAX <%= errors.length + warnings.length %> @@ -75,16 +87,16 @@ typedef struct { * * `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 + /* 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 + /* 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 + /* 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 + /* 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 }, @@ -102,6 +114,8 @@ static const pm_diagnostic_data_t diagnostic_messages[PM_DIAGNOSTIC_ID_MAX] = { [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_ELLIPSES_LAMBDA] = { "unexpected ... in lambda argument", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_ARGUMENT_NO_FORWARDING_ELLIPSES_BLOCK] = { "unexpected ... in block argument", 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 }, @@ -146,6 +160,7 @@ static const pm_diagnostic_data_t diagnostic_messages[PM_DIAGNOSTIC_ID_MAX] = { [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_ENDLESS_DO_BLOCK] = { "unexpected `do` for block 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 }, @@ -327,13 +342,15 @@ static const pm_diagnostic_data_t diagnostic_messages[PM_DIAGNOSTIC_ID_MAX] = { [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_ESCAPED_NON_ASCII_IN_UTF8] = { "escaped non ASCII character in UTF-8 regexp: /%.*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_CHAR_PROPERTY] = { "invalid character property name {%.*s}: /%.*s/", PM_ERROR_LEVEL_SYNTAX }, [PM_ERR_REGEXP_INVALID_UNICODE_RANGE] = { "invalid Unicode range: /%.*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_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_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 }, @@ -348,7 +365,7 @@ static const pm_diagnostic_data_t diagnostic_messages[PM_DIAGNOSTIC_ID_MAX] = { [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_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 }, @@ -375,7 +392,7 @@ static const pm_diagnostic_data_t diagnostic_messages[PM_DIAGNOSTIC_ID_MAX] = { [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 + /* 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 }, @@ -411,8 +428,8 @@ static const pm_diagnostic_data_t diagnostic_messages[PM_DIAGNOSTIC_ID_MAX] = { /** * Get the human-readable name of the given diagnostic ID. */ -const char * -pm_diagnostic_id_human(pm_diagnostic_id_t diag_id) { +static const char * +pm_diagnostic_id_name(pm_diagnostic_id_t diag_id) { switch (diag_id) { <%- errors.each do |error| -%> case PM_ERR_<%= error.name %>: return "<%= error.name.downcase %>"; @@ -426,8 +443,8 @@ pm_diagnostic_id_human(pm_diagnostic_id_t diag_id) { return ""; } -static inline const char * -pm_diagnostic_message(pm_diagnostic_id_t diag_id) { +static PRISM_INLINE const char * +pm_diagnostic_id_message(pm_diagnostic_id_t diag_id) { assert(diag_id < PM_DIAGNOSTIC_ID_MAX); const char *message = diagnostic_messages[diag_id].message; @@ -436,61 +453,91 @@ pm_diagnostic_message(pm_diagnostic_id_t diag_id) { return message; } -static inline uint8_t -pm_diagnostic_level(pm_diagnostic_id_t diag_id) { +static PRISM_INLINE uint8_t +pm_diagnostic_id_level(pm_diagnostic_id_t diag_id) { assert(diag_id < PM_DIAGNOSTIC_ID_MAX); return (uint8_t) diagnostic_messages[diag_id].level; } +/** + * Get the type of the given diagnostic. + */ +const char * +pm_diagnostic_type(const pm_diagnostic_t *diagnostic) { + return pm_diagnostic_id_name(diagnostic->diag_id); +} + +/** + * Get the location of the given diagnostic. + */ +pm_location_t +pm_diagnostic_location(const pm_diagnostic_t *diagnostic) { + return diagnostic->location; +} + +/** + * Get the message of the given diagnostic. + */ +const char * +pm_diagnostic_message(const pm_diagnostic_t *diagnostic) { + return diagnostic->message; +} + +/** + * Get the error level associated with the given diagnostic. + */ +pm_error_level_t +pm_diagnostic_error_level(const pm_diagnostic_t *diagnostic) { + return (pm_error_level_t) pm_diagnostic_id_level(diagnostic->diag_id); +} + +/** + * Get the warning level associated with the given diagnostic. + */ +pm_warning_level_t +pm_diagnostic_warning_level(const pm_diagnostic_t *diagnostic) { + return (pm_warning_level_t) pm_diagnostic_id_level(diagnostic->diag_id); +} + /** * Append an error to the given list of diagnostic. */ -bool -pm_diagnostic_list_append(pm_list_t *list, uint32_t start, uint32_t length, pm_diagnostic_id_t diag_id) { - pm_diagnostic_t *diagnostic = (pm_diagnostic_t *) xcalloc(1, sizeof(pm_diagnostic_t)); - if (diagnostic == NULL) return false; +void +pm_diagnostic_list_append(pm_arena_t *arena, pm_list_t *list, uint32_t start, uint32_t length, pm_diagnostic_id_t diag_id) { + pm_diagnostic_t *diagnostic = (pm_diagnostic_t *) pm_arena_zalloc(arena, sizeof(pm_diagnostic_t), PRISM_ALIGNOF(pm_diagnostic_t)); *diagnostic = (pm_diagnostic_t) { .location = { .start = start, .length = length }, .diag_id = diag_id, - .message = pm_diagnostic_message(diag_id), - .owned = false, - .level = pm_diagnostic_level(diag_id) + .message = pm_diagnostic_id_message(diag_id), + .level = pm_diagnostic_id_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, uint32_t start, uint32_t length, pm_diagnostic_id_t diag_id, ...) { +void +pm_diagnostic_list_append_format(pm_arena_t *arena, pm_list_t *list, uint32_t start, uint32_t length, pm_diagnostic_id_t diag_id, ...) { va_list arguments; va_start(arguments, diag_id); - const char *format = pm_diagnostic_message(diag_id); + const char *format = pm_diagnostic_id_message(diag_id); int result = vsnprintf(NULL, 0, format, arguments); va_end(arguments); if (result < 0) { - return false; + return; } - pm_diagnostic_t *diagnostic = (pm_diagnostic_t *) xcalloc(1, sizeof(pm_diagnostic_t)); - if (diagnostic == NULL) { - return false; - } + pm_diagnostic_t *diagnostic = (pm_diagnostic_t *) pm_arena_zalloc(arena, sizeof(pm_diagnostic_t), PRISM_ALIGNOF(pm_diagnostic_t)); size_t message_length = (size_t) (result + 1); - char *message = (char *) xmalloc(message_length); - if (message == NULL) { - xfree(diagnostic); - return false; - } + char *message = (char *) pm_arena_alloc(arena, message_length, 1); va_start(arguments, diag_id); vsnprintf(message, message_length, format, arguments); @@ -500,27 +547,8 @@ pm_diagnostic_list_append_format(pm_list_t *list, uint32_t start, uint32_t lengt .location = { .start = start, .length = length }, .diag_id = diag_id, .message = message, - .owned = true, - .level = pm_diagnostic_level(diag_id) + .level = pm_diagnostic_id_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/templates/src/json.c.erb b/templates/src/json.c.erb new file mode 100644 index 0000000000..5c4ab8d92a --- /dev/null +++ b/templates/src/json.c.erb @@ -0,0 +1,130 @@ +#include "prism/json.h" + +// Ensure this translation unit is never empty, even when JSON is excluded. +typedef int pm_json_unused_t; + +#ifndef PRISM_EXCLUDE_JSON + +#include "prism/internal/buffer.h" +#include "prism/internal/constant_pool.h" +#include "prism/internal/integer.h" +#include "prism/internal/parser.h" + +#include + +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_location_t *location) { + pm_buffer_append_format(buffer, "{\"start\":%" PRIu32 ",\"length\":%" PRIu32 "}", location->start, location->length); +} + +/** + * Dump JSON to the given buffer. + */ +void +pm_dump_json(pm_buffer_t *buffer, const pm_parser_t *parser, const pm_node_t *node) { + switch (PM_NODE_TYPE(node)) { + <%- nodes.each do |node| -%> + case <%= node.type %>: { + pm_buffer_append_string(buffer, "{\"type\":\"<%= node.name %>\",\"location\":", <%= node.name.bytesize + 22 %>); + + const pm_<%= node.human %>_t *cast = (const pm_<%= node.human %>_t *) node; + pm_dump_json_location(buffer, &cast->base.location); + <%- [*node.flags, *node.fields].each_with_index do |field, index| -%> + + // Dump the <%= field.name %> field + pm_buffer_append_byte(buffer, ','); + <%- if field.is_a?(Prism::Template::Flags) -%> + pm_buffer_append_string(buffer, "\"flags\":", 8); + <%- else -%> + pm_buffer_append_string(buffer, "\"<%= field.name %>\":", <%= field.name.bytesize + 3 %>); + <%- end -%> + <%- case field -%> + <%- when Prism::Template::NodeField -%> + pm_dump_json(buffer, parser, (const pm_node_t *) cast-><%= field.name %>); + <%- when Prism::Template::OptionalNodeField -%> + if (cast-><%= field.name %> != NULL) { + pm_dump_json(buffer, parser, (const pm_node_t *) cast-><%= field.name %>); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + <%- when Prism::Template::NodeListField -%> + const pm_node_list_t *<%= field.name %> = &cast-><%= field.name %>; + pm_buffer_append_byte(buffer, '['); + + for (size_t index = 0; index < <%= field.name %>->size; index++) { + if (index != 0) pm_buffer_append_byte(buffer, ','); + pm_dump_json(buffer, parser, <%= field.name %>->nodes[index]); + } + pm_buffer_append_byte(buffer, ']'); + <%- when Prism::Template::StringField -%> + const pm_string_t *<%= field.name %> = &cast-><%= field.name %>; + pm_buffer_append_byte(buffer, '"'); + pm_buffer_append_source(buffer, pm_string_source(<%= field.name %>), pm_string_length(<%= field.name %>), PM_BUFFER_ESCAPING_JSON); + pm_buffer_append_byte(buffer, '"'); + <%- when Prism::Template::ConstantField -%> + pm_dump_json_constant(buffer, parser, cast-><%= field.name %>); + <%- when Prism::Template::OptionalConstantField -%> + if (cast-><%= field.name %> != PM_CONSTANT_ID_UNSET) { + pm_dump_json_constant(buffer, parser, cast-><%= field.name %>); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + <%- when Prism::Template::ConstantListField -%> + const pm_constant_id_list_t *<%= field.name %> = &cast-><%= field.name %>; + pm_buffer_append_byte(buffer, '['); + + for (size_t index = 0; index < <%= field.name %>->size; index++) { + if (index != 0) pm_buffer_append_byte(buffer, ','); + pm_dump_json_constant(buffer, parser, <%= field.name %>->ids[index]); + } + pm_buffer_append_byte(buffer, ']'); + <%- when Prism::Template::LocationField -%> + pm_dump_json_location(buffer, &cast-><%= field.name %>); + <%- when Prism::Template::OptionalLocationField -%> + if (cast-><%= field.name %>.length != 0) { + pm_dump_json_location(buffer, &cast-><%= field.name %>); + } else { + pm_buffer_append_string(buffer, "null", 4); + } + <%- when Prism::Template::UInt8Field -%> + pm_buffer_append_format(buffer, "%" PRIu8, cast-><%= field.name %>); + <%- when Prism::Template::UInt32Field -%> + pm_buffer_append_format(buffer, "%" PRIu32, cast-><%= field.name %>); + <%- when Prism::Template::Flags -%> + size_t flags = 0; + pm_buffer_append_byte(buffer, '['); + <%- node.flags.values.each_with_index do |value, index| -%> + if (PM_NODE_FLAG_P(cast, PM_<%= node.flags.human.upcase %>_<%= value.name %>)) { + if (flags != 0) pm_buffer_append_byte(buffer, ','); + pm_buffer_append_string(buffer, "\"<%= value.name %>\"", <%= value.name.bytesize + 2 %>); + flags++; + } + <%- end -%> + pm_buffer_append_byte(buffer, ']'); + <%- when Prism::Template::IntegerField -%> + pm_integer_string(buffer, &cast-><%= field.name %>); + <%- when Prism::Template::DoubleField -%> + pm_buffer_append_format(buffer, "%f", cast-><%= field.name %>); + <%- else -%> + <%- raise %> + <%- end -%> + <%- end -%> + + pm_buffer_append_byte(buffer, '}'); + break; + } + <%- end -%> + case PM_SCOPE_NODE: + break; + } +} + +#endif diff --git a/templates/src/node.c.erb b/templates/src/node.c.erb index f1709a0249..f51aff6e53 100644 --- a/templates/src/node.c.erb +++ b/templates/src/node.c.erb @@ -1,153 +1,85 @@ #line <%= __LINE__ + 1 %> "prism/templates/src/<%= File.basename(__FILE__) %>" -#include "prism/node.h" +#include "prism/internal/node.h" + +#include "prism/internal/arena.h" + +#include /** * 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. + * capacity in the list, this function does nothing. Otherwise it allocates a + * new array from the arena (abandon-and-copy strategy) and copies the existing + * data into it. */ -static bool -pm_node_list_grow(pm_node_list_t *list, size_t size) { +static void +pm_node_list_grow(pm_arena_t *arena, 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; + // Guard against overflow on the addition. + if (requested_size < list->size) abort(); - // If the requested size is within the existing capacity, return true. - if (requested_size < list->capacity) return true; + // If the requested size is within the existing capacity, return. + if (requested_size <= list->capacity) return; - // Otherwise, reallocate the list to be twice as large as it was before. + // Otherwise, compute the next capacity by doubling. 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. + // Guard against overflow on the doubling. 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; + if (next_capacity == 0) abort(); + next_capacity *= 2; } - pm_node_t **nodes = (pm_node_t **) xrealloc(list->nodes, sizeof(pm_node_t *) * next_capacity); - if (nodes == NULL) return false; + // Allocate a new array from the arena (old array is abandoned). + pm_node_t **nodes = (pm_node_t **) pm_arena_alloc(arena, sizeof(pm_node_t *) * next_capacity, PRISM_ALIGNOF(pm_node_t *)); + + // Copy old data into the new array. + if (list->size > 0) { + memcpy(nodes, list->nodes, list->size * sizeof(pm_node_t *)); + } list->nodes = nodes; list->capacity = next_capacity; - return true; } /** - * Append a new node onto the end of the node list. + * Slow path for pm_node_list_append: grow the list and append the node. + * Do not call directly - use pm_node_list_append instead. */ 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; - } +pm_node_list_append_slow(pm_arena_t *arena, pm_node_list_t *list, pm_node_t *node) { + pm_node_list_grow(arena, 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++; - } +pm_node_list_prepend(pm_arena_t *arena, pm_node_list_t *list, pm_node_t *node) { + pm_node_list_grow(arena, 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)) { +pm_node_list_concat(pm_arena_t *arena, pm_node_list_t *list, pm_node_list_t *other) { + if (other->size > 0) { + pm_node_list_grow(arena, list, other->size); memcpy(list->nodes + list->size, other->nodes, other->size * sizeof(pm_node_t *)); list->size += other->size; } } -/** - * Free the internal memory associated with the given node list. - */ -void -pm_node_list_free(pm_node_list_t *list) { - if (list->capacity > 0) { - xfree(list->nodes); - *list = (pm_node_list_t) { 0 }; - } -} - -PRISM_EXPORTED_FUNCTION void -pm_node_destroy(pm_parser_t *parser, pm_node_t *node); - -/** - * Destroy the nodes that are contained within the given node list. - */ -static void -pm_node_list_destroy(pm_parser_t *parser, pm_node_list_t *list) { - pm_node_t *node; - PM_NODE_LIST_FOREACH(list, index, node) pm_node_destroy(parser, node); - pm_node_list_free(list); -} - -/** - * Deallocate the space for a pm_node_t. Similarly to pm_node_alloc, we're not - * using the parser argument, but it's there to allow for the future possibility - * of pre-allocating larger memory pools. - */ -PRISM_EXPORTED_FUNCTION void -pm_node_destroy(pm_parser_t *parser, pm_node_t *node) { - switch (PM_NODE_TYPE(node)) { - <%- nodes.each do |node| -%> -#line <%= __LINE__ + 1 %> "prism/templates/src/<%= File.basename(__FILE__) %>" - case <%= node.type %>: { - <%- if node.fields.any? { |field| ![Prism::Template::LocationField, Prism::Template::OptionalLocationField, Prism::Template::UInt8Field, Prism::Template::UInt32Field, Prism::Template::ConstantField, Prism::Template::OptionalConstantField, Prism::Template::DoubleField].include?(field.class) } -%> - pm_<%= node.human %>_t *cast = (pm_<%= node.human %>_t *) node; - <%- end -%> - <%- node.fields.each do |field| -%> - <%- case field -%> - <%- when Prism::Template::LocationField, Prism::Template::OptionalLocationField, Prism::Template::UInt8Field, Prism::Template::UInt32Field, Prism::Template::ConstantField, Prism::Template::OptionalConstantField, Prism::Template::DoubleField -%> - <%- when Prism::Template::NodeField -%> - pm_node_destroy(parser, (pm_node_t *)cast-><%= field.name %>); - <%- when Prism::Template::OptionalNodeField -%> - if (cast-><%= field.name %> != NULL) { - pm_node_destroy(parser, (pm_node_t *)cast-><%= field.name %>); - } - <%- when Prism::Template::StringField -%> - pm_string_free(&cast-><%= field.name %>); - <%- when Prism::Template::NodeListField -%> - pm_node_list_destroy(parser, &cast-><%= field.name %>); - <%- when Prism::Template::ConstantListField -%> - pm_constant_id_list_free(&cast-><%= field.name %>); - <%- when Prism::Template::IntegerField -%> - pm_integer_free(&cast-><%= field.name %>); - <%- else -%> - <%- raise -%> - <%- end -%> - <%- end -%> - break; - } - <%- end -%> -#line <%= __LINE__ + 1 %> "prism/templates/src/<%= File.basename(__FILE__) %>" - 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) +const char * +pm_node_type(pm_node_type_t node_type) { switch (node_type) { <%- nodes.each do |node| -%> @@ -166,7 +98,7 @@ pm_node_type_to_str(pm_node_type_t node_type) * pointer and is passed to the visitor callback for consumers to use as they * see fit. */ -PRISM_EXPORTED_FUNCTION void +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); } @@ -176,7 +108,7 @@ pm_visit_node(const pm_node_t *node, bool (*visitor)(const pm_node_t *node, void * default behavior for walking the tree that is called from pm_visit_node if * the callback returns true. */ -PRISM_EXPORTED_FUNCTION void +void pm_visit_child_nodes(const pm_node_t *node, bool (*visitor)(const pm_node_t *node, void *data), void *data) { switch (PM_NODE_TYPE(node)) { <%- nodes.each do |node| -%> @@ -212,120 +144,23 @@ pm_visit_child_nodes(const pm_node_t *node, bool (*visitor)(const pm_node_t *nod break; } } +<%- nodes.each do |node| -%> -// 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_location_t *location) { - pm_buffer_append_format(buffer, "{\"start\":%" PRIu32 ",\"length\":%" PRIu32 "}", location->start, location->length); -} - +<%- params = node.fields.map(&:c_param) -%> /** - * Dump JSON to the given buffer. + * Allocate and initialize a new <%= node.name %> node. */ -PRISM_EXPORTED_FUNCTION void -pm_dump_json(pm_buffer_t *buffer, const pm_parser_t *parser, const pm_node_t *node) { - switch (PM_NODE_TYPE(node)) { - <%- nodes.each do |node| -%> - case <%= node.type %>: { - pm_buffer_append_string(buffer, "{\"type\":\"<%= node.name %>\",\"location\":", <%= node.name.bytesize + 22 %>); - - const pm_<%= node.human %>_t *cast = (const pm_<%= node.human %>_t *) node; - pm_dump_json_location(buffer, &cast->base.location); - <%- [*node.flags, *node.fields].each_with_index do |field, index| -%> - - // Dump the <%= field.name %> field - pm_buffer_append_byte(buffer, ','); - pm_buffer_append_string(buffer, "\"<%= field.name %>\":", <%= field.name.bytesize + 3 %>); - <%- case field -%> - <%- when Prism::Template::NodeField -%> - pm_dump_json(buffer, parser, (const pm_node_t *) cast-><%= field.name %>); - <%- when Prism::Template::OptionalNodeField -%> - if (cast-><%= field.name %> != NULL) { - pm_dump_json(buffer, parser, (const pm_node_t *) cast-><%= field.name %>); - } else { - pm_buffer_append_string(buffer, "null", 4); - } - <%- when Prism::Template::NodeListField -%> - const pm_node_list_t *<%= field.name %> = &cast-><%= field.name %>; - pm_buffer_append_byte(buffer, '['); - - for (size_t index = 0; index < <%= field.name %>->size; index++) { - if (index != 0) pm_buffer_append_byte(buffer, ','); - pm_dump_json(buffer, parser, <%= field.name %>->nodes[index]); - } - pm_buffer_append_byte(buffer, ']'); - <%- when Prism::Template::StringField -%> - const pm_string_t *<%= field.name %> = &cast-><%= field.name %>; - pm_buffer_append_byte(buffer, '"'); - pm_buffer_append_source(buffer, pm_string_source(<%= field.name %>), pm_string_length(<%= field.name %>), PM_BUFFER_ESCAPING_JSON); - pm_buffer_append_byte(buffer, '"'); - <%- when Prism::Template::ConstantField -%> - pm_dump_json_constant(buffer, parser, cast-><%= field.name %>); - <%- when Prism::Template::OptionalConstantField -%> - if (cast-><%= field.name %> != PM_CONSTANT_ID_UNSET) { - pm_dump_json_constant(buffer, parser, cast-><%= field.name %>); - } else { - pm_buffer_append_string(buffer, "null", 4); - } - <%- when Prism::Template::ConstantListField -%> - const pm_constant_id_list_t *<%= field.name %> = &cast-><%= field.name %>; - pm_buffer_append_byte(buffer, '['); - - for (size_t index = 0; index < <%= field.name %>->size; index++) { - if (index != 0) pm_buffer_append_byte(buffer, ','); - pm_dump_json_constant(buffer, parser, <%= field.name %>->ids[index]); - } - pm_buffer_append_byte(buffer, ']'); - <%- when Prism::Template::LocationField -%> - pm_dump_json_location(buffer, &cast-><%= field.name %>); - <%- when Prism::Template::OptionalLocationField -%> - if (cast-><%= field.name %>.length != 0) { - pm_dump_json_location(buffer, &cast-><%= field.name %>); - } else { - pm_buffer_append_string(buffer, "null", 4); - } - <%- when Prism::Template::UInt8Field -%> - pm_buffer_append_format(buffer, "%" PRIu8, cast-><%= field.name %>); - <%- when Prism::Template::UInt32Field -%> - pm_buffer_append_format(buffer, "%" PRIu32, cast-><%= field.name %>); - <%- when Prism::Template::Flags -%> - size_t flags = 0; - pm_buffer_append_byte(buffer, '['); - <%- node.flags.values.each_with_index do |value, index| -%> - if (PM_NODE_FLAG_P(cast, PM_<%= node.flags.human.upcase %>_<%= value.name %>)) { - if (flags != 0) pm_buffer_append_byte(buffer, ','); - pm_buffer_append_string(buffer, "\"<%= value.name %>\"", <%= value.name.bytesize + 2 %>); - flags++; - } - <%- end -%> - pm_buffer_append_byte(buffer, ']'); - <%- when Prism::Template::IntegerField -%> - pm_integer_string(buffer, &cast-><%= field.name %>); - <%- when Prism::Template::DoubleField -%> - pm_buffer_append_format(buffer, "%f", cast-><%= field.name %>); - <%- else -%> - <%- raise %> - <%- end -%> - <%- end -%> +pm_<%= node.human %>_t * +pm_<%= node.human %>_new(pm_arena_t *arena, uint32_t node_id, pm_node_flags_t flags, pm_location_t location<%= params.empty? ? "" : ", #{params.join(", ")}" %>) { + pm_<%= node.human %>_t *node = (pm_<%= node.human %>_t *) pm_arena_alloc(arena, sizeof(pm_<%= node.human %>_t), PRISM_ALIGNOF(pm_<%= node.human %>_t)); + + *node = (pm_<%= node.human %>_t) { + .base = { .type = <%= node.type %>, .flags = flags, .node_id = node_id, .location = location }<%= node.fields.empty? ? "" : "," %> +<%- node.fields.each_with_index do |field, index| -%> + .<%= field.name %> = <%= field.name %><%= index < node.fields.size - 1 ? "," : "" %> +<%- end -%> + }; - pm_buffer_append_byte(buffer, '}'); - break; - } - <%- end -%> - case PM_SCOPE_NODE: - break; - } + return node; } - -#endif +<%- end -%> diff --git a/templates/src/prettyprint.c.erb b/templates/src/prettyprint.c.erb index 74c0f6dbdf..f12531d934 100644 --- a/templates/src/prettyprint.c.erb +++ b/templates/src/prettyprint.c.erb @@ -1,23 +1,34 @@ <%# encoding: ASCII -%> #include "prism/prettyprint.h" -// We optionally support pretty printing nodes. For systems that don't want or -// need this functionality, it can be turned off with the -// PRISM_EXCLUDE_PRETTYPRINT define. +/* 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) {} +/* Ensure this translation unit is never empty, even when prettyprint is + * excluded. */ +typedef int pm_prettyprint_unused_t; #else -static inline void +#include "prism/compiler/inline.h" +#include "prism/internal/buffer.h" +#include "prism/internal/constant_pool.h" +#include "prism/internal/integer.h" +#include "prism/internal/parser.h" +#include "prism/line_offset_list.h" + +#include + +static PRISM_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->start + location->length, parser->start_line); + pm_line_column_t start = pm_line_offset_list_line_column(&parser->line_offsets, location->start, parser->start_line); + pm_line_column_t end = pm_line_offset_list_line_column(&parser->line_offsets, location->start + location->length, parser->start_line); pm_buffer_append_format(output_buffer, "(%" PRIi32 ",%" PRIu32 ")-(%" PRIi32 ",%" PRIu32 ")", start.line, start.column, end.line, end.column); } -static inline void +static PRISM_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); @@ -156,11 +167,11 @@ prettyprint_node(pm_buffer_t *output_buffer, const pm_parser_t *parser, const pm /** * Pretty-prints the AST represented by the given node to the given buffer. */ -PRISM_EXPORTED_FUNCTION void +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); + pm_buffer_cleanup(&prefix_buffer); } #endif diff --git a/templates/src/serialize.c.erb b/templates/src/serialize.c.erb index 958b0fd7cf..3d9811e5db 100644 --- a/templates/src/serialize.c.erb +++ b/templates/src/serialize.c.erb @@ -1,19 +1,42 @@ -#include "prism.h" +#include "prism/excludes.h" + +/* We optionally support serializing to a binary string. For systems that do not + * want or need this functionality, it can be turned off with the + * PRISM_EXCLUDE_SERIALIZATION define. */ +#ifdef PRISM_EXCLUDE_SERIALIZATION + +/* Ensure this translation unit is never empty, even when serialization is + * excluded. */ +typedef int pm_serialize_unused_t; + +#else + +#include "prism/compiler/inline.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 "prism/internal/buffer.h" +#include "prism/internal/comments.h" +#include "prism/internal/diagnostic.h" +#include "prism/internal/encoding.h" +#include "prism/internal/list.h" +#include "prism/internal/magic_comments.h" +#include "prism/internal/options.h" +#include "prism/internal/parser.h" +#include "prism.h" +#include "prism/ast.h" +#include "prism/line_offset_list.h" + +#include #include +#include -static inline uint32_t +static PRISM_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 +static PRISM_INLINE uint32_t pm_sizet_to_u32(size_t value) { assert(value < UINT32_MAX); return (uint32_t) value; @@ -26,28 +49,10 @@ pm_serialize_location(const pm_location_t *location, pm_buffer_t *buffer) { } 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 - } +pm_serialize_string(const pm_string_t *string, pm_buffer_t *buffer) { + uint32_t length = pm_sizet_to_u32(pm_string_length(string)); + pm_buffer_append_varuint(buffer, length); + pm_buffer_append_bytes(buffer, pm_string_source(string), length); } static void @@ -68,8 +73,6 @@ 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; - <%- if Prism::Template::INCLUDE_NODE_ID -%> pm_buffer_append_varuint(buffer, node->node_id); <%- end -%> @@ -102,7 +105,7 @@ pm_serialize_node(pm_parser_t *parser, pm_node_t *node, pm_buffer_t *buffer) { pm_serialize_node(parser, (pm_node_t *)((pm_<%= node.human %>_t *)node)-><%= field.name %>, buffer); } <%- when Prism::Template::StringField -%> - pm_serialize_string(parser, &((pm_<%= node.human %>_t *)node)-><%= field.name %>, buffer); + pm_serialize_string(&((pm_<%= node.human %>_t *)node)-><%= field.name %>, buffer); <%- when Prism::Template::NodeListField -%> uint32_t <%= field.name %>_size = pm_sizet_to_u32(((pm_<%= node.human %>_t *)node)-><%= field.name %>.size); pm_buffer_append_varuint(buffer, <%= field.name %>_size); @@ -144,7 +147,7 @@ pm_serialize_node(pm_parser_t *parser, pm_node_t *node, pm_buffer_t *buffer) { <%- end -%> <%- if node.needs_serialized_length? -%> // serialize length - uint32_t length = pm_sizet_to_u32(buffer->length - offset - sizeof(uint32_t)); + uint32_t length = pm_sizet_to_u32(buffer->length - length_offset); memcpy(buffer->value + length_offset, &length, sizeof(uint32_t)); <%- end -%> break; @@ -154,7 +157,7 @@ pm_serialize_node(pm_parser_t *parser, pm_node_t *node, pm_buffer_t *buffer) { } static void -pm_serialize_newline_list(pm_newline_list_t *list, pm_buffer_t *buffer) { +pm_serialize_line_offset_list(pm_line_offset_list_t *list, pm_buffer_t *buffer) { uint32_t size = pm_sizet_to_u32(list->size); pm_buffer_append_varuint(buffer, size); @@ -257,7 +260,7 @@ 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_line_offset_list(&parser->line_offsets, buffer); <%- unless Prism::Template::SERIALIZE_ONLY_SEMANTICS_FIELDS -%> pm_serialize_comment_list(&parser->comment_list, buffer); <%- end -%> @@ -265,6 +268,7 @@ pm_serialize_metadata(pm_parser_t *parser, pm_buffer_t *buffer) { pm_serialize_data_loc(parser, buffer); pm_serialize_diagnostic_list(&parser->error_list, buffer); pm_serialize_diagnostic_list(&parser->warning_list, buffer); + pm_buffer_append_byte(buffer, (uint8_t) parser->continuable); } #line <%= __LINE__ + 1 %> "prism/templates/src/<%= File.basename(__FILE__) %>" @@ -304,28 +308,12 @@ pm_serialize_content(pm_parser_t *parser, pm_node_t *node, pm_buffer_t *buffer) 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); - } + // Write the constant contents into the buffer after the constant + // pool. In place of the source offset, we store a buffer offset. + uint32_t content_offset = pm_sizet_to_u32(buffer->length); + memcpy(buffer->value + buffer_offset, &content_offset, 4); + pm_buffer_append_bytes(buffer, constant->start, constant->length); - // 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); } @@ -333,7 +321,7 @@ pm_serialize_content(pm_parser_t *parser, pm_node_t *node, pm_buffer_t *buffer) } static void -serialize_token(void *data, pm_parser_t *parser, pm_token_t *token) { +serialize_token(pm_parser_t *parser, pm_token_t *token, void *data) { pm_buffer_t *buffer = (pm_buffer_t *) data; pm_buffer_append_varuint(buffer, token->type); @@ -345,58 +333,72 @@ serialize_token(void *data, pm_parser_t *parser, pm_token_t *token) { /** * Lex the given source and serialize to the given buffer. */ -PRISM_EXPORTED_FUNCTION void +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_arena_t arena = { 0 }; pm_parser_t parser; - pm_parser_init(&parser, source, size, &options); + pm_parser_init(&arena, &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_parser_lex_callback_set(&parser, serialize_token, buffer); + 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); + pm_parser_cleanup(&parser); + pm_arena_cleanup(&arena); + pm_options_cleanup(&options); } /** * Parse and serialize both the AST and the tokens represented by the given * source to the given buffer. */ -PRISM_EXPORTED_FUNCTION void +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_arena_t arena = { 0 }; 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, - }; + pm_parser_init(&arena, &parser, source, size, &options); - parser.lex_callback = &lex_callback; + pm_parser_lex_callback_set(&parser, serialize_token, buffer); 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); + pm_parser_cleanup(&parser); + pm_arena_cleanup(&arena); + pm_options_cleanup(&options); +} + +/** + * Parse the source and return true if it parses without errors or warnings. + */ +bool +pm_serialize_parse_success_p(const uint8_t *source, size_t size, const char *data) { + pm_options_t options = { 0 }; + pm_options_read(&options, data); + + pm_arena_t arena = { 0 }; + pm_parser_t parser; + pm_parser_init(&arena, &parser, source, size, &options); + + pm_parse(&parser); + + bool result = parser.error_list.size == 0; + pm_parser_cleanup(&parser); + pm_arena_cleanup(&arena); + pm_options_cleanup(&options); + + return result; } #endif diff --git a/templates/src/token_type.c.erb b/templates/src/tokens.c.erb similarity index 97% rename from templates/src/token_type.c.erb rename to templates/src/tokens.c.erb index 5c6f271310..1e82954738 100644 --- a/templates/src/token_type.c.erb +++ b/templates/src/tokens.c.erb @@ -1,12 +1,12 @@ -#include - #include "prism/ast.h" +#include + /** * Returns a string representation of the given token type. */ -PRISM_EXPORTED_FUNCTION const char * -pm_token_type_name(pm_token_type_t token_type) { +const char * +pm_token_type(pm_token_type_t token_type) { switch (token_type) { <%- tokens.each do |token| -%> case PM_TOKEN_<%= token.name %>: @@ -27,7 +27,7 @@ pm_token_type_name(pm_token_type_t token_type) { * Returns the human name of the given token type. */ const char * -pm_token_type_human(pm_token_type_t token_type) { +pm_token_str(pm_token_type_t token_type) { switch (token_type) { case PM_TOKEN_EOF: return "end-of-input"; @@ -167,6 +167,8 @@ pm_token_type_human(pm_token_type_t token_type) { return "'defined?'"; case PM_TOKEN_KEYWORD_DO: return "'do'"; + case PM_TOKEN_KEYWORD_DO_BLOCK: + return "'do'"; case PM_TOKEN_KEYWORD_DO_LOOP: return "'do'"; case PM_TOKEN_KEYWORD_ELSE: @@ -358,8 +360,8 @@ pm_token_type_human(pm_token_type_t token_type) { return ""; } - // Provide a default, because some compilers can't determine that the above - // switch is exhaustive. + /* Provide a default, because some compilers cannot determine that the above + * switch is exhaustive. */ assert(false && "unreachable"); return ""; } diff --git a/templates/template.rb b/templates/template.rb index aca626b5eb..c455b36d03 100755 --- a/templates/template.rb +++ b/templates/template.rb @@ -6,13 +6,13 @@ require "yaml" module Prism - module Template + module Template # :nodoc: all SERIALIZE_ONLY_SEMANTICS_FIELDS = ENV.fetch("PRISM_SERIALIZE_ONLY_SEMANTICS_FIELDS", false) REMOVE_ON_ERROR_TYPES = SERIALIZE_ONLY_SEMANTICS_FIELDS CHECK_FIELD_KIND = ENV.fetch("CHECK_FIELD_KIND", false) - JAVA_BACKEND = ENV["PRISM_JAVA_BACKEND"] || "truffleruby" - JAVA_STRING_TYPE = JAVA_BACKEND == "jruby" ? "org.jruby.RubySymbol" : "String" + JAVA_BACKEND = ENV["PRISM_JAVA_BACKEND"] || "default" + JAVA_IDENTIFIER_TYPE = JAVA_BACKEND == "truffleruby" ? "String" : "byte[]" INCLUDE_NODE_ID = !SERIALIZE_ONLY_SEMANTICS_FIELDS || JAVA_BACKEND == "jruby" COMMON_FLAGS_COUNT = 2 @@ -53,7 +53,7 @@ def self.escape(value) module Doxygen # Similar to /verbatim ... /endverbatim but doesn't wrap the result in a code block. def self.verbatim(value) - value.gsub(/[\.*%!`#<>_+-]/, '\\\\\0') + value.gsub(/[*%!`#<>_+@-]/, '\\\\\0') end end @@ -105,6 +105,11 @@ def should_be_serialized? # Some node fields can be specialized if they point to a specific kind of # node and not just a generic node. class NodeKindField < Field + # The C type to use for this field as a function parameter. + def c_param + "struct #{c_type} *#{name}" + end + def initialize(kind:, **options) @kind = kind super(**options) @@ -150,19 +155,19 @@ def rbs_class if specific_kind specific_kind elsif union_kind - union_kind.join(" | ") + "(#{union_kind.join(" | ")})" else "Prism::node" end end - def rbi_class + def call_seq_type if specific_kind - "Prism::#{specific_kind}" + specific_kind elsif union_kind - "T.any(#{union_kind.map { |kind| "Prism::#{kind}" }.join(", ")})" + union_kind.join(" | ") else - "Prism::Node" + "Node" end end @@ -182,19 +187,19 @@ def rbs_class if specific_kind "#{specific_kind}?" elsif union_kind - [*union_kind, "nil"].join(" | ") + "(#{union_kind.join(" | ")})?" else "Prism::node?" end end - def rbi_class + def call_seq_type if specific_kind - "T.nilable(Prism::#{specific_kind})" + "#{specific_kind} | nil" elsif union_kind - "T.nilable(T.any(#{union_kind.map { |kind| "Prism::#{kind}" }.join(", ")}))" + [*union_kind, "nil"].join(" | ") else - "T.nilable(Prism::Node)" + "Node | nil" end end @@ -210,23 +215,31 @@ def check_field_kind # This represents a field on a node that is a list of nodes. We pass them as # references and store them directly on the struct. class NodeListField < NodeKindField - def rbs_class + def c_param + "pm_node_list_t #{name}" + end + + def element_rbs_class if specific_kind - "Array[#{specific_kind}]" + "#{specific_kind}" elsif union_kind - "Array[#{union_kind.join(" | ")}]" + "#{union_kind.join(" | ")}" else - "Array[Prism::node]" + "Prism::node" end end - def rbi_class + def rbs_class + "Array[#{element_rbs_class}]" + end + + def call_seq_type if specific_kind - "T::Array[Prism::#{specific_kind}]" + "Array[#{specific_kind}]" elsif union_kind - "T::Array[T.any(#{union_kind.map { |kind| "Prism::#{kind}" }.join(", ")})]" + "Array[#{union_kind.join(" | ")}]" else - "T::Array[Prism::Node]" + "Array[Node]" end end @@ -246,58 +259,74 @@ def check_field_kind # This represents a field on a node that is the ID of a string interned # through the parser's constant pool. class ConstantField < Field + def c_param + "pm_constant_id_t #{name}" + end + def rbs_class "Symbol" end - def rbi_class + def call_seq_type "Symbol" end def java_type - JAVA_STRING_TYPE + JAVA_IDENTIFIER_TYPE end end # This represents a field on a node that is the ID of a string interned # through the parser's constant pool and can be optionally null. class OptionalConstantField < Field + def c_param + "pm_constant_id_t #{name}" + end + def rbs_class "Symbol?" end - def rbi_class - "T.nilable(Symbol)" + def call_seq_type + "Symbol | nil" end def java_type - JAVA_STRING_TYPE + JAVA_IDENTIFIER_TYPE end end # This represents a field on a node that is a list of IDs that are associated # with strings interned through the parser's constant pool. class ConstantListField < Field + def c_param + "pm_constant_id_list_t #{name}" + end + def rbs_class "Array[Symbol]" end - def rbi_class - "T::Array[Symbol]" + def call_seq_type + "Array[Symbol]" end def java_type - "#{JAVA_STRING_TYPE}[]" + "#{JAVA_IDENTIFIER_TYPE}[]" end end # This represents a field on a node that is a string. class StringField < Field + def c_param + "pm_string_t #{name}" + end + def rbs_class "String" end - def rbi_class + def call_seq_type "String" end @@ -308,6 +337,10 @@ def java_type # This represents a field on a node that is a location. class LocationField < Field + def c_param + "pm_location_t #{name}" + end + def semantic_field? false end @@ -316,8 +349,8 @@ def rbs_class "Location" end - def rbi_class - "Prism::Location" + def call_seq_type + "Location" end def java_type @@ -327,6 +360,10 @@ def java_type # This represents a field on a node that is a location that is optional. class OptionalLocationField < Field + def c_param + "pm_location_t #{name}" + end + def semantic_field? false end @@ -335,8 +372,8 @@ def rbs_class "Location?" end - def rbi_class - "T.nilable(Prism::Location)" + def call_seq_type + "Location | nil" end def java_type @@ -346,11 +383,15 @@ def java_type # This represents an integer field. class UInt8Field < Field + def c_param + "uint8_t #{name}" + end + def rbs_class "Integer" end - def rbi_class + def call_seq_type "Integer" end @@ -361,11 +402,15 @@ def java_type # This represents an integer field. class UInt32Field < Field + def c_param + "uint32_t #{name}" + end + def rbs_class "Integer" end - def rbi_class + def call_seq_type "Integer" end @@ -377,11 +422,15 @@ def java_type # This represents an arbitrarily-sized integer. When it gets to Ruby it will # be an Integer. class IntegerField < Field + def c_param + "pm_integer_t #{name}" + end + def rbs_class "Integer" end - def rbi_class + def call_seq_type "Integer" end @@ -393,11 +442,15 @@ def java_type # This represents a double-precision floating point number. When it gets to # Ruby it will be a Float. class DoubleField < Field + def c_param + "double #{name}" + end + def rbs_class "Float" end - def rbi_class + def call_seq_type "Float" end @@ -555,8 +608,7 @@ def render(name, write_to: nil) extension = File.extname(filepath.gsub(".erb", "")) heading = - case extension - when ".rb" + if extension == ".rb" <<~HEADING # frozen_string_literal: true # :markup: markdown @@ -569,29 +621,9 @@ def render(name, write_to: nil) ++ =end - HEADING - when ".rbs" - <<~HEADING - # This file is generated by the templates/template.rb script and should not be - # modified manually. See #{filepath} - # if you are looking to modify the template - - HEADING - when ".rbi" - <<~HEADING - # typed: strict - - =begin - This file is generated by the templates/template.rb script and should not be - modified manually. See #{filepath} - if you are looking to modify the template - =end - HEADING else <<~HEADING - /* :markup: markdown */ - /*----------------------------------------------------------------------------*/ /* This file is generated by the templates/template.rb script and should not */ /* be modified manually. See */ @@ -652,13 +684,13 @@ def locals TEMPLATES = [ "ext/prism/api_node.c", "include/prism/ast.h", - "include/prism/diagnostic.h", + "include/prism/internal/diagnostic.h", "javascript/src/deserialize.js", "javascript/src/nodes.js", "javascript/src/visitor.js", - "java/org/prism/Loader.java", - "java/org/prism/Nodes.java", - "java/org/prism/AbstractNodeVisitor.java", + "java/api/src/main/java/org/ruby_lang/prism/Loader.java", + "java/api/src/main/java/org/ruby_lang/prism/Nodes.java", + "java/api/src/main/java/org/ruby_lang/prism/AbstractNodeVisitor.java", "lib/prism/compiler.rb", "lib/prism/dispatcher.rb", "lib/prism/dot_visitor.rb", @@ -670,19 +702,11 @@ def locals "lib/prism/serialize.rb", "lib/prism/visitor.rb", "src/diagnostic.c", + "src/json.c", "src/node.c", "src/prettyprint.c", "src/serialize.c", - "src/token_type.c", - "rbi/prism/dsl.rbi", - "rbi/prism/node.rbi", - "rbi/prism/visitor.rbi", - "sig/prism.rbs", - "sig/prism/dsl.rbs", - "sig/prism/mutation_compiler.rbs", - "sig/prism/node.rbs", - "sig/prism/visitor.rbs", - "sig/prism/_private/dot_visitor.rbs" + "src/tokens.c" ] end end diff --git a/test/prism/api/parse_stream_test.rb b/test/prism/api/parse_stream_test.rb index 1c068c617c..3bc86fbd61 100644 --- a/test/prism/api/parse_stream_test.rb +++ b/test/prism/api/parse_stream_test.rb @@ -30,16 +30,28 @@ def test_multi_read end def test___END__ - io = StringIO.new("1 + 2\n3 + 4\n__END__\n5 + 6") + io = StringIO.new(<<~RUBY) + 1 + 2 + 3 + 4 + __END__ + 5 + 6 + RUBY result = Prism.parse_stream(io) assert result.success? assert_equal 2, result.value.statements.body.length - assert_equal "5 + 6", io.read + assert_equal "5 + 6\n", io.read end def test_false___END___in_string - io = StringIO.new("1 + 2\n3 + 4\n\"\n__END__\n\"\n5 + 6") + io = StringIO.new(<<~RUBY) + 1 + 2 + 3 + 4 + " + __END__ + " + 5 + 6 + RUBY result = Prism.parse_stream(io) assert result.success? @@ -47,7 +59,14 @@ def test_false___END___in_string end def test_false___END___in_regexp - io = StringIO.new("1 + 2\n3 + 4\n/\n__END__\n/\n5 + 6") + io = StringIO.new(<<~RUBY) + 1 + 2 + 3 + 4 + / + __END__ + / + 5 + 6 + RUBY result = Prism.parse_stream(io) assert result.success? @@ -55,7 +74,14 @@ def test_false___END___in_regexp end def test_false___END___in_list - io = StringIO.new("1 + 2\n3 + 4\n%w[\n__END__\n]\n5 + 6") + io = StringIO.new(<<~RUBY) + 1 + 2 + 3 + 4 + %w[ + __END__ + ] + 5 + 6 + RUBY result = Prism.parse_stream(io) assert result.success? @@ -63,7 +89,14 @@ def test_false___END___in_list end def test_false___END___in_heredoc - io = StringIO.new("1 + 2\n3 + 4\n<<-EOF\n__END__\nEOF\n5 + 6") + io = StringIO.new(<<~RUBY) + 1 + 2 + 3 + 4 + <<-EOF + __END__ + EOF + 5 + 6 + RUBY result = Prism.parse_stream(io) assert result.success? @@ -71,7 +104,11 @@ def test_false___END___in_heredoc end def test_nul_bytes - io = StringIO.new("1 # \0\0\0 \n2 # \0\0\0\n3") + io = StringIO.new(<<~RUBY) + 1 # \0\0\0\t + 2 # \0\0\0 + 3 + RUBY result = Prism.parse_stream(io) assert result.success? diff --git a/test/prism/api/parse_test.rb b/test/prism/api/parse_test.rb index bbf28201ff..c9a47c1a61 100644 --- a/test/prism/api/parse_test.rb +++ b/test/prism/api/parse_test.rb @@ -154,6 +154,10 @@ def test_version_current end end + def test_nearest + assert Prism.parse_success?("1 + 1", version: "nearest") + end + def test_scopes assert_kind_of Prism::CallNode, Prism.parse_statement("foo") assert_kind_of Prism::LocalVariableReadNode, Prism.parse_statement("foo", scopes: [[:foo]]) diff --git a/test/prism/encoding/regular_expression_encoding_test.rb b/test/prism/encoding/regular_expression_encoding_test.rb index e2daae1d7f..fdff1e3281 100644 --- a/test/prism/encoding/regular_expression_encoding_test.rb +++ b/test/prism/encoding/regular_expression_encoding_test.rb @@ -2,6 +2,7 @@ return unless defined?(RubyVM::InstructionSequence) return if RubyVM::InstructionSequence.compile("").to_a[4][:parser] == :prism +return if RUBY_VERSION < "3.2" require_relative "../test_helper" @@ -21,7 +22,7 @@ class RegularExpressionEncodingTest < TestCase ["n", "u", "e", "s"].each do |modifier| define_method(:"test_regular_expression_encoding_modifiers_/#{modifier}_#{encoding.name}") do - regexp_sources = ["abc", "garçon", "\\x80", "gar\\xC3\\xA7on", "gar\\u{E7}on", "abc\\u{FFFFFF}", "\\x80\\u{80}" ] + regexp_sources = ["abc", "garçon", "\\x80", "gar\\xC3\\xA7on", "gar\\u{E7}on", "abc\\u{FFFFFF}", "\\x80\\u{80}", "\\p{L}" ] assert_regular_expression_encoding_flags( encoding, @@ -35,17 +36,15 @@ class RegularExpressionEncodingTest < TestCase def assert_regular_expression_encoding_flags(encoding, regexps) regexps.each do |regexp| - regexp_modifier_used = regexp.end_with?("/u") || regexp.end_with?("/e") || regexp.end_with?("/s") || regexp.end_with?("/n") source = "# encoding: #{encoding.name}\n#{regexp}" - encoding_errors = ["invalid multibyte char", "escaped non ASCII character in UTF-8 regexp", "differs from source encoding"] - skipped_errors = ["invalid multibyte escape", "incompatible character encoding", "UTF-8 character in non UTF-8 regexp", "invalid Unicode range", "invalid Unicode list"] - - # TODO (nirvdrum 21-Feb-2024): Prism currently does not handle Regexp validation unless modifiers are used. So, skip processing those errors for now: https://github.com/ruby/prism/issues/2104 - unless regexp_modifier_used - skipped_errors += encoding_errors - encoding_errors.clear - end + encoding_errors = [ + "invalid multibyte char", "escaped non ASCII character in UTF-8 regexp", + "differs from source encoding", "incompatible character encoding", + "invalid multibyte escape", "UTF-8 character in non UTF-8 regexp", + "invalid Unicode range", "non escaped non ASCII character", + "invalid character property name", "invalid Unicode list", + ] expected = begin @@ -53,8 +52,6 @@ def assert_regular_expression_encoding_flags(encoding, regexps) rescue SyntaxError => error if encoding_errors.find { |e| error.message.include?(e) } error.message.split("\n").map { |m| m[/: (.+?)$/, 1] } - elsif skipped_errors.find { |e| error.message.include?(e) } - next else raise end @@ -111,19 +108,6 @@ def assert_regular_expression_encoding_flags(encoding, regexps) end end - # TODO (nirvdrum 22-Feb-2024): Remove this workaround once Prism better maps CRuby's error messages. - # This class of error message is tricky. The part not being compared is a representation of the regexp. - # Depending on the source encoding and any encoding modifiers being used, CRuby alters how the regexp is represented. - # Sometimes it's an MBC string. Other times it uses hexadecimal character escapes. And in other cases it uses - # the long-form Unicode escape sequences. This short-circuit checks that the error message is mostly correct. - if expected.is_a?(Array) && actual.is_a?(Array) - if expected.last.start_with?("/.../n has a non escaped non ASCII character in non ASCII-8BIT script:") && - actual.last.start_with?("/.../n has a non escaped non ASCII character in non ASCII-8BIT script:") - expected.pop - actual.pop - end - end - assert_equal expected, actual end end diff --git a/test/prism/errors/do_not_allow_trailing_commas_in_method_parameters.txt b/test/prism/errors/3.3-4.0/do_not_allow_trailing_commas_in_method_parameters.txt similarity index 100% rename from test/prism/errors/do_not_allow_trailing_commas_in_method_parameters.txt rename to test/prism/errors/3.3-4.0/do_not_allow_trailing_commas_in_method_parameters.txt diff --git a/test/prism/errors/3.3-4.0/noblock.txt b/test/prism/errors/3.3-4.0/noblock.txt new file mode 100644 index 0000000000..07939041bb --- /dev/null +++ b/test/prism/errors/3.3-4.0/noblock.txt @@ -0,0 +1,6 @@ +def foo(&nil) + ^~~ unexpected 'nil'; expected a `)` to close the parameters + ^ unexpected ')', expecting end-of-input + ^ unexpected ')', ignoring it +end + diff --git a/test/prism/errors/4.1/do_not_allow_trailing_commas_after_terminating_arguments.txt b/test/prism/errors/4.1/do_not_allow_trailing_commas_after_terminating_arguments.txt new file mode 100644 index 0000000000..b3e06f4154 --- /dev/null +++ b/test/prism/errors/4.1/do_not_allow_trailing_commas_after_terminating_arguments.txt @@ -0,0 +1,6 @@ +def foo(a,b,...,);end + ^ unexpected `,` in parameters + +def foo(a,b,&block,);end + ^ unexpected `,` in parameters + diff --git a/test/prism/errors/4.1/multiple_blocks.txt b/test/prism/errors/4.1/multiple_blocks.txt new file mode 100644 index 0000000000..7e8433cf82 --- /dev/null +++ b/test/prism/errors/4.1/multiple_blocks.txt @@ -0,0 +1,12 @@ +def foo(&nil, &nil); end + ^ unexpected parameter order + ^~~~ multiple block parameters; only one block is allowed + +def foo(&foo, &nil); end + ^ unexpected parameter order + ^~~~ multiple block parameters; only one block is allowed + +def foo(&nil, &foo); end + ^ unexpected parameter order + ^~~~ multiple block parameters; only one block is allowed + diff --git a/test/prism/errors/command_call_in.txt b/test/prism/errors/command_call_in.txt index 2fdcf09738..2b7286abc3 100644 --- a/test/prism/errors/command_call_in.txt +++ b/test/prism/errors/command_call_in.txt @@ -2,4 +2,5 @@ foo 1 in a ^~ unexpected 'in', expecting end-of-input ^~ unexpected 'in', ignoring it a = foo 2 in b + ^~ unexpected 'in', expecting end-of-input diff --git a/test/prism/errors/command_call_in_2.txt b/test/prism/errors/command_call_in_2.txt new file mode 100644 index 0000000000..6676b1acba --- /dev/null +++ b/test/prism/errors/command_call_in_2.txt @@ -0,0 +1,4 @@ +a.b x in pattern + ^~ unexpected 'in', expecting end-of-input + ^~ unexpected 'in', ignoring it + diff --git a/test/prism/errors/command_call_in_3.txt b/test/prism/errors/command_call_in_3.txt new file mode 100644 index 0000000000..6fe026d7d3 --- /dev/null +++ b/test/prism/errors/command_call_in_3.txt @@ -0,0 +1,4 @@ +a.b x: in pattern + ^~ unexpected 'in', expecting end-of-input + ^~ unexpected 'in', ignoring it + diff --git a/test/prism/errors/command_call_in_4.txt b/test/prism/errors/command_call_in_4.txt new file mode 100644 index 0000000000..045afe6498 --- /dev/null +++ b/test/prism/errors/command_call_in_4.txt @@ -0,0 +1,4 @@ +a.b &x in pattern + ^~ unexpected 'in', expecting end-of-input + ^~ unexpected 'in', ignoring it + diff --git a/test/prism/errors/command_call_in_5.txt b/test/prism/errors/command_call_in_5.txt new file mode 100644 index 0000000000..be07287f81 --- /dev/null +++ b/test/prism/errors/command_call_in_5.txt @@ -0,0 +1,4 @@ +a.b *x => pattern + ^~ unexpected '=>', expecting end-of-input + ^~ unexpected '=>', ignoring it + diff --git a/test/prism/errors/command_call_in_6.txt b/test/prism/errors/command_call_in_6.txt new file mode 100644 index 0000000000..470f323872 --- /dev/null +++ b/test/prism/errors/command_call_in_6.txt @@ -0,0 +1,4 @@ +a.b x: => pattern + ^~ unexpected '=>', expecting end-of-input + ^~ unexpected '=>', ignoring it + diff --git a/test/prism/errors/command_call_in_7.txt b/test/prism/errors/command_call_in_7.txt new file mode 100644 index 0000000000..a8bea912b5 --- /dev/null +++ b/test/prism/errors/command_call_in_7.txt @@ -0,0 +1,4 @@ +a.b &x => pattern + ^~ unexpected '=>', expecting end-of-input + ^~ unexpected '=>', ignoring it + diff --git a/test/prism/errors/command_call_value_and.txt b/test/prism/errors/command_call_value_and.txt new file mode 100644 index 0000000000..a131aa5530 --- /dev/null +++ b/test/prism/errors/command_call_value_and.txt @@ -0,0 +1,3 @@ +a = b c and 1 + ^~~ unexpected 'and', expecting end-of-input + diff --git a/test/prism/errors/command_call_value_or.txt b/test/prism/errors/command_call_value_or.txt new file mode 100644 index 0000000000..cc75714166 --- /dev/null +++ b/test/prism/errors/command_call_value_or.txt @@ -0,0 +1,3 @@ +a = b c or 1 + ^~ unexpected 'or', expecting end-of-input + diff --git a/test/prism/errors/command_calls_34.txt b/test/prism/errors/command_calls_34.txt index ce62bc1492..bc0ea5e81c 100644 --- a/test/prism/errors/command_calls_34.txt +++ b/test/prism/errors/command_calls_34.txt @@ -1,12 +1,19 @@ foo(bar 1 do end, 2) - ^ invalid comma - ^ unexpected integer; expected a `)` to close the arguments - ^ unexpected integer, expecting end-of-input + ^~ unexpected 'do'; expected a `)` to close the arguments + ^~ unexpected 'do', expecting end-of-input + ^~ unexpected 'do', ignoring it + ^~~ unexpected 'end', ignoring it + ^ unexpected ',', ignoring it ^ unexpected ')', expecting end-of-input ^ unexpected ')', ignoring it foo(bar 1 do end,) - ^ invalid comma + ^~ unexpected 'do'; expected a `)` to close the arguments + ^~ unexpected 'do', expecting end-of-input + ^~ unexpected 'do', ignoring it + ^~~ unexpected 'end', ignoring it + ^ unexpected ',', ignoring it + ^ unexpected ')', ignoring it foo(1, bar 2 do end) ^ unexpected integer; expected a `)` to close the arguments diff --git a/test/prism/errors/command_calls_35.txt b/test/prism/errors/command_calls_35.txt index 45f569b117..bd72d1be56 100644 --- a/test/prism/errors/command_calls_35.txt +++ b/test/prism/errors/command_calls_35.txt @@ -14,7 +14,11 @@ p(p a, &block => value) ^ unexpected ')', ignoring it p(p a do end => value) - ^~ unexpected '=>'; expected a `)` to close the arguments + ^~ unexpected 'do'; expected a `)` to close the arguments + ^~ unexpected 'do', expecting end-of-input + ^~ unexpected 'do', ignoring it + ^~~ unexpected 'end', ignoring it + ^~ unexpected '=>', ignoring it ^ unexpected ')', expecting end-of-input ^ unexpected ')', ignoring it diff --git a/test/prism/errors/def_endless_do.txt b/test/prism/errors/def_endless_do.txt new file mode 100644 index 0000000000..d66b7086da --- /dev/null +++ b/test/prism/errors/def_endless_do.txt @@ -0,0 +1,6 @@ +def a = a b do 1 end + ^~ unexpected 'do', expecting end-of-input + ^~ unexpected 'do', ignoring it + ^~~ unexpected 'end', expecting end-of-input + ^~~ unexpected 'end', ignoring it + diff --git a/test/prism/errors/destroy_call_operator_write_arguments.txt b/test/prism/errors/destroy_call_operator_write_arguments.txt index c3c72f9226..b6933d61d1 100644 --- a/test/prism/errors/destroy_call_operator_write_arguments.txt +++ b/test/prism/errors/destroy_call_operator_write_arguments.txt @@ -2,9 +2,9 @@ t next&&do end&= ^~ unexpected 'do'; expected an expression after the operator ^~~~ unexpected void value expression ^~~~ unexpected void value expression -^~~~~~~~~~~~~~ unexpected write target - ^~ unexpected operator after a call with arguments - ^~ unexpected operator after a call with a block + ^~ unexpected '&=', expecting end-of-input + ^~ unexpected '&=', ignoring it + ^~~~ Invalid next ''while= ^~~~~ expected a predicate expression for the `while` statement ^ unexpected '='; target cannot be written diff --git a/test/prism/errors/do_not_allow_forward_arguments_in_blocks.txt b/test/prism/errors/do_not_allow_forward_arguments_in_blocks.txt index df49557617..639dec3af2 100644 --- a/test/prism/errors/do_not_allow_forward_arguments_in_blocks.txt +++ b/test/prism/errors/do_not_allow_forward_arguments_in_blocks.txt @@ -1,3 +1,13 @@ a {|...|} - ^~~ unexpected ... when the parent method is not forwarding + ^~~ unexpected ... in block argument + +def foo(...) + a {|...|} + ^~~ unexpected ... in block argument +end + +def foo + a {|...|} + ^~~ unexpected ... in block argument +end diff --git a/test/prism/errors/do_not_allow_forward_arguments_in_lambda_literals.txt b/test/prism/errors/do_not_allow_forward_arguments_in_lambda_literals.txt index c2405a5c66..03e17683e4 100644 --- a/test/prism/errors/do_not_allow_forward_arguments_in_lambda_literals.txt +++ b/test/prism/errors/do_not_allow_forward_arguments_in_lambda_literals.txt @@ -1,3 +1,13 @@ ->(...) {} - ^~~ unexpected ... when the parent method is not forwarding + ^~~ unexpected ... in lambda argument + +def foo(...) + ->(...) {} + ^~~ unexpected ... in lambda argument +end + +def foo + ->(...) {} + ^~~ unexpected ... in lambda argument +end diff --git a/test/prism/errors/match_predicate_after_rescue_with_dot_method_call.txt b/test/prism/errors/match_predicate_after_rescue_with_dot_method_call.txt index fead8aaf23..f599dc476b 100644 --- a/test/prism/errors/match_predicate_after_rescue_with_dot_method_call.txt +++ b/test/prism/errors/match_predicate_after_rescue_with_dot_method_call.txt @@ -1,3 +1,4 @@ 'a' rescue 2 in 3.upcase ^ unexpected '.', expecting end-of-input + ^ unexpected '.', ignoring it diff --git a/test/prism/errors/match_predicate_after_rescue_with_opreator.txt b/test/prism/errors/match_predicate_after_rescue_with_opreator.txt index b2363a544d..44a4ba8488 100644 --- a/test/prism/errors/match_predicate_after_rescue_with_opreator.txt +++ b/test/prism/errors/match_predicate_after_rescue_with_opreator.txt @@ -1,3 +1,4 @@ 1 rescue 2 in 3 << 4 ^~ unexpected <<, expecting end-of-input + ^~ unexpected <<, ignoring it diff --git a/test/prism/errors/match_required_after_rescue_with_dot_method_call.txt b/test/prism/errors/match_required_after_rescue_with_dot_method_call.txt index d72d72ce60..abcfaf094d 100644 --- a/test/prism/errors/match_required_after_rescue_with_dot_method_call.txt +++ b/test/prism/errors/match_required_after_rescue_with_dot_method_call.txt @@ -1,3 +1,4 @@ 1 rescue 2 => 3.inspect ^ unexpected '.', expecting end-of-input + ^ unexpected '.', ignoring it diff --git a/test/prism/errors/match_required_after_rescue_with_opreator.txt b/test/prism/errors/match_required_after_rescue_with_opreator.txt index 903e2ccc8e..5e6387ca4d 100644 --- a/test/prism/errors/match_required_after_rescue_with_opreator.txt +++ b/test/prism/errors/match_required_after_rescue_with_opreator.txt @@ -1,3 +1,4 @@ 1 rescue 2 => 3 ** 4 ^~ unexpected '**', expecting end-of-input + ^~ unexpected '**', ignoring it diff --git a/test/prism/errors/not_without_parens_assignment.txt b/test/prism/errors/not_without_parens_assignment.txt new file mode 100644 index 0000000000..32d58efedf --- /dev/null +++ b/test/prism/errors/not_without_parens_assignment.txt @@ -0,0 +1,4 @@ +x = not y + ^ expected a `(` after `not` + ^ unexpected local variable or method, expecting end-of-input + diff --git a/test/prism/errors/not_without_parens_call.txt b/test/prism/errors/not_without_parens_call.txt new file mode 100644 index 0000000000..a778193400 --- /dev/null +++ b/test/prism/errors/not_without_parens_call.txt @@ -0,0 +1,7 @@ +foo(not y) + ^ expected a `(` after `not` + ^ unexpected local variable or method; expected a `)` to close the arguments + ^ unexpected local variable or method, expecting end-of-input + ^ unexpected ')', expecting end-of-input + ^ unexpected ')', ignoring it + diff --git a/test/prism/errors/not_without_parens_command.txt b/test/prism/errors/not_without_parens_command.txt new file mode 100644 index 0000000000..957a06f8f1 --- /dev/null +++ b/test/prism/errors/not_without_parens_command.txt @@ -0,0 +1,4 @@ +foo not y + ^ expected a `(` after `not` + ^ unexpected local variable or method, expecting end-of-input + diff --git a/test/prism/errors/not_without_parens_command_call.txt b/test/prism/errors/not_without_parens_command_call.txt new file mode 100644 index 0000000000..564833c7de --- /dev/null +++ b/test/prism/errors/not_without_parens_command_call.txt @@ -0,0 +1,4 @@ +a.b not y + ^ expected a `(` after `not` + ^ unexpected local variable or method, expecting end-of-input + diff --git a/test/prism/errors/not_without_parens_return.txt b/test/prism/errors/not_without_parens_return.txt new file mode 100644 index 0000000000..1c7edb6ff1 --- /dev/null +++ b/test/prism/errors/not_without_parens_return.txt @@ -0,0 +1,4 @@ +return not y + ^ expected a `(` after `not` + ^ unexpected local variable or method, expecting end-of-input + diff --git a/test/prism/errors/rescue_pattern.txt b/test/prism/errors/rescue_pattern.txt new file mode 100644 index 0000000000..c85feb27bd --- /dev/null +++ b/test/prism/errors/rescue_pattern.txt @@ -0,0 +1,4 @@ +a rescue b => c in d + ^~ unexpected 'in', expecting end-of-input + ^~ unexpected 'in', ignoring it + diff --git a/test/prism/errors/unterminated_heredoc_and_embexpr.txt b/test/prism/errors/unterminated_heredoc_and_embexpr.txt new file mode 100644 index 0000000000..bed7fcd24e --- /dev/null +++ b/test/prism/errors/unterminated_heredoc_and_embexpr.txt @@ -0,0 +1,11 @@ +< (&nil) {} diff --git a/test/prism/fixtures/4.1/trailing_comma_after_method_arguments.txt b/test/prism/fixtures/4.1/trailing_comma_after_method_arguments.txt new file mode 100644 index 0000000000..ef1385d973 --- /dev/null +++ b/test/prism/fixtures/4.1/trailing_comma_after_method_arguments.txt @@ -0,0 +1,15 @@ +def foo(a,b,c,);end + +def foo(a,b,*c,);end + +def foo(a,b,*,);end + +def foo(a,b,**c,);end + +def foo(a,b,**,);end + +def foo( + a, + b, + c, +);end diff --git a/test/prism/fixtures/and_or_with_suffix.txt b/test/prism/fixtures/and_or_with_suffix.txt new file mode 100644 index 0000000000..59ee4d0b88 --- /dev/null +++ b/test/prism/fixtures/and_or_with_suffix.txt @@ -0,0 +1,17 @@ +foo +and? + +foo +or? + +foo +and! + +foo +or! + +foo +andbar + +foo +orbar diff --git a/test/prism/fixtures/blocks.txt b/test/prism/fixtures/blocks.txt index e33d95c150..51ec84950c 100644 --- a/test/prism/fixtures/blocks.txt +++ b/test/prism/fixtures/blocks.txt @@ -52,3 +52,11 @@ foo lambda { | } foo do |bar,| end + +foo bar baz, qux do end + +foo.bar baz do end + +foo.bar baz do end.qux quux do end + +foo bar, baz do |x| x end diff --git a/test/prism/fixtures/case_in_in.txt b/test/prism/fixtures/case_in_in.txt new file mode 100644 index 0000000000..a5f9e4ec41 --- /dev/null +++ b/test/prism/fixtures/case_in_in.txt @@ -0,0 +1,4 @@ +case args +in [event] + context.event in ^event +end diff --git a/test/prism/fixtures/command_method_call_2.txt b/test/prism/fixtures/command_method_call_2.txt index 165c45987a..8bd40cff9e 100644 --- a/test/prism/fixtures/command_method_call_2.txt +++ b/test/prism/fixtures/command_method_call_2.txt @@ -1,3 +1 @@ -foo(bar baz do end) - foo(bar baz, bat) diff --git a/test/prism/fixtures/endless_methods.txt b/test/prism/fixtures/endless_methods.txt index 7eb3bf4318..6e0488a5ee 100644 --- a/test/prism/fixtures/endless_methods.txt +++ b/test/prism/fixtures/endless_methods.txt @@ -5,3 +5,7 @@ def bar = A "" def method = 1 + 2 + 3 x = def f = p 1 + +def foo = bar baz + +def foo = bar(baz) diff --git a/test/prism/fixtures/heredoc_dedent_line_continuation.txt b/test/prism/fixtures/heredoc_dedent_line_continuation.txt new file mode 100644 index 0000000000..661db490c7 --- /dev/null +++ b/test/prism/fixtures/heredoc_dedent_line_continuation.txt @@ -0,0 +1,5 @@ +<<~FOO + foo\ + \ + bar +FOO diff --git a/test/prism/fixtures/write_command_operator.txt b/test/prism/fixtures/write_command_operator.txt new file mode 100644 index 0000000000..d719d24f87 --- /dev/null +++ b/test/prism/fixtures/write_command_operator.txt @@ -0,0 +1,3 @@ +foo = 123 | '456' or return + +foo = 123 | '456' in BAR diff --git a/test/prism/fixtures_test.rb b/test/prism/fixtures_test.rb index 3a704b8389..dcbcb7c117 100644 --- a/test/prism/fixtures_test.rb +++ b/test/prism/fixtures_test.rb @@ -28,6 +28,8 @@ class FixturesTest < TestCase except << "command_method_call_2.txt" # https://bugs.ruby-lang.org/issues/21669 except << "4.1/void_value.txt" + # https://bugs.ruby-lang.org/issues/19107 + except << "4.1/trailing_comma_after_method_arguments.txt" Fixture.each_for_current_ruby(except: except) do |fixture| define_method(fixture.test_name) { assert_valid_syntax(fixture.read) } diff --git a/test/prism/lex_test.rb b/test/prism/lex_test.rb index 9a9f203c28..8ea7ce7e9b 100644 --- a/test/prism/lex_test.rb +++ b/test/prism/lex_test.rb @@ -48,11 +48,58 @@ def test_parse_lex_file end if RUBY_VERSION >= "3.3" - def test_lex_compare - prism = Prism.lex_compat(File.read(__FILE__), version: "current").value - ripper = Ripper.lex(File.read(__FILE__)) + def test_lex_compat + source = "foo bar" + prism = Prism.lex_compat(source, version: "current").value + ripper = Ripper.lex(source) assert_equal(ripper, prism) end end + + def test_lex_interpolation_unterminated + assert_equal( + %i[STRING_BEGIN EMBEXPR_BEGIN EOF], + token_types('"#{') + ) + + assert_equal( + %i[STRING_BEGIN EMBEXPR_BEGIN IGNORED_NEWLINE EOF], + token_types('"#{' + "\n") + ) + end + + def test_lex_interpolation_unterminated_with_content + # FIXME: Emits EOL twice. + assert_equal( + %i[STRING_BEGIN EMBEXPR_BEGIN CONSTANT EOF EOF], + token_types('"#{C') + ) + + assert_equal( + %i[STRING_BEGIN EMBEXPR_BEGIN CONSTANT NEWLINE EOF], + token_types('"#{C' + "\n") + ) + end + + def test_lex_heredoc_unterminated + code = <<~'RUBY'.strip + < ["), :continuable? + assert_predicate Prism.parse("case foo; when"), :continuable? + end + + def test_splat_and_block_pass + assert_predicate Prism.parse("[*"), :continuable? + assert_predicate Prism.parse("f(**"), :continuable? + assert_predicate Prism.parse("f(&"), :continuable? + end + + def test_default_parameter_value + assert_predicate Prism.parse("def f(x ="), :continuable? + end + + def test_line_continuation + assert_predicate Prism.parse("1 +\\"), :continuable? + assert_predicate Prism.parse("\"foo\" \\"), :continuable? + end + + def test_embedded_document + # Embedded document (=begin) truncated at various points. + assert_predicate Prism.parse("=b"), :continuable? + assert_predicate Prism.parse("=beg"), :continuable? + assert_predicate Prism.parse("=begin"), :continuable? + assert_predicate Prism.parse("foo\n=b"), :continuable? + end + end +end diff --git a/test/prism/result/source_location_test.rb b/test/prism/result/source_location_test.rb index 993150f581..dbda848211 100644 --- a/test/prism/result/source_location_test.rb +++ b/test/prism/result/source_location_test.rb @@ -650,6 +650,10 @@ def test_NilNode assert_location(NilNode, "nil") end + def test_NoBlockParameterNode + assert_location(NoBlockParameterNode, "def foo(&nil); end", 8...12) { |node| node.parameters.block } + end + def test_NoKeywordsParameterNode assert_location(NoKeywordsParameterNode, "def foo(**nil); end", 8...13) { |node| node.parameters.keyword_rest } end diff --git a/test/prism/ruby/find_fixtures.rb b/test/prism/ruby/find_fixtures.rb new file mode 100644 index 0000000000..df82cc7004 --- /dev/null +++ b/test/prism/ruby/find_fixtures.rb @@ -0,0 +1,70 @@ +# frozen_string_literal: true + +# Test fixtures for Prism.find. These must be in a separate file because +# source_location returns the file path and Prism.find re-parses the file. + +module Prism + module FindFixtures + module Methods + def simple_method + 42 + end + + def method_with_params(a, b, c) + a + b + c + end + + def method_with_block(&block) + block.call + end + + def self.singleton_method_fixture + :singleton + end + + def été + :utf8 + end + + def inline_method; :inline; end + end + + module Procs + SIMPLE_PROC = proc { 42 } + SIMPLE_LAMBDA = ->(x) { x * 2 } + MULTI_LINE_LAMBDA = lambda do |x| + x + 1 + end + DO_BLOCK_PROC = proc do |x| + x - 1 + end + end + + module DefineMethod + define_method(:dynamic) { |x| x + 1 } + end + + module ForLoop + items = [1, 2, 3] + for_proc = nil + o = Object.new + def o.each(&block) = block.call(block) + for for_proc in o; end + FOR_PROC = for_proc + end + + module MultipleOnLine + def self.first; end; def self.second; end + end + + module Errors + def self.divide(a, b) + a / b + end + + def self.call_undefined + undefined_method_call + end + end + end +end diff --git a/test/prism/ruby/find_test.rb b/test/prism/ruby/find_test.rb new file mode 100644 index 0000000000..5b59113d30 --- /dev/null +++ b/test/prism/ruby/find_test.rb @@ -0,0 +1,242 @@ +# frozen_string_literal: true + +return if RUBY_ENGINE == "ruby" && RUBY_VERSION < "3.4" +return if defined?(RubyVM::InstructionSequence) && RubyVM::InstructionSequence.compile("").to_a[4][:parser] != :prism + +require_relative "../test_helper" +require_relative "find_fixtures" + +module Prism + class FindTest < TestCase + Fixtures = FindFixtures + FIXTURES_PATH = File.expand_path("find_fixtures.rb", __dir__) + + # === Method / UnboundMethod tests === + + def test_simple_method + assert_def_node Prism.find(Fixtures::Methods.instance_method(:simple_method)), :simple_method + end + + def test_method_with_params + node = Prism.find(Fixtures::Methods.instance_method(:method_with_params)) + assert_def_node node, :method_with_params + assert_equal 3, node.parameters.requireds.length + end + + def test_method_with_block_param + assert_def_node Prism.find(Fixtures::Methods.instance_method(:method_with_block)), :method_with_block + end + + def test_singleton_method + assert_def_node Prism.find(Fixtures::Methods.method(:singleton_method_fixture)), :singleton_method_fixture + end + + def test_utf8_method_name + assert_def_node Prism.find(Fixtures::Methods.instance_method(:été)), :été + end + + def test_inline_method + assert_def_node Prism.find(Fixtures::Methods.instance_method(:inline_method)), :inline_method + end + + def test_bound_method + obj = Object.new + obj.extend(Fixtures::Methods) + assert_def_node Prism.find(obj.method(:simple_method)), :simple_method + end + + # === Proc / Lambda tests === + + def test_simple_proc + assert_not_nil Prism.find(Fixtures::Procs::SIMPLE_PROC) + end + + def test_simple_lambda + assert_not_nil Prism.find(Fixtures::Procs::SIMPLE_LAMBDA) + end + + def test_multi_line_lambda + assert_not_nil Prism.find(Fixtures::Procs::MULTI_LINE_LAMBDA) + end + + def test_do_block_proc + assert_not_nil Prism.find(Fixtures::Procs::DO_BLOCK_PROC) + end + + # === define_method tests === + + def test_define_method + assert_not_nil Prism.find(Fixtures::DefineMethod.instance_method(:dynamic)) + end + + def test_define_method_bound + obj = Object.new + obj.extend(Fixtures::DefineMethod) + assert_not_nil Prism.find(obj.method(:dynamic)) + end + + # === for loop test === + + def test_for_loop_proc + node = Prism.find(Fixtures::ForLoop::FOR_PROC) + assert_instance_of ForNode, node + end + + # === Thread::Backtrace::Location tests === + + def test_backtrace_location_zero_division + location = zero_division_location + assert_not_nil location, "could not find backtrace location in fixtures file" + assert_not_nil Prism.find(location) + end + + def test_backtrace_location_name_error + location = begin + Fixtures::Errors.call_undefined + rescue NameError => e + fixture_backtrace_location(e) + end + + assert_not_nil location, "could not find backtrace location in fixtures file" + assert_not_nil Prism.find(location) + end + + def test_backtrace_location_from_caller + # caller_locations returns locations for the current call stack + location = caller_locations(0, 1).first + node = Prism.find(location) + assert_not_nil node + end + + def test_backtrace_location_eval_returns_nil + location = begin + eval("raise 'eval error'") + rescue RuntimeError => e + e.backtrace_locations.find { |loc| loc.path == "(eval)" || loc.label&.include?("eval") } + end + + # eval locations have no file on disk + assert_nil Prism.find(location) if location + end + + # === Edge cases === + + def test_nil_source_location + # Built-in methods have nil source_location + assert_nil Prism.find(method(:puts)) + end + + def test_argument_error_on_wrong_type + assert_raise(ArgumentError) { Prism.find("not a callable") } + assert_raise(ArgumentError) { Prism.find(42) } + assert_raise(ArgumentError) { Prism.find(nil) } + end + + def test_eval_returns_nil + # eval'd code has no file on disk + m = eval("proc { 1 }") + assert_nil Prism.find(m) + end + + def test_multiple_methods_on_same_line + assert_def_node Prism.find(Fixtures::MultipleOnLine.method(:first)), :first + assert_def_node Prism.find(Fixtures::MultipleOnLine.method(:second)), :second + end + + # === Fallback (line-based) tests via rubyvm: false === + + def test_fallback_simple_method + assert_def_node Prism.find(Fixtures::Methods.instance_method(:simple_method), rubyvm: false), :simple_method + end + + def test_fallback_singleton_method + assert_def_node Prism.find(Fixtures::Methods.method(:singleton_method_fixture), rubyvm: false), :singleton_method_fixture + end + + def test_fallback_lambda + node = Prism.find(Fixtures::Procs::SIMPLE_LAMBDA, rubyvm: false) + assert_instance_of LambdaNode, node + end + + def test_fallback_proc + node = Prism.find(Fixtures::Procs::SIMPLE_PROC, rubyvm: false) + assert_instance_of CallNode, node + assert node.block.is_a?(BlockNode) + end + + def test_fallback_define_method + node = Prism.find(Fixtures::DefineMethod.instance_method(:dynamic), rubyvm: false) + assert_instance_of CallNode, node + assert node.block.is_a?(BlockNode) + end + + def test_fallback_for_loop + node = Prism.find(Fixtures::ForLoop::FOR_PROC, rubyvm: false) + assert_instance_of ForNode, node + end + + def test_fallback_backtrace_location + location = zero_division_location + assert_not_nil location + node = Prism.find(location, rubyvm: false) + assert_not_nil node + assert_equal location.lineno, node.location.start_line + end + + # === Node identity with node_id (CRuby only) === + + if defined?(RubyVM::InstructionSequence) + def test_node_id_matches_iseq + m = Fixtures::Methods.instance_method(:simple_method) + node = Prism.find(m) + assert_equal node_id_of(m), node.node_id + end + + def test_node_id_for_lambda + node = Prism.find(Fixtures::Procs::SIMPLE_LAMBDA) + assert_equal node_id_of(Fixtures::Procs::SIMPLE_LAMBDA), node.node_id + end + + def test_node_id_for_proc + node = Prism.find(Fixtures::Procs::SIMPLE_PROC) + assert_equal node_id_of(Fixtures::Procs::SIMPLE_PROC), node.node_id + end + + def test_node_id_for_define_method + m = Fixtures::DefineMethod.instance_method(:dynamic) + node = Prism.find(m) + assert_equal node_id_of(m), node.node_id + end + + def test_node_id_for_backtrace_location + location = zero_division_location + assert_not_nil location + expected_node_id = RubyVM::AbstractSyntaxTree.node_id_for_backtrace_location(location) + + node = Prism.find(location) + assert_equal expected_node_id, node.node_id + end + end + + private + + def assert_def_node(node, expected_name) + assert_instance_of DefNode, node + assert_equal expected_name, node.name + end + + def fixture_backtrace_location(exception) + exception.backtrace_locations.find { |loc| loc.path == FIXTURES_PATH } + end + + def zero_division_location + Fixtures::Errors.divide(1, 0) + rescue ZeroDivisionError => e + fixture_backtrace_location(e) + end + + def node_id_of(callable) + RubyVM::InstructionSequence.of(callable).to_a[4][:node_id] + end + end +end diff --git a/test/prism/ruby/parameters_signature_test.rb b/test/prism/ruby/parameters_signature_test.rb index ea1eea106b..5a225862c3 100644 --- a/test/prism/ruby/parameters_signature_test.rb +++ b/test/prism/ruby/parameters_signature_test.rb @@ -50,6 +50,11 @@ def test_nokey assert_parameters([[:nokey]], "**nil") end + def test_noblock + # FIXME: `compare: RUBY_VERSION >= "4.1"` once builds are available + assert_parameters([[:noblock]], "&nil", compare: false) + end + def test_keyrest_anonymous assert_parameters([[:keyrest, :**]], "**") end @@ -74,10 +79,11 @@ def test_forwarding private - def assert_parameters(expected, source) + def assert_parameters(expected, source, compare: true) # Compare against our expectation. assert_equal(expected, signature(source)) + return unless compare # Compare against Ruby's expectation. object = Object.new eval("def object.m(#{source}); end") diff --git a/test/prism/ruby/parser_test.rb b/test/prism/ruby/parser_test.rb index 55c12cab6f..ad9fa0c92c 100644 --- a/test/prism/ruby/parser_test.rb +++ b/test/prism/ruby/parser_test.rb @@ -187,13 +187,7 @@ def test_invalid_syntax end def test_it_block_parameter_syntax - it_fixture_path = Pathname(__dir__).join("../../../test/prism/fixtures/3.4/it.txt") - - buffer = Parser::Source::Buffer.new(it_fixture_path) - buffer.source = it_fixture_path.read - actual_ast = Prism::Translation::Parser34.new.tokenize(buffer)[0] - - it_block_parameter_sexp = parse_sexp { + assert_new_syntax("3.4/it.txt", Prism::Translation::Parser34) do s(:begin, s(:itblock, s(:send, nil, :x), :it, @@ -201,9 +195,20 @@ def test_it_block_parameter_syntax s(:itblock, s(:lambda), :it, s(:lvar, :it))) - } + end + end - assert_equal(it_block_parameter_sexp, actual_ast.to_sexp) + def test_nil_block_parameter_syntax + assert_new_syntax("4.1/noblock.txt", Prism::Translation::Parser41) do + s(:begin, + s(:def, :foo, + s(:args, + s(:blocknilarg)), nil), + s(:block, + s(:lambda), + s(:args, + s(:blocknilarg)), nil)) + end end private @@ -301,6 +306,16 @@ def assert_equal_comments(expected_comments, actual_comments) } end + def assert_new_syntax(path, parser, &sexp) + fixture_path = Pathname(__dir__).join("../../../test/prism/fixtures", path) + + buffer = Parser::Source::Buffer.new(fixture_path) + buffer.source = fixture_path.read + actual_ast = parser.new.tokenize(buffer)[0] + + assert_equal(parse_sexp(&sexp), actual_ast.to_sexp) + end + def parse_sexp(&block) Class.new { extend AST::Sexp }.instance_eval(&block).to_sexp end diff --git a/test/prism/ruby/ripper_test.rb b/test/prism/ruby/ripper_test.rb index 15f535f3d6..92aa1ad0b3 100644 --- a/test/prism/ruby/ripper_test.rb +++ b/test/prism/ruby/ripper_test.rb @@ -37,8 +37,17 @@ class RipperTest < TestCase ] end + if RUBY_VERSION.start_with?("4.") + incorrect += [ + # https://bugs.ruby-lang.org/issues/21945 + "and_or_with_suffix.txt", + ] + end + # https://bugs.ruby-lang.org/issues/21669 incorrect << "4.1/void_value.txt" + # https://bugs.ruby-lang.org/issues/19107 + incorrect << "4.1/trailing_comma_after_method_arguments.txt" # Skip these tests that we haven't implemented yet. omitted_sexp_raw = [ @@ -81,6 +90,16 @@ class RipperTest < TestCase define_method("#{fixture.test_name}_lex") { assert_ripper_lex(fixture.read) } end + def test_lex_ignored_missing_heredoc_end + ["", "-", "~"].each do |type| + source = "<<#{type}FOO\n" + assert_ripper_lex(source) + + source = "<<#{type}'FOO'\n" + assert_ripper_lex(source) + end + end + module Events attr_reader :events diff --git a/test/prism/ruby/ruby_parser_test.rb b/test/prism/ruby/ruby_parser_test.rb index 6e4ba8ce16..bc89bdae72 100644 --- a/test/prism/ruby/ruby_parser_test.rb +++ b/test/prism/ruby/ruby_parser_test.rb @@ -37,6 +37,7 @@ class RubyParserTest < TestCase "alias.txt", "dsym_str.txt", "dos_endings.txt", + "heredoc_dedent_line_continuation.txt", "heredoc_percent_q_newline_delimiter.txt", "heredocs_with_fake_newlines.txt", "heredocs_with_ignored_newlines.txt", @@ -85,16 +86,11 @@ class RubyParserTest < TestCase "3.3-4.0/void_value.txt", - "3.4/circular_parameters.txt", - - "4.0/endless_methods_command_call.txt", - "4.0/leading_logical.txt", - # https://bugs.ruby-lang.org/issues/21168#note-5 "command_method_call_2.txt", ] - Fixture.each(except: failures) do |fixture| + Fixture.each_for_version(version: "3.3", except: failures) do |fixture| define_method(fixture.test_name) do assert_ruby_parser(fixture, todos.include?(fixture.path)) end