diff --git a/.gitignore b/.gitignore index fb352a6..37a39fd 100644 --- a/.gitignore +++ b/.gitignore @@ -7,3 +7,4 @@ /pkg/ /spec/reports/ /tmp/ +/*.md \ No newline at end of file diff --git a/AGENTS.md b/AGENTS.md index 49dbd5a..55e29bf 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -3,15 +3,18 @@ ## Project status `arpaka` is a Ruby language parser frontend and an output backend for Lrama. -Keep implementation decisions aligned with both roles. The frontend is exposed -as `Arpaka`; the generic Ruby parser generator remains under `Lrama::Ruby`. +Keep implementation decisions aligned with both roles. Arpaka generates a +standalone Ruby frontend from a user-supplied Ruby source +tree; the generic Ruby parser generator remains under `Lrama::Ruby`. The initial backend generates standalone Ruby parsers from Lrama's LALR/IELR tables. `Lrama::Ruby.generate` returns source; `Lrama::Ruby.compile` returns a parser class loaded in its own Ruby Box. See README for supported grammar features. Ruby >= 4.0.0 is required. CI runs Ruby 4.0 and head. Generation uses Ruby Box to isolate Ruby-specific extensions to Lrama; start Ruby -with `RUBY_BOX=1`. Prism handles Ruby action tokens and generated syntax validation. +with `RUBY_BOX=1`. A Ruby action scanner handles action tokens. Generated frontend +code is validated +in an isolated Ruby Box. Whether to provide a fallback for Ruby 3.4 and earlier will be decided during implementation; compatibility with those versions is not currently promised. @@ -23,8 +26,10 @@ versions is not currently promised. - `lib/lrama/ruby/parser.rb.erb`: standalone parser template. - `lib/lrama/ruby/backend.rb` and `action_code.rb`: internal files loaded in the generator's box. - `examples/calculator.y`: executable example grammar. -- `lib/lrama/ruby/languages/ruby/`: bundled grammar, AST, builder and runtime metadata. -- `tool/ruby/`: grammar adapter, action definitions and original rule inventory. +- `lib/arpaka/`: frontend generator, CLI, source profile and Action mappings. +- `lib/arpaka/runtime/`: AST, builder, lexer and frontend source templates. +- `exe/arpaka`: installed generation command. +- `tool/ruby/`: development entry point for frontend generation and checks. - `vendor/ruby/`: pinned upstream grammar, preprocessing tools and license files. - `sig/lrama/ruby.rbs`: RBS declarations. - `test/lrama/*_test.rb`: Test::Unit tests; `test/test_helper.rb` loads the library. @@ -36,9 +41,9 @@ versions is not currently promised. - `bin/setup`: install dependencies with Bundler. - `RUBY_BOX=1 bundle exec rake test`: run the test suite. -- `RUBY_BOX=1 bundle exec rake`: verify the bundled grammar and run the test suite. -- `bundle exec rake ruby:generate`: regenerate bundled grammar and metadata after action changes. -- `bundle exec rake ruby:check`: verify source checksums, rule mappings and generated artifacts without editing them. +- `RUBY_BOX=1 bundle exec rake`: verify generation, tests and installed packaging. +- `RUBY_BOX=1 OUTPUT=/path/to/frontend.rb bundle exec rake ruby:generate`: generate a standalone frontend. +- `RUBY_BOX=1 bundle exec rake ruby:check`: verify source checksums, rule mappings and frontend generation without editing artifacts. - `RUBY_BOX=1 bundle exec ruby -Itest test/lrama/ruby_test.rb`: run one test file. - `RUBY_BOX=1 bin/console`: open an interactive Ruby session with the library loaded. @@ -46,7 +51,8 @@ versions is not currently promised. Follow the existing Ruby style: two-space indentation, double-quoted strings, snake_case filenames and methods, and `# frozen_string_literal: true` in Ruby -source files. Keep library code under `Lrama::Ruby`. Add relevant Test::Unit +source files. Keep generic backend code under `Lrama::Ruby` and frontend +generation under `Arpaka`. Add relevant Test::Unit coverage for behavior changes and maintain RBS declarations when changing the public API. No formatter or linter is configured. @@ -55,10 +61,13 @@ public API. No formatter or linter is configured. Keep `Gemfile.lock` untracked; it is ignored intentionally. The gemspec packages Git-tracked files, so review package contents when adding repository documentation or tooling files. -Runtime language grammars and licenses must be packaged; `vendor/`, `tool/` -and local design/plan documents are excluded. Parsing must work without these -development files and without filesystem writes. The Ruby grammar is generated -and cached in memory on first use, with a fresh builder per parse. +The gem packages frontend generation templates, Action mappings, source profiles +and notices, but no Ruby parse.y or generated Ruby frontend. `vendor/`, `tool/` +and local design/plan documents are excluded. The generated single Ruby file must +parse without Arpaka/Lrama gems, Ruby Box, auxiliary files or filesystem writes. +Embed required metadata and license notices; create fresh state per parse. +The user supplies Ruby sources to `Arpaka.generate` or `Arpaka.compile`; the +returned frontend exposes `.parse(source, filename:)` to obtain its AST. `bundle exec rake release` creates tags, pushes to Git, and publishes a gem. Run it only when a release is explicitly requested. diff --git a/README.md b/README.md index 6aa640b..64fd1d9 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,21 @@ # Arpaka -A Ruby language parser frontend and Ruby output backend for -[Lrama](https://github.com/ruby/lrama). `Arpaka` parses Ruby tokens into an AST, -while `Lrama::Ruby` generates standalone Ruby parsers with Ruby semantic actions. -The Ruby frontend is experimental and partial. +

+ Generate Ruby parsers with Lrama, and use their ASTs from Ruby. +

+ +Arpaka provides a Ruby output backend for +[Lrama](https://github.com/ruby/lrama) and a tool for generating a Ruby language +frontend. Its goal is to make Ruby syntax accessible as an AST from Ruby code. + +`Lrama::Ruby` turns Yacc-style grammars with Ruby semantic actions into +standalone Ruby parsers using Lrama's LALR/IELR tables. Arpaka applies its Ruby +Action mappings to a Ruby source tree supplied by the user and emits a single +Ruby file containing the parser, lexer, AST and builder. Users choose the output +location and regenerate when updating the grammar or Actions. + +The Ruby frontend is experimental and partial. The gem contains generation +support, not a bundled `parse.y` or a pre-generated Ruby frontend. ## Requirements @@ -111,68 +123,77 @@ the tree's shape. Parentheses affect grouping but are omitted from the AST by the action `$$ = $2`. Evaluation or compilation of the resulting tree belongs to the application; even `1 / 0` produces a tree without performing division. -### Using the bundled Ruby grammar +### Generating a Ruby frontend -```ruby -require "arpaka" +Provide a Ruby source tree containing `parse.y`, `tool/id2token.rb`, +`defs/id.def`, `COPYING` and `BSDL`. Arpaka does not download Ruby sources. +The first supported source profile is ruby/ruby revision +`37d60dd3241d5fdb10ca43f36077f5d556ae1b8d`; other revisions are not automatically +supported. Input checksums and expanded rule mappings are checked before +applying Actions. Changed input files are reported and generation stops. +Even comment-only changes currently require updating the source profile. -tree = Arpaka.parse( - [[:tIDENTIFIER, :a], ["=", nil], [:tINTEGER, 1]] -) -# Arpaka::AST::Program containing LocalWrite(:a, Literal(1)) +```sh +RUBY_BOX=1 bundle exec arpaka generate \ + --ruby-source /path/to/ruby \ + --class-name MyRubyParser \ + --output lib/my_library/ruby_parser.rb ``` -Run with `RUBY_BOX=1`. The first call generates and compiles the bundled grammar -in memory; later calls reuse the class. Each call has its own parser and local -variable table. No build command, network access or writable working directory -is needed. `language:` is required; currently only the symbol `:ruby` is supported. +The output directory must already exist. `--class-name` defaults to +`RubyParser` and must be a single Ruby constant name. Existing output files are +preserved unless `--force` is supplied. A failed generation leaves the previous +file intact. The output includes its source profile, generator version and +copyright/license notices. + +The consumer only needs the generated file and Ruby 4.0 or later: -Supply token pairs from your own lexer. `tINTEGER` values are Integers, `tFLOAT` -values are Floats, and `tIDENTIFIER` values are Symbols or Strings. Operators and -keywords may carry `nil`. Parentheses use Ruby's context-dependent token names -(for example `tLPAREN` followed by `")"`), not a source string tokenizer. +```ruby +require_relative "lib/my_library/ruby_parser" + +tree = MyRubyParser.parse("a = 1\na + 2", filename: "example.rb") +# MyRubyParser::AST::Program containing the assignment and addition +``` + +`parse` takes Ruby source and returns an AST. The parser, lexer and builder are +internal; callers do not need to supply tokens. Each call has fresh parsing and +local-variable state. No Arpaka/Lrama gem, Ruby Box, grammar files, network access +or filesystem writes are needed at runtime. -For source input, use `Arpaka.parse_source`: +For generation from Ruby, `Arpaka.generate` returns that same standalone source. +To try a frontend in memory, use `Arpaka.compile`; both require `RUBY_BOX=1`: ```ruby -tree = Arpaka.parse_source("a = 1\na + 2", filename: "example.rb") +require "arpaka" + +frontend = Arpaka.compile(ruby_source: "/path/to/ruby", class_name: "MyRubyParser") +tree = frontend.parse("a = 1") + +code = Arpaka.generate(ruby_source: "/path/to/ruby", class_name: "MyRubyParser") +File.write("ruby_parser.rb", code) ``` -The source lexer is byte-oriented and context-sensitive. It supports the same -frontend slice as the token API, including numbers, variables, calls, arrays, -hashes, control flow, definitions, strings, regular expressions, percent -literals and static heredocs. Lexer failures include filename, line and byte -column. Ruby interpolation and syntax whose value cannot be represented by the -current AST raises an explicit lexer error. - -The supported slice includes numeric and nil/boolean literals, binary `+ - * /`, -unary `+ -`, single-expression parentheses, simple local assignments, and -statements separated by semicolons or newlines. Every successful parse returns -an `AST::Program`. Its frozen `statements` array contains immutable Data nodes: -`Literal(value)`, `Binary(operator, left, right)`, `Unary(operator, operand)`, -`LocalRead(name)`, `LocalWrite(name, value)`, and `BareCall(name)`. -An unassigned bare identifier is a `BareCall`; assignment registers a local -before reading its right-hand side, including in `a = a`. - -Explicit receiver calls without blocks produce -`ReceiverCall(receiver, operator, name, arguments)`. They preserve chained calls, -ordered positional arguments, the `.` / `::` / `&.` operator, and the implicit -`call` method in `handler.(message)`. Receiverless calls use `Call(name, arguments)`. - -Double quoted strings and backtick strings may contain simple interpolation and -produce `InterpolatedString(parts)`, where each part is a string or an AST node. - -This is not yet a complete Ruby frontend. Percent literal interpolation, blocks, -method argument scopes, qualified constant paths, and other constructs are still incomplete; a successful -parse does not guarantee that all source semantics appear in the AST. Nodes have -no source locations. Token input must already reflect the lexical decisions -Ruby's parser and lexer normally make together; use `parse_source` for source text. - -`Arpaka::ParseError` exposes `token` and `state`. `Arpaka::UnsupportedSyntax` -exposes the original `rule` and upstream `line`. Both inherit from -`Lrama::Ruby::Error`. Unported rules raise -instead of returning a partial AST. Lexer exceptions and invalid token values -propagate to the caller. +The former `Arpaka.parse`, `Arpaka.parse_source` and `Lrama::Ruby.parse` +APIs have been replaced by explicitly generating or compiling a frontend. +The generic `Lrama::Ruby.generate` and `Lrama::Ruby.compile` APIs are unchanged. + +This is not a complete Ruby frontend. Existing support includes literals, +assignments, calls, collections and portions of control flow and definitions. +Some constructs parse without preserving all their semantics in the current +AST, and nodes do not have source locations. Known unported rules raise +`MyRubyParser::UnsupportedSyntax`, exposing `rule` and upstream `line`. +Syntax errors raise `MyRubyParser::ParseError`, exposing `token` and `state`. +Lexer failures raise `MyRubyParser::LexerError` with filename, line and byte +column. These inherit from the generated `MyRubyParser::Error`. + +The generated frontend has also been exercised against real-world source. On +Ruby 4.0.6, it parsed all 3,436 Ruby files in a local Rails checkout. This is +the practical compatibility signal for the current frontend. Ruby's Trick +contest programs are kept as a separate language-compatibility challenge: the +frontend currently parses 12 of 23 Ruby files in the `trick18` checkout, while +the remaining files use deliberately adversarial syntax that is not yet +represented by the current Action mappings. These are smoke-test results, not +a claim of complete Ruby language coverage. ## Current scope @@ -186,7 +207,7 @@ Locations, typed values/`%union`, `%code`, prologues, epilogues, parse/lex param initial actions, hooks, printers, destructors and error recovery are not implemented. The generator rejects these features rather than dropping their behavior. Use plain Ruby values in actions; C actions are not translated into Ruby. -Prism tokenizes Ruby actions and checks generated syntax; Bison is not required. +A Ruby action scanner handles semantic action tokens; Bison is not required. ### Inspecting a C grammar @@ -219,26 +240,58 @@ ruby /path/to/ruby/tool/id2token.rb /path/to/ruby/parse.y > parse.preprocessed.y ## Development ```sh +RUBY_BOX=1 bundle exec rake RUBY_BOX=1 bundle exec rake test -RUBY_BOX=1 bin/console +RUBY_BOX=1 bundle exec rake ruby:check +RUBY_BOX=1 OUTPUT=/path/to/frontend.rb bundle exec rake ruby:generate +RUBY_BOX=1 bundle exec rake benchmark:parse ``` -Tests cover generated parsers, Ruby actions, parse errors and Box isolation, -including execution of a generated file without gems. CI uses Ruby 4.0.6. -`Gemfile.lock` stays local and is not tracked. - -The default task also checks the bundled Ruby grammar against the pinned -upstream source in `vendor/ruby`. `tool/ruby/actions.rb` defines the ported -actions; `tool/ruby/upstream_rules.json` records the original expanded rules. -Run `bundle exec rake ruby:generate` after changing action definitions, and -`bundle exec rake ruby:check` to check for stale artifacts without writing files. -The check compares every production, token ID and precedence, including empty -productions representing midrule actions. Update the pinned revision and rule -inventory together when deliberately upgrading upstream. - -Runtime grammar and rule metadata are packaged; upstream source and developer -tools are not. Ruby-derived artifacts retain the upstream license notices in -`lib/lrama/ruby/languages/ruby/COPYING` and `BSDL`. +Development commands default to the pinned Ruby source fixture in `vendor/ruby`. +In an uninstalled checkout, invoke the CLI with +`RUBY_BOX=1 bundle exec ruby -Ilib exe/arpaka generate ...`. +Set `RUBY_SOURCE` to select another source directory. Normal CLI/API usage always +requires an explicit Ruby source tree. `ruby:check` verifies the source profile, +Action inventory, grammar transformation and generated frontend without editing +artifacts. `ruby:generate` writes the standalone frontend to `OUTPUT`. + +`lib/arpaka/actions.rb` holds the Ruby Action mappings; +`lib/arpaka/upstream_rules.json` records the expected expanded upstream rules. +Update these and the source checksums together when adding support for a changed +Ruby grammar. Runtime templates live under `lib/arpaka/runtime/`. + +Tests exercise the generated frontend and the generic backend. Package checks +install the gem, generate from external Ruby sources, and execute the resulting +file with gems and Ruby Box disabled. `Gemfile.lock` stays local and untracked. +Developer fixtures and tools are excluded from the gem. + +### Benchmark + +`benchmark:parse` measures frontend generation separately from loading the +generated file and parsing in a fresh process with Ruby Box disabled. +Set `ITERATIONS` to change the measured parsing iterations (default: 100). +Each warm measurement follows ten warmup calls. File reading/class definition +is measured separately; process startup and `require "prism"` are not timed. + +The RubyVM and Prism measurements use the same source but return different AST +representations. They are reference measurements, not equivalent-work guarantees. + +Example results on Ruby 4.0.6, x86_64 Linux, YJIT disabled, Prism 1.9.0 +(10 measured iterations per input, ten warmup calls): + +| Input | Arpaka (warm) | RubyVM AST | Prism | +| ---: | ---: | ---: | ---: | +| 10 bytes | 0.054 ms | 0.003 ms | 0.003 ms | +| 1,697 bytes | 5.901 ms | 0.060 ms | 0.128 ms | +| 18,800 bytes | 61.475 ms | 0.850 ms | 1.859 ms | + +Frontend generation took 2.190s and loading the generated file took 20.571ms +in the same run. The first Arpaka parse took 0.130ms, 6.707ms and 73.381ms +for the small, medium and large inputs respectively. + +Generation is a one-time build step per regeneration, not a cost of parsing an +input file. The generated frontend is loaded and measured with Ruby Box disabled. +Results vary with input and environment. ## License diff --git a/Rakefile b/Rakefile index 76e4a1e..f385e9c 100644 --- a/Rakefile +++ b/Rakefile @@ -10,22 +10,30 @@ Rake::TestTask.new(:test) do |t| end namespace :ruby do - desc "Regenerate the bundled Ruby grammar and action metadata" + desc "Generate a standalone Ruby frontend (requires OUTPUT; optional RUBY_SOURCE)" task :generate do - ruby "tool/ruby/grammar.rb" + abort "Set OUTPUT to the destination Ruby file" unless ENV["OUTPUT"] + ruby "tool/ruby/grammar.rb", "--output", ENV.fetch("OUTPUT") end - desc "Verify the bundled Ruby grammar against the pinned upstream source" + desc "Verify the Ruby source profile, Action mappings and generated frontend" task :check do ruby "tool/ruby/grammar.rb", "--check" end end namespace :package do - desc "Build and install a local gem and verify its bundled language runtime" + desc "Verify installed frontend generation and standalone execution" task :check do ruby "tool/check_package.rb" end end task default: ["ruby:check", :test, "package:check"] + +namespace :benchmark do + desc "Benchmark Ruby source and token parsing" + task :parse do + ruby "benchmark/parse.rb" + end +end diff --git a/arpaka.gemspec b/arpaka.gemspec index a012033..f3bfd1a 100644 --- a/arpaka.gemspec +++ b/arpaka.gemspec @@ -19,7 +19,7 @@ Gem::Specification.new do |spec| spec.files = IO.popen(%w[git ls-files -z], chdir: __dir__, err: IO::NULL) do |ls| ls.readlines("\x0", chomp: true).reject do |f| (f == gemspec) || - f.start_with?(*%w[bin/ Gemfile .gitignore test/ .github/ tool/ vendor/]) || + f.start_with?(*%w[bin/ benchmark/ Gemfile .gitignore test/ .github/ .local/ tool/ vendor/]) || %w[AGENTS.md DESIGN.md plan.md].include?(f) end end diff --git a/benchmark/parse.rb b/benchmark/parse.rb new file mode 100644 index 0000000..97cd198 --- /dev/null +++ b/benchmark/parse.rb @@ -0,0 +1,63 @@ +# frozen_string_literal: true + +require "rbconfig" + +iterations = Integer(ENV.fetch("ITERATIONS", "100")) +raise ArgumentError, "ITERATIONS must be positive" unless iterations.positive? +sources = { + small: "value = 1\n", + medium: (1..100).map { |index| "value#{index} = #{index} * 2\n" }.join + "value100 + 1\n", + large: (1..1_000).map { |index| "value#{index} = #{index} * 2\n" }.join + "value1000 + 1\n" +} + +def measure(label, iterations = 1) + GC.start + started_at = Process.clock_gettime(Process::CLOCK_MONOTONIC) + result = nil + iterations.times { result = yield } + elapsed = Process.clock_gettime(Process::CLOCK_MONOTONIC) - started_at + puts format("%-34s %9.6fs total, %9.3fms/call", label, elapsed, elapsed * 1000 / iterations) + result +end + +if ARGV.first == "--runtime" + puts "Runtime: #{RUBY_DESCRIPTION}; Ruby Box: #{Ruby::Box.enabled?}" + measure("load generated file") { require File.expand_path(ARGV.fetch(1)) } + sources.each do |size, source| + puts "#{size}: #{source.bytesize} bytes" + measure("parse #{size} (first)") { BenchmarkRuby.parse(source) } + 10.times { BenchmarkRuby.parse(source) } + measure("parse #{size} (warm)", iterations) { BenchmarkRuby.parse(source) } + end + + if defined?(RubyVM::AbstractSyntaxTree) + sources.each do |size, source| + 10.times { RubyVM::AbstractSyntaxTree.parse(source) } + measure("RubyVM AST #{size}", iterations) { RubyVM::AbstractSyntaxTree.parse(source) } + end + end + require "prism" + puts "Prism: #{Prism::VERSION}" + sources.each do |size, source| + 10.times { Prism.parse(source) } + measure("Prism #{size}", iterations) { Prism.parse(source) } + end +else + require "arpaka" + require "tmpdir" + ruby_source = ENV.fetch("RUBY_SOURCE", File.expand_path("../vendor/ruby", __dir__)) + puts "Arpaka frontend benchmark" + puts "Generator: #{RUBY_DESCRIPTION}" + puts "Inputs: small/medium/large; iterations: #{iterations}; warmup: 10" + code = measure("generate frontend") do + Arpaka.generate(ruby_source: ruby_source, class_name: "BenchmarkRuby") + end + Dir.mktmpdir("arpaka-benchmark-") do |directory| + path = File.join(directory, "frontend.rb") + File.write(path, code) + flags = defined?(RubyVM::YJIT) && RubyVM::YJIT.enabled? ? ["--yjit"] : [] + success = system({ "RUBY_BOX" => nil, "RUBYOPT" => nil }, + RbConfig.ruby, *flags, File.expand_path(__FILE__), "--runtime", path) + abort "Runtime benchmark failed" unless success + end +end diff --git a/exe/arpaka b/exe/arpaka new file mode 100755 index 0000000..027f29e --- /dev/null +++ b/exe/arpaka @@ -0,0 +1,6 @@ +#!/usr/bin/env ruby +# frozen_string_literal: true + +require "arpaka/cli" + +exit Arpaka::CLI.run(ARGV) diff --git a/lib/arpaka.rb b/lib/arpaka.rb index ba46fd0..65281f8 100644 --- a/lib/arpaka.rb +++ b/lib/arpaka.rb @@ -1,9 +1,20 @@ # frozen_string_literal: true require_relative "lrama/ruby" -require_relative "lrama/ruby/languages/ruby" -# Public name for the Ruby language frontend. The implementation currently -# lives below Lrama::Ruby::Languages::Ruby while the backend is being split -# from the frontend. -Arpaka = Lrama::Ruby::Languages::Ruby unless defined?(Arpaka) +module Arpaka + class Error < Lrama::Ruby::Error; end + + def self.generate(ruby_source:, class_name: "RubyParser") + Generator.new.generate(ruby_source: ruby_source, class_name: class_name) + end + + def self.compile(ruby_source:, class_name: "RubyParser") + source = generate(ruby_source: ruby_source, class_name: class_name) + box = ::Ruby::Box.new + box.eval(source) + box.const_get(class_name, false) + end +end + +require_relative "arpaka/generator" diff --git a/lib/lrama/ruby/languages/ruby/BSDL b/lib/arpaka/BSDL similarity index 100% rename from lib/lrama/ruby/languages/ruby/BSDL rename to lib/arpaka/BSDL diff --git a/lib/lrama/ruby/languages/ruby/COPYING b/lib/arpaka/COPYING similarity index 100% rename from lib/lrama/ruby/languages/ruby/COPYING rename to lib/arpaka/COPYING diff --git a/tool/ruby/actions.rb b/lib/arpaka/actions.rb similarity index 99% rename from tool/ruby/actions.rb rename to lib/arpaka/actions.rb index 2c252b4..22eba93 100644 --- a/tool/ruby/actions.rb +++ b/lib/arpaka/actions.rb @@ -2,7 +2,7 @@ # Rule IDs refer to upstream_rules.json, validated against the pinned source. # Each entry is [classification, Ruby expression used as the reduction value]. -module RubyGrammarActions +module Arpaka::RubyGrammarActions ACTIONS = { 1 => ["empty", "nil"], 2 => ["empty", "nil"], 3 => ["empty", "nil"], 4 => ["pass_through", "$1"], 5 => ["ported", "@builder.program($2)"], @@ -116,8 +116,7 @@ module RubyGrammarActions 526 => ["pass_through", "$1"], 527 => ["ported", "[$1].freeze"], 412 => ["pass_through", "nil"], - 419 => ["pass_through", "nil"], 420 => ["pass_through", "nil"], 421 => ["pass_through", "nil"], - 426 => ["empty", "nil"], + 420 => ["pass_through", "nil"], 421 => ["pass_through", "nil"], 95 => ["ported", "$2"], 94 => ["pass_through", "$1"], 82 => ["pass_through", "$1"], @@ -253,7 +252,6 @@ module RubyGrammarActions 452 => ["pass_through", "$1"], 451 => ["pass_through", "$1"], 448 => ["pass_through", "$1"], - 445 => ["pass_through", "$1"], 368 => ["ported", "@builder.block_call($1, $2)"], 366 => ["ported", "@builder.block_call(@builder.call($1, []), $2)"], 356 => ["pass_through", "$2"], @@ -310,7 +308,7 @@ module RubyGrammarActions 551 => ["pass_through", "$2"], 550 => ["pass_through", "$2"], 539 => ["pass_through", "$1"], - 522 => ["pass_through", "$2"], 427 => ["pass_through", "$2"], + 522 => ["pass_through", "$2"], 630 => ["pass_through", "$1"], 631 => ["pass_through", "$1"], 632 => ["empty", "nil"], 633 => ["pass_through", "$2"], 634 => ["empty", "nil"], 635 => ["pass_through", "$2"], diff --git a/lib/arpaka/cli.rb b/lib/arpaka/cli.rb new file mode 100644 index 0000000..9f40132 --- /dev/null +++ b/lib/arpaka/cli.rb @@ -0,0 +1,60 @@ +# frozen_string_literal: true + +require "optparse" +require "tempfile" +require_relative "../arpaka" + +module Arpaka + class CLI + def self.run(argv, out: $stdout, err: $stderr) + options = { class_name: "RubyParser" } + force = false + help = false + parser = OptionParser.new do |opts| + opts.banner = "Usage: arpaka generate --ruby-source DIR --output FILE [--class-name NAME] [--force]" + opts.on("--ruby-source DIR", "Ruby source tree (provided by the user)") { |v| options[:ruby_source] = v } + opts.on("--output FILE", "Write a standalone Ruby frontend") { |v| options[:output] = v } + opts.on("--class-name NAME", "Generated class name (default: RubyParser)") { |v| options[:class_name] = v } + opts.on("--force", "Replace an existing output file") { force = true } + opts.on("-h", "--help", "Show usage") { help = true } + end + args = argv.dup + command = args.shift + if ["--help", "-h"].include?(command) + out.puts(parser) + return 0 + end + raise OptionParser::InvalidArgument, "expected generate" unless command == "generate" + parser.parse!(args) + if help + out.puts(parser) + return 0 + end + raise OptionParser::InvalidArgument, args.join(" ") unless args.empty? + %i[ruby_source output].each do |key| + raise OptionParser::MissingArgument, "--#{key.to_s.tr('_', '-')}" unless options[key] + end + output = File.expand_path(options.delete(:output)) + if !force && (File.exist?(output) || File.symlink?(output)) + raise Error, "Output already exists: #{output}; use --force to replace it" + end + source = Arpaka.generate(**options) + Tempfile.create([".arpaka-", ".rb"], File.dirname(output)) do |file| + file.write(source) + file.flush + file.chmod(0o644) + if force + File.rename(file.path, output) + else + # Publish only the complete file, without overwriting a concurrent writer. + File.link(file.path, output) + end + end + out.puts("Generated #{output}") + 0 + rescue OptionParser::ParseError, Lrama::Ruby::Error, SystemCallError => error + err.puts("arpaka: #{error.message}") + 1 + end + end +end diff --git a/lib/arpaka/generator.rb b/lib/arpaka/generator.rb new file mode 100644 index 0000000..f65d574 --- /dev/null +++ b/lib/arpaka/generator.rb @@ -0,0 +1,43 @@ +# frozen_string_literal: true + +require "erb" +require "json" +require_relative "ruby_grammar" + +module Arpaka + class Generator + def generate(ruby_source:, class_name:) + unless /\A[A-Z][a-zA-Z0-9_]*\z/.match?(class_name) + raise Error, "class_name must be a single Ruby constant name" + end + unless ::Ruby::Box.enabled? + raise Error, "Ruby Box is required for generation. Start Ruby with RUBY_BOX=1." + end + artifacts = RubyGrammar.artifacts(ruby_source: ruby_source) + parser = Lrama::Ruby.generate(artifacts.fetch("parse.y"), + filename: File.join(ruby_source, "parse.y"), class_name: "Parser", allow_error_rules: true) + parser = parser.delete_prefix("# frozen_string_literal: true\n") + rules = JSON.parse(artifacts.fetch("rules.json")) + runtime = %w[frontend ast builder lexer].map do |name| + template = File.read(File.join(__dir__, "runtime", "#{name}.rb.erb")) + name == "builder" ? ERB.new(template).result_with_hash(rules: rules) : template + end.join("\n") + notices = [File.read(File.expand_path("../../LICENSE.txt", __dir__))] + %w[COPYING BSDL].each { |name| notices << File.read(File.join(ruby_source, name)) } + source = "# frozen_string_literal: true\n" \ + "# Generated by Arpaka #{Lrama::Ruby::VERSION}. Do not edit.\n" \ + "# Ruby source profile: #{RubyGrammar::REVISION}\n" \ + "# parse.y SHA256: #{Digest::SHA256.file(File.join(ruby_source, 'parse.y')).hexdigest}\n" \ + "#{notices.join("\n").lines.map { |line| "# #{line}" }.join}\n" \ + "class #{class_name}\n#{parser}\n#{runtime}\nend\n" + # Compile in an isolated box to validate syntax without executing actions. + box = ::Ruby::Box.new + box.eval(source) + source + rescue SyntaxError => error + raise Error, "Invalid generated frontend: #{error.message}" + rescue SystemCallError => error + raise Error, "Cannot read Ruby frontend inputs: #{error.message}" + end + end +end diff --git a/lib/arpaka/ruby_grammar.rb b/lib/arpaka/ruby_grammar.rb new file mode 100644 index 0000000..8e80926 --- /dev/null +++ b/lib/arpaka/ruby_grammar.rb @@ -0,0 +1,170 @@ +# frozen_string_literal: true + +require "lrama" +require "json" +require "digest" +require "open3" +require "rbconfig" +require_relative "actions" + +module Arpaka::RubyGrammar + REVISION = "37d60dd3241d5fdb10ca43f36077f5d556ae1b8d" + EXTENDED_RULES = { + ["value_expr_command", ["command"]] => "tLBRACE_ARG" + }.freeze + + def self.parse(source, filename) + grammar = Lrama::Parser.new(source, filename).parse + unless grammar.no_stdlib + stdlib = Lrama::Command::STDLIB_FILE_PATH + grammar.prepend_parameterized_rules(Lrama::Parser.new(File.read(stdlib), stdlib).parse.parameterized_rules) + end + grammar.prepare + grammar.validate! + grammar + end + + def self.load_upstream(ruby_source) + vendor = File.expand_path(ruby_source) + manifest = JSON.parse(File.read(File.join(__dir__, "source.json"))) + raise ::Arpaka::Error, "Unexpected upstream revision" unless manifest.fetch("revision") == REVISION + manifest.fetch("files").each do |path, digest| + actual = Digest::SHA256.file(File.join(vendor, path)).hexdigest + next if actual == digest + + raise ::Arpaka::Error, "Unsupported Ruby source change: #{path}; " \ + "expected SHA256 #{digest}, got #{actual}. " \ + "Update the Action mappings and source profile before regenerating." + end + source, error, result = Open3.capture3(RbConfig.ruby, + File.join(vendor, "tool/id2token.rb"), File.join(vendor, "parse.y")) + raise ::Arpaka::Error, "Ruby preprocessing failed: #{error}" unless result.success? + grammar = parse(source, File.join(vendor, "parse.y")) + inventory = grammar.rules.reject(&:initial_rule?).map do |rule| + { "id" => rule.id, "lhs" => rule.lhs.id.s_value, + "rhs" => rule.rhs.map { |symbol| symbol.id.s_value }, + "line" => rule.token_code&.location&.first_line || rule.lineno, + "code" => rule.token_code&.s_value, + "midrule_position" => rule.position_in_original_rule_rhs, + "precedence" => rule.precedence_sym&.id&.s_value } + end + expected = JSON.parse(File.read(File.join(__dir__, "upstream_rules.json"))) + unless inventory == expected + changed = (0...[inventory.length, expected.length].max).select { |i| inventory[i] != expected[i] } + details = changed.first(5).map do |i| + before, after = expected[i], inventory[i] + "rule #{before&.fetch('id') || after.fetch('id')}: " \ + "#{before&.fetch('lhs') || '(absent)'} -> #{after&.fetch('lhs') || '(absent)'}" + end + raise ::Arpaka::Error, "Upstream rules changed (#{changed.length} entries); " \ + "review Action mappings: #{details.join('; ')}" + end + unknown = ::Arpaka::RubyGrammarActions::ACTIONS.keys - inventory.map { |rule| rule.fetch("id") } + raise ::Arpaka::Error, "Unknown action IDs: #{unknown.inspect}" unless unknown.empty? + [grammar, inventory] + end + + def self.symbol_names(grammar) + used = grammar.terms.map { |symbol| symbol.id.s_value } + grammar.nterms.to_h do |symbol| + original = symbol.id.s_value + candidate = if original.match?(/\A\$?@\d+\z/) + "midrule_#{original.delete('$@')}" + else + original.gsub("'\\n'", "newline").gsub(/[^a-zA-Z0-9_]/, "_") + end + candidate = "rule_#{candidate}" unless candidate.match?(/\A[a-zA-Z_]/) + candidate += "_#{symbol.number}" if used.include?(candidate) + raise ::Arpaka::Error, "Symbol name collision: #{candidate}" if used.include?(candidate) + used << candidate + [original, candidate] + end + end + + def self.artifacts(ruby_source:) + grammar, inventory = load_upstream(ruby_source) + names = symbol_names(grammar) + name = ->(symbol) { symbol.term ? symbol.id.s_value : names.fetch(symbol.id.s_value) } + lines = ["/* Generated from ruby/ruby #{REVISION}. See Arpaka Action mappings. */", + "%no-stdlib", "%expect #{grammar.expect || 0}"] + lines << "%define lr.type ielr" if grammar.ielr_defined? + grammar.terms.each do |symbol| + next if symbol.error_symbol? || symbol.undef_symbol? + declaration = "%token #{name.call(symbol)} #{symbol.token_id}" + declaration += " #{symbol.alias_name}" if symbol.alias_name + lines << declaration + end + grammar.precedences.group_by(&:precedence).sort.each do |_level, group| + lines << "%#{group.first.type} #{group.map(&:s_value).join(' ')}" + end + lines << "%left keyword_do_block" + lines << "%start #{name.call(grammar.rules.first.rhs.first)}" + lines << "%%" + metadata = inventory.to_h { |rule| [rule.fetch("id"), rule] } + rules = grammar.rules.reject(&:initial_rule?) + rules.each do |rule| + rhs = rule.rhs.empty? ? "%empty" : rule.rhs.map(&name).join(" ") + extended_precedence = EXTENDED_RULES[[rule.lhs.id.s_value, rule.rhs.map { |symbol| symbol.id.s_value }]] + precedence = if rule.precedence_sym + " %prec #{name.call(rule.precedence_sym)}" + elsif extended_precedence + " %prec #{extended_precedence}" + else + "" + end + expression = ::Arpaka::RubyGrammarActions::ACTIONS[rule.id]&.last || "@builder.unsupported(#{rule.id})" + lines << "/* upstream parse.y:#{metadata.fetch(rule.id).fetch('line')}: #{rule.as_comment} */" + lines << "#{name.call(rule.lhs)}: #{rhs}#{precedence} { $$ = #{expression} };" + end + lines << "/* arpaka extension: block call as a parenthesized argument */" + lines << "call_args: block_call { $$ = [$1].freeze };" + lines << "call_args: args ',' block_call %prec tLOWEST { $$ = ($1 + [$3]).freeze };" + source = lines.join("\n") + "\n" + verify_structure(grammar, source, names) + compact = rules.map do |rule| + { id: rule.id, rule: rule.as_comment, + line: metadata.fetch(rule.id).fetch("line"), + status: ::Arpaka::RubyGrammarActions::ACTIONS[rule.id]&.first || "unsupported" } + end + { "parse.y" => source, "rules.json" => JSON.pretty_generate(compact) + "\n" } + end + + def self.verify_structure(original, source, names) + # C-lexer inspection only: @builder is a Ruby ivar, not a yacc location. + port = parse(source.gsub("@builder.", "builder_"), "generated Ruby grammar") + signature = lambda do |grammar, rename| + grammar.rules.reject(&:initial_rule?).map do |rule| + name = ->(symbol) { rename.fetch(symbol.id.s_value, symbol.id.s_value) } + [name.call(rule.lhs), rule.rhs.map(&name), rule.precedence_sym && name.call(rule.precedence_sym)] + end.sort_by(&:inspect) + end + expected_rules = signature.call(original, names).map do |rule| + if rule[0] == names.fetch("value_expr_command") && rule[1] == [names.fetch("command")] + rule[0, 2] + ["tLBRACE_ARG"] + else + rule + end + end + expected_rules << ["call_args", ["block_call"], nil] + expected_rules << ["call_args", ["args", "','", "block_call"], "tLOWEST"] + actual_rules = signature.call(port, {}) + unless expected_rules.sort_by(&:inspect) == actual_rules.sort_by(&:inspect) + raise ::Arpaka::Error, "Production structure changed" + end + terms = lambda do |grammar| + grammar.terms.map { |symbol| [symbol.id.s_value, symbol.token_id, symbol.precedence&.type, symbol.precedence&.precedence] }.sort_by(&:inspect) + end + expected_terms = terms.call(original).map do |term| + if term[0] == "keyword_do_block" + [term[0], term[1], :left, original.precedences.map(&:precedence).max + 1] + else + term + end + end + actual_terms = terms.call(port) + unless expected_terms.sort_by(&:inspect) == actual_terms + raise ::Arpaka::Error, "Tokens or precedence changed: expected=#{expected_terms.sort_by(&:inspect).inspect} actual=#{actual_terms.inspect}" + end + end + +end diff --git a/lib/arpaka/runtime/ast.rb.erb b/lib/arpaka/runtime/ast.rb.erb new file mode 100644 index 0000000..6d4962f --- /dev/null +++ b/lib/arpaka/runtime/ast.rb.erb @@ -0,0 +1,35 @@ +module AST + Program = Data.define(:statements) + Literal = Data.define(:value) + Binary = Data.define(:operator, :left, :right) + Unary = Data.define(:operator, :operand) + If = Data.define(:condition, :then_body, :else_body) + LocalRead = Data.define(:name) + LocalWrite = Data.define(:name, :value) + BareCall = Data.define(:name) + ArrayLiteral = Data.define(:elements) + HashLiteral = Data.define(:pairs) + Pair = Data.define(:key, :value) + RangeLiteral = Data.define(:operator, :begin_value, :end_value) + Ternary = Data.define(:condition, :then_value, :else_value) + Call = Data.define(:name, :arguments) + ReceiverCall = Data.define(:receiver, :operator, :name, :arguments) + BlockCall = Data.define(:call, :body) + StringLiteral = Data.define(:value) + InterpolatedString = Data.define(:parts) + Index = Data.define(:receiver, :arguments) + Loop = Data.define(:kind, :condition, :body) + Control = Data.define(:kind) + Variable = Data.define(:kind, :name) + Rescue = Data.define(:expression, :fallback) + RegexpLiteral = Data.define(:value) + XStringLiteral = Data.define(:value) + For = Data.define(:variable, :enumerable, :body) + Def = Data.define(:name, :arguments, :body) + ClassDef = Data.define(:name, :superclass, :body) + ModuleDef = Data.define(:name, :body) + When = Data.define(:patterns, :body) + Case = Data.define(:expression, :whens, :else_body) + Lambda = Data.define(:arguments, :body) + Sequence = Data.define(:statements) +end diff --git a/lib/arpaka/runtime/builder.rb.erb b/lib/arpaka/runtime/builder.rb.erb new file mode 100644 index 0000000..c8979cf --- /dev/null +++ b/lib/arpaka/runtime/builder.rb.erb @@ -0,0 +1,247 @@ +class Builder + RULES = <%= rules.inspect %>.to_h { |rule| [rule.fetch("id"), rule.freeze] }.freeze + private_constant :RULES + + def initialize + @locals = {} + end + + def program(statements) + AST::Program.new(statements.freeze) + end + + def integer(value) + raise ArgumentError, "tINTEGER requires an Integer value" unless value.is_a?(Integer) + AST::Literal.new(value) + end + + def float(value) + raise ArgumentError, "tFLOAT requires a Float value" unless value.is_a?(Float) + AST::Literal.new(value) + end + + def literal(value) + AST::Literal.new(value) + end + + def reserved_word(value) + value.to_sym + end + + def binary(operator, left, right) + AST::Binary.new(operator, left, right) + end + + def operator(value) + value.to_sym + end + + def range(operator, begin_value, end_value) + AST::RangeLiteral.new(operator, begin_value, end_value) + end + + def ternary(condition, then_value, else_value) + AST::Ternary.new(condition, then_value, else_value) + end + + def call(name, arguments) + AST::Call.new(name, arguments.freeze) + end + + def receiver_call(receiver, operator, name, arguments) + AST::ReceiverCall.new(receiver, operator, name, (arguments || []).freeze) + end + + def block_call(call, body) + AST::BlockCall.new(call, body_nodes(body)) + end + + def append_arguments(arguments, argument) + list = arguments.is_a?(Array) ? arguments : [arguments] + (list + [argument]).freeze + end + + def string(value) + if value.is_a?(Array) + return AST::InterpolatedString.new(value.freeze) if value.any? { |part| !part.is_a?(String) } + value = value.join + end + AST::StringLiteral.new(value.to_s) + end + + def regexp(value) + AST::RegexpLiteral.new(value.is_a?(Array) ? value.join : value.to_s) + end + + def xstring(value) + AST::XStringLiteral.new(value.is_a?(Array) ? value.join : value.to_s) + end + + def for_node(variable, enumerable, body) + AST::For.new(variable, enumerable, body) + end + + def def_node(name, arguments, body) + AST::Def.new(name.to_sym, (arguments || []).freeze, body || [].freeze) + end + + def class_node(name, superclass, body) + AST::ClassDef.new(name.to_sym, superclass, body_nodes(body)) + end + + def module_node(name, body) + AST::ModuleDef.new(name.to_sym, body_nodes(body)) + end + + def body_nodes(body) + value = body + while value.is_a?(Array) && value.length == 1 && value.first.is_a?(Array) + value = value.first + end + value || [].freeze + end + + def symbol(value) + value = value.map { |part| part.is_a?(String) ? part : part.to_s }.join if value.is_a?(Array) + value = value.name if value.is_a?(AST::Variable) + AST::Literal.new(value.to_sym) + end + + def join_strings(parts) + parts + end + + def interpolation(value) + value.is_a?(Array) && value.length == 1 ? value.first : value + end + + def concat_strings(left, right) + left_parts = left.is_a?(AST::InterpolatedString) ? left.parts : [left.value] + right_parts = right.is_a?(AST::InterpolatedString) ? right.parts : [right.value] + parts = left_parts + right_parts + parts.all? { |part| part.is_a?(String) } ? AST::StringLiteral.new(parts.join) : AST::InterpolatedString.new(parts.freeze) + end + + def index(receiver, arguments) + AST::Index.new(receiver, arguments.freeze) + end + + def loop(kind, condition, body) + AST::Loop.new(kind, condition, body) + end + + def control(kind) + AST::Control.new(kind) + end + + def variable(kind, name) + AST::Variable.new(kind, name.is_a?(String) ? name.to_sym : name) + end + + def rescue_modifier(expression, fallback) + AST::Rescue.new(expression, fallback) + end + + def when_node(patterns, body) + AST::When.new(patterns.freeze, body) + end + + def case_node(expression, whens, else_body) + AST::Case.new(expression, whens.freeze, else_body) + end + + def case_clauses(value) + value.is_a?(Array) ? value : [value] + end + + def case_parts(value) + values = case_clauses(value) + if values.all? { |item| item.is_a?(AST::When) } + [values, nil] + else + [values.grep(AST::When), values.reject { |item| item.is_a?(AST::When) }] + end + end + + def case_chain(when_node, rest) + [when_node] + (rest.nil? ? [] : case_clauses(rest)) + end + + def lambda_node(arguments, body) + AST::Lambda.new((arguments || []).freeze, body_nodes(body)) + end + + def unary(operator, operand) + AST::Unary.new(operator, operand) + end + + def if_node(condition, then_body, else_body) + AST::If.new(condition, then_body, else_body) + end + + def unless_node(condition, then_body, else_body) + AST::If.new(AST::Unary.new(:!, condition), then_body, else_body) + end + + def elsif_node(condition, then_body, else_body) + AST::If.new(condition, then_body, else_body) + end + + def parentheses(statements, rule_id) + return statements.first if statements.length == 1 + AST::Sequence.new(statements.freeze) + end + + def identifier(value) + unless value.is_a?(Symbol) || value.is_a?(String) + raise ArgumentError, "tIDENTIFIER requires a Symbol or String value" + end + value.to_sym + end + + def declare_local(name) + @locals[name] = true + name + end + + def read_local(name) + return name if name.is_a?(AST::Variable) + (@locals.key?(name) ? AST::LocalRead : AST::BareCall).new(name) + end + + def assign(name, value) + AST::LocalWrite.new(name, value) + end + + def array(elements) + AST::ArrayLiteral.new(elements.freeze) + end + + def word_array(elements, symbols: false) + elements ||= [] + elements = elements.elements if elements.is_a?(AST::ArrayLiteral) + values = elements.map do |value| + value = value.value if value.is_a?(AST::StringLiteral) + symbols ? value.to_sym : AST::StringLiteral.new(value.to_s) + end + AST::ArrayLiteral.new(values.freeze) + end + + def hash(pairs) + AST::HashLiteral.new(pairs.freeze) + end + + def pair(key, value) + AST::Pair.new(key, value) + end + + def append(list, value) + value.nil? ? list : (list + [value]).freeze + end + + def unsupported(id) + rule = RULES.fetch(id) + raise UnsupportedSyntax.new(rule.fetch("rule"), rule.fetch("line")) + end +end +private_constant :Builder diff --git a/lib/arpaka/runtime/frontend.rb.erb b/lib/arpaka/runtime/frontend.rb.erb new file mode 100644 index 0000000..eb0c97d --- /dev/null +++ b/lib/arpaka/runtime/frontend.rb.erb @@ -0,0 +1,37 @@ +class Error < StandardError; end + +class ParseError < Error + attr_reader :token, :state + + def initialize(error) + @token, @state = error.token, error.state + super(error.message) + end +end + +class UnsupportedSyntax < Error + attr_reader :rule, :line + + def initialize(rule, line) + @rule, @line = rule, line + super("Unsupported syntax: #{rule} (upstream parse.y:#{line})") + end +end + +class LexerError < Error; end + +def self.parse(source, filename: "(ruby)") + lexer = Lexer.new(source, filename: filename) + parse_tokens(lexer.each, lexical_context: lexer.context) +end + +def self.parse_tokens(tokens, lexical_context: nil) + parser = Parser.new + parser.instance_variable_set(:@builder, Builder.new) + parser.lexical_context = lexical_context + parser.parse(tokens) +rescue Parser::ParseError => error + raise ParseError.new(error) +end +private_class_method :parse_tokens +private_constant :Parser diff --git a/lib/arpaka/runtime/lexer.rb.erb b/lib/arpaka/runtime/lexer.rb.erb new file mode 100644 index 0000000..0018687 --- /dev/null +++ b/lib/arpaka/runtime/lexer.rb.erb @@ -0,0 +1,1371 @@ +# Lexer for Ruby source input. This deliberately does not use Prism: +# the parser needs the same small amount of lexical context that CRuby +# keeps in its parser (EXPR_BEG/EXPR_END and delimiter nesting). +class LexicalContext + attr_accessor :begin_expression, :condition_do, :condition_line, + :ternary_depth, :lambda_pending, :alias_context, :argument_label, + :lex_state, :command_start, :label_pending, :singleton_class_depth, :control_depth, + :method_header, :method_body_start + attr_reader :delimiter_stack, :cmdarg_stack, :condition_stack, + :token_history, :state_history, :block_stack, :scope_stack, :parser_events, + :parser_delimiter_stack, :parser_cmdarg_stack, :parser_block_stack, + :parser_condition_stack, :parser_scope_stack + + def initialize + @begin_expression = true + @condition_do = false + @condition_line = false + @ternary_depth = 0 + @lambda_pending = false + @alias_context = false + @argument_label = nil + @label_pending = false + @singleton_class_depth = 0 + @control_depth = 0 + @method_header = false + @method_body_start = false + @lex_state = :expr_beg + @command_start = true + @delimiter_stack = [] + @cmdarg_stack = [] + @condition_stack = [] + @token_history = [] + @state_history = [] + @block_stack = [] + @block_delimiter_depths = [] + @scope_stack = [] + @parser_events = [] + @parser_delimiter_stack = [] + @parser_cmdarg_stack = [] + @parser_block_stack = [] + @parser_condition_stack = [] + @parser_scope_stack = [] + end + + def delimiter_depth + @delimiter_stack.length + end + + def push_delimiter(value) + @delimiter_stack << value + end + + def pop_delimiter(value) + openers = case value + when ")", "(" then ["(", :tLPAREN, :tLPAREN_ARG] + when "]" then ["[", :tLBRACK] + when "}" then ["{"] + else [value] + end + @delimiter_stack.pop if openers.include?(@delimiter_stack.last) + end + + def push_cmdarg(value) + @cmdarg_stack << value + end + + def pop_cmdarg + @cmdarg_stack.pop + end + + def cmdarg? + @cmdarg_stack.last == true + end + + def push_condition(value = true) + @condition_stack << value + end + + def pop_condition + @condition_stack.pop + end + + def condition? + @condition_stack.any? + end + + def push_block(value, delimiter_depth: self.delimiter_depth) + @block_stack << value + @block_delimiter_depths << delimiter_depth + end + + def pop_block + @block_delimiter_depths.pop + @block_stack.pop + end + + def block_depth + @block_stack.length + end + + def block_body_at_current_delimiter? + @block_delimiter_depths.last == delimiter_depth + end + + def push_scope(value) + @scope_stack << value + end + + def pop_scope + @scope_stack.pop + end + + def scope_depth + @scope_stack.length + end + + def remember_token(token) + @token_history << token + @token_history.shift while @token_history.length > 8 + end + + def remember_state(token) + @state_history << { + token: token, + lex_state: @lex_state, + begin_expression: @begin_expression, + command_start: @command_start, + delimiter_depth: delimiter_depth, + cmdarg_depth: @cmdarg_stack.length, + condition_depth: @condition_stack.length, + block_depth: @block_stack.length, + scope_depth: @scope_stack.length + }.freeze + end + + def parser_shift(token, value, state, action) + @parser_events << [:shift, token, state, action].freeze + if ["(", "[", :tLPAREN_ARG].include?(token) + @parser_delimiter_stack << token + @parser_cmdarg_stack << (token == :tLPAREN_ARG) + elsif [")", "]", "}"].include?(token) + opener = {")" => "(", "]" => "[", "}" => "{" + }.fetch(token) + parser_openers = { + "(" => ["(", :tLPAREN, :tLPAREN_ARG], + "[" => ["[", :tLBRACK], + "{" => ["{"] + } + @parser_delimiter_stack.pop if parser_openers.fetch(opener).include?(@parser_delimiter_stack.last) + @parser_cmdarg_stack.pop + end + if [:keyword_do, :keyword_do_block, :keyword_do_LAMBDA, :tLAMBEG].include?(token) || + token == "{" || token == :tLBRACE_ARG + @parser_block_stack << token + elsif token == "}" && @parser_block_stack.last + @parser_block_stack.pop + elsif token == :keyword_end && @parser_block_stack.last + @parser_block_stack.pop + end + if [:keyword_while, :keyword_until, :keyword_for].include?(token) + @parser_condition_stack << token + elsif token == :keyword_do_cond && @parser_condition_stack.last + @parser_condition_stack.pop + elsif token == :keyword_end && @parser_condition_stack.last + @parser_condition_stack.pop + end + if token == :keyword_def + @parser_scope_stack << :method + elsif [:keyword_class, :keyword_module].include?(token) + @parser_scope_stack << token + elsif token == :tLAMBDA + @parser_scope_stack << :lambda + elsif token == "}" && @parser_scope_stack.last == :lambda + @parser_scope_stack.pop + elsif token == :keyword_end && @parser_scope_stack.last + @parser_scope_stack.pop + end + end + + def parser_reduce(rule, state) + @parser_events << [:reduce, rule, state].freeze + end +end + +class Lexer + Error = LexerError + + attr_reader :context + + KEYWORDS = { + "class" => :keyword_class, "module" => :keyword_module, + "def" => :keyword_def, "undef" => :keyword_undef, "begin" => :keyword_begin, + "rescue" => :keyword_rescue, "ensure" => :keyword_ensure, + "end" => :keyword_end, "if" => :keyword_if, + "unless" => :keyword_unless, "then" => :keyword_then, + "elsif" => :keyword_elsif, "else" => :keyword_else, + "case" => :keyword_case, "when" => :keyword_when, + "while" => :keyword_while, "until" => :keyword_until, + "for" => :keyword_for, "break" => :keyword_break, + "next" => :keyword_next, "redo" => :keyword_redo, + "retry" => :keyword_retry, "return" => :keyword_return, + "yield" => :keyword_yield, "super" => :keyword_super, + "self" => :keyword_self, "nil" => :keyword_nil, + "true" => :keyword_true, "false" => :keyword_false, + "and" => :keyword_and, "or" => :keyword_or, + "not" => :keyword_not, "alias" => :keyword_alias, + "defined?" => :keyword_defined, "in" => :keyword_in, + "BEGIN" => :keyword_BEGIN, "END" => :keyword_END, + "__LINE__" => :keyword__LINE__, "__FILE__" => :keyword__FILE__, + "__ENCODING__" => :keyword__ENCODING__ + }.freeze + + OPERATORS = %w[[]= [] ... .. <=> === == != =~ !~ >= <= && || << >> ** => :: &. -> += -= *= /= %= **= <<= >>= &&= ||= |= &= ^=].freeze + OP_TOKENS = { + "**" => :tPOW, "<=>" => :tCMP, "==" => :tEQ, "===" => :tEQQ, + "!=" => :tNEQ, ">=" => :tGEQ, "<=" => :tLEQ, "&&" => :tANDOP, + "||" => :tOROP, "=~" => :tMATCH, "!~" => :tNMATCH, + ".." => :tDOT2, "..." => :tDOT3, "<<" => :tLSHFT, + ">>" => :tRSHFT, "&." => :tANDDOT, "::" => :tCOLON2, + "=>" => :tASSOC, "->" => :tLAMBDA, "[]" => :tAREF, "[]=" => :tASET + }.freeze + + def initialize(source, filename: "(ruby)") + unless source.is_a?(String) + raise ArgumentError, "source must be a String" + end + unless [Encoding::UTF_8, Encoding::US_ASCII].include?(source.encoding) && source.valid_encoding? + raise Error, "#{filename}:1:0: invalid UTF-8 source" + end + @source = source.b + @filename = filename + @index = 0 + @line = 1 + @column = 0 + @previous_previous = nil + @previous = nil + @previous_value = nil + @context = LexicalContext.new + @pending = [] + @heredoc_queue = [] + @deferred_suffix = nil + @heredoc_newline_pending = false + @class_superclass = false + @local_variables = {} + end + + def each + return enum_for(__method__) unless block_given? + until eof? && @pending.empty? && @heredoc_queue.empty? && @deferred_suffix.nil? + token = next_token + yield token if token + end + yield [0, nil] + end + + private + + def eof? + @index >= @source.bytesize + end + + def byte(offset = 0) + @source.getbyte(@index + offset) + end + + def advance(count = 1) + count.times do + value = byte + @index += 1 + if value == 10 + @line += 1 + @column = 0 + else + @column += 1 + end + end + end + + def fail!(message, index = @index, line = @line, column = @column) + raise Error, "#{@filename}:#{line}:#{column}: #{message}" + end + + def next_token + if @pending.empty? && @heredoc_queue.any? + load_queued_heredoc + elsif @pending.empty? && @deferred_suffix + suffix = @deferred_suffix + @deferred_suffix = nil + @pending.concat(heredoc_suffix_tokens(suffix)) + @pending.pop if @pending.last == [0, nil] + @pending << ["\n", nil] + @heredoc_newline_pending = false + elsif @pending.empty? && @heredoc_newline_pending + @pending << ["\n", nil] + @heredoc_newline_pending = false + end + unless @pending.empty? + value = @pending.shift + return next_token if value[0] == "\n" && newline_ignored? + @previous_previous = @previous + @previous = value[0] + @previous_value = value[1] + update_pending_delimiter(value[0]) + update_argument_label(value[0], value[1]) + @context.alias_context = false if value[0] == ";" + @context.remember_token(value[0]) + @context.begin_expression = expression_begin_after(value[0]) + @context.lex_state = lexical_state_after(value[0]) + @context.command_start = command_start_after(value[0]) + update_block_context(value[0]) + @context.remember_state(value[0]) + return value + end + skip_space_and_comments + return nil if eof? + + start = @index + value = case byte + when 10 + advance + return next_token if @previous == "\n" + return next_token if next_chain_byte == 46 + if @context.alias_context && [:tEQ, :tUMINUS, :tUPLUS].include?(@previous) + @context.alias_context = false + ["\n", nil] + elsif @class_superclass && next_word_is_terminator? + @class_superclass = false + return [";", nil] + else + @class_superclass = false + return next_token if newline_ignored? || @previous == ";" + @context.pop_condition if @context.condition? && @context.condition_do + @context.condition_do = false + ["\n", nil] + end + when 39, 34, 96 + if byte == 96 && @previous == :tSYMBEG + operator_or_punctuation(start) + else + string_token(byte, start) + end + when 47 + [:keyword_def, :keyword_alias, :tSYMBEG].include?(@previous) ? operator_or_punctuation(start) : regexp_or_operator(start) + when 37 + [:keyword_def, :keyword_alias, :tSYMBEG].include?(@previous) ? operator_or_punctuation(start) : percent_token(start) + when 60 + heredoc_or_operator(start) + when 48..57 + number_token(start) + when 36, 64 + variable_token(start) + when 63 + character_or_question(start) + when 58 + symbol_or_colon(start) + when 65..90, 95, 97..122, 128..255 + identifier_token(start) + else + operator_or_punctuation(start) + end + @previous_previous = @previous + @previous = value && value[0] + @previous_value = value && value[1] + update_argument_label(value && value[0], value && value[1]) + @context.alias_context = false if value && value[0] == ";" + @context.remember_token(value && value[0]) + @context.begin_expression = expression_begin_after(value && value[0]) + @context.lex_state = lexical_state_after(value && value[0]) + @context.command_start = command_start_after(value && value[0]) + update_block_context(value && value[0]) + @context.remember_state(value && value[0]) + value + end + + def skip_space_and_comments + loop do + while [9, 11, 12, 13, 32].include?(byte) + advance + end + if byte == 92 && byte(1) == 10 + advance(2) + next + end + break unless byte == 35 + advance + advance while !eof? && byte != 10 + end + end + + def newline_ignored? + ignored = (@context.delimiter_depth.positive? && + ["(", "[", "{", :tLPAREN, :tLPAREN_ARG, :tLBRACK, :tLBRACE, :tLBRACE_ARG].include?(@previous)) || + (["[", :tLBRACK].include?(@context.delimiter_stack.last) && next_non_space_byte == 93) || + @previous == "\n" || + ["+", "-", "*", "/", "%", "=", "?", ":", ",", ".", "&", "|", "^", "!", "<<", ">>", "&&", "||", "=>", "<", ">", :keyword_and, :keyword_or, :keyword_not, :tAMPER, :tPIPE, :tSTAR, :tDSTAR, :tUMINUS, :tUPLUS, :tDOT2, :tDOT3, :tPOW, :tCMP, :tEQ, :tEQQ, :tNEQ, :tGEQ, :tLEQ, :tANDOP, :tOROP, :tMATCH, :tNMATCH, :tLSHFT, :tRSHFT, :tASSOC, :tLAMBDA, :tCOLON2, :tCOLON3, :tANDDOT].include?(@previous) || + @previous == :tLABEL || + [:modifier_if, :modifier_unless, :modifier_while, :modifier_until].include?(@previous) || + [:tANDOP, :tOROP, :tMATCH, :tNMATCH, :tASSOC, :tOP_ASGN].include?(@previous) || + (@previous == "{" && next_non_space_byte == 124) || + (@context.ternary_depth.positive? && next_non_space_byte == 58) || + @previous == :keyword_def || + next_chain_byte == 46 + ignored = false if @previous_previous == :tSYMBEG && + [:tLSHFT, :tRSHFT, :tSTAR, :tDSTAR, :tPOW, :tEQ, :tEQQ, :tNEQ, "&", + :tCMP, :tGEQ, :tLEQ, :tANDOP, :tOROP, :tMATCH, :tNMATCH, + :tASSOC, :tOP_ASGN, :tUMINUS, :tUPLUS, "<", ">"].include?(@previous) + ignored = false if @previous_previous == :keyword_def && + [:tUMINUS, :tUPLUS].include?(@previous) + @context.condition_line = false if ignored && @context.condition_line + ignored + end + + def next_word_is_terminator? + position = @index + position += 1 while [9, 11, 12, 13, 32].include?(@source.getbyte(position)) + %w[else elsif end when].any? do |word| + @source.byteslice(position, word.bytesize) == word && + !identifier_byte?(@source.getbyte(position + word.bytesize)) + end + end + + def next_non_space_byte + position = @index + position += 1 while [9, 11, 12, 13, 32].include?(@source.getbyte(position)) + @source.getbyte(position) + end + + def next_chain_byte + position = @index + loop do + position += 1 while [9, 11, 12, 13, 32, 10].include?(@source.getbyte(position)) + break unless @source.getbyte(position) == 35 + position += 1 + position += 1 while @source.getbyte(position) && @source.getbyte(position) != 10 + end + @source.getbyte(position) + end + + def update_pending_delimiter(token) + if ["(", "[", :tLPAREN_ARG].include?(token) + @context.push_delimiter(token) + @context.push_cmdarg(token == :tLPAREN_ARG) + elsif [")", "]", "}"].include?(token) + opener = {")" => "(", "]" => "[", "}" => "{" + }.fetch(token) + @context.pop_delimiter(opener) + @context.pop_cmdarg + @context.method_body_start = true if token == ")" && @context.method_header + @context.method_header = false if token == ")" + end + end + + def update_argument_label(token, value) + if token == :tLABEL + @context.argument_label = value + @context.label_pending = true + elsif ["\n", ";"].include?(token) + @context.argument_label = nil + end + end + + def expression_begin_after(token) + return true if token.nil? || token == "\n" + return false if receiver_operator?(token) + return false if token == ")" || token == "]" || token == "}" + return false if token == :keyword_end + return false if token == :tINTEGER || token == :tFLOAT || token == :tRATIONAL || token == :tIMAGINARY || token == :tCHAR + return false if @previous_previous == :tSYMBEG && [:tASET, "&"].include?(token) + return false if token == :tIDENTIFIER || token == :tCONSTANT || token == :tFID || token == :tSTRING_END || token == :tREGEXP_END + return false if token == :keyword_true || token == :keyword_false || token == :keyword_nil || token == :keyword_self + return false if [:keyword__LINE__, :keyword__FILE__, :keyword__ENCODING__].include?(token) + return false if token == :tIVAR || token == :tGVAR || token == :tCVAR || token == :tNTH_REF + return false if token == :keyword_redo + true + end + + def lexical_state_after(token) + return :expr_beg if token.nil? || token == "\n" + return :expr_arg if receiver_operator?(token) + return :expr_fname if @previous_previous == :keyword_def || @context.alias_context + return :expr_end if token == 0 + if @context.label_pending && token != :tLABEL + @context.label_pending = false + return :expr_labeled + end + return :expr_end if [")", "]", "}", :keyword_end, :tSTRING_END, :tREGEXP_END, + :tINTEGER, :tFLOAT, :tRATIONAL, :tIMAGINARY, :tCHAR, :tIDENTIFIER, + :tCONSTANT, :tFID, :tIVAR, :tGVAR, :tCVAR, :tNTH_REF, + :keyword_true, :keyword_false, :keyword_nil, :keyword_self, + :keyword__LINE__, :keyword__FILE__, :keyword__ENCODING__].include?(token) + return :expr_end if @previous_previous == :tSYMBEG && [:tASET, "&"].include?(token) + return :expr_label if token == :tLABEL + :expr_beg + end + + def command_start_after(token) + [nil, 0, "\n", ";"].include?(token) + end + + def update_block_context(token) + case token + when :keyword_if, :keyword_unless, :keyword_case, :keyword_begin, + :keyword_while, :keyword_until, :keyword_for + @context.control_depth += 1 + when :keyword_def + @context.method_header = true + @context.push_scope(:method) + when :keyword_do, :keyword_do_block + @context.push_block(:do_block) + when :keyword_do_LAMBDA + @context.push_block(:lambda) + when :tLAMBDA + @context.push_scope(:lambda) + when :tLSHFT + @context.singleton_class_depth += 1 if @previous_previous == :keyword_class + when :tLAMBEG + @context.push_block(:lambda) + when "{", :tLBRACE_ARG + @context.push_block(:brace_block) + when "}" + @context.pop_block if [:brace_block, :lambda].include?(@context.block_stack.last) + @context.pop_scope if @context.scope_stack.last == :lambda + when :keyword_end + if @context.control_depth.positive? + @context.control_depth -= 1 + elsif @context.singleton_class_depth.positive? + @context.singleton_class_depth -= 1 + @context.pop_scope if @context.scope_stack.last == :keyword_class + elsif [:method, :keyword_class, :keyword_module].include?(@context.scope_stack.last) + @context.pop_scope + else + @context.pop_block if [:do_block, :lambda].include?(@context.block_stack.last) + @context.pop_scope if @context.scope_stack.last == :lambda + end + end + if @context.method_header && token != :keyword_def && token != "(" && + ![".", :tCOLON2, :tANDDOT].include?(token) && + !(@context.method_header && [".", :tCOLON2, :tANDDOT].include?(@previous_previous)) && + !@context.delimiter_stack.include?("(") && + !(@previous_previous == :keyword_def && + [:keyword_self, :tIDENTIFIER, :tCONSTANT, :tIVAR, :tCVAR, :tGVAR, :tFID].include?(token)) + @context.method_header = false + end + @context.method_body_start = false unless [")", "\n"].include?(token) + end + + def identifier_token(start) + while identifier_byte?(byte) + advance + end + word = @source.byteslice(start, @index - start).force_encoding(Encoding::UTF_8) + if byte == 63 && word == "defined" + advance + word = "defined?" + end + if @previous == :keyword_undef && (KEYWORDS.key?(word) || word == "do") + return [:tFID, word.to_sym] + end + if byte == 63 && byte(1) == 58 + advance(2) + return [:tLABEL, (word + "?").to_sym] + end + if [33, 63].include?(byte) && byte(1) != 61 + suffix = byte.chr + advance + return [:tFID, (word + suffix).to_sym] + end + if byte == 61 && byte(1) != 61 && + (@previous == :keyword_def || + @context.method_header && [".", :tCOLON2, :tANDDOT].include?(@previous)) + advance + return [:tFID, (word + "=").to_sym] + end + if byte == 61 && @context.alias_context + advance + return [:tFID, (word + "=").to_sym] + end + if byte == 61 && byte(1) != 61 && @previous == :tSYMBEG + advance + return [:tIDENTIFIER, (word + "=").to_sym] + end + @local_variables[word.to_sym] = true if byte == 61 && byte(1) != 61 && + ![".", :tCOLON2, :tANDDOT].include?(@previous) + label_in_ternary = ["(", "[", :tLPAREN, :tLPAREN_ARG, :tLBRACK, ","].include?(@previous) + if byte == 58 && byte(1) != 58 && + (@context.ternary_depth.zero? || label_in_ternary) && + (byte(1).nil? || [9, 10, 11, 12, 13, 32, 44, 41, 93, 125, 124].include?(byte(1)) || @context.delimiter_stack.any?) + advance + return [:tLABEL, word.to_sym] + end + if word == "do" && [".", :tCOLON2, :tANDDOT].include?(@previous) + return [:tFID, word.to_sym] + end + if word == "do" && @context.lambda_pending + return [do_token, word.to_sym] + end + if word == "do" && !@context.condition_do && no_argument_block? + return [:keyword_do, word.to_sym] + end + token = KEYWORDS[word] + if token && [".", :tCOLON2, :tANDDOT].include?(@previous) + token = next_non_space_byte == 61 ? :tIDENTIFIER : :tFID + elsif token && @previous == :keyword_def && word != "self" + token = :tFID + elsif (!@context.begin_expression || @context.alias_context && @previous == :keyword_for || [:keyword_return, :keyword_break, :keyword_next, :keyword_end, :keyword_yield, :keyword_super].include?(@previous)) && { "if" => :modifier_if, "unless" => :modifier_unless, + "while" => :modifier_while, "until" => :modifier_until, + "rescue" => :modifier_rescue }.key?(word) + token = { "if" => :modifier_if, "unless" => :modifier_unless, + "while" => :modifier_while, "until" => :modifier_until, + "rescue" => :modifier_rescue }.fetch(word, token) + end + token ||= word == "do" ? do_token : (word.getbyte(0).between?(65, 90) ? :tCONSTANT : :tIDENTIFIER) + if [:keyword_while, :keyword_until, :keyword_for].include?(token) + @context.condition_do = true + @context.push_condition + end + @context.alias_context = true if token == :keyword_alias + [token, word.to_sym] + rescue EncodingError + fail!("invalid UTF-8 identifier", start) + end + + def identifier_byte?(value) + value && (value == 95 || value.between?(65, 90) || value.between?(97, 122) || value >= 128 || value.between?(48, 57)) + end + + def do_token + if @context.condition_do && @context.condition? + @context.pop_condition + @context.condition_do = false + :keyword_do_cond + elsif @context.lambda_pending + @context.lambda_pending = false + :keyword_do_LAMBDA + elsif @previous == :tIDENTIFIER && @previous_previous == :tLABEL && @previous_value == :lambda + :keyword_do + elsif @previous == ")" && @context.token_history.each_cons(2).any? { |left, right| left == :tUMINUS && right == :tLPAREN } + :keyword_do_block + elsif [")", :keyword_super, :keyword_yield].include?(@previous) + :keyword_do + elsif @previous == :tLAMBDA + :keyword_do_LAMBDA + else + :keyword_do_block + end + end + + def no_argument_block? + return false unless [:tIDENTIFIER, :tCONSTANT, :tFID].include?(@previous) + return false if @context.argument_label == :at && [".", :tCOLON2, :tANDDOT].include?(@previous_previous) + return false if @context.token_history == ["+", :tINTEGER, ".", :tIDENTIFIER] + return false if [:tIDENTIFIER, :tCONSTANT, :tFID].include?(@previous_previous) + return false if [:tSYMBEG, :tLABEL, :tCOLON2].include?(@previous_previous) + return true if @previous_value == :lambda && @previous_previous == :tLSHFT + return false if [:tLSHFT, :tLAMBDA].include?(@previous_previous) + true + end + + def no_argument_brace_block? + return false if [".", :tCOLON2].include?(@previous_previous) + return false if [:tLABEL, :tLSHFT, :tOROP, :tANDOP, :tASSOC, :tAMPER, "=", "(", ",", ":", "?"].include?(@previous_previous) + no_argument_block? + end + + def number_token(start) + base = 10 + if byte == 48 && [120, 88, 98, 66, 111, 79].include?(byte(1)) + base = { 120 => 16, 88 => 16, 98 => 2, 66 => 2, 111 => 8, 79 => 8 }.fetch(byte(1)) + advance(2) + digit_start = @index + advance while digit_byte?(byte, base) || byte == 95 + fail!("invalid numeric literal", start) if @index == digit_start + text = @source.byteslice(digit_start, @index - digit_start).delete("_") + return [:tINTEGER, text.to_i(base)] + end + advance while digit_byte?(byte, 10) || byte == 95 + float = false + if byte == 46 && digit_byte?(byte(1), 10) + float = true + advance + advance while digit_byte?(byte, 10) || byte == 95 + end + if byte == 101 || byte == 69 + float = true + advance + advance if byte == 43 || byte == 45 + fail!("invalid numeric literal", start) unless digit_byte?(byte, 10) + advance while digit_byte?(byte, 10) || byte == 95 + end + suffix = byte + advance if [105, 114].include?(suffix) + text = @source.byteslice(start, @index - start).delete("_") + return [:tRATIONAL, Rational(text.delete_suffix("r"))] if suffix == 114 + return [:tIMAGINARY, Complex(0, text.delete_suffix("i").to_f)] if suffix == 105 + [float ? :tFLOAT : :tINTEGER, float ? text.to_f : text.to_i] + rescue ArgumentError, ZeroDivisionError + fail!("invalid numeric literal", start) + end + + def digit_byte?(value, base) + return false unless value + value.between?(48, 57) && value - 48 < base || base == 16 && value.between?(65, 70) || base == 16 && value.between?(97, 102) + end + + def variable_token(start) + marker = byte + advance + advance if marker == 64 && byte == 64 + if marker == 36 && [33, 38, 39, 42, 43, 46, 47, 58, 60, 62, 61, 63, 96, 126].include?(byte) + advance + text = @source.byteslice(start, @index - start) + return [:tGVAR, text.to_sym] + end + if marker == 36 && byte == 36 + advance + return [:tGVAR, :"$$"] + end + if marker == 36 && byte && byte.between?(48, 57) + advance while byte && byte.between?(48, 57) + return [:tNTH_REF, @source.byteslice(start + 1, @index - start - 1).to_i] + end + advance while identifier_byte?(byte) + text = @source.byteslice(start, @index - start) + token = marker == 36 ? :tGVAR : (text.start_with?("@@") ? :tCVAR : :tIVAR) + [token, text.to_sym] + end + + def character_or_question(start) + # Since Ruby 2.4, `?""` is the ternary operator followed by an empty + # string, rather than a character literal. Keep the question mark as + # punctuation in this one ambiguous case. + if [39, 34].include?(byte(1)) && byte(1) == byte(2) + advance(3) + @pending = [[:tSTRING_BEG, nil], [:tSTRING_END, nil]] + return ["?", nil] + end + command_character = !@context.begin_expression && + [:tIDENTIFIER, :tCONSTANT, :tFID, :tCHAR].include?(@previous) && + start.positive? && [9, 32].include?(@source.getbyte(start - 1)) + command_character = true if @previous == :tFID && @previous_value.to_s.end_with?("!") + command_character = true if @previous == :tCHAR && start.positive? && [9, 32].include?(@source.getbyte(start - 1)) + command_character = false if command_character && ternary_question?(start) + return operator_or_punctuation(start) unless (@context.begin_expression || command_character) && + byte(1) && byte(1) != 32 && byte(1) != 10 + advance + value = if byte == 92 + escape_sequence(start) + else + length = utf8_character_length(byte) + character = @source.byteslice(@index, length).force_encoding(Encoding::UTF_8) + advance(length) + character + end + [:tCHAR, value] + end + + def ternary_question?(start) + depth = 0 + quote = nil + escaped = false + index = start + 1 + while index < @source.bytesize + value = @source.getbyte(index) + if quote + if escaped + escaped = false + elsif value == 92 + escaped = true + elsif value == quote + quote = nil + end + else + case value + when 39, 34, 96 + quote = value + when 40, 91, 123 + depth += 1 + when 41, 93, 125 + depth -= 1 if depth.positive? + when 58 + return true if depth.zero? + when 10, 59 + return false if depth.zero? + end + end + index += 1 + end + false + end + + def utf8_character_length(first_byte) + return 1 if first_byte < 0x80 + return 2 if first_byte.between?(0xC2, 0xDF) + return 3 if first_byte.between?(0xE0, 0xEF) + return 4 if first_byte.between?(0xF0, 0xF4) + fail!("invalid UTF-8 character") + end + + def symbol_or_colon(start) + symbol_position = @context.begin_expression || ([:tIDENTIFIER, :tCONSTANT, :tFID].include?(@previous) && @context.ternary_depth.zero?) + return operator_or_punctuation(start) unless symbol_position && byte(1) && + ![9, 10, 11, 12, 13, 32].include?(byte(1)) && byte(1) != 58 + advance + if byte == 39 || byte == 34 + quote = byte + advance + @pending = read_interpolated_quoted(quote, start) + @pending << [:tSTRING_END, nil] + end + [:tSYMBEG, nil] + end + + def string_token(quote, start) + advance + content_tokens = read_interpolated_quoted(quote, start) + label = byte == 58 && @context.ternary_depth.zero? + advance if label + terminator = label ? :tLABEL_END : :tSTRING_END + @pending = content_tokens + @pending << [terminator, nil] + [quote == 96 ? :tXSTRING_BEG : :tSTRING_BEG, nil] + end + + def read_interpolated_quoted(quote, start) + return [[:tSTRING_CONTENT, read_quoted(quote, interpolate: false, start: start)]] if quote == 39 + content = +"" + tokens = [] + until eof? + value = byte + if value == quote + advance + tokens << [:tSTRING_CONTENT, content] unless content.empty? + return tokens + elsif value == 92 + content << escape_sequence(start) + elsif value == 35 && byte(1) == 123 + tokens << [:tSTRING_CONTENT, content] unless content.empty? + content = +"" + advance(2) + expression = read_interpolation_source(start) + inner = self.class.new(expression, filename: @filename).each.to_a + inner.pop if inner.last == [0, nil] + tokens << [:tSTRING_DBEG, nil] + tokens.concat(inner) + tokens << [:tSTRING_DEND, nil] + else + content << value.chr + advance + end + end + fail!("unterminated literal", start) + end + + def read_interpolation_source(start) + begin_index = @index + depth = 1 + quote = nil + escaped = false + while !eof? + value = byte + if quote + if escaped + escaped = false + elsif value == 92 + escaped = true + elsif value == quote + quote = nil + end + elsif [39, 34, 96].include?(value) && @source.getbyte(@index - 1) != 36 + quote = value + elsif value == 123 + depth += 1 + elsif value == 125 + depth -= 1 + if depth.zero? + expression = @source.byteslice(begin_index, @index - begin_index) + advance + return expression.force_encoding(Encoding::UTF_8) + end + end + advance + end + fail!("unterminated string interpolation", start) + end + + def read_quoted(quote, interpolate:, start:) + result = +"" + until eof? + value = byte + if value == quote + advance + return result + elsif value == 92 + result << escape_sequence(start) + elsif value == 35 && interpolate && byte(1) == 123 + fail!("string interpolation cannot be represented by the AST", start) + else + result << value + advance + end + end + fail!("unterminated literal", start) + end + + def escape_sequence(start) + advance + fail!("unterminated escape", start) if eof? + value = byte + advance + return "\n" if value == 110 + return "\t" if value == 116 + return "\r" if value == 114 + return "\f" if value == 102 + return "\a" if value == 97 + return "\e" if value == 101 + return value.chr if value == 92 || value == 34 || value == 39 + value.chr + end + + def regexp_or_operator(start) + return operator_or_punctuation(start) if [".", :tCOLON2, :tANDDOT, :keyword_def, :tSYMBEG].include?(@previous) + if @context.begin_expression + advance + value = read_regexp(start) + content = value.is_a?(Array) ? value : [[:tSTRING_CONTENT, value]] + @pending = content + [[:tREGEXP_END, nil]] + return [:tREGEXP_BEG, nil] + end + operator_or_punctuation(start) + end + + def read_regexp(start) + result = +"" + tokens = [] + interpolated = false + in_class = false + until eof? + value = byte + if value == 92 + result << value.chr + advance + fail!("unterminated regexp", start) if eof? + result << byte.chr + advance + elsif value == 91 + in_class = true + result << value.chr + advance + elsif value == 93 + in_class = false + result << value.chr + advance + elsif value == 35 && byte(1) == 123 && !in_class + tokens << [:tSTRING_CONTENT, result] unless result.empty? + result = +"" + interpolated = true + advance(2) + expression = read_interpolation_source(start) + inner = self.class.new(expression, filename: @filename).each.to_a + inner.pop if inner.last == [0, nil] + tokens << [:tSTRING_DBEG, nil] + tokens.concat(inner) + tokens << [:tSTRING_DEND, nil] + elsif value == 47 && !in_class + advance + advance while byte && byte.between?(97, 122) + tokens << [:tSTRING_CONTENT, result] unless result.empty? + return interpolated ? tokens : result + else + result << value.chr + advance + end + end + fail!("unterminated regexp", start) + end + + def percent_token(start) + return operator_or_punctuation(start) if [".", :tCOLON2, :tANDDOT].include?(@previous) + literal_kind = [113, 81, 119, 87, 105, 73, 114, 115, 120, 88].include?(byte(1)) + command_argument = start.positive? && [9, 32].include?(@source.getbyte(start - 1)) && + [:tIDENTIFIER, :tCONSTANT, :tFID].include?(@previous) && + ![:tIDENTIFIER, :tCONSTANT, :tFID].include?(@previous_previous) + shorthand_delimiter = byte(1) && !identifier_byte?(byte(1)) && + ![9, 10, 11, 12, 13, 32, 61].include?(byte(1)) + return operator_or_punctuation(start) unless @context.begin_expression || + ((literal_kind || shorthand_delimiter) && command_argument) + advance + kind = byte + if [113, 81, 119, 87, 105, 73, 114, 115, 120, 88].include?(kind) + advance + else + kind = 81 + end + delimiter = byte + fail!("invalid percent literal", start) unless delimiter + advance + closing = { 40 => 41, 91 => 93, 123 => 125, 60 => 62 }.fetch(delimiter, delimiter) + interpolate = [81, 87, 73, 114, 120, 88].include?(kind) + content = interpolate ? + read_interpolated_delimited(closing, start) : + read_delimited(closing, start, interpolate: interpolate) + advance while kind == 114 && byte && byte.between?(97, 122) + token = { 113 => :tSTRING_BEG, 81 => :tSTRING_BEG, 119 => :tWORDS_BEG, 87 => :tQWORDS_BEG, + 105 => :tSYMBOLS_BEG, 73 => :tQSYMBOLS_BEG, 114 => :tREGEXP_BEG, + 115 => :tSYMBEG, 120 => :tXSTRING_BEG, 88 => :tXSTRING_BEG }.fetch(kind) + terminator = kind == 114 ? :tREGEXP_END : :tSTRING_END + @pending = if [119, 87, 105, 73].include?(kind) + content = content.map { |token, value| value.to_s }.join if content.is_a?(Array) + word_tokens(content, symbols: [105, 73].include?(kind)) + [[terminator, nil]] + elsif content.is_a?(Array) + content + [[terminator, nil]] + else + [[:tSTRING_CONTENT, content], [terminator, nil]] + end + [token, nil] + end + + def word_tokens(content, symbols:) + words = content.split(/[\t\n\f\r ]+/).reject(&:empty?) + tokens = [[" ", nil]] + words.each do |word| + tokens << [:tSTRING_CONTENT, symbols ? word.to_sym : word] + tokens << [" ", nil] + end + tokens + end + + def read_delimited(closing, start, interpolate: false) + result = +"" + depth = 0 + until eof? + value = byte + if value == 92 + result << escape_sequence(start) + elsif value == 35 && interpolate && byte(1) == 123 + fail!("string interpolation cannot be represented by the AST", start) + elsif value == closing && depth.zero? + advance + return result + else + depth += 1 if value == ({ 41 => 40, 93 => 91, 125 => 123, 62 => 60 }.fetch(closing, -1)) + depth -= 1 if value == closing && depth.positive? + result << value.chr + advance + end + end + fail!("unterminated percent literal", start) + end + + def read_interpolated_delimited(closing, start) + result = +"" + tokens = [] + depth = 0 + opening = { 41 => 40, 93 => 91, 125 => 123, 62 => 60 }.fetch(closing, -1) + until eof? + value = byte + if value == 92 + result << escape_sequence(start) + elsif value == 35 && byte(1) == 123 + tokens << [:tSTRING_CONTENT, result] unless result.empty? + result = +"" + advance(2) + expression = read_interpolation_source(start) + inner = self.class.new(expression, filename: @filename).each.to_a + inner.pop if inner.last == [0, nil] + tokens << [:tSTRING_DBEG, nil] + tokens.concat(inner) + tokens << [:tSTRING_DEND, nil] + elsif value == closing && depth.zero? + advance + tokens << [:tSTRING_CONTENT, result] unless result.empty? + return tokens + else + depth += 1 if value == opening + depth -= 1 if value == closing && depth.positive? + result << value.chr + advance + end + end + fail!("unterminated percent literal", start) + end + + def interpolated_content_tokens(source, start) + return [[:tSTRING_CONTENT, source]] unless source.include?("#" + "{") + tokens = [] + literal = +"" + index = 0 + while index < source.bytesize + if source.getbyte(index) == 35 && source.getbyte(index + 1) == 123 + tokens << [:tSTRING_CONTENT, literal] unless literal.empty? + depth = 1 + expr_start = index + 2 + index = expr_start + while index < source.bytesize && depth.positive? + depth += 1 if source.getbyte(index) == 123 + depth -= 1 if source.getbyte(index) == 125 + index += 1 + end + fail!("unterminated string interpolation", start) unless depth.zero? + expression = source.byteslice(expr_start, index - expr_start - 1) + inner = self.class.new(expression, filename: @filename).each.to_a + inner.pop if inner.last == [0, nil] + tokens << [:tSTRING_DBEG, nil] + tokens.concat(inner) + tokens << [:tSTRING_DEND, nil] + literal = +"" + else + literal << source.getbyte(index).chr + index += 1 + end + end + tokens << [:tSTRING_CONTENT, literal] unless literal.empty? + tokens + end + + def heredoc_or_operator(start) + return operator_or_punctuation(start) if [".", :keyword_class].include?(@previous) + if byte(1) == 60 && heredoc_prefix? + heredoc_after_command = @previous == :tIDENTIFIER && start.positive? && + [9, 32].include?(@source.getbyte(start - 1)) + return heredoc_token(start) if @context.begin_expression || heredoc_after_command + end + operator_or_punctuation(start) + end + + def heredoc_prefix? + value = byte(2) + return false unless value + if [45, 126].include?(value) + value = byte(3) + end + if [39, 34, 96].include?(value) + modifier = [45, 126].include?(@source.getbyte(@index + 2)) + value = byte(modifier ? 4 : 3) + end + identifier_byte?(value) + end + + def heredoc_token(start) + advance(2) + indent = byte == 45 || byte == 126 + squiggly = byte == 126 + advance if indent + quote = byte + if quote == 39 || quote == 34 || quote == 96 + advance + delimiter_start = @index + advance while byte && byte != quote + fail!("unterminated heredoc identifier", start) if eof? + delimiter = @source.byteslice(delimiter_start, @index - delimiter_start) + advance + else + delimiter_start = @index + advance while identifier_byte?(byte) + delimiter = @source.byteslice(delimiter_start, @index - delimiter_start) + end + fail!("invalid heredoc identifier", start) if delimiter.empty? + suffix_start = @index + advance while byte && byte != 10 + suffix = @source.byteslice(suffix_start, @index - suffix_start).force_encoding(Encoding::UTF_8) + advance if byte == 10 + interpolate = quote != 39 + body = read_heredoc_body(delimiter, indent, squiggly, start) + content_tokens = interpolate ? interpolated_content_tokens(body, start) : [[:tSTRING_CONTENT, body]] + @pending = content_tokens + [[:tSTRING_END, nil]] + prefix, headers, suffix = extract_heredoc_headers(suffix) + unless prefix.empty? + prefix_tokens = heredoc_suffix_tokens(prefix) + @pending.concat(prefix_tokens) + end + @heredoc_queue.concat(headers) + @deferred_suffix = suffix unless suffix.empty? + @heredoc_newline_pending = true + [quote == 96 ? :tXSTRING_BEG : :tSTRING_BEG, nil] + end + + def read_heredoc_body(delimiter, indent, squiggly, start) + body = +"" + loop do + line_start = @index + advance while !eof? && byte != 10 + line = @source.byteslice(line_start, @index - line_start) + line_without_cr = line.delete_suffix("\r") + terminator = indent ? line_without_cr.sub(/\A[ \t]*/, "") : line_without_cr + if terminator == delimiter + advance if byte == 10 + break + end + body << line << "\n" + advance if byte == 10 + fail!("unterminated heredoc", start) if eof? + end + body = body.force_encoding(Encoding::UTF_8) + squiggly ? dedent_heredoc(body) : body + end + + def extract_heredoc_headers(suffix) + pattern = /<<(\-|~)?([A-Za-z_][A-Za-z0-9_]*)/ + match = pattern.match(suffix) + return [suffix, [], ""] unless match + + headers = [{delimiter: match[2], indent: !match[1].nil?, squiggly: match[1] == "~", quote: nil}] + trailing = suffix[match.end(0)..].to_s.gsub(pattern) do + headers << {delimiter: Regexp.last_match(2), indent: !Regexp.last_match(1).nil?, squiggly: Regexp.last_match(1) == "~", quote: nil} + "" + end + [suffix[0...match.begin(0)], headers, trailing] + end + + def heredoc_suffix_tokens(source) + suffix_lexer = self.class.new(source, filename: @filename) + suffix_lexer.context.begin_expression = false + suffix_lexer.context.lex_state = :expr_end + suffix_lexer.instance_variable_set(:@previous, :tSTRING_END) + tokens = suffix_lexer.each.to_a + tokens.pop if tokens.last == [0, nil] + if [:keyword_if, :keyword_unless, :keyword_while, :keyword_until, :keyword_rescue].include?(tokens.first&.first) + token = {keyword_if: :modifier_if, keyword_unless: :modifier_unless, + keyword_while: :modifier_while, keyword_until: :modifier_until, + keyword_rescue: :modifier_rescue}.fetch(tokens.first.first) + tokens[0] = [token, tokens.first.last] + end + tokens + end + + def load_queued_heredoc + descriptor = @heredoc_queue.shift + body = read_heredoc_body(descriptor.fetch(:delimiter), descriptor.fetch(:indent), descriptor.fetch(:squiggly), @index) + content = descriptor.fetch(:quote) == 39 ? [[:tSTRING_CONTENT, body]] : interpolated_content_tokens(body, @index) + @pending.concat([descriptor.fetch(:quote) == 96 ? [:tXSTRING_BEG, nil] : [:tSTRING_BEG, nil], *content, [:tSTRING_END, nil]]) + end + + def dedent_heredoc(body) + indents = body.lines.filter_map do |line| + next if line.strip.empty? + line[/\A[ \t]*/].bytesize + end + return body if indents.empty? + + width = indents.min + body.lines.map { |line| line.sub(/\A[ \t]{0,#{width}}/, "") }.join + end + + def operator_or_punctuation(start) + if @previous == :keyword_def && @source.getbyte(@index) == 38 + advance + return ["&", nil] + end + if @previous == :keyword_def && ["-@", "+@"].include?(@source.byteslice(@index, 2)) + value = @source.byteslice(@index, 2) + advance(2) + return [value == "-@" ? :tUMINUS : :tUPLUS, nil] + end + if (@previous == :tSYMBEG || @context.alias_context) && ["-@", "+@"].include?(@source.byteslice(@index, 2)) + value = @source.byteslice(@index, 2) + advance(2) + return [value == "-@" ? :tUMINUS : :tUPLUS, nil] + end + text = OPERATORS.sort_by { |operator| -operator.bytesize }.find do |operator| + next false if ["[]", "[]="].include?(operator) && start.positive? && + [9, 10, 11, 12, 13, 32].include?(@source.getbyte(start - 1)) && + ![:keyword_def, :keyword_alias].include?(@previous) && + @previous_previous != :keyword_alias + next false if ["[]", "[]="].include?(operator) && + [:tIDENTIFIER, :tCONSTANT, :tFID, :tGVAR, :tIVAR, :tCVAR, :tNTH_REF, + :tINTEGER, :tSTRING_END, :tREGEXP_END, "]", "}"].include?(@previous) && + @previous_previous != :keyword_alias + @source.byteslice(@index, operator.bytesize) == operator + end + operator_method = [:keyword_def, :keyword_alias, :keyword_undef, ".", :tCOLON2, :tANDDOT, :tSYMBEG].include?(@previous) || + @previous_previous == :keyword_alias + if text && !(begin_expression? && !operator_method && ["[]", "[]="].include?(text)) + advance(text.bytesize) + token = if text == "**" && @context.begin_expression + :tDSTAR + elsif text == ".." && @context.begin_expression + :tBDOT2 + elsif text == "..." && (@context.begin_expression || [:tLPAREN, "(", ","].include?(@previous)) + :tBDOT3 + elsif text == "::" && (@context.begin_expression || + (start.positive? && [9, 32].include?(@source.getbyte(start - 1)) && + [:tIDENTIFIER, :tCONSTANT, :tFID].include?(@previous))) + :tCOLON3 + elsif text == "&" && @previous == :keyword_def + "&" + else + OP_TOKENS.fetch(text, :tOP_ASGN) + end + @context.lambda_pending = true if token == :tLAMBDA + return [token, nil] + end + value = byte.chr + advance + if value == "(" || value == "[" || value == "{" + brace_block = value == "{" && @previous != "(" && + ([:tIDENTIFIER, :tCONSTANT, :tFID].include?(@previous) || + (@previous == ")" && !(@previous_previous == :tSTAR && @context.scope_stack.last == :method)) || + [".", :tCOLON2].include?(@previous_previous) || + [:keyword_super, :keyword_yield].include?(@previous) || + [:proc, :lambda].include?(@previous_value) || + [:keyword_BEGIN, :keyword_END].include?(@previous)) + lambda_block = value == "{" && @context.lambda_pending + command_arg = value == "(" && !@context.begin_expression && start.positive? && [9, 32].include?(@source.getbyte(start - 1)) && + [:tIDENTIFIER, :tCONSTANT, :tFID].include?(@previous) && + !@context.method_header + unless brace_block || lambda_block + @context.push_delimiter(value) + @context.push_cmdarg(command_arg) + end + if value == "(" + if command_arg + return [:tLPAREN_ARG, nil] + end + method_definition = @previous == :keyword_def || @previous_previous == :keyword_def + return [@context.begin_expression && !method_definition && + ![".", :tCOLON2, :tANDDOT, :keyword_super, :keyword_yield, :tLAMBDA, :tAREF, :tASET].include?(@previous) ? :tLPAREN : "(", nil] + end + if value == "{" && @context.lambda_pending + @context.lambda_pending = false + return [:tLAMBEG, nil] + end + array_argument = value == "[" && start.positive? && + [9, 10, 11, 12, 13, 32].include?(@source.getbyte(start - 1)) && + [:tIDENTIFIER, :tFID, :tCONSTANT].include?(@previous) + array_argument ||= value == "[" && @previous == ")" && + @previous_previous == "(" && @context.scope_stack.last == :method + array_argument ||= value == "[" && @context.method_body_start + array_argument = false if array_argument && @local_variables[@previous_value] + array_argument = false if array_argument && [",", "*"].include?(@previous_previous) && + @context.delimiter_stack.any? { |delimiter| ["[", :tLBRACK].include?(delimiter) } + return [value == "[" && (@context.begin_expression || array_argument) ? :tLBRACK : (value == "[" ? "[" : (brace_block ? "{" : :tLBRACE)), nil] + elsif value == ")" || value == "]" || value == "}" + opener = { ")" => "(", "]" => "[", "}" => "{" }.fetch(value) + @context.pop_delimiter(opener) + @context.pop_cmdarg + if value == ")" && @context.method_header + @context.method_body_start = true + @context.method_header = false + end + elsif value == "-" && @context.begin_expression + return [:tUMINUS, nil] + elsif value == "+" && @context.begin_expression + return [:tUPLUS, nil] + elsif value == "*" && @context.begin_expression + return [:tSTAR, nil] + elsif value == "&" && @previous == :tSYMBEG + return [value, nil] + elsif value == "&" && @context.begin_expression + return [:tAMPER, nil] + elsif value == "?" && !@context.begin_expression + @context.ternary_depth += 1 + elsif value == ":" && @context.ternary_depth.positive? + @context.ternary_depth -= 1 + elsif value == "<" && @previous == :tCONSTANT && @previous_previous == :keyword_class + @class_superclass = true + end + [value, nil] + rescue EncodingError + fail!("invalid byte", start) + end + + def begin_expression? + @context.begin_expression + end + + def receiver_operator?(token) + [".", :tCOLON2, :tANDDOT].include?(@previous_previous) && + ["/", :tLSHFT].include?(token) + end +end +private_constant :Lexer, :LexicalContext diff --git a/lib/arpaka/source.json b/lib/arpaka/source.json new file mode 100644 index 0000000..cc85b14 --- /dev/null +++ b/lib/arpaka/source.json @@ -0,0 +1,11 @@ +{ + "revision": "37d60dd3241d5fdb10ca43f36077f5d556ae1b8d", + "repository": "https://github.com/ruby/ruby", + "files": { + "parse.y": "e301da1ee65b740f0bf55345c48229ef0e52e01325d7afc3ceb5dd527b97086c", + "tool/id2token.rb": "bf7eef9acc93fe2b5c9207f8c5b4e44670a0ba51ee87f0508d2297a5c71be714", + "defs/id.def": "4c8a5c45677c000527f288da9d2f2a7cf7069e6010bbca4da4cf89db343f0ec0", + "COPYING": "4ebe48076b3ee165fbb9350aa8a9552f8043c8ed8a7edca2089133d33c1ea45f", + "BSDL": "36a9a6e7347214bbba599a412617204e65bff065dcbe5c46f5cb454c80de9eb0" + } +} diff --git a/tool/ruby/upstream_rules.json b/lib/arpaka/upstream_rules.json similarity index 100% rename from tool/ruby/upstream_rules.json rename to lib/arpaka/upstream_rules.json diff --git a/lib/lrama/ruby.rb b/lib/lrama/ruby.rb index 5e76003..203eb19 100644 --- a/lib/lrama/ruby.rb +++ b/lib/lrama/ruby.rb @@ -6,13 +6,6 @@ module Lrama module Ruby class Error < StandardError; end - def self.parse(tokens, language:) - raise ArgumentError, "Unsupported language: #{language.inspect}" unless language == :ruby - - require_relative "ruby/languages/ruby" - Languages::Ruby.parse(tokens) - end - def self.generate(source, filename: "(grammar)", class_name: "Parser", mode: :parser, allow_error_rules: false) Generator.new.generate(source, filename: filename, class_name: class_name, mode: mode, allow_error_rules: allow_error_rules) diff --git a/lib/lrama/ruby/languages/ruby.rb b/lib/lrama/ruby/languages/ruby.rb deleted file mode 100644 index c38c2ed..0000000 --- a/lib/lrama/ruby/languages/ruby.rb +++ /dev/null @@ -1,67 +0,0 @@ -# frozen_string_literal: true - -require_relative "../../ruby" - -module Lrama - module Ruby - module Languages - module Ruby - class ParseError < ::Lrama::Ruby::Error - attr_reader :token, :state - - def initialize(error) - @token, @state = error.token, error.state - super(error.message) - end - end - - class UnsupportedSyntax < ::Lrama::Ruby::Error - attr_reader :rule, :line - - def initialize(rule, line) - @rule, @line = rule, line - super("Unsupported syntax: #{rule} (upstream parse.y:#{line})") - end - end - - class LexerError < ::Lrama::Ruby::Error - end - - PARSER_MUTEX = Mutex.new - private_constant :PARSER_MUTEX - - def self.parse(tokens, lexical_context: nil) - parser_class = generated_parser - parser = parser_class.new - parser.instance_variable_set(:@builder, Builder.new) - parser.lexical_context = lexical_context - begin - parser.parse(tokens) - rescue parser_class::ParseError => error - raise ParseError.new(error) - end - end - - def self.parse_source(source, filename: "(ruby)") - lexer = Lexer.new(source, filename: filename) - parse(lexer.each, lexical_context: lexer.context) - end - - def self.generated_parser - PARSER_MUTEX.synchronize do - @generated_parser ||= begin - path = File.expand_path("ruby/parse.y", __dir__) - ::Lrama::Ruby.compile(File.read(path), filename: path, - class_name: "RubyParser", allow_error_rules: true) - end - end - end - private_class_method :generated_parser - end - end - end -end - -require_relative "ruby/ast" -require_relative "ruby/builder" -require_relative "ruby/lexer" diff --git a/lib/lrama/ruby/languages/ruby/ast.rb b/lib/lrama/ruby/languages/ruby/ast.rb deleted file mode 100644 index 1f17c1e..0000000 --- a/lib/lrama/ruby/languages/ruby/ast.rb +++ /dev/null @@ -1,44 +0,0 @@ -# frozen_string_literal: true - -module Lrama - module Ruby - module Languages - module Ruby - module AST - Program = Data.define(:statements) - Literal = Data.define(:value) - Binary = Data.define(:operator, :left, :right) - Unary = Data.define(:operator, :operand) - If = Data.define(:condition, :then_body, :else_body) - LocalRead = Data.define(:name) - LocalWrite = Data.define(:name, :value) - BareCall = Data.define(:name) - ArrayLiteral = Data.define(:elements) - HashLiteral = Data.define(:pairs) - Pair = Data.define(:key, :value) - RangeLiteral = Data.define(:operator, :begin_value, :end_value) - Ternary = Data.define(:condition, :then_value, :else_value) - Call = Data.define(:name, :arguments) - ReceiverCall = Data.define(:receiver, :operator, :name, :arguments) - BlockCall = Data.define(:call, :body) - StringLiteral = Data.define(:value) - InterpolatedString = Data.define(:parts) - Index = Data.define(:receiver, :arguments) - Loop = Data.define(:kind, :condition, :body) - Control = Data.define(:kind) - Variable = Data.define(:kind, :name) - Rescue = Data.define(:expression, :fallback) - RegexpLiteral = Data.define(:value) - XStringLiteral = Data.define(:value) - For = Data.define(:variable, :enumerable, :body) - Def = Data.define(:name, :arguments, :body) - ClassDef = Data.define(:name, :superclass, :body) - ModuleDef = Data.define(:name, :body) - When = Data.define(:patterns, :body) - Case = Data.define(:expression, :whens, :else_body) - Lambda = Data.define(:arguments, :body) - end - end - end - end -end diff --git a/lib/lrama/ruby/languages/ruby/builder.rb b/lib/lrama/ruby/languages/ruby/builder.rb deleted file mode 100644 index aaa2580..0000000 --- a/lib/lrama/ruby/languages/ruby/builder.rb +++ /dev/null @@ -1,259 +0,0 @@ -# frozen_string_literal: true - -require "json" - -module Lrama - module Ruby - module Languages - module Ruby - class Builder - RULES = JSON.parse(File.read(File.expand_path("rules.json", __dir__))) - .to_h { |rule| [rule.fetch("id"), rule.freeze] }.freeze - private_constant :RULES - - def initialize - @locals = {} - end - - def program(statements) - AST::Program.new(statements.freeze) - end - - def integer(value) - raise ArgumentError, "tINTEGER requires an Integer value" unless value.is_a?(Integer) - AST::Literal.new(value) - end - - def float(value) - raise ArgumentError, "tFLOAT requires a Float value" unless value.is_a?(Float) - AST::Literal.new(value) - end - - def literal(value) - AST::Literal.new(value) - end - - def reserved_word(value) - value.to_sym - end - - def binary(operator, left, right) - AST::Binary.new(operator, left, right) - end - - def operator(value) - value.to_sym - end - - def range(operator, begin_value, end_value) - AST::RangeLiteral.new(operator, begin_value, end_value) - end - - def ternary(condition, then_value, else_value) - AST::Ternary.new(condition, then_value, else_value) - end - - def call(name, arguments) - AST::Call.new(name, arguments.freeze) - end - - def receiver_call(receiver, operator, name, arguments) - AST::ReceiverCall.new(receiver, operator, name, (arguments || []).freeze) - end - - def block_call(call, body) - AST::BlockCall.new(call, body_nodes(body)) - end - - def append_arguments(arguments, argument) - list = arguments.is_a?(Array) ? arguments : [arguments] - (list + [argument]).freeze - end - - def string(value) - if value.is_a?(Array) - return AST::InterpolatedString.new(value.freeze) if value.any? { |part| !part.is_a?(String) } - value = value.join - end - AST::StringLiteral.new(value.to_s) - end - - def regexp(value) - AST::RegexpLiteral.new(value.is_a?(Array) ? value.join : value.to_s) - end - - def xstring(value) - AST::XStringLiteral.new(value.is_a?(Array) ? value.join : value.to_s) - end - - def for_node(variable, enumerable, body) - AST::For.new(variable, enumerable, body) - end - - def def_node(name, arguments, body) - AST::Def.new(name.to_sym, (arguments || []).freeze, body || [].freeze) - end - - def class_node(name, superclass, body) - AST::ClassDef.new(name.to_sym, superclass, body_nodes(body)) - end - - def module_node(name, body) - AST::ModuleDef.new(name.to_sym, body_nodes(body)) - end - - def body_nodes(body) - value = body - while value.is_a?(Array) && value.length == 1 && value.first.is_a?(Array) - value = value.first - end - value || [].freeze - end - - def symbol(value) - value = value.map { |part| part.is_a?(String) ? part : part.to_s }.join if value.is_a?(Array) - value = value.name if value.is_a?(AST::Variable) - AST::Literal.new(value.to_sym) - end - - def join_strings(parts) - parts - end - - def interpolation(value) - value.is_a?(Array) && value.length == 1 ? value.first : value - end - - def concat_strings(left, right) - left_parts = left.is_a?(AST::InterpolatedString) ? left.parts : [left.value] - right_parts = right.is_a?(AST::InterpolatedString) ? right.parts : [right.value] - parts = left_parts + right_parts - parts.all? { |part| part.is_a?(String) } ? AST::StringLiteral.new(parts.join) : AST::InterpolatedString.new(parts.freeze) - end - - def index(receiver, arguments) - AST::Index.new(receiver, arguments.freeze) - end - - def loop(kind, condition, body) - AST::Loop.new(kind, condition, body) - end - - def control(kind) - AST::Control.new(kind) - end - - def variable(kind, name) - AST::Variable.new(kind, name.is_a?(String) ? name.to_sym : name) - end - - def rescue_modifier(expression, fallback) - AST::Rescue.new(expression, fallback) - end - - def when_node(patterns, body) - AST::When.new(patterns.freeze, body) - end - - def case_node(expression, whens, else_body) - AST::Case.new(expression, whens.freeze, else_body) - end - - def case_clauses(value) - value.is_a?(Array) ? value : [value] - end - - def case_parts(value) - values = case_clauses(value) - if values.all? { |item| item.is_a?(AST::When) } - [values, nil] - else - [values.grep(AST::When), values.reject { |item| item.is_a?(AST::When) }] - end - end - - def case_chain(when_node, rest) - [when_node] + (rest.nil? ? [] : case_clauses(rest)) - end - - def lambda_node(arguments, body) - AST::Lambda.new((arguments || []).freeze, body_nodes(body)) - end - - def unary(operator, operand) - AST::Unary.new(operator, operand) - end - - def if_node(condition, then_body, else_body) - AST::If.new(condition, then_body, else_body) - end - - def unless_node(condition, then_body, else_body) - AST::If.new(AST::Unary.new(:!, condition), then_body, else_body) - end - - def elsif_node(condition, then_body, else_body) - AST::If.new(condition, then_body, else_body) - end - - def parentheses(statements, rule_id) - unsupported(rule_id) unless statements.length == 1 - statements.first - end - - def identifier(value) - unless value.is_a?(Symbol) || value.is_a?(String) - raise ArgumentError, "tIDENTIFIER requires a Symbol or String value" - end - value.to_sym - end - - def declare_local(name) - @locals[name] = true - name - end - - def read_local(name) - return name if name.is_a?(AST::Variable) - (@locals.key?(name) ? AST::LocalRead : AST::BareCall).new(name) - end - - def assign(name, value) - AST::LocalWrite.new(name, value) - end - - def array(elements) - AST::ArrayLiteral.new(elements.freeze) - end - - def word_array(elements, symbols: false) - elements = elements.elements if elements.is_a?(AST::ArrayLiteral) - values = elements.map do |value| - value = value.value if value.is_a?(AST::StringLiteral) - symbols ? value.to_sym : AST::StringLiteral.new(value.to_s) - end - AST::ArrayLiteral.new(values.freeze) - end - - def hash(pairs) - AST::HashLiteral.new(pairs.freeze) - end - - def pair(key, value) - AST::Pair.new(key, value) - end - - def append(list, value) - value.nil? ? list : (list + [value]).freeze - end - - def unsupported(id) - rule = RULES.fetch(id) - raise UnsupportedSyntax.new(rule.fetch("rule"), rule.fetch("line")) - end - end - private_constant :Builder - end - end - end -end diff --git a/lib/lrama/ruby/languages/ruby/lexer.rb b/lib/lrama/ruby/languages/ruby/lexer.rb deleted file mode 100644 index 3207ff7..0000000 --- a/lib/lrama/ruby/languages/ruby/lexer.rb +++ /dev/null @@ -1,1263 +0,0 @@ -# frozen_string_literal: true - -module Lrama - module Ruby - module Languages - module Ruby - # Lexer for Ruby source input. This deliberately does not use Prism: - # the parser needs the same small amount of lexical context that CRuby - # keeps in its parser (EXPR_BEG/EXPR_END and delimiter nesting). - class LexicalContext - attr_accessor :begin_expression, :condition_do, :condition_line, - :ternary_depth, :lambda_pending, :alias_context, :argument_label, - :lex_state, :command_start, :label_pending, :singleton_class_depth, :control_depth, - :method_header, :method_body_start - attr_reader :delimiter_stack, :cmdarg_stack, :condition_stack, - :token_history, :state_history, :block_stack, :scope_stack, :parser_events, - :parser_delimiter_stack, :parser_cmdarg_stack, :parser_block_stack, - :parser_condition_stack, :parser_scope_stack - - def initialize - @begin_expression = true - @condition_do = false - @condition_line = false - @ternary_depth = 0 - @lambda_pending = false - @alias_context = false - @argument_label = nil - @label_pending = false - @singleton_class_depth = 0 - @control_depth = 0 - @method_header = false - @method_body_start = false - @lex_state = :expr_beg - @command_start = true - @delimiter_stack = [] - @cmdarg_stack = [] - @condition_stack = [] - @token_history = [] - @state_history = [] - @block_stack = [] - @block_delimiter_depths = [] - @scope_stack = [] - @parser_events = [] - @parser_delimiter_stack = [] - @parser_cmdarg_stack = [] - @parser_block_stack = [] - @parser_condition_stack = [] - @parser_scope_stack = [] - end - - def delimiter_depth - @delimiter_stack.length - end - - def push_delimiter(value) - @delimiter_stack << value - end - - def pop_delimiter(value) - @delimiter_stack.pop if @delimiter_stack.last == value - end - - def push_cmdarg(value) - @cmdarg_stack << value - end - - def pop_cmdarg - @cmdarg_stack.pop - end - - def cmdarg? - @cmdarg_stack.last == true - end - - def push_condition(value = true) - @condition_stack << value - end - - def pop_condition - @condition_stack.pop - end - - def condition? - @condition_stack.any? - end - - def push_block(value, delimiter_depth: self.delimiter_depth) - @block_stack << value - @block_delimiter_depths << delimiter_depth - end - - def pop_block - @block_delimiter_depths.pop - @block_stack.pop - end - - def block_depth - @block_stack.length - end - - def block_body_at_current_delimiter? - @block_delimiter_depths.last == delimiter_depth - end - - def push_scope(value) - @scope_stack << value - end - - def pop_scope - @scope_stack.pop - end - - def scope_depth - @scope_stack.length - end - - def remember_token(token) - @token_history << token - @token_history.shift while @token_history.length > 8 - end - - def remember_state(token) - @state_history << { - token: token, - lex_state: @lex_state, - begin_expression: @begin_expression, - command_start: @command_start, - delimiter_depth: delimiter_depth, - cmdarg_depth: @cmdarg_stack.length, - condition_depth: @condition_stack.length, - block_depth: @block_stack.length, - scope_depth: @scope_stack.length - }.freeze - end - - def parser_shift(token, value, state, action) - @parser_events << [:shift, token, state, action].freeze - if ["(", "[", :tLPAREN_ARG].include?(token) - @parser_delimiter_stack << token - @parser_cmdarg_stack << (token == :tLPAREN_ARG) - elsif [")", "]", "}"].include?(token) - opener = {")" => "(", "]" => "[", "}" => "{" - }.fetch(token) - @parser_delimiter_stack.pop if @parser_delimiter_stack.last == opener - @parser_cmdarg_stack.pop - end - if [:keyword_do, :keyword_do_block, :keyword_do_LAMBDA, :tLAMBEG].include?(token) || - token == "{" || token == :tLBRACE_ARG - @parser_block_stack << token - elsif token == "}" && @parser_block_stack.last - @parser_block_stack.pop - elsif token == :keyword_end && @parser_block_stack.last - @parser_block_stack.pop - end - if [:keyword_while, :keyword_until, :keyword_for].include?(token) - @parser_condition_stack << token - elsif token == :keyword_do_cond && @parser_condition_stack.last - @parser_condition_stack.pop - elsif token == :keyword_end && @parser_condition_stack.last - @parser_condition_stack.pop - end - if token == :keyword_def - @parser_scope_stack << :method - elsif [:keyword_class, :keyword_module].include?(token) - @parser_scope_stack << token - elsif token == :tLAMBDA - @parser_scope_stack << :lambda - elsif token == "}" && @parser_scope_stack.last == :lambda - @parser_scope_stack.pop - elsif token == :keyword_end && @parser_scope_stack.last - @parser_scope_stack.pop - end - end - - def parser_reduce(rule, state) - @parser_events << [:reduce, rule, state].freeze - end - end - - class Lexer - Error = LexerError - - attr_reader :context - - KEYWORDS = { - "class" => :keyword_class, "module" => :keyword_module, - "def" => :keyword_def, "begin" => :keyword_begin, - "rescue" => :keyword_rescue, "ensure" => :keyword_ensure, - "end" => :keyword_end, "if" => :keyword_if, - "unless" => :keyword_unless, "then" => :keyword_then, - "elsif" => :keyword_elsif, "else" => :keyword_else, - "case" => :keyword_case, "when" => :keyword_when, - "while" => :keyword_while, "until" => :keyword_until, - "for" => :keyword_for, "break" => :keyword_break, - "next" => :keyword_next, "redo" => :keyword_redo, - "retry" => :keyword_retry, "return" => :keyword_return, - "yield" => :keyword_yield, "super" => :keyword_super, - "self" => :keyword_self, "nil" => :keyword_nil, - "true" => :keyword_true, "false" => :keyword_false, - "and" => :keyword_and, "or" => :keyword_or, - "not" => :keyword_not, "alias" => :keyword_alias, - "defined?" => :keyword_defined, "in" => :keyword_in, - "BEGIN" => :keyword_BEGIN, "END" => :keyword_END, - "__LINE__" => :keyword__LINE__, "__FILE__" => :keyword__FILE__, - "__ENCODING__" => :keyword__ENCODING__ - }.freeze - - OPERATORS = %w[[]= [] ... .. <=> === == != =~ !~ >= <= && || << >> ** => :: &. -> += -= *= /= %= **= <<= >>= &&= ||= |= &= ^=].freeze - OP_TOKENS = { - "**" => :tPOW, "<=>" => :tCMP, "==" => :tEQ, "===" => :tEQQ, - "!=" => :tNEQ, ">=" => :tGEQ, "<=" => :tLEQ, "&&" => :tANDOP, - "||" => :tOROP, "=~" => :tMATCH, "!~" => :tNMATCH, - ".." => :tDOT2, "..." => :tDOT3, "<<" => :tLSHFT, - ">>" => :tRSHFT, "&." => :tANDDOT, "::" => :tCOLON2, - "=>" => :tASSOC, "->" => :tLAMBDA, "[]" => :tAREF, "[]=" => :tASET - }.freeze - - def initialize(source, filename: "(ruby)") - unless source.is_a?(String) - raise ArgumentError, "source must be a String" - end - unless [Encoding::UTF_8, Encoding::US_ASCII].include?(source.encoding) && source.valid_encoding? - raise Error, "#{filename}:1:0: invalid UTF-8 source" - end - @source = source.b - @filename = filename - @index = 0 - @line = 1 - @column = 0 - @previous_previous = nil - @previous = nil - @previous_value = nil - @context = LexicalContext.new - @pending = [] - @heredoc_queue = [] - @deferred_suffix = nil - @heredoc_newline_pending = false - @class_superclass = false - end - - def each - return enum_for(__method__) unless block_given? - until eof? && @pending.empty? && @heredoc_queue.empty? && @deferred_suffix.nil? - token = next_token - yield token if token - end - yield [0, nil] - end - - private - - def eof? - @index >= @source.bytesize - end - - def byte(offset = 0) - @source.getbyte(@index + offset) - end - - def advance(count = 1) - count.times do - value = byte - @index += 1 - if value == 10 - @line += 1 - @column = 0 - else - @column += 1 - end - end - end - - def fail!(message, index = @index, line = @line, column = @column) - raise Error, "#{@filename}:#{line}:#{column}: #{message}" - end - - def next_token - if @pending.empty? && @heredoc_queue.any? - load_queued_heredoc - elsif @pending.empty? && @deferred_suffix - suffix = @deferred_suffix - @deferred_suffix = nil - @pending.concat(heredoc_suffix_tokens(suffix)) - @pending.pop if @pending.last == [0, nil] - @pending << ["\n", nil] - @heredoc_newline_pending = false - elsif @pending.empty? && @heredoc_newline_pending - @pending << ["\n", nil] - @heredoc_newline_pending = false - end - unless @pending.empty? - value = @pending.shift - return next_token if value[0] == "\n" && newline_ignored? - @previous_previous = @previous - @previous = value[0] - @previous_value = value[1] - update_pending_delimiter(value[0]) - update_argument_label(value[0], value[1]) - @context.remember_token(value[0]) - @context.begin_expression = expression_begin_after(value[0]) - @context.lex_state = lexical_state_after(value[0]) - @context.command_start = command_start_after(value[0]) - update_block_context(value[0]) - @context.remember_state(value[0]) - return value - end - skip_space_and_comments - return nil if eof? - - start = @index - value = case byte - when 10 - advance - return next_token if @previous == "\n" - return next_token if next_chain_byte == 46 - if @context.alias_context && @previous == :tEQ - @context.alias_context = false - ["\n", nil] - elsif @class_superclass && next_word_is_terminator? - @class_superclass = false - return [";", nil] - else - @class_superclass = false - return next_token if newline_ignored? - @context.pop_condition if @context.condition? && @context.condition_do - @context.condition_do = false - ["\n", nil] - end - when 39, 34, 96 - string_token(byte, start) - when 47 - [:keyword_def, :keyword_alias, :tSYMBEG].include?(@previous) ? operator_or_punctuation(start) : regexp_or_operator(start) - when 37 - [:keyword_def, :keyword_alias, :tSYMBEG].include?(@previous) ? operator_or_punctuation(start) : percent_token(start) - when 60 - heredoc_or_operator(start) - when 48..57 - number_token(start) - when 36, 64 - variable_token(start) - when 63 - character_or_question(start) - when 58 - symbol_or_colon(start) - when 65..90, 95, 97..122, 128..255 - identifier_token(start) - else - operator_or_punctuation(start) - end - @previous_previous = @previous - @previous = value && value[0] - @previous_value = value && value[1] - update_argument_label(value && value[0], value && value[1]) - @context.remember_token(value && value[0]) - @context.begin_expression = expression_begin_after(value && value[0]) - @context.lex_state = lexical_state_after(value && value[0]) - @context.command_start = command_start_after(value && value[0]) - update_block_context(value && value[0]) - @context.remember_state(value && value[0]) - value - end - - def skip_space_and_comments - loop do - while [9, 11, 12, 13, 32].include?(byte) - advance - end - if byte == 92 && byte(1) == 10 - advance(2) - next - end - break unless byte == 35 - advance - advance while !eof? && byte != 10 - end - end - - def newline_ignored? - ignored = (@context.delimiter_depth.positive? && !@context.block_body_at_current_delimiter? && @previous != "}" && - !(@previous == :keyword_end && @context.block_depth.positive?)) || @previous == "\n" || - ["+", "-", "*", "/", "%", "=", "?", ":", ",", ".", "&", "|", "^", "<<", ">>", "&&", "||", "=>", :keyword_and, :keyword_or, :keyword_not, :tAMPER, :tPIPE, :tSTAR, :tDSTAR, :tDOT2, :tDOT3, :tPOW, :tCMP, :tEQ, :tEQQ, :tNEQ, :tGEQ, :tLEQ, :tANDOP, :tOROP, :tMATCH, :tNMATCH, :tLSHFT, :tRSHFT, :tASSOC, :tLAMBDA, :tCOLON2, :tANDDOT].include?(@previous) || - @previous == :tLABEL || - [:modifier_if, :modifier_unless, :modifier_while, :modifier_until].include?(@previous) || - [:tANDOP, :tOROP, :tMATCH, :tNMATCH, :tASSOC, :tOP_ASGN].include?(@previous) || - (@previous == "{" && next_non_space_byte == 124) || - next_chain_byte == 46 - ignored = false if @previous_previous == :tSYMBEG && - [:tLSHFT, :tRSHFT, :tSTAR, :tDSTAR, :tPOW, :tEQ, :tEQQ, :tNEQ, "&", - :tCMP, :tGEQ, :tLEQ, :tANDOP, :tOROP, :tMATCH, :tNMATCH, - :tASSOC, :tOP_ASGN].include?(@previous) - @context.condition_line = false if ignored && @context.condition_line - ignored - end - - def next_word_is_terminator? - position = @index - position += 1 while [9, 11, 12, 13, 32].include?(@source.getbyte(position)) - %w[else elsif end when].any? do |word| - @source.byteslice(position, word.bytesize) == word && - !identifier_byte?(@source.getbyte(position + word.bytesize)) - end - end - - def next_non_space_byte - position = @index - position += 1 while [9, 11, 12, 13, 32].include?(@source.getbyte(position)) - @source.getbyte(position) - end - - def next_chain_byte - position = @index - loop do - position += 1 while [9, 11, 12, 13, 32, 10].include?(@source.getbyte(position)) - break unless @source.getbyte(position) == 35 - position += 1 - position += 1 while @source.getbyte(position) && @source.getbyte(position) != 10 - end - @source.getbyte(position) - end - - def update_pending_delimiter(token) - if ["(", "[", :tLPAREN_ARG].include?(token) - @context.push_delimiter(token) - @context.push_cmdarg(token == :tLPAREN_ARG) - elsif [")", "]", "}"].include?(token) - opener = {")" => "(", "]" => "[", "}" => "{" - }.fetch(token) - @context.pop_delimiter(opener) - @context.pop_cmdarg - @context.method_body_start = true if token == ")" && @context.method_header - @context.method_header = false if token == ")" - end - end - - def update_argument_label(token, value) - if token == :tLABEL - @context.argument_label = value - @context.label_pending = true - elsif ["\n", ";"].include?(token) - @context.argument_label = nil - end - end - - def expression_begin_after(token) - return true if token.nil? || token == "\n" - return false if token == ")" || token == "]" || token == "}" - return false if token == :keyword_end - return false if token == :tINTEGER || token == :tFLOAT || token == :tRATIONAL || token == :tIMAGINARY - return false if @previous_previous == :tSYMBEG && [:tASET, "&"].include?(token) - return false if token == :tIDENTIFIER || token == :tCONSTANT || token == :tFID || token == :tSTRING_END || token == :tREGEXP_END - return false if token == :keyword_true || token == :keyword_false || token == :keyword_nil || token == :keyword_self - return false if [:keyword__LINE__, :keyword__FILE__, :keyword__ENCODING__].include?(token) - return false if token == :tIVAR || token == :tGVAR || token == :tCVAR || token == :tNTH_REF - true - end - - def lexical_state_after(token) - return :expr_beg if token.nil? || token == "\n" - return :expr_fname if @previous_previous == :keyword_def || @context.alias_context - return :expr_end if token == 0 - if @context.label_pending && token != :tLABEL - @context.label_pending = false - return :expr_labeled - end - return :expr_end if [")", "]", "}", :keyword_end, :tSTRING_END, :tREGEXP_END, - :tINTEGER, :tFLOAT, :tRATIONAL, :tIMAGINARY, :tIDENTIFIER, - :tCONSTANT, :tFID, :tIVAR, :tGVAR, :tCVAR, :tNTH_REF, - :keyword_true, :keyword_false, :keyword_nil, :keyword_self, - :keyword__LINE__, :keyword__FILE__, :keyword__ENCODING__].include?(token) - return :expr_end if @previous_previous == :tSYMBEG && [:tASET, "&"].include?(token) - return :expr_label if token == :tLABEL - :expr_beg - end - - def command_start_after(token) - [nil, 0, "\n", ";"].include?(token) - end - - def update_block_context(token) - case token - when :keyword_if, :keyword_unless, :keyword_case, :keyword_begin, - :keyword_while, :keyword_until, :keyword_for - @context.control_depth += 1 - when :keyword_def - @context.method_header = true - @context.push_scope(:method) - when :keyword_do, :keyword_do_block - @context.push_block(:do_block) - when :keyword_do_LAMBDA - @context.push_block(:lambda) - when :tLAMBDA - @context.push_scope(:lambda) - when :tLSHFT - @context.singleton_class_depth += 1 if @previous_previous == :keyword_class - when :tLAMBEG - @context.push_block(:lambda) - when "{", :tLBRACE_ARG - @context.push_block(:brace_block) - when "}" - @context.pop_block if [:brace_block, :lambda].include?(@context.block_stack.last) - @context.pop_scope if @context.scope_stack.last == :lambda - when :keyword_end - if @context.control_depth.positive? - @context.control_depth -= 1 - elsif @context.singleton_class_depth.positive? - @context.singleton_class_depth -= 1 - @context.pop_scope if @context.scope_stack.last == :keyword_class - elsif [:method, :keyword_class, :keyword_module].include?(@context.scope_stack.last) - @context.pop_scope - else - @context.pop_block if [:do_block, :lambda].include?(@context.block_stack.last) - @context.pop_scope if @context.scope_stack.last == :lambda - end - end - if @context.method_header && token != :keyword_def && token != "(" && - !@context.delimiter_stack.include?("(") && - !(@previous_previous == :keyword_def && [:tIDENTIFIER, :tFID].include?(token)) - @context.method_header = false - end - @context.method_body_start = false unless [")", "\n"].include?(token) - end - - def identifier_token(start) - while identifier_byte?(byte) - advance - end - word = @source.byteslice(start, @index - start).force_encoding(Encoding::UTF_8) - if byte == 63 && word == "defined" - advance - word = "defined?" - end - if byte == 63 && byte(1) == 58 - advance(2) - return [:tLABEL, (word + "?").to_sym] - end - if [33, 63].include?(byte) && !identifier_byte?(byte(1)) && byte(1) != 61 - suffix = byte.chr - advance - return [:tFID, (word + suffix).to_sym] - end - if byte == 61 && [:keyword_def, ".", :tCOLON2].include?(@previous) - advance - return [:tFID, (word + "=").to_sym] - end - if byte == 61 && @context.alias_context - advance - return [:tFID, (word + "=").to_sym] - end - if byte == 61 && @previous == :tSYMBEG - advance - return [:tIDENTIFIER, (word + "=").to_sym] - end - if byte == 58 && byte(1) != 58 - advance - return [:tLABEL, word.to_sym] - end - if word == "do" && @context.lambda_pending - return [do_token, word.to_sym] - end - if word == "do" && !@context.condition_do && no_argument_block? - return [:keyword_do, word.to_sym] - end - token = KEYWORDS[word] - if token && [".", :tCOLON2, :tANDDOT].include?(@previous) - token = next_non_space_byte == 61 ? :tIDENTIFIER : :tFID - elsif token && @previous == :keyword_def && word != "self" - token = :tFID - elsif (!@context.begin_expression || [:keyword_return, :keyword_break, :keyword_next, :keyword_end, :keyword_yield, :keyword_super].include?(@previous)) && { "if" => :modifier_if, "unless" => :modifier_unless, - "while" => :modifier_while, "until" => :modifier_until, - "rescue" => :modifier_rescue }.key?(word) - token = { "if" => :modifier_if, "unless" => :modifier_unless, - "while" => :modifier_while, "until" => :modifier_until, - "rescue" => :modifier_rescue }.fetch(word, token) - end - token ||= word == "do" ? do_token : (word.getbyte(0).between?(65, 90) ? :tCONSTANT : :tIDENTIFIER) - if [:keyword_while, :keyword_until, :keyword_for].include?(token) - @context.condition_do = true - @context.push_condition - end - @context.alias_context = true if token == :keyword_alias - [token, word.to_sym] - rescue EncodingError - fail!("invalid UTF-8 identifier", start) - end - - def identifier_byte?(value) - value && (value == 95 || value.between?(65, 90) || value.between?(97, 122) || value >= 128 || value.between?(48, 57)) - end - - def do_token - if @context.condition_do && @context.condition? - @context.pop_condition - @context.condition_do = false - :keyword_do_cond - elsif @context.lambda_pending - @context.lambda_pending = false - :keyword_do_LAMBDA - elsif @previous == :tIDENTIFIER && @previous_previous == :tLABEL && @previous_value == :lambda - :keyword_do - elsif @previous == ")" && @context.token_history.each_cons(2).any? { |left, right| left == :tUMINUS && right == :tLPAREN } - :keyword_do_block - elsif [")", :keyword_super, :keyword_yield].include?(@previous) - :keyword_do - elsif @previous == :tLAMBDA - :keyword_do_LAMBDA - else - :keyword_do_block - end - end - - def no_argument_block? - return false unless [:tIDENTIFIER, :tCONSTANT, :tFID].include?(@previous) - return false if @context.argument_label == :at && [".", :tCOLON2, :tANDDOT].include?(@previous_previous) - return false if @context.token_history == ["+", :tINTEGER, ".", :tIDENTIFIER] - return false if [:tIDENTIFIER, :tCONSTANT, :tFID].include?(@previous_previous) - return false if [:tSYMBEG, :tLABEL, :tCOLON2].include?(@previous_previous) - return true if @previous_value == :lambda && @previous_previous == :tLSHFT - return false if [:tLSHFT, :tLAMBDA].include?(@previous_previous) - true - end - - def no_argument_brace_block? - return false if [".", :tCOLON2].include?(@previous_previous) - return false if [:tLABEL, :tLSHFT, :tOROP, :tANDOP, :tASSOC, :tAMPER, "=", "(", ",", ":", "?"].include?(@previous_previous) - no_argument_block? - end - - def number_token(start) - base = 10 - if byte == 48 && [120, 88, 98, 66, 111, 79].include?(byte(1)) - base = { 120 => 16, 88 => 16, 98 => 2, 66 => 2, 111 => 8, 79 => 8 }.fetch(byte(1)) - advance(2) - digit_start = @index - advance while digit_byte?(byte, base) || byte == 95 - fail!("invalid numeric literal", start) if @index == digit_start - text = @source.byteslice(digit_start, @index - digit_start).delete("_") - return [:tINTEGER, text.to_i(base)] - end - advance while digit_byte?(byte, 10) || byte == 95 - float = false - if byte == 46 && digit_byte?(byte(1), 10) - float = true - advance - advance while digit_byte?(byte, 10) || byte == 95 - end - if byte == 101 || byte == 69 - float = true - advance - advance if byte == 43 || byte == 45 - fail!("invalid numeric literal", start) unless digit_byte?(byte, 10) - advance while digit_byte?(byte, 10) || byte == 95 - end - suffix = byte - advance if [105, 114].include?(suffix) - text = @source.byteslice(start, @index - start).delete("_") - return [:tRATIONAL, Rational(text.delete_suffix("r"))] if suffix == 114 - return [:tIMAGINARY, Complex(0, text.delete_suffix("i").to_f)] if suffix == 105 - [float ? :tFLOAT : :tINTEGER, float ? text.to_f : text.to_i] - rescue ArgumentError, ZeroDivisionError - fail!("invalid numeric literal", start) - end - - def digit_byte?(value, base) - return false unless value - value.between?(48, 57) && value - 48 < base || base == 16 && value.between?(65, 70) || base == 16 && value.between?(97, 102) - end - - def variable_token(start) - marker = byte - advance - advance if marker == 64 && byte == 64 - if marker == 36 && [33, 38, 39, 43, 58, 60, 62, 61, 63, 96, 126].include?(byte) - advance - text = @source.byteslice(start, @index - start) - return [:tGVAR, text.to_sym] - end - if marker == 36 && byte == 36 - advance - return [:tGVAR, :"$$"] - end - if marker == 36 && byte && byte.between?(48, 57) - advance while byte && byte.between?(48, 57) - return [:tNTH_REF, @source.byteslice(start + 1, @index - start - 1).to_i] - end - advance while identifier_byte?(byte) - text = @source.byteslice(start, @index - start) - token = marker == 36 ? :tGVAR : (text.start_with?("@@") ? :tCVAR : :tIVAR) - [token, text.to_sym] - end - - def character_or_question(start) - command_character = !@context.begin_expression && - [:tIDENTIFIER, :tCONSTANT, :tFID].include?(@previous) && - start.positive? && [9, 32].include?(@source.getbyte(start - 1)) - return operator_or_punctuation(start) unless (@context.begin_expression || command_character) && - byte(1) && byte(1) != 32 && byte(1) != 10 - advance - value = if byte == 92 - escape_sequence(start) - else - length = utf8_character_length(byte) - character = @source.byteslice(@index, length).force_encoding(Encoding::UTF_8) - advance(length) - character - end - [:tCHAR, value] - end - - def utf8_character_length(first_byte) - return 1 if first_byte < 0x80 - return 2 if first_byte.between?(0xC2, 0xDF) - return 3 if first_byte.between?(0xE0, 0xEF) - return 4 if first_byte.between?(0xF0, 0xF4) - fail!("invalid UTF-8 character") - end - - def symbol_or_colon(start) - symbol_position = @context.begin_expression || ([:tIDENTIFIER, :tFID].include?(@previous) && @context.ternary_depth.zero?) - return operator_or_punctuation(start) unless symbol_position && byte(1) && - ![9, 10, 11, 12, 13, 32].include?(byte(1)) && byte(1) != 58 - advance - if byte == 39 || byte == 34 - quote = byte - advance - @pending = read_interpolated_quoted(quote, start) - @pending << [:tSTRING_END, nil] - end - [:tSYMBEG, nil] - end - - def string_token(quote, start) - advance - content_tokens = read_interpolated_quoted(quote, start) - label = byte == 58 - advance if label - terminator = label ? :tLABEL_END : :tSTRING_END - @pending = content_tokens - @pending << [terminator, nil] - [quote == 96 ? :tXSTRING_BEG : :tSTRING_BEG, nil] - end - - def read_interpolated_quoted(quote, start) - return [[:tSTRING_CONTENT, read_quoted(quote, interpolate: false, start: start)]] if quote == 39 - content = +"" - tokens = [] - until eof? - value = byte - if value == quote - advance - tokens << [:tSTRING_CONTENT, content] unless content.empty? - return tokens - elsif value == 92 - content << escape_sequence(start) - elsif value == 35 && byte(1) == 123 - tokens << [:tSTRING_CONTENT, content] unless content.empty? - content = +"" - advance(2) - expression = read_interpolation_source(start) - inner = self.class.new(expression, filename: @filename).each.to_a - inner.pop if inner.last == [0, nil] - tokens << [:tSTRING_DBEG, nil] - tokens.concat(inner) - tokens << [:tSTRING_DEND, nil] - else - content << value.chr - advance - end - end - fail!("unterminated literal", start) - end - - def read_interpolation_source(start) - begin_index = @index - depth = 1 - quote = nil - escaped = false - while !eof? - value = byte - if quote - if escaped - escaped = false - elsif value == 92 - escaped = true - elsif value == quote - quote = nil - end - elsif [39, 34, 96].include?(value) && @source.getbyte(@index - 1) != 36 - quote = value - elsif value == 123 - depth += 1 - elsif value == 125 - depth -= 1 - if depth.zero? - expression = @source.byteslice(begin_index, @index - begin_index) - advance - return expression.force_encoding(Encoding::UTF_8) - end - end - advance - end - fail!("unterminated string interpolation", start) - end - - def read_quoted(quote, interpolate:, start:) - result = +"" - until eof? - value = byte - if value == quote - advance - return result - elsif value == 92 - result << escape_sequence(start) - elsif value == 35 && interpolate && byte(1) == 123 - fail!("string interpolation cannot be represented by the AST", start) - else - result << value - advance - end - end - fail!("unterminated literal", start) - end - - def escape_sequence(start) - advance - fail!("unterminated escape", start) if eof? - value = byte - advance - return "\n" if value == 110 - return "\t" if value == 116 - return "\r" if value == 114 - return "\f" if value == 102 - return "\a" if value == 97 - return "\e" if value == 101 - return value.chr if value == 92 || value == 34 || value == 39 - value.chr - end - - def regexp_or_operator(start) - return operator_or_punctuation(start) if @previous == :keyword_def - return operator_or_punctuation(start) if @previous == :tSYMBEG - if @context.begin_expression - advance - value = read_regexp(start) - content = value.is_a?(Array) ? value : [[:tSTRING_CONTENT, value]] - @pending = content + [[:tREGEXP_END, nil]] - return [:tREGEXP_BEG, nil] - end - operator_or_punctuation(start) - end - - def read_regexp(start) - result = +"" - tokens = [] - interpolated = false - in_class = false - until eof? - value = byte - if value == 92 - result << value.chr - advance - fail!("unterminated regexp", start) if eof? - result << byte.chr - advance - elsif value == 91 - in_class = true - result << value.chr - advance - elsif value == 93 - in_class = false - result << value.chr - advance - elsif value == 35 && byte(1) == 123 && !in_class - tokens << [:tSTRING_CONTENT, result] unless result.empty? - result = +"" - interpolated = true - advance(2) - expression = read_interpolation_source(start) - inner = self.class.new(expression, filename: @filename).each.to_a - inner.pop if inner.last == [0, nil] - tokens << [:tSTRING_DBEG, nil] - tokens.concat(inner) - tokens << [:tSTRING_DEND, nil] - elsif value == 47 && !in_class - advance - advance while byte && byte.between?(97, 122) - tokens << [:tSTRING_CONTENT, result] unless result.empty? - return interpolated ? tokens : result - else - result << value.chr - advance - end - end - fail!("unterminated regexp", start) - end - - def percent_token(start) - literal_kind = [113, 81, 119, 87, 105, 73, 114, 115, 120, 88].include?(byte(1)) - command_argument = start.positive? && [9, 32].include?(@source.getbyte(start - 1)) && - [:tIDENTIFIER, :tCONSTANT, :tFID].include?(@previous) && - ![:tIDENTIFIER, :tCONSTANT, :tFID].include?(@previous_previous) - shorthand_delimiter = byte(1) && !identifier_byte?(byte(1)) && - ![9, 10, 11, 12, 13, 32, 61].include?(byte(1)) - return operator_or_punctuation(start) unless @context.begin_expression || - ((literal_kind || shorthand_delimiter) && command_argument) - advance - kind = byte - if [113, 81, 119, 87, 105, 73, 114, 115, 120, 88].include?(kind) - advance - else - kind = 81 - end - delimiter = byte - fail!("invalid percent literal", start) unless delimiter - advance - closing = { 40 => 41, 91 => 93, 123 => 125, 60 => 62 }.fetch(delimiter, delimiter) - interpolate = [81, 87, 73, 114, 120, 88].include?(kind) - content = interpolate ? - read_interpolated_delimited(closing, start) : - read_delimited(closing, start, interpolate: interpolate) - advance while kind == 114 && byte && byte.between?(97, 122) - token = { 113 => :tSTRING_BEG, 81 => :tSTRING_BEG, 119 => :tWORDS_BEG, 87 => :tQWORDS_BEG, - 105 => :tSYMBOLS_BEG, 73 => :tQSYMBOLS_BEG, 114 => :tREGEXP_BEG, - 115 => :tSYMBEG, 120 => :tXSTRING_BEG, 88 => :tXSTRING_BEG }.fetch(kind) - terminator = kind == 114 ? :tREGEXP_END : :tSTRING_END - @pending = if [119, 87, 105, 73].include?(kind) - content = content.map { |token, value| value.to_s }.join if content.is_a?(Array) - word_tokens(content, symbols: [105, 73].include?(kind)) + [[terminator, nil]] - elsif content.is_a?(Array) - content + [[terminator, nil]] - else - [[:tSTRING_CONTENT, content], [terminator, nil]] - end - [token, nil] - end - - def word_tokens(content, symbols:) - words = content.split(/[\t\n\f\r ]+/).reject(&:empty?) - tokens = [[" ", nil]] - words.each do |word| - tokens << [:tSTRING_CONTENT, symbols ? word.to_sym : word] - tokens << [" ", nil] - end - tokens - end - - def read_delimited(closing, start, interpolate: false) - result = +"" - depth = 0 - until eof? - value = byte - if value == 92 - result << escape_sequence(start) - elsif value == 35 && interpolate && byte(1) == 123 - fail!("string interpolation cannot be represented by the AST", start) - elsif value == closing && depth.zero? - advance - return result - else - depth += 1 if value == ({ 41 => 40, 93 => 91, 125 => 123, 62 => 60 }.fetch(closing, -1)) - depth -= 1 if value == closing && depth.positive? - result << value.chr - advance - end - end - fail!("unterminated percent literal", start) - end - - def read_interpolated_delimited(closing, start) - result = +"" - tokens = [] - depth = 0 - opening = { 41 => 40, 93 => 91, 125 => 123, 62 => 60 }.fetch(closing, -1) - until eof? - value = byte - if value == 92 - result << escape_sequence(start) - elsif value == 35 && byte(1) == 123 - tokens << [:tSTRING_CONTENT, result] unless result.empty? - result = +"" - advance(2) - expression = read_interpolation_source(start) - inner = self.class.new(expression, filename: @filename).each.to_a - inner.pop if inner.last == [0, nil] - tokens << [:tSTRING_DBEG, nil] - tokens.concat(inner) - tokens << [:tSTRING_DEND, nil] - elsif value == closing && depth.zero? - advance - tokens << [:tSTRING_CONTENT, result] unless result.empty? - return tokens - else - depth += 1 if value == opening - depth -= 1 if value == closing && depth.positive? - result << value.chr - advance - end - end - fail!("unterminated percent literal", start) - end - - def interpolated_content_tokens(source, start) - return [[:tSTRING_CONTENT, source]] unless source.include?("#" + "{") - tokens = [] - literal = +"" - index = 0 - while index < source.bytesize - if source.getbyte(index) == 35 && source.getbyte(index + 1) == 123 - tokens << [:tSTRING_CONTENT, literal] unless literal.empty? - depth = 1 - expr_start = index + 2 - index = expr_start - while index < source.bytesize && depth.positive? - depth += 1 if source.getbyte(index) == 123 - depth -= 1 if source.getbyte(index) == 125 - index += 1 - end - fail!("unterminated string interpolation", start) unless depth.zero? - expression = source.byteslice(expr_start, index - expr_start - 1) - inner = self.class.new(expression, filename: @filename).each.to_a - inner.pop if inner.last == [0, nil] - tokens << [:tSTRING_DBEG, nil] - tokens.concat(inner) - tokens << [:tSTRING_DEND, nil] - literal = +"" - else - literal << source.getbyte(index).chr - index += 1 - end - end - tokens << [:tSTRING_CONTENT, literal] unless literal.empty? - tokens - end - - def heredoc_or_operator(start) - if byte(1) == 60 && heredoc_prefix? - return heredoc_token(start) if @context.begin_expression || @previous == :tIDENTIFIER - end - operator_or_punctuation(start) - end - - def heredoc_prefix? - value = byte(2) - value && ([39, 34, 96, 45, 126].include?(value) || identifier_byte?(value)) - end - - def heredoc_token(start) - advance(2) - indent = byte == 45 || byte == 126 - squiggly = byte == 126 - advance if indent - quote = byte - if quote == 39 || quote == 34 || quote == 96 - advance - delimiter_start = @index - advance while byte && byte != quote - fail!("unterminated heredoc identifier", start) if eof? - delimiter = @source.byteslice(delimiter_start, @index - delimiter_start) - advance - else - delimiter_start = @index - advance while identifier_byte?(byte) - delimiter = @source.byteslice(delimiter_start, @index - delimiter_start) - end - fail!("invalid heredoc identifier", start) if delimiter.empty? - suffix_start = @index - advance while byte && byte != 10 - suffix = @source.byteslice(suffix_start, @index - suffix_start).force_encoding(Encoding::UTF_8) - advance if byte == 10 - interpolate = quote != 39 - body = read_heredoc_body(delimiter, indent, squiggly, start) - content_tokens = interpolate ? interpolated_content_tokens(body, start) : [[:tSTRING_CONTENT, body]] - @pending = content_tokens + [[:tSTRING_END, nil]] - prefix, headers, suffix = extract_heredoc_headers(suffix) - unless prefix.empty? - prefix_tokens = heredoc_suffix_tokens(prefix) - @pending.concat(prefix_tokens) - end - @heredoc_queue.concat(headers) - @deferred_suffix = suffix unless suffix.empty? - @heredoc_newline_pending = true - [quote == 96 ? :tXSTRING_BEG : :tSTRING_BEG, nil] - end - - def read_heredoc_body(delimiter, indent, squiggly, start) - body = +"" - loop do - line_start = @index - advance while !eof? && byte != 10 - line = @source.byteslice(line_start, @index - line_start) - line_without_cr = line.delete_suffix("\r") - terminator = indent ? line_without_cr.sub(/\A[ \t]*/, "") : line_without_cr - if terminator == delimiter - advance if byte == 10 - break - end - body << line << "\n" - advance if byte == 10 - fail!("unterminated heredoc", start) if eof? - end - squiggly ? dedent_heredoc(body) : body - end - - def extract_heredoc_headers(suffix) - pattern = /<<(\-|~)?([A-Za-z_][A-Za-z0-9_]*)/ - match = pattern.match(suffix) - return [suffix, [], ""] unless match - - headers = [{delimiter: match[2], indent: !match[1].nil?, squiggly: match[1] == "~", quote: nil}] - trailing = suffix[match.end(0)..].to_s.gsub(pattern) do - headers << {delimiter: Regexp.last_match(2), indent: !Regexp.last_match(1).nil?, squiggly: Regexp.last_match(1) == "~", quote: nil} - "" - end - [suffix[0...match.begin(0)], headers, trailing] - end - - def heredoc_suffix_tokens(source) - suffix_lexer = self.class.new(source, filename: @filename) - suffix_lexer.context.begin_expression = false - suffix_lexer.context.lex_state = :expr_end - suffix_lexer.instance_variable_set(:@previous, :tSTRING_END) - tokens = suffix_lexer.each.to_a - tokens.pop if tokens.last == [0, nil] - if [:keyword_if, :keyword_unless, :keyword_while, :keyword_until, :keyword_rescue].include?(tokens.first&.first) - token = {keyword_if: :modifier_if, keyword_unless: :modifier_unless, - keyword_while: :modifier_while, keyword_until: :modifier_until, - keyword_rescue: :modifier_rescue}.fetch(tokens.first.first) - tokens[0] = [token, tokens.first.last] - end - tokens - end - - def load_queued_heredoc - descriptor = @heredoc_queue.shift - body = read_heredoc_body(descriptor.fetch(:delimiter), descriptor.fetch(:indent), descriptor.fetch(:squiggly), @index) - content = descriptor.fetch(:quote) == 39 ? [[:tSTRING_CONTENT, body]] : interpolated_content_tokens(body, @index) - @pending.concat([descriptor.fetch(:quote) == 96 ? [:tXSTRING_BEG, nil] : [:tSTRING_BEG, nil], *content, [:tSTRING_END, nil]]) - end - - def dedent_heredoc(body) - indents = body.lines.filter_map do |line| - next if line.strip.empty? - line[/\A[ \t]*/].bytesize - end - return body if indents.empty? - - width = indents.min - body.lines.map { |line| line.sub(/\A[ \t]{0,#{width}}/, "") }.join - end - - def operator_or_punctuation(start) - if @previous == :keyword_def && @source.getbyte(@index) == 38 - advance - return ["&", nil] - end - if @previous == :tSYMBEG && ["-@", "+@"].include?(@source.byteslice(@index, 2)) - value = @source.byteslice(@index, 2) - advance(2) - return [value == "-@" ? :tUMINUS : :tUPLUS, nil] - end - text = OPERATORS.sort_by { |operator| -operator.bytesize }.find do |operator| - next false if ["[]", "[]="].include?(operator) && start.positive? && - [9, 10, 11, 12, 13, 32].include?(@source.getbyte(start - 1)) && - ![:keyword_def, :keyword_alias].include?(@previous) && - @previous_previous != :keyword_alias - next false if ["[]", "[]="].include?(operator) && - [:tIDENTIFIER, :tCONSTANT, :tFID, :tINTEGER, :tSTRING_END, :tREGEXP_END].include?(@previous) && - @previous_previous != :keyword_alias - @source.byteslice(@index, operator.bytesize) == operator - end - operator_method = [:keyword_def, :keyword_alias, ".", :tCOLON2, :tANDDOT, :tSYMBEG].include?(@previous) || - @previous_previous == :keyword_alias - if text && !(begin_expression? && !operator_method && ["[]", "[]="].include?(text)) - advance(text.bytesize) - token = if text == "**" && @context.begin_expression - :tDSTAR - elsif text == ".." && @context.begin_expression - :tBDOT2 - elsif text == "..." && (@context.begin_expression || [:tLPAREN, "(", ","].include?(@previous)) - :tBDOT3 - elsif text == "::" && (@context.begin_expression || (start.positive? && [9, 32].include?(@source.getbyte(start - 1)))) - :tCOLON3 - elsif text == "&" && @previous == :keyword_def - "&" - else - OP_TOKENS.fetch(text, :tOP_ASGN) - end - @context.lambda_pending = true if token == :tLAMBDA - return [token, nil] - end - value = byte.chr - advance - if value == "(" || value == "[" || value == "{" - brace_block = value == "{" && @previous != "(" && - ([:tIDENTIFIER, :tCONSTANT, :tFID].include?(@previous) || - (@previous == ")" && !(@previous_previous == :tSTAR && @context.scope_stack.last == :method)) || - [".", :tCOLON2].include?(@previous_previous) || - [:keyword_super, :keyword_yield].include?(@previous) || - [:proc, :lambda].include?(@previous_value)) - lambda_block = value == "{" && @context.lambda_pending - command_arg = value == "(" && !@context.begin_expression && start.positive? && [9, 32].include?(@source.getbyte(start - 1)) && - [:tIDENTIFIER, :tCONSTANT, :tFID].include?(@previous) - unless brace_block || lambda_block - @context.push_delimiter(value) - @context.push_cmdarg(command_arg) - end - if value == "(" - if command_arg - return [:tLPAREN_ARG, nil] - end - method_definition = @previous == :keyword_def || @previous_previous == :keyword_def - return [@context.begin_expression && !method_definition && - ![".", :tCOLON2, :tANDDOT, :keyword_super, :keyword_yield, :tLAMBDA, :tAREF].include?(@previous) ? :tLPAREN : "(", nil] - end - if value == "{" && @context.lambda_pending - @context.lambda_pending = false - return [:tLAMBEG, nil] - end - array_argument = value == "[" && start.positive? && - [9, 10, 11, 12, 13, 32].include?(@source.getbyte(start - 1)) && - [:tIDENTIFIER, :tFID, :tCONSTANT].include?(@previous) - array_argument ||= value == "[" && @previous == ")" && - @previous_previous == "(" && @context.scope_stack.last == :method - array_argument ||= value == "[" && @context.method_body_start - return [value == "[" && (@context.begin_expression || array_argument) ? :tLBRACK : (value == "[" ? "[" : (brace_block ? "{" : :tLBRACE)), nil] - elsif value == ")" || value == "]" || value == "}" - opener = { ")" => "(", "]" => "[", "}" => "{" }.fetch(value) - @context.pop_delimiter(opener) - @context.pop_cmdarg - if value == ")" && @context.method_header - @context.method_body_start = true - @context.method_header = false - end - elsif value == "-" && @context.begin_expression - return [:tUMINUS, nil] - elsif value == "+" && @context.begin_expression - return [:tUPLUS, nil] - elsif value == "*" && @context.begin_expression - return [:tSTAR, nil] - elsif value == "&" && @previous == :tSYMBEG - return [value, nil] - elsif value == "&" && @context.begin_expression - return [:tAMPER, nil] - elsif value == "?" && !@context.begin_expression - @context.ternary_depth += 1 - elsif value == ":" && @context.ternary_depth.positive? - @context.ternary_depth -= 1 - elsif value == "<" && @previous == :tCONSTANT && @previous_previous == :keyword_class - @class_superclass = true - end - [value, nil] - rescue EncodingError - fail!("invalid byte", start) - end - - def begin_expression? - @context.begin_expression - end - end - private_constant :Lexer - end - end - end -end diff --git a/lib/lrama/ruby/languages/ruby/parse.y b/lib/lrama/ruby/languages/ruby/parse.y deleted file mode 100644 index 00a26f2..0000000 --- a/lib/lrama/ruby/languages/ruby/parse.y +++ /dev/null @@ -1,1897 +0,0 @@ -/* Generated from ruby/ruby 37d60dd3241d5fdb10ca43f36077f5d556ae1b8d. See tool/ruby/actions.rb. */ -%no-stdlib -%expect 0 -%token END_OF_INPUT 0 "end-of-input" -%token keyword_class 258 "'class'" -%token keyword_module 259 "'module'" -%token keyword_def 260 "'def'" -%token keyword_undef 261 "'undef'" -%token keyword_begin 262 "'begin'" -%token keyword_rescue 263 "'rescue'" -%token keyword_ensure 264 "'ensure'" -%token keyword_end 265 "'end'" -%token keyword_if 266 "'if'" -%token keyword_unless 267 "'unless'" -%token keyword_then 268 "'then'" -%token keyword_elsif 269 "'elsif'" -%token keyword_else 270 "'else'" -%token keyword_case 271 "'case'" -%token keyword_when 272 "'when'" -%token keyword_while 273 "'while'" -%token keyword_until 274 "'until'" -%token keyword_for 275 "'for'" -%token keyword_break 276 "'break'" -%token keyword_next 277 "'next'" -%token keyword_redo 278 "'redo'" -%token keyword_retry 279 "'retry'" -%token keyword_in 280 "'in'" -%token keyword_do 281 "'do'" -%token keyword_do_cond 282 "'do' for condition" -%token keyword_do_block 283 "'do' for block" -%token keyword_do_LAMBDA 284 "'do' for lambda" -%token keyword_return 285 "'return'" -%token keyword_yield 286 "'yield'" -%token keyword_super 287 "'super'" -%token keyword_self 288 "'self'" -%token keyword_nil 289 "'nil'" -%token keyword_true 290 "'true'" -%token keyword_false 291 "'false'" -%token keyword_and 292 "'and'" -%token keyword_or 293 "'or'" -%token keyword_not 294 "'not'" -%token modifier_if 295 "'if' modifier" -%token modifier_unless 296 "'unless' modifier" -%token modifier_while 297 "'while' modifier" -%token modifier_until 298 "'until' modifier" -%token modifier_rescue 299 "'rescue' modifier" -%token keyword_alias 300 "'alias'" -%token keyword_defined 301 "'defined?'" -%token keyword_BEGIN 302 "'BEGIN'" -%token keyword_END 303 "'END'" -%token keyword__LINE__ 304 "'__LINE__'" -%token keyword__FILE__ 305 "'__FILE__'" -%token keyword__ENCODING__ 306 "'__ENCODING__'" -%token tIDENTIFIER 307 "local variable or method" -%token tFID 308 "method" -%token tGVAR 309 "global variable" -%token tIVAR 310 "instance variable" -%token tCONSTANT 311 "constant" -%token tCVAR 312 "class variable" -%token tLABEL 313 "label" -%token tINTEGER 314 "integer literal" -%token tFLOAT 315 "float literal" -%token tRATIONAL 316 "rational literal" -%token tIMAGINARY 317 "imaginary literal" -%token tCHAR 318 "char literal" -%token tNTH_REF 319 "numbered reference" -%token tBACK_REF 320 "back reference" -%token tSTRING_CONTENT 321 "literal content" -%token tREGEXP_END 322 -%token tDUMNY_END 323 "dummy end" -%token '.' 46 -%token '\\' 92 "backslash" -%token tSP 324 "escaped space" -%token '\t' 9 "escaped horizontal tab" -%token '\f' 12 "escaped form feed" -%token '\r' 13 "escaped carriage return" -%token '\13' 11 "escaped vertical tab" -%token tUPLUS 132 "unary+" -%token tUMINUS 133 "unary-" -%token tPOW 134 "**" -%token tCMP 135 "<=>" -%token tEQ 140 "==" -%token tEQQ 141 "===" -%token tNEQ 142 "!=" -%token tGEQ 139 ">=" -%token tLEQ 138 "<=" -%token tANDOP 148 "&&" -%token tOROP 149 "||" -%token tMATCH 143 "=~" -%token tNMATCH 144 "!~" -%token tDOT2 128 ".." -%token tDOT3 129 "..." -%token tBDOT2 130 "(.." -%token tBDOT3 131 "(..." -%token tAREF 145 "[]" -%token tASET 146 "[]=" -%token tLSHFT 136 "<<" -%token tRSHFT 137 ">>" -%token tANDDOT 150 "&." -%token tCOLON2 147 "::" -%token tCOLON3 325 ":: at EXPR_BEG" -%token tOP_ASGN 326 "operator-assignment" -%token tASSOC 327 "=>" -%token tLPAREN 328 "(" -%token tLPAREN_ARG 329 "( arg" -%token tLBRACK 330 "[" -%token tLBRACE 331 "{" -%token tLBRACE_ARG 332 "{ arg" -%token tSTAR 333 "*" -%token tDSTAR 334 "**arg" -%token tAMPER 335 "&" -%token tLAMBDA 336 "->" -%token tSYMBEG 337 "symbol literal" -%token tSTRING_BEG 338 "string literal" -%token tXSTRING_BEG 339 "backtick literal" -%token tREGEXP_BEG 340 "regexp literal" -%token tWORDS_BEG 341 "word list" -%token tQWORDS_BEG 342 "verbatim word list" -%token tSYMBOLS_BEG 343 "symbol list" -%token tQSYMBOLS_BEG 344 "verbatim symbol list" -%token tSTRING_END 345 "terminator" -%token tSTRING_DEND 346 "'}'" -%token tSTRING_DBEG 347 "'#{'" -%token tSTRING_DVAR 348 -%token tLAMBEG 349 -%token tLABEL_END 350 -%token tIGNORED_NL 351 -%token tCOMMENT 352 -%token tEMBDOC_BEG 353 -%token tEMBDOC 354 -%token tEMBDOC_END 355 -%token tHEREDOC_BEG 356 -%token tHEREDOC_END 357 -%token k__END__ 358 -%token tLOWEST 359 -%token '=' 61 -%token '?' 63 -%token ':' 58 -%token '>' 62 -%token '<' 60 -%token '|' 124 -%token '^' 94 -%token '&' 38 -%token '+' 43 -%token '-' 45 -%token '*' 42 -%token '/' 47 -%token '%' 37 -%token tUMINUS_NUM 360 -%token '!' 33 -%token '~' 126 -%token tLAST_TOKEN 361 -%token '{' 123 -%token '}' 125 -%token '[' 91 -%token '\n' 10 -%token ',' 44 -%token '`' 96 -%token '(' 40 -%token ')' 41 -%token ']' 93 -%token ';' 59 -%token ' ' 32 -%nonassoc tLOWEST -%nonassoc tLBRACE_ARG -%nonassoc keyword_in modifier_if modifier_unless modifier_while modifier_until -%left keyword_and keyword_or -%right keyword_not -%nonassoc keyword_defined -%right tOP_ASGN '=' -%left modifier_rescue -%right '?' ':' -%nonassoc tDOT2 tDOT3 tBDOT2 tBDOT3 -%left tOROP -%left tANDOP -%nonassoc tCMP tEQ tEQQ tNEQ tMATCH tNMATCH -%left tGEQ tLEQ '>' '<' -%left '|' '^' -%left '&' -%left tLSHFT tRSHFT -%left '+' '-' -%left '*' '/' '%' -%right tUMINUS tUMINUS_NUM -%right tPOW -%right tUPLUS '!' '~' -%left keyword_do_block -%start program -%% -/* upstream parse.y:3176: $@1: %empty */ -midrule_1: %empty { $$ = nil }; -/* upstream parse.y:3182: option_terms: %empty */ -option_terms: %empty { $$ = nil }; -/* upstream parse.y:3182: option_terms: terms */ -option_terms: terms { $$ = nil }; -/* upstream parse.y:2991: compstmt_top_stmts: top_stmts option_terms */ -compstmt_top_stmts: top_stmts option_terms { $$ = $1 }; -/* upstream parse.y:3183: program: $@1 compstmt_top_stmts */ -program: midrule_1 compstmt_top_stmts { $$ = @builder.program($2) }; -/* upstream parse.y:3203: top_stmts: none */ -top_stmts: none { $$ = [].freeze }; -/* upstream parse.y:3208: top_stmts: top_stmt */ -top_stmts: top_stmt { $$ = [$1].freeze }; -/* upstream parse.y:3213: top_stmts: top_stmts terms top_stmt */ -top_stmts: top_stmts terms top_stmt { $$ = ($1 + [$3]).freeze }; -/* upstream parse.y:3220: top_stmt: stmt */ -top_stmt: stmt { $$ = $1 }; -/* upstream parse.y:3225: top_stmt: "'BEGIN'" begin_block */ -top_stmt: keyword_BEGIN begin_block %prec keyword_BEGIN { $$ = $2 }; -/* upstream parse.y:3231: block_open: '{' */ -block_open: '{' %prec '{' { $$ = nil }; -/* upstream parse.y:3234: begin_block: block_open compstmt_top_stmts '}' */ -begin_block: block_open compstmt_top_stmts '}' %prec '}' { $$ = $2 }; -/* upstream parse.y:2991: compstmt_stmts: stmts option_terms */ -compstmt_stmts: stmts option_terms { $$ = $1 }; -/* upstream parse.y:3247: $@2: %empty */ -midrule_2: %empty { $$ = nil }; -/* upstream parse.y:3252: $@3: %empty */ -midrule_3: %empty { $$ = nil }; -/* upstream parse.y:3256: bodystmt: compstmt_stmts lex_ctxt opt_rescue k_else $@2 compstmt_stmts $@3 opt_ensure */ -bodystmt: compstmt_stmts lex_ctxt opt_rescue k_else midrule_2 compstmt_stmts midrule_3 opt_ensure { $$ = @builder.body_nodes($1) }; -/* upstream parse.y:3263: $@4: %empty */ -midrule_4: %empty { $$ = nil }; -/* upstream parse.y:3267: bodystmt: compstmt_stmts lex_ctxt opt_rescue $@4 opt_ensure */ -bodystmt: compstmt_stmts lex_ctxt opt_rescue midrule_4 opt_ensure { $$ = @builder.body_nodes($1) }; -/* upstream parse.y:3274: stmts: none */ -stmts: none { $$ = [].freeze }; -/* upstream parse.y:3279: stmts: stmt_or_begin */ -stmts: stmt_or_begin { $$ = [$1].freeze }; -/* upstream parse.y:3284: stmts: stmts terms stmt_or_begin */ -stmts: stmts terms stmt_or_begin { $$ = ($1 + [$3]).freeze }; -/* upstream parse.y:3290: stmt_or_begin: stmt */ -stmt_or_begin: stmt { $$ = $1 }; -/* upstream parse.y:3292: $@5: %empty */ -midrule_5: %empty { $$ = nil }; -/* upstream parse.y:3296: stmt_or_begin: "'BEGIN'" $@5 begin_block */ -stmt_or_begin: keyword_BEGIN midrule_5 begin_block %prec keyword_BEGIN { $$ = $3 }; -/* upstream parse.y:3301: allow_exits: %empty */ -allow_exits: %empty { $$ = nil }; -/* upstream parse.y:3304: k_END: "'END'" lex_ctxt */ -k_END: keyword_END lex_ctxt %prec keyword_END { $$ = $1 }; -/* upstream parse.y:3313: $@6: %empty */ -midrule_6: %empty { $$ = nil }; -/* upstream parse.y:3314: stmt: "'alias'" fitem $@6 fitem */ -stmt: keyword_alias fitem midrule_6 fitem %prec keyword_alias { $$ = $2 }; -/* upstream parse.y:3319: stmt: "'alias'" "global variable" "global variable" */ -stmt: keyword_alias tGVAR tGVAR %prec tGVAR { $$ = $2 }; -/* upstream parse.y:3324: stmt: "'alias'" "global variable" "back reference" */ -stmt: keyword_alias tGVAR tBACK_REF %prec tBACK_REF { $$ = $2 }; -/* upstream parse.y:3332: stmt: "'alias'" "global variable" "numbered reference" */ -stmt: keyword_alias tGVAR tNTH_REF %prec tNTH_REF { $$ = $2 }; -/* upstream parse.y:3341: stmt: "'undef'" undef_list */ -stmt: keyword_undef undef_list %prec keyword_undef { $$ = $2 }; -/* upstream parse.y:3348: stmt: stmt "'if' modifier" expr_value */ -stmt: stmt modifier_if expr_value %prec modifier_if { $$ = @builder.if_node($3, [$1], nil) }; -/* upstream parse.y:3354: stmt: stmt "'unless' modifier" expr_value */ -stmt: stmt modifier_unless expr_value %prec modifier_unless { $$ = @builder.unless_node($3, [$1], nil) }; -/* upstream parse.y:3360: stmt: stmt "'while' modifier" expr_value */ -stmt: stmt modifier_while expr_value %prec modifier_while { $$ = @builder.loop(:while, $3, [$1]) }; -/* upstream parse.y:3371: stmt: stmt "'until' modifier" expr_value */ -stmt: stmt modifier_until expr_value %prec modifier_until { $$ = @builder.loop(:until, $3, [$1]) }; -/* upstream parse.y:3382: stmt: stmt "'rescue' modifier" after_rescue stmt */ -stmt: stmt modifier_rescue after_rescue stmt %prec modifier_rescue { $$ = @builder.rescue_modifier($1, $4) }; -/* upstream parse.y:3391: stmt: k_END block_open compstmt_stmts '}' */ -stmt: k_END block_open compstmt_stmts '}' %prec '}' { $$ = $3 }; -/* upstream parse.y:3402: stmt: command_asgn */ -stmt: command_asgn { $$ = $1 }; -/* upstream parse.y:3404: stmt: mlhs '=' lex_ctxt command_call_value */ -stmt: mlhs '=' lex_ctxt command_call_value %prec '=' { $$ = $1 }; -/* upstream parse.y:2926: asgn_mrhs: lhs '=' lex_ctxt mrhs */ -asgn_mrhs: lhs '=' lex_ctxt mrhs %prec '=' { $$ = $1 }; -/* upstream parse.y:3408: stmt: asgn_mrhs */ -stmt: asgn_mrhs { $$ = $1 }; -/* upstream parse.y:3411: stmt: mlhs '=' lex_ctxt mrhs_arg "'rescue' modifier" after_rescue stmt */ -stmt: mlhs '=' lex_ctxt mrhs_arg modifier_rescue after_rescue stmt %prec modifier_rescue { $$ = @builder.assign($1, @builder.rescue_modifier($4, $7)) }; -/* upstream parse.y:3421: stmt: mlhs '=' lex_ctxt mrhs_arg */ -stmt: mlhs '=' lex_ctxt mrhs_arg %prec '=' { $$ = $1 }; -/* upstream parse.y:3425: stmt: expr */ -stmt: expr { $$ = $1 }; -/* upstream parse.y:3427: stmt: error */ -stmt: YYerror %prec YYerror { $$ = nil }; -/* upstream parse.y:2926: asgn_command_rhs: lhs '=' lex_ctxt command_rhs */ -asgn_command_rhs: lhs '=' lex_ctxt command_rhs %prec '=' { $$ = $1 }; -/* upstream parse.y:3433: command_asgn: asgn_command_rhs */ -command_asgn: asgn_command_rhs { $$ = $1 }; -/* upstream parse.y:3061: op_asgn_command_rhs: var_lhs "operator-assignment" lex_ctxt command_rhs */ -op_asgn_command_rhs: var_lhs tOP_ASGN lex_ctxt command_rhs %prec tOP_ASGN { $$ = $1 }; -/* upstream parse.y:3066: op_asgn_command_rhs: primary_value '[' opt_call_args rbracket "operator-assignment" lex_ctxt command_rhs */ -op_asgn_command_rhs: primary_value '[' opt_call_args rbracket tOP_ASGN lex_ctxt command_rhs %prec tOP_ASGN { $$ = $1 }; -/* upstream parse.y:3071: op_asgn_command_rhs: primary_value call_op "local variable or method" "operator-assignment" lex_ctxt command_rhs */ -op_asgn_command_rhs: primary_value call_op tIDENTIFIER tOP_ASGN lex_ctxt command_rhs %prec tOP_ASGN { $$ = $1 }; -/* upstream parse.y:3076: op_asgn_command_rhs: primary_value call_op "constant" "operator-assignment" lex_ctxt command_rhs */ -op_asgn_command_rhs: primary_value call_op tCONSTANT tOP_ASGN lex_ctxt command_rhs %prec tOP_ASGN { $$ = $1 }; -/* upstream parse.y:3081: op_asgn_command_rhs: primary_value "::" "local variable or method" "operator-assignment" lex_ctxt command_rhs */ -op_asgn_command_rhs: primary_value tCOLON2 tIDENTIFIER tOP_ASGN lex_ctxt command_rhs %prec tOP_ASGN { $$ = $1 }; -/* upstream parse.y:3086: op_asgn_command_rhs: primary_value "::" "constant" "operator-assignment" lex_ctxt command_rhs */ -op_asgn_command_rhs: primary_value tCOLON2 tCONSTANT tOP_ASGN lex_ctxt command_rhs %prec tOP_ASGN { $$ = $1 }; -/* upstream parse.y:3092: op_asgn_command_rhs: ":: at EXPR_BEG" "constant" "operator-assignment" lex_ctxt command_rhs */ -op_asgn_command_rhs: tCOLON3 tCONSTANT tOP_ASGN lex_ctxt command_rhs %prec tOP_ASGN { $$ = $2 }; -/* upstream parse.y:3098: op_asgn_command_rhs: backref "operator-assignment" lex_ctxt command_rhs */ -op_asgn_command_rhs: backref tOP_ASGN lex_ctxt command_rhs %prec tOP_ASGN { $$ = $1 }; -/* upstream parse.y:3434: command_asgn: op_asgn_command_rhs */ -command_asgn: op_asgn_command_rhs { $$ = $1 }; -/* upstream parse.y:2966: def_endless_method_endless_command: defn_head f_opt_paren_args '=' endless_command */ -def_endless_method_endless_command: defn_head f_opt_paren_args '=' endless_command %prec '=' { $$ = $1 }; -/* upstream parse.y:2977: def_endless_method_endless_command: defs_head f_opt_paren_args '=' endless_command */ -def_endless_method_endless_command: defs_head f_opt_paren_args '=' endless_command %prec '=' { $$ = $1 }; -/* upstream parse.y:3435: command_asgn: def_endless_method_endless_command */ -command_asgn: def_endless_method_endless_command { $$ = $1 }; -/* upstream parse.y:3438: endless_command: command */ -endless_command: command { $$ = $1 }; -/* upstream parse.y:3440: endless_command: endless_command "'rescue' modifier" after_rescue arg */ -endless_command: endless_command modifier_rescue after_rescue arg %prec modifier_rescue { $$ = $1 }; -/* upstream parse.y:3445: option_'\n': %empty */ -option_newline: %empty { $$ = nil }; -/* upstream parse.y:3445: option_'\n': '\n' */ -option_newline: '\n' %prec '\n' { $$ = nil }; -/* upstream parse.y:3446: endless_command: "'not'" option_'\n' endless_command */ -endless_command: keyword_not option_newline endless_command %prec keyword_not { $$ = $3 }; -/* upstream parse.y:3452: command_rhs: command_call_value */ -command_rhs: command_call_value %prec tOP_ASGN { $$ = $1 }; -/* upstream parse.y:3454: command_rhs: command_call_value "'rescue' modifier" after_rescue stmt */ -command_rhs: command_call_value modifier_rescue after_rescue stmt %prec modifier_rescue { $$ = $1 }; -/* upstream parse.y:3460: command_rhs: command_asgn */ -command_rhs: command_asgn { $$ = $1 }; -/* upstream parse.y:3463: expr: command_call */ -expr: command_call { $$ = $1 }; -/* upstream parse.y:3465: expr: expr "'and'" expr */ -expr: expr keyword_and expr %prec keyword_and { $$ = @builder.binary(:and, $1, $3) }; -/* upstream parse.y:3470: expr: expr "'or'" expr */ -expr: expr keyword_or expr %prec keyword_or { $$ = @builder.binary(:or, $1, $3) }; -/* upstream parse.y:3475: expr: "'not'" option_'\n' expr */ -expr: keyword_not option_newline expr %prec keyword_not { $$ = @builder.unary(:"!", $3) }; -/* upstream parse.y:3480: expr: '!' command_call */ -expr: '!' command_call %prec '!' { $$ = @builder.unary(:"!", $2) }; -/* upstream parse.y:3485: $@7: %empty */ -midrule_7: %empty { $$ = nil }; -/* upstream parse.y:3490: expr: arg "=>" $@7 p_in_kwarg p_pvtbl p_pktbl p_top_expr_body */ -expr: arg tASSOC midrule_7 p_in_kwarg p_pvtbl p_pktbl p_top_expr_body %prec tASSOC { $$ = $1 }; -/* upstream parse.y:3500: $@8: %empty */ -midrule_8: %empty { $$ = nil }; -/* upstream parse.y:3505: expr: arg "'in'" $@8 p_in_kwarg p_pvtbl p_pktbl p_top_expr_body */ -expr: arg keyword_in midrule_8 p_in_kwarg p_pvtbl p_pktbl p_top_expr_body %prec keyword_in { $$ = $1 }; -/* upstream parse.y:3514: expr: arg */ -expr: arg %prec tLBRACE_ARG { $$ = $1 }; -/* upstream parse.y:3518: def_name: fname */ -def_name: fname { $$ = $1 }; -/* upstream parse.y:3529: defn_head: k_def def_name */ -defn_head: k_def def_name { $$ = $2 }; -/* upstream parse.y:3538: $@9: %empty */ -midrule_9: %empty { $$ = nil }; -/* upstream parse.y:3542: defs_head: k_def singleton dot_or_colon $@9 def_name */ -defs_head: k_def singleton dot_or_colon midrule_9 def_name { $$ = $1 }; -/* upstream parse.y:3161: value_expr_expr: expr */ -value_expr_expr: expr { $$ = $1 }; -/* upstream parse.y:3551: expr_value: value_expr_expr */ -expr_value: value_expr_expr { $$ = $1 }; -/* upstream parse.y:3553: expr_value: error */ -expr_value: YYerror %prec YYerror { $$ = nil }; -/* upstream parse.y:3558: $@10: %empty */ -midrule_10: %empty { $$ = nil }; -/* upstream parse.y:3558: $@11: %empty */ -midrule_11: %empty { $$ = nil }; -/* upstream parse.y:3559: expr_value_do: $@10 expr_value do $@11 */ -expr_value_do: midrule_10 expr_value do midrule_11 { $$ = $2 }; -/* upstream parse.y:3565: command_call: command */ -command_call: command { $$ = $1 }; -/* upstream parse.y:3566: command_call: block_command */ -command_call: block_command { $$ = $1 }; -/* upstream parse.y:3161: value_expr_command_call: command_call */ -value_expr_command_call: command_call { $$ = $1 }; -/* upstream parse.y:3569: command_call_value: value_expr_command_call */ -command_call_value: value_expr_command_call { $$ = $1 }; -/* upstream parse.y:3572: block_command: block_call */ -block_command: block_call { $$ = $1 }; -/* upstream parse.y:3574: block_command: block_call call_op2 operation2 command_args */ -block_command: block_call call_op2 operation2 command_args { $$ = $1 }; -/* upstream parse.y:3581: cmd_brace_block: "{ arg" brace_body '}' */ -cmd_brace_block: tLBRACE_ARG brace_body '}' %prec '}' { $$ = $2 }; -/* upstream parse.y:3589: fcall: "local variable or method" */ -fcall: tIDENTIFIER %prec tIDENTIFIER { $$ = $1 }; -/* upstream parse.y:3589: fcall: "constant" */ -fcall: tCONSTANT %prec tCONSTANT { $$ = $1 }; -/* upstream parse.y:3589: fcall: "method" */ -fcall: tFID %prec tFID { $$ = $1 }; -/* upstream parse.y:3596: command: fcall command_args */ -command: fcall command_args %prec tLOWEST { $$ = @builder.call($1, $2 || []) }; -/* upstream parse.y:3603: command: fcall command_args cmd_brace_block */ -command: fcall command_args cmd_brace_block { $$ = @builder.block_call(@builder.call($1, $2 || []), $3) }; -/* upstream parse.y:3612: command: primary_value call_op operation2 command_args */ -command: primary_value call_op operation2 command_args %prec tLOWEST { $$ = @builder.receiver_call($1, $2, $3, $4) }; -/* upstream parse.y:3617: command: primary_value call_op operation2 command_args cmd_brace_block */ -command: primary_value call_op operation2 command_args cmd_brace_block { $$ = @builder.block_call(@builder.receiver_call($1, $2, $3, $4), $5) }; -/* upstream parse.y:3622: command: primary_value "::" operation2 command_args */ -command: primary_value tCOLON2 operation2 command_args %prec tLOWEST { $$ = @builder.receiver_call($1, :"::", $3, $4) }; -/* upstream parse.y:3627: command: primary_value "::" operation2 command_args cmd_brace_block */ -command: primary_value tCOLON2 operation2 command_args cmd_brace_block %prec tCOLON2 { $$ = @builder.block_call(@builder.receiver_call($1, :"::", $3, $4), $5) }; -/* upstream parse.y:3632: command: primary_value "::" "constant" '{' brace_body '}' */ -command: primary_value tCOLON2 tCONSTANT '{' brace_body '}' %prec '}' { $$ = @builder.block_call(@builder.receiver_call($1, :"::", $3, []), $5) }; -/* upstream parse.y:3638: command: "'super'" command_args */ -command: keyword_super command_args %prec keyword_super { $$ = @builder.call(:super, $2 || []) }; -/* upstream parse.y:3644: command: k_yield command_args */ -command: k_yield command_args { $$ = @builder.call(:yield, $2 || []) }; -/* upstream parse.y:3650: command: k_return call_args */ -command: k_return call_args { $$ = @builder.call(:return, $2 || []) }; -/* upstream parse.y:3655: command: "'break'" call_args */ -command: keyword_break call_args %prec keyword_break { $$ = @builder.call(:break, $2 || []) }; -/* upstream parse.y:3662: command: "'next'" call_args */ -command: keyword_next call_args %prec keyword_next { $$ = @builder.call(:next, $2 || []) }; -/* upstream parse.y:3670: mlhs: mlhs_basic */ -mlhs: mlhs_basic { $$ = $1 }; -/* upstream parse.y:3672: mlhs: "(" mlhs_inner rparen */ -mlhs: tLPAREN mlhs_inner rparen %prec tLPAREN { $$ = $2 }; -/* upstream parse.y:3678: mlhs_inner: mlhs_basic */ -mlhs_inner: mlhs_basic { $$ = $1 }; -/* upstream parse.y:3680: mlhs_inner: "(" mlhs_inner rparen */ -mlhs_inner: tLPAREN mlhs_inner rparen %prec tLPAREN { $$ = $2 }; -/* upstream parse.y:3687: mlhs_basic: mlhs_head */ -mlhs_basic: mlhs_head { $$ = $1 }; -/* upstream parse.y:3692: mlhs_basic: mlhs_head mlhs_item */ -mlhs_basic: mlhs_head mlhs_item { $$ = (Array($1) + [$2]).freeze }; -/* upstream parse.y:3697: mlhs_basic: mlhs_head "*" mlhs_node */ -mlhs_basic: mlhs_head tSTAR mlhs_node %prec tSTAR { $$ = (Array($1) + [$3]).freeze }; -/* upstream parse.y:3048: mlhs_items_mlhs_item: mlhs_item */ -mlhs_items_mlhs_item: mlhs_item { $$ = $1 }; -/* upstream parse.y:3053: mlhs_items_mlhs_item: mlhs_items_mlhs_item ',' mlhs_item */ -mlhs_items_mlhs_item: mlhs_items_mlhs_item ',' mlhs_item %prec ',' { $$ = (Array($1) + [$3]).freeze }; -/* upstream parse.y:3702: mlhs_basic: mlhs_head "*" mlhs_node ',' mlhs_items_mlhs_item */ -mlhs_basic: mlhs_head tSTAR mlhs_node ',' mlhs_items_mlhs_item %prec ',' { $$ = (Array($1) + [$3] + Array($5)).freeze }; -/* upstream parse.y:3707: mlhs_basic: mlhs_head "*" */ -mlhs_basic: mlhs_head tSTAR %prec tSTAR { $$ = $1 }; -/* upstream parse.y:3712: mlhs_basic: mlhs_head "*" ',' mlhs_items_mlhs_item */ -mlhs_basic: mlhs_head tSTAR ',' mlhs_items_mlhs_item %prec ',' { $$ = $1 }; -/* upstream parse.y:3717: mlhs_basic: "*" mlhs_node */ -mlhs_basic: tSTAR mlhs_node %prec tSTAR { $$ = $2 }; -/* upstream parse.y:3722: mlhs_basic: "*" mlhs_node ',' mlhs_items_mlhs_item */ -mlhs_basic: tSTAR mlhs_node ',' mlhs_items_mlhs_item %prec ',' { $$ = $2 }; -/* upstream parse.y:3727: mlhs_basic: "*" */ -mlhs_basic: tSTAR %prec tSTAR { $$ = nil }; -/* upstream parse.y:3732: mlhs_basic: "*" ',' mlhs_items_mlhs_item */ -mlhs_basic: tSTAR ',' mlhs_items_mlhs_item %prec ',' { $$ = $3 }; -/* upstream parse.y:3738: mlhs_item: mlhs_node */ -mlhs_item: mlhs_node { $$ = $1 }; -/* upstream parse.y:3740: mlhs_item: "(" mlhs_inner rparen */ -mlhs_item: tLPAREN mlhs_inner rparen %prec tLPAREN { $$ = $2 }; -/* upstream parse.y:3747: mlhs_head: mlhs_item ',' */ -mlhs_head: mlhs_item ',' %prec ',' { $$ = [$1].freeze }; -/* upstream parse.y:3752: mlhs_head: mlhs_head mlhs_item ',' */ -mlhs_head: mlhs_head mlhs_item ',' %prec ',' { $$ = ($1 + [$2]).freeze }; -/* upstream parse.y:3760: mlhs_node: user_variable */ -mlhs_node: user_variable { $$ = $1 }; -/* upstream parse.y:3760: mlhs_node: keyword_variable */ -mlhs_node: keyword_variable { $$ = $1 }; -/* upstream parse.y:3765: mlhs_node: primary_value '[' opt_call_args rbracket */ -mlhs_node: primary_value '[' opt_call_args rbracket %prec '[' { $$ = @builder.index($1, $3 || []) }; -/* upstream parse.y:3770: mlhs_node: primary_value call_op "local variable or method" */ -mlhs_node: primary_value call_op tIDENTIFIER %prec tIDENTIFIER { $$ = $1 }; -/* upstream parse.y:3770: mlhs_node: primary_value call_op "constant" */ -mlhs_node: primary_value call_op tCONSTANT %prec tCONSTANT { $$ = $1 }; -/* upstream parse.y:3776: mlhs_node: primary_value "::" "local variable or method" */ -mlhs_node: primary_value tCOLON2 tIDENTIFIER %prec tIDENTIFIER { $$ = $1 }; -/* upstream parse.y:3781: mlhs_node: primary_value "::" "constant" */ -mlhs_node: primary_value tCOLON2 tCONSTANT %prec tCONSTANT { $$ = @builder.variable(:constant, $3) }; -/* upstream parse.y:3786: mlhs_node: ":: at EXPR_BEG" "constant" */ -mlhs_node: tCOLON3 tCONSTANT %prec tCONSTANT { $$ = @builder.variable(:constant, $2) }; -/* upstream parse.y:3791: mlhs_node: backref */ -mlhs_node: backref { $$ = $1 }; -/* upstream parse.y:3799: lhs: user_variable */ -lhs: user_variable { $$ = @builder.declare_local($1) }; -/* upstream parse.y:3799: lhs: keyword_variable */ -lhs: keyword_variable { $$ = $1 }; -/* upstream parse.y:3804: lhs: primary_value '[' opt_call_args rbracket */ -lhs: primary_value '[' opt_call_args rbracket %prec '[' { $$ = @builder.index($1, $3 || []) }; -/* upstream parse.y:3809: lhs: primary_value call_op "local variable or method" */ -lhs: primary_value call_op tIDENTIFIER %prec tIDENTIFIER { $$ = $1 }; -/* upstream parse.y:3809: lhs: primary_value call_op "constant" */ -lhs: primary_value call_op tCONSTANT %prec tCONSTANT { $$ = $1 }; -/* upstream parse.y:3814: lhs: primary_value "::" "local variable or method" */ -lhs: primary_value tCOLON2 tIDENTIFIER %prec tIDENTIFIER { $$ = $1 }; -/* upstream parse.y:3819: lhs: primary_value "::" "constant" */ -lhs: primary_value tCOLON2 tCONSTANT %prec tCONSTANT { $$ = @builder.variable(:constant, $3) }; -/* upstream parse.y:3824: lhs: ":: at EXPR_BEG" "constant" */ -lhs: tCOLON3 tCONSTANT %prec tCONSTANT { $$ = @builder.variable(:constant, $2) }; -/* upstream parse.y:3829: lhs: backref */ -lhs: backref { $$ = $1 }; -/* upstream parse.y:3837: cname: "local variable or method" */ -cname: tIDENTIFIER %prec tIDENTIFIER { $$ = $1 }; -/* upstream parse.y:3844: cname: "constant" */ -cname: tCONSTANT %prec tCONSTANT { $$ = $1 }; -/* upstream parse.y:3848: cpath: ":: at EXPR_BEG" cname */ -cpath: tCOLON3 cname %prec tCOLON3 { $$ = $2 }; -/* upstream parse.y:3853: cpath: cname */ -cpath: cname { $$ = $1 }; -/* upstream parse.y:3858: cpath: primary_value "::" cname */ -cpath: primary_value tCOLON2 cname %prec tCOLON2 { $$ = $3 }; -/* upstream parse.y:3864: fname: "local variable or method" */ -fname: tIDENTIFIER %prec tIDENTIFIER { $$ = $1 }; -/* upstream parse.y:3864: fname: "constant" */ -fname: tCONSTANT %prec tCONSTANT { $$ = $1 }; -/* upstream parse.y:3864: fname: "method" */ -fname: tFID %prec tFID { $$ = $1 }; -/* upstream parse.y:3866: fname: op */ -fname: op { $$ = $1 }; -/* upstream parse.y:3870: fname: reswords */ -fname: reswords { $$ = $1 }; -/* upstream parse.y:3874: fitem: fname */ -fitem: fname { $$ = $1 }; -/* upstream parse.y:3878: fitem: symbol */ -fitem: symbol { $$ = $1 }; -/* upstream parse.y:3882: undef_list: fitem */ -undef_list: fitem { $$ = $1 }; -/* upstream parse.y:3886: $@12: %empty */ -midrule_12: %empty { $$ = nil }; -/* upstream parse.y:3887: undef_list: undef_list ',' $@12 fitem */ -undef_list: undef_list ',' midrule_12 fitem %prec ',' { $$ = ($1 + [$4]).freeze }; -/* upstream parse.y:3894: op: '|' */ -op: '|' %prec '|' { $$ = @builder.operator("|") }; -/* upstream parse.y:3895: op: '^' */ -op: '^' %prec '^' { $$ = @builder.operator("^") }; -/* upstream parse.y:3896: op: '&' */ -op: '&' %prec '&' { $$ = @builder.operator("&") }; -/* upstream parse.y:3897: op: "<=>" */ -op: tCMP %prec tCMP { $$ = @builder.operator("<=>") }; -/* upstream parse.y:3898: op: "==" */ -op: tEQ %prec tEQ { $$ = @builder.operator("==") }; -/* upstream parse.y:3899: op: "===" */ -op: tEQQ %prec tEQQ { $$ = @builder.operator("===") }; -/* upstream parse.y:3900: op: "=~" */ -op: tMATCH %prec tMATCH { $$ = @builder.operator("=~") }; -/* upstream parse.y:3901: op: "!~" */ -op: tNMATCH %prec tNMATCH { $$ = @builder.operator("!~") }; -/* upstream parse.y:3902: op: '>' */ -op: '>' %prec '>' { $$ = @builder.operator(">") }; -/* upstream parse.y:3903: op: ">=" */ -op: tGEQ %prec tGEQ { $$ = @builder.operator(">=") }; -/* upstream parse.y:3904: op: '<' */ -op: '<' %prec '<' { $$ = @builder.operator("<") }; -/* upstream parse.y:3905: op: "<=" */ -op: tLEQ %prec tLEQ { $$ = @builder.operator("<=") }; -/* upstream parse.y:3906: op: "!=" */ -op: tNEQ %prec tNEQ { $$ = @builder.operator("!=") }; -/* upstream parse.y:3907: op: "<<" */ -op: tLSHFT %prec tLSHFT { $$ = @builder.operator("<<") }; -/* upstream parse.y:3908: op: ">>" */ -op: tRSHFT %prec tRSHFT { $$ = @builder.operator(">>") }; -/* upstream parse.y:3909: op: '+' */ -op: '+' %prec '+' { $$ = @builder.operator("+") }; -/* upstream parse.y:3910: op: '-' */ -op: '-' %prec '-' { $$ = @builder.operator("-") }; -/* upstream parse.y:3911: op: '*' */ -op: '*' %prec '*' { $$ = @builder.operator("*") }; -/* upstream parse.y:3912: op: "*" */ -op: tSTAR %prec tSTAR { $$ = @builder.operator("*") }; -/* upstream parse.y:3913: op: '/' */ -op: '/' %prec '/' { $$ = @builder.operator("/") }; -/* upstream parse.y:3914: op: '%' */ -op: '%' %prec '%' { $$ = @builder.operator("%") }; -/* upstream parse.y:3915: op: "**" */ -op: tPOW %prec tPOW { $$ = @builder.operator("**") }; -/* upstream parse.y:3916: op: "**arg" */ -op: tDSTAR %prec tDSTAR { $$ = @builder.operator("**") }; -/* upstream parse.y:3917: op: '!' */ -op: '!' %prec '!' { $$ = @builder.operator("!") }; -/* upstream parse.y:3918: op: '~' */ -op: '~' %prec '~' { $$ = @builder.operator("~") }; -/* upstream parse.y:3919: op: "unary+" */ -op: tUPLUS %prec tUPLUS { $$ = @builder.operator("+@") }; -/* upstream parse.y:3920: op: "unary-" */ -op: tUMINUS %prec tUMINUS { $$ = @builder.operator("-@") }; -/* upstream parse.y:3921: op: "[]" */ -op: tAREF %prec tAREF { $$ = @builder.operator("[]") }; -/* upstream parse.y:3922: op: "[]=" */ -op: tASET %prec tASET { $$ = @builder.operator("[]=") }; -/* upstream parse.y:3923: op: '`' */ -op: '`' %prec '`' { $$ = @builder.operator("`") }; -/* upstream parse.y:3926: reswords: "'__LINE__'" */ -reswords: keyword__LINE__ %prec keyword__LINE__ { $$ = @builder.reserved_word("__LINE__") }; -/* upstream parse.y:3926: reswords: "'__FILE__'" */ -reswords: keyword__FILE__ %prec keyword__FILE__ { $$ = @builder.reserved_word("__FILE__") }; -/* upstream parse.y:3926: reswords: "'__ENCODING__'" */ -reswords: keyword__ENCODING__ %prec keyword__ENCODING__ { $$ = @builder.reserved_word("__ENCODING__") }; -/* upstream parse.y:3927: reswords: "'BEGIN'" */ -reswords: keyword_BEGIN %prec keyword_BEGIN { $$ = @builder.reserved_word("BEGIN") }; -/* upstream parse.y:3927: reswords: "'END'" */ -reswords: keyword_END %prec keyword_END { $$ = @builder.reserved_word("END") }; -/* upstream parse.y:3928: reswords: "'alias'" */ -reswords: keyword_alias %prec keyword_alias { $$ = @builder.reserved_word("alias") }; -/* upstream parse.y:3928: reswords: "'and'" */ -reswords: keyword_and %prec keyword_and { $$ = @builder.reserved_word("and") }; -/* upstream parse.y:3928: reswords: "'begin'" */ -reswords: keyword_begin %prec keyword_begin { $$ = @builder.reserved_word("begin") }; -/* upstream parse.y:3929: reswords: "'break'" */ -reswords: keyword_break %prec keyword_break { $$ = @builder.reserved_word("break") }; -/* upstream parse.y:3929: reswords: "'case'" */ -reswords: keyword_case %prec keyword_case { $$ = @builder.reserved_word("case") }; -/* upstream parse.y:3929: reswords: "'class'" */ -reswords: keyword_class %prec keyword_class { $$ = @builder.reserved_word("class") }; -/* upstream parse.y:3929: reswords: "'def'" */ -reswords: keyword_def %prec keyword_def { $$ = @builder.reserved_word("def") }; -/* upstream parse.y:3930: reswords: "'defined?'" */ -reswords: keyword_defined %prec keyword_defined { $$ = @builder.reserved_word("defined?") }; -/* upstream parse.y:3930: reswords: "'do'" */ -reswords: keyword_do %prec keyword_do { $$ = @builder.reserved_word("do") }; -/* upstream parse.y:3930: reswords: "'else'" */ -reswords: keyword_else %prec keyword_else { $$ = @builder.reserved_word("else") }; -/* upstream parse.y:3930: reswords: "'elsif'" */ -reswords: keyword_elsif %prec keyword_elsif { $$ = @builder.reserved_word("elsif") }; -/* upstream parse.y:3931: reswords: "'end'" */ -reswords: keyword_end %prec keyword_end { $$ = @builder.reserved_word("end") }; -/* upstream parse.y:3931: reswords: "'ensure'" */ -reswords: keyword_ensure %prec keyword_ensure { $$ = @builder.reserved_word("ensure") }; -/* upstream parse.y:3931: reswords: "'false'" */ -reswords: keyword_false %prec keyword_false { $$ = @builder.reserved_word("false") }; -/* upstream parse.y:3932: reswords: "'for'" */ -reswords: keyword_for %prec keyword_for { $$ = @builder.reserved_word("for") }; -/* upstream parse.y:3932: reswords: "'in'" */ -reswords: keyword_in %prec keyword_in { $$ = @builder.reserved_word("in") }; -/* upstream parse.y:3932: reswords: "'module'" */ -reswords: keyword_module %prec keyword_module { $$ = @builder.reserved_word("module") }; -/* upstream parse.y:3932: reswords: "'next'" */ -reswords: keyword_next %prec keyword_next { $$ = @builder.reserved_word("next") }; -/* upstream parse.y:3933: reswords: "'nil'" */ -reswords: keyword_nil %prec keyword_nil { $$ = @builder.reserved_word("nil") }; -/* upstream parse.y:3933: reswords: "'not'" */ -reswords: keyword_not %prec keyword_not { $$ = @builder.reserved_word("not") }; -/* upstream parse.y:3933: reswords: "'or'" */ -reswords: keyword_or %prec keyword_or { $$ = @builder.reserved_word("or") }; -/* upstream parse.y:3933: reswords: "'redo'" */ -reswords: keyword_redo %prec keyword_redo { $$ = @builder.reserved_word("redo") }; -/* upstream parse.y:3934: reswords: "'rescue'" */ -reswords: keyword_rescue %prec keyword_rescue { $$ = @builder.reserved_word("rescue") }; -/* upstream parse.y:3934: reswords: "'retry'" */ -reswords: keyword_retry %prec keyword_retry { $$ = @builder.reserved_word("retry") }; -/* upstream parse.y:3934: reswords: "'return'" */ -reswords: keyword_return %prec keyword_return { $$ = @builder.reserved_word("return") }; -/* upstream parse.y:3934: reswords: "'self'" */ -reswords: keyword_self %prec keyword_self { $$ = @builder.reserved_word("self") }; -/* upstream parse.y:3935: reswords: "'super'" */ -reswords: keyword_super %prec keyword_super { $$ = @builder.reserved_word("super") }; -/* upstream parse.y:3935: reswords: "'then'" */ -reswords: keyword_then %prec keyword_then { $$ = @builder.reserved_word("then") }; -/* upstream parse.y:3935: reswords: "'true'" */ -reswords: keyword_true %prec keyword_true { $$ = @builder.reserved_word("true") }; -/* upstream parse.y:3935: reswords: "'undef'" */ -reswords: keyword_undef %prec keyword_undef { $$ = @builder.reserved_word("undef") }; -/* upstream parse.y:3936: reswords: "'when'" */ -reswords: keyword_when %prec keyword_when { $$ = @builder.reserved_word("when") }; -/* upstream parse.y:3936: reswords: "'yield'" */ -reswords: keyword_yield %prec keyword_yield { $$ = @builder.reserved_word("yield") }; -/* upstream parse.y:3936: reswords: "'if'" */ -reswords: keyword_if %prec keyword_if { $$ = @builder.reserved_word("if") }; -/* upstream parse.y:3936: reswords: "'unless'" */ -reswords: keyword_unless %prec keyword_unless { $$ = @builder.reserved_word("unless") }; -/* upstream parse.y:3937: reswords: "'while'" */ -reswords: keyword_while %prec keyword_while { $$ = @builder.reserved_word("while") }; -/* upstream parse.y:3937: reswords: "'until'" */ -reswords: keyword_until %prec keyword_until { $$ = @builder.reserved_word("until") }; -/* upstream parse.y:2926: asgn_arg_rhs: lhs '=' lex_ctxt arg_rhs */ -asgn_arg_rhs: lhs '=' lex_ctxt arg_rhs %prec '=' { $$ = @builder.assign($1, $4) }; -/* upstream parse.y:3940: arg: asgn_arg_rhs */ -arg: asgn_arg_rhs { $$ = $1 }; -/* upstream parse.y:3061: op_asgn_arg_rhs: var_lhs "operator-assignment" lex_ctxt arg_rhs */ -op_asgn_arg_rhs: var_lhs tOP_ASGN lex_ctxt arg_rhs %prec tOP_ASGN { $$ = $1 }; -/* upstream parse.y:3066: op_asgn_arg_rhs: primary_value '[' opt_call_args rbracket "operator-assignment" lex_ctxt arg_rhs */ -op_asgn_arg_rhs: primary_value '[' opt_call_args rbracket tOP_ASGN lex_ctxt arg_rhs %prec tOP_ASGN { $$ = $1 }; -/* upstream parse.y:3071: op_asgn_arg_rhs: primary_value call_op "local variable or method" "operator-assignment" lex_ctxt arg_rhs */ -op_asgn_arg_rhs: primary_value call_op tIDENTIFIER tOP_ASGN lex_ctxt arg_rhs %prec tOP_ASGN { $$ = $1 }; -/* upstream parse.y:3076: op_asgn_arg_rhs: primary_value call_op "constant" "operator-assignment" lex_ctxt arg_rhs */ -op_asgn_arg_rhs: primary_value call_op tCONSTANT tOP_ASGN lex_ctxt arg_rhs %prec tOP_ASGN { $$ = $1 }; -/* upstream parse.y:3081: op_asgn_arg_rhs: primary_value "::" "local variable or method" "operator-assignment" lex_ctxt arg_rhs */ -op_asgn_arg_rhs: primary_value tCOLON2 tIDENTIFIER tOP_ASGN lex_ctxt arg_rhs %prec tOP_ASGN { $$ = $1 }; -/* upstream parse.y:3086: op_asgn_arg_rhs: primary_value "::" "constant" "operator-assignment" lex_ctxt arg_rhs */ -op_asgn_arg_rhs: primary_value tCOLON2 tCONSTANT tOP_ASGN lex_ctxt arg_rhs %prec tOP_ASGN { $$ = $1 }; -/* upstream parse.y:3092: op_asgn_arg_rhs: ":: at EXPR_BEG" "constant" "operator-assignment" lex_ctxt arg_rhs */ -op_asgn_arg_rhs: tCOLON3 tCONSTANT tOP_ASGN lex_ctxt arg_rhs %prec tOP_ASGN { $$ = nil }; -/* upstream parse.y:3098: op_asgn_arg_rhs: backref "operator-assignment" lex_ctxt arg_rhs */ -op_asgn_arg_rhs: backref tOP_ASGN lex_ctxt arg_rhs %prec tOP_ASGN { $$ = $1 }; -/* upstream parse.y:3941: arg: op_asgn_arg_rhs */ -arg: op_asgn_arg_rhs { $$ = $1 }; -/* upstream parse.y:3120: range_expr_arg: arg ".." arg */ -range_expr_arg: arg tDOT2 arg %prec tDOT2 { $$ = @builder.range(:"..", $1, $3) }; -/* upstream parse.y:3127: range_expr_arg: arg "..." arg */ -range_expr_arg: arg tDOT3 arg %prec tDOT3 { $$ = @builder.range(:"...", $1, $3) }; -/* upstream parse.y:3134: range_expr_arg: arg ".." */ -range_expr_arg: arg tDOT2 %prec tDOT2 { $$ = @builder.range(:"..", $1, nil) }; -/* upstream parse.y:3140: range_expr_arg: arg "..." */ -range_expr_arg: arg tDOT3 %prec tDOT3 { $$ = @builder.range(:"...", $1, nil) }; -/* upstream parse.y:3146: range_expr_arg: "(.." arg */ -range_expr_arg: tBDOT2 arg %prec tBDOT2 { $$ = @builder.range(:"..", nil, $2) }; -/* upstream parse.y:3152: range_expr_arg: "(..." arg */ -range_expr_arg: tBDOT3 arg %prec tBDOT3 { $$ = @builder.range(:"...", nil, $2) }; -/* upstream parse.y:3942: arg: range_expr_arg */ -arg: range_expr_arg { $$ = $1 }; -/* upstream parse.y:3944: arg: arg '+' arg */ -arg: arg '+' arg %prec '+' { $$ = @builder.binary(:+, $1, $3) }; -/* upstream parse.y:3949: arg: arg '-' arg */ -arg: arg '-' arg %prec '-' { $$ = @builder.binary(:-, $1, $3) }; -/* upstream parse.y:3954: arg: arg '*' arg */ -arg: arg '*' arg %prec '*' { $$ = @builder.binary(:*, $1, $3) }; -/* upstream parse.y:3959: arg: arg '/' arg */ -arg: arg '/' arg %prec '/' { $$ = @builder.binary(:/, $1, $3) }; -/* upstream parse.y:3964: arg: arg '%' arg */ -arg: arg '%' arg %prec '%' { $$ = @builder.binary(:%, $1, $3) }; -/* upstream parse.y:3969: arg: arg "**" arg */ -arg: arg tPOW arg %prec tPOW { $$ = @builder.binary(:"**", $1, $3) }; -/* upstream parse.y:3974: arg: tUMINUS_NUM simple_numeric "**" arg */ -arg: tUMINUS_NUM simple_numeric tPOW arg %prec tPOW { $$ = @builder.binary(:"**", @builder.unary(:-@, $2), $4) }; -/* upstream parse.y:3979: arg: "unary+" arg */ -arg: tUPLUS arg %prec tUPLUS { $$ = @builder.unary(:+, $2) }; -/* upstream parse.y:3984: arg: "unary-" arg */ -arg: tUMINUS arg %prec tUMINUS { $$ = @builder.unary(:-, $2) }; -/* upstream parse.y:3989: arg: arg '|' arg */ -arg: arg '|' arg %prec '|' { $$ = @builder.binary(:"|", $1, $3) }; -/* upstream parse.y:3994: arg: arg '^' arg */ -arg: arg '^' arg %prec '^' { $$ = @builder.binary(:"^", $1, $3) }; -/* upstream parse.y:3999: arg: arg '&' arg */ -arg: arg '&' arg %prec '&' { $$ = @builder.binary(:"&", $1, $3) }; -/* upstream parse.y:4004: arg: arg "<=>" arg */ -arg: arg tCMP arg %prec tCMP { $$ = @builder.binary(:"<=>", $1, $3) }; -/* upstream parse.y:4008: arg: rel_expr */ -arg: rel_expr %prec tCMP { $$ = $1 }; -/* upstream parse.y:4010: arg: arg "==" arg */ -arg: arg tEQ arg %prec tEQ { $$ = @builder.binary(:"==", $1, $3) }; -/* upstream parse.y:4015: arg: arg "===" arg */ -arg: arg tEQQ arg %prec tEQQ { $$ = @builder.binary(:"===", $1, $3) }; -/* upstream parse.y:4020: arg: arg "!=" arg */ -arg: arg tNEQ arg %prec tNEQ { $$ = @builder.binary(:"!=", $1, $3) }; -/* upstream parse.y:4025: arg: arg "=~" arg */ -arg: arg tMATCH arg %prec tMATCH { $$ = @builder.binary(:"=~", $1, $3) }; -/* upstream parse.y:4030: arg: arg "!~" arg */ -arg: arg tNMATCH arg %prec tNMATCH { $$ = @builder.binary(:"!~", $1, $3) }; -/* upstream parse.y:4035: arg: '!' arg */ -arg: '!' arg %prec '!' { $$ = @builder.unary(:"!", $2) }; -/* upstream parse.y:4040: arg: '~' arg */ -arg: '~' arg %prec '~' { $$ = @builder.unary(:"~", $2) }; -/* upstream parse.y:4045: arg: arg "<<" arg */ -arg: arg tLSHFT arg %prec tLSHFT { $$ = @builder.binary(:"<<", $1, $3) }; -/* upstream parse.y:4050: arg: arg ">>" arg */ -arg: arg tRSHFT arg %prec tRSHFT { $$ = @builder.binary(:">>", $1, $3) }; -/* upstream parse.y:4055: arg: arg "&&" arg */ -arg: arg tANDOP arg %prec tANDOP { $$ = @builder.binary(:"&&", $1, $3) }; -/* upstream parse.y:4060: arg: arg "||" arg */ -arg: arg tOROP arg %prec tOROP { $$ = @builder.binary(:"||", $1, $3) }; -/* upstream parse.y:4065: arg: "'defined?'" option_'\n' begin_defined arg */ -arg: keyword_defined option_newline begin_defined arg %prec keyword_defined { $$ = @builder.call(:defined, [$4]) }; -/* upstream parse.y:2966: def_endless_method_endless_arg: defn_head f_opt_paren_args '=' endless_arg */ -def_endless_method_endless_arg: defn_head f_opt_paren_args '=' endless_arg %prec '=' { $$ = $1 }; -/* upstream parse.y:2977: def_endless_method_endless_arg: defs_head f_opt_paren_args '=' endless_arg */ -def_endless_method_endless_arg: defs_head f_opt_paren_args '=' endless_arg %prec '=' { $$ = $1 }; -/* upstream parse.y:4071: arg: def_endless_method_endless_arg */ -arg: def_endless_method_endless_arg { $$ = $1 }; -/* upstream parse.y:4072: arg: ternary */ -arg: ternary { $$ = $1 }; -/* upstream parse.y:4073: arg: primary */ -arg: primary { $$ = $1 }; -/* upstream parse.y:4077: ternary: arg '?' arg option_'\n' ':' arg */ -ternary: arg '?' arg option_newline ':' arg %prec ':' { $$ = @builder.ternary($1, $3, $6) }; -/* upstream parse.y:4085: endless_arg: arg */ -endless_arg: arg %prec modifier_rescue { $$ = $1 }; -/* upstream parse.y:4087: endless_arg: endless_arg "'rescue' modifier" after_rescue arg */ -endless_arg: endless_arg modifier_rescue after_rescue arg %prec modifier_rescue { $$ = @builder.rescue_modifier($1, $4) }; -/* upstream parse.y:4093: endless_arg: "'not'" option_'\n' endless_arg */ -endless_arg: keyword_not option_newline endless_arg %prec keyword_not { $$ = @builder.unary(:"!", $3) }; -/* upstream parse.y:4099: relop: '>' */ -relop: '>' %prec '>' { $$ = @builder.operator(">") }; -/* upstream parse.y:4100: relop: '<' */ -relop: '<' %prec '<' { $$ = @builder.operator("<") }; -/* upstream parse.y:4101: relop: ">=" */ -relop: tGEQ %prec tGEQ { $$ = @builder.operator(">=") }; -/* upstream parse.y:4102: relop: "<=" */ -relop: tLEQ %prec tLEQ { $$ = @builder.operator("<=") }; -/* upstream parse.y:4106: rel_expr: arg relop arg */ -rel_expr: arg relop arg %prec '>' { $$ = @builder.binary($2, $1, $3) }; -/* upstream parse.y:4111: rel_expr: rel_expr relop arg */ -rel_expr: rel_expr relop arg %prec '>' { $$ = @builder.binary($2, $1, $3) }; -/* upstream parse.y:4119: lex_ctxt: none */ -lex_ctxt: none { $$ = nil }; -/* upstream parse.y:4125: begin_defined: lex_ctxt */ -begin_defined: lex_ctxt { $$ = nil }; -/* upstream parse.y:4132: after_rescue: lex_ctxt */ -after_rescue: lex_ctxt { $$ = nil }; -/* upstream parse.y:3161: value_expr_arg: arg */ -value_expr_arg: arg { $$ = $1 }; -/* upstream parse.y:4138: arg_value: value_expr_arg */ -arg_value: value_expr_arg { $$ = $1 }; -/* upstream parse.y:4141: aref_args: none */ -aref_args: none { $$ = [].freeze }; -/* upstream parse.y:4142: aref_args: args trailer */ -aref_args: args trailer { $$ = $1 }; -/* upstream parse.y:4144: aref_args: args ',' assocs trailer */ -aref_args: args ',' assocs trailer %prec ',' { $$ = ($1 + $3).freeze }; -/* upstream parse.y:4149: aref_args: assocs trailer */ -aref_args: assocs trailer { $$ = $1 }; -/* upstream parse.y:4156: arg_rhs: arg */ -arg_rhs: arg %prec tOP_ASGN { $$ = $1 }; -/* upstream parse.y:4161: arg_rhs: arg "'rescue' modifier" after_rescue arg */ -arg_rhs: arg modifier_rescue after_rescue arg %prec modifier_rescue { $$ = @builder.rescue_modifier($1, $4) }; -/* upstream parse.y:4170: paren_args: '(' opt_call_args rparen */ -paren_args: '(' opt_call_args rparen %prec '(' { $$ = $2 }; -/* upstream parse.y:4175: paren_args: '(' args ',' args_forward rparen */ -paren_args: '(' args ',' args_forward rparen %prec ',' { $$ = ($2 + [$4]).freeze }; -/* upstream parse.y:4185: paren_args: '(' args_forward rparen */ -paren_args: '(' args_forward rparen %prec '(' { $$ = $2 }; -/* upstream parse.y:4196: opt_paren_args: none */ -opt_paren_args: none { $$ = nil }; -/* upstream parse.y:4198: opt_paren_args: paren_args */ -opt_paren_args: paren_args { $$ = $1 }; -/* upstream parse.y:4203: opt_call_args: none */ -opt_call_args: none { $$ = nil }; -/* upstream parse.y:4204: opt_call_args: call_args */ -opt_call_args: call_args { $$ = $1 }; -/* upstream parse.y:4205: opt_call_args: args ',' */ -opt_call_args: args ',' %prec ',' { $$ = $1 }; -/* upstream parse.y:4207: opt_call_args: args ',' assocs ',' */ -opt_call_args: args ',' assocs ',' %prec ',' { $$ = $1 }; -/* upstream parse.y:4212: opt_call_args: assocs ',' */ -opt_call_args: assocs ',' %prec ',' { $$ = $1 }; -/* upstream parse.y:3161: value_expr_command: command */ -value_expr_command: command %prec tLBRACE_ARG { $$ = $1 }; -/* upstream parse.y:4219: call_args: value_expr_command */ -call_args: value_expr_command { $$ = $1 }; -/* upstream parse.y:4224: call_args: def_endless_method_endless_command */ -call_args: def_endless_method_endless_command { $$ = $1 }; -/* upstream parse.y:4229: call_args: args opt_block_arg */ -call_args: args opt_block_arg { $$ = $1 }; -/* upstream parse.y:4234: call_args: assocs opt_block_arg */ -call_args: assocs opt_block_arg { $$ = $1 }; -/* upstream parse.y:4240: call_args: args ',' assocs opt_block_arg */ -call_args: args ',' assocs opt_block_arg %prec ',' { $$ = (($1 || []) + ($3 || [])).freeze }; -/* upstream parse.y:4245: call_args: block_arg */ -call_args: block_arg { $$ = $1 }; -/* upstream parse.y:4249: $@13: %empty */ -midrule_13: %empty { $$ = nil }; -/* upstream parse.y:4267: command_args: $@13 call_args */ -command_args: midrule_13 call_args { $$ = $2 }; -/* upstream parse.y:4288: block_arg: "&" arg_value */ -block_arg: tAMPER arg_value %prec tAMPER { $$ = $2 }; -/* upstream parse.y:4293: block_arg: "&" */ -block_arg: tAMPER %prec tAMPER { $$ = nil }; -/* upstream parse.y:4301: opt_block_arg: ',' block_arg */ -opt_block_arg: ',' block_arg %prec ',' { $$ = $2 }; -/* upstream parse.y:4306: opt_block_arg: none */ -opt_block_arg: none { $$ = nil }; -/* upstream parse.y:4314: args: arg_value */ -args: arg_value { $$ = [$1].freeze }; -/* upstream parse.y:4319: args: arg_splat */ -args: arg_splat { $$ = $1 }; -/* upstream parse.y:4324: args: args ',' arg_value */ -args: args ',' arg_value %prec ',' { $$ = @builder.append_arguments($1, $3) }; -/* upstream parse.y:4329: args: args ',' arg_splat */ -args: args ',' arg_splat %prec ',' { $$ = @builder.append_arguments($1, $3) }; -/* upstream parse.y:4337: arg_splat: "*" arg_value */ -arg_splat: tSTAR arg_value %prec tSTAR { $$ = [$2].freeze }; -/* upstream parse.y:4342: arg_splat: "*" */ -arg_splat: tSTAR %prec tSTAR { $$ = nil }; -/* upstream parse.y:4350: mrhs_arg: mrhs */ -mrhs_arg: mrhs { $$ = $1 }; -/* upstream parse.y:4351: mrhs_arg: arg_value */ -mrhs_arg: arg_value { $$ = $1 }; -/* upstream parse.y:4356: mrhs: args ',' arg_value */ -mrhs: args ',' arg_value %prec ',' { $$ = ($1 + [$3]).freeze }; -/* upstream parse.y:4361: mrhs: args ',' "*" arg_value */ -mrhs: args ',' tSTAR arg_value %prec tSTAR { $$ = ($1 + [$4]).freeze }; -/* upstream parse.y:4366: mrhs: "*" arg_value */ -mrhs: tSTAR arg_value %prec tSTAR { $$ = [$2].freeze }; -/* upstream parse.y:4383: primary: literal */ -primary: literal { $$ = $1 }; -/* upstream parse.y:4383: primary: strings */ -primary: strings { $$ = $1 }; -/* upstream parse.y:4383: primary: xstring */ -primary: xstring { $$ = $1 }; -/* upstream parse.y:4383: primary: regexp */ -primary: regexp { $$ = $1 }; -/* upstream parse.y:4383: primary: words */ -primary: words { $$ = @builder.word_array($1) }; -/* upstream parse.y:4383: primary: qwords */ -primary: qwords { $$ = @builder.word_array($1, symbols: true) }; -/* upstream parse.y:4383: primary: symbols */ -primary: symbols { $$ = $1 }; -/* upstream parse.y:4383: primary: qsymbols */ -primary: qsymbols { $$ = $1 }; -/* upstream parse.y:4384: primary: var_ref */ -primary: var_ref { $$ = $1 }; -/* upstream parse.y:4385: primary: backref */ -primary: backref { $$ = $1 }; -/* upstream parse.y:4387: primary: "method" */ -primary: tFID %prec tFID { $$ = @builder.reserved_word("method") }; -/* upstream parse.y:4392: $@14: %empty */ -midrule_14: %empty { $$ = nil }; -/* upstream parse.y:4397: primary: k_begin $@14 bodystmt k_end */ -primary: k_begin midrule_14 bodystmt k_end { $$ = @builder.body_nodes($3) }; -/* upstream parse.y:4404: $@15: %empty */ -midrule_15: %empty { $$ = nil }; -/* upstream parse.y:4405: primary: "( arg" compstmt_stmts $@15 ')' */ -primary: tLPAREN_ARG compstmt_stmts midrule_15 ')' %prec ')' { $$ = @builder.parentheses($2, 353) }; -/* upstream parse.y:4411: primary: "(" compstmt_stmts ')' */ -primary: tLPAREN compstmt_stmts ')' %prec ')' { $$ = @builder.parentheses($2, 354) }; -/* upstream parse.y:4417: primary: primary_value "::" "constant" */ -primary: primary_value tCOLON2 tCONSTANT %prec tCONSTANT { $$ = @builder.variable(:constant, $3) }; -/* upstream parse.y:4422: primary: ":: at EXPR_BEG" "constant" */ -primary: tCOLON3 tCONSTANT %prec tCONSTANT { $$ = $2 }; -/* upstream parse.y:4427: primary: "[" aref_args ']' */ -primary: tLBRACK aref_args ']' %prec ']' { $$ = @builder.array($2) }; -/* upstream parse.y:4432: primary: "{" assoc_list '}' */ -primary: tLBRACE assoc_list '}' %prec '}' { $$ = @builder.hash($2) }; -/* upstream parse.y:4438: primary: k_return */ -primary: k_return { $$ = @builder.control(:return) }; -/* upstream parse.y:4443: primary: k_yield '(' call_args rparen */ -primary: k_yield '(' call_args rparen %prec '(' { $$ = @builder.call(:yield, $3 || []) }; -/* upstream parse.y:4448: primary: k_yield '(' rparen */ -primary: k_yield '(' rparen %prec '(' { $$ = @builder.call(:yield, []) }; -/* upstream parse.y:4453: primary: k_yield */ -primary: k_yield { $$ = @builder.call(:yield, []) }; -/* upstream parse.y:4458: primary: "'defined?'" option_'\n' '(' begin_defined expr rparen */ -primary: keyword_defined option_newline '(' begin_defined expr rparen %prec '(' { $$ = @builder.call(:defined, [$5]) }; -/* upstream parse.y:4465: primary: "'not'" '(' expr rparen */ -primary: keyword_not '(' expr rparen %prec '(' { $$ = @builder.unary(:"!", $3) }; -/* upstream parse.y:4470: primary: "'not'" '(' rparen */ -primary: keyword_not '(' rparen %prec '(' { $$ = @builder.reserved_word("not") }; -/* upstream parse.y:4475: primary: fcall brace_block */ -primary: fcall brace_block { $$ = @builder.block_call(@builder.call($1, []), $2) }; -/* upstream parse.y:4479: primary: method_call */ -primary: method_call { $$ = $1 }; -/* upstream parse.y:4481: primary: method_call brace_block */ -primary: method_call brace_block { $$ = @builder.block_call($1, $2) }; -/* upstream parse.y:4486: primary: lambda */ -primary: lambda { $$ = $1 }; -/* upstream parse.y:4491: primary: k_if expr_value then compstmt_stmts if_tail k_end */ -primary: k_if expr_value then compstmt_stmts if_tail k_end { $$ = @builder.if_node($2, $4, $5) }; -/* upstream parse.y:4503: primary: k_unless expr_value then compstmt_stmts opt_else k_end */ -primary: k_unless expr_value then compstmt_stmts opt_else k_end { $$ = @builder.unless_node($2, $4, $5) }; -/* upstream parse.y:4511: primary: k_while expr_value_do compstmt_stmts k_end */ -primary: k_while expr_value_do compstmt_stmts k_end { $$ = @builder.loop(:while, $2, $3) }; -/* upstream parse.y:4520: primary: k_until expr_value_do compstmt_stmts k_end */ -primary: k_until expr_value_do compstmt_stmts k_end { $$ = @builder.loop(:until, $2, $3) }; -/* upstream parse.y:4527: @16: %empty */ -midrule_16: %empty { $$ = nil }; -/* upstream parse.y:4533: primary: k_case expr_value option_terms @16 case_body k_end */ -primary: k_case expr_value option_terms midrule_16 case_body k_end { $$ = @builder.case_node($2, @builder.case_parts($5).first, @builder.case_parts($5).last) }; -/* upstream parse.y:4541: @17: %empty */ -midrule_17: %empty { $$ = nil }; -/* upstream parse.y:4547: primary: k_case option_terms @17 case_body k_end */ -primary: k_case option_terms midrule_17 case_body k_end { $$ = @builder.case_node(@builder.literal(nil), @builder.case_parts($4).first, @builder.case_parts($4).last) }; -/* upstream parse.y:4556: primary: k_case expr_value option_terms p_case_body k_end */ -primary: k_case expr_value option_terms p_case_body k_end { $$ = $2 }; -/* upstream parse.y:4561: $@18: %empty */ -midrule_18: %empty { $$ = nil }; -/* upstream parse.y:4561: $@19: %empty */ -midrule_19: %empty { $$ = nil }; -/* upstream parse.y:4564: primary: k_for for_var "'in'" $@18 expr_value do $@19 compstmt_stmts k_end */ -primary: k_for for_var keyword_in midrule_18 expr_value do midrule_19 compstmt_stmts k_end %prec keyword_in { $$ = @builder.for_node($2, $5, $8) }; -/* upstream parse.y:4606: $@20: %empty */ -midrule_20: %empty { $$ = nil }; -/* upstream parse.y:4611: primary: k_class cpath superclass $@20 bodystmt k_end */ -primary: k_class cpath superclass midrule_20 bodystmt k_end { $$ = @builder.class_node($2, $3, $5) }; -/* upstream parse.y:4628: $@21: %empty */ -midrule_21: %empty { $$ = nil }; -/* upstream parse.y:4634: primary: k_class "<<" expr_value $@21 term bodystmt k_end */ -primary: k_class tLSHFT expr_value midrule_21 term bodystmt k_end %prec tLSHFT { $$ = $3 }; -/* upstream parse.y:4647: $@22: %empty */ -midrule_22: %empty { $$ = nil }; -/* upstream parse.y:4652: primary: k_module cpath $@22 bodystmt k_end */ -primary: k_module cpath midrule_22 bodystmt k_end { $$ = @builder.module_node($2, $4) }; -/* upstream parse.y:4665: $@23: %empty */ -midrule_23: %empty { $$ = nil }; -/* upstream parse.y:4670: primary: defn_head f_arglist $@23 bodystmt k_end */ -primary: defn_head f_arglist midrule_23 bodystmt k_end { $$ = @builder.def_node($1, $2, @builder.body_nodes($4)) }; -/* upstream parse.y:4680: $@24: %empty */ -midrule_24: %empty { $$ = nil }; -/* upstream parse.y:4685: primary: defs_head f_arglist $@24 bodystmt k_end */ -primary: defs_head f_arglist midrule_24 bodystmt k_end { $$ = $1 }; -/* upstream parse.y:4694: primary: "'break'" */ -primary: keyword_break %prec keyword_break { $$ = @builder.control(:break) }; -/* upstream parse.y:4699: primary: "'next'" */ -primary: keyword_next %prec keyword_next { $$ = @builder.control(:next) }; -/* upstream parse.y:4704: primary: "'redo'" */ -primary: keyword_redo %prec keyword_redo { $$ = @builder.control(:redo) }; -/* upstream parse.y:4709: primary: "'retry'" */ -primary: keyword_retry %prec keyword_retry { $$ = @builder.control(:retry) }; -/* upstream parse.y:3161: value_expr_primary: primary */ -value_expr_primary: primary { $$ = $1 }; -/* upstream parse.y:4723: primary_value: value_expr_primary */ -primary_value: value_expr_primary { $$ = $1 }; -/* upstream parse.y:4727: k_begin: "'begin'" */ -k_begin: keyword_begin %prec keyword_begin { $$ = nil }; -/* upstream parse.y:4734: k_if: "'if'" */ -k_if: keyword_if %prec keyword_if { $$ = nil }; -/* upstream parse.y:4752: k_unless: "'unless'" */ -k_unless: keyword_unless %prec keyword_unless { $$ = nil }; -/* upstream parse.y:4759: k_while: "'while'" allow_exits */ -k_while: keyword_while allow_exits %prec keyword_while { $$ = nil }; -/* upstream parse.y:4767: k_until: "'until'" allow_exits */ -k_until: keyword_until allow_exits %prec keyword_until { $$ = nil }; -/* upstream parse.y:4775: k_case: "'case'" */ -k_case: keyword_case %prec keyword_case { $$ = nil }; -/* upstream parse.y:4782: k_for: "'for'" allow_exits */ -k_for: keyword_for allow_exits %prec keyword_for { $$ = nil }; -/* upstream parse.y:4790: k_class: "'class'" */ -k_class: keyword_class %prec keyword_class { $$ = nil }; -/* upstream parse.y:4799: k_module: "'module'" */ -k_module: keyword_module %prec keyword_module { $$ = nil }; -/* upstream parse.y:4808: k_def: "'def'" */ -k_def: keyword_def %prec keyword_def { $$ = nil }; -/* upstream parse.y:4816: k_do: "'do'" */ -k_do: keyword_do %prec keyword_do { $$ = nil }; -/* upstream parse.y:4823: k_do_block: "'do' for block" */ -k_do_block: keyword_do_block %prec keyword_do_block { $$ = nil }; -/* upstream parse.y:4830: k_rescue: "'rescue'" */ -k_rescue: keyword_rescue %prec keyword_rescue { $$ = nil }; -/* upstream parse.y:4838: k_ensure: "'ensure'" */ -k_ensure: keyword_ensure %prec keyword_ensure { $$ = nil }; -/* upstream parse.y:4845: k_when: "'when'" */ -k_when: keyword_when %prec keyword_when { $$ = nil }; -/* upstream parse.y:4851: k_else: "'else'" */ -k_else: keyword_else %prec keyword_else { $$ = nil }; -/* upstream parse.y:4866: k_elsif: "'elsif'" */ -k_elsif: keyword_elsif %prec keyword_elsif { $$ = nil }; -/* upstream parse.y:4873: k_end: "'end'" */ -k_end: keyword_end %prec keyword_end { $$ = nil }; -/* upstream parse.y:4878: k_end: "dummy end" */ -k_end: tDUMNY_END %prec tDUMNY_END { $$ = nil }; -/* upstream parse.y:4884: k_return: "'return'" */ -k_return: keyword_return %prec keyword_return { $$ = nil }; -/* upstream parse.y:4891: k_yield: "'yield'" */ -k_yield: keyword_yield %prec keyword_yield { $$ = nil }; -/* upstream parse.y:4897: then: term */ -then: term { $$ = nil }; -/* upstream parse.y:4898: then: "'then'" */ -then: keyword_then %prec keyword_then { $$ = nil }; -/* upstream parse.y:4899: then: term "'then'" */ -then: term keyword_then %prec keyword_then { $$ = nil }; -/* upstream parse.y:4902: do: term */ -do: term { $$ = $1 }; -/* upstream parse.y:4903: do: "'do' for condition" */ -do: keyword_do_cond %prec keyword_do_cond { $$ = nil }; -/* upstream parse.y:4906: if_tail: opt_else */ -if_tail: opt_else { $$ = $1 }; -/* upstream parse.y:4910: if_tail: k_elsif expr_value then compstmt_stmts if_tail */ -if_tail: k_elsif expr_value then compstmt_stmts if_tail { $$ = @builder.elsif_node($2, $4, $5) }; -/* upstream parse.y:4917: opt_else: none */ -opt_else: none { $$ = nil }; -/* upstream parse.y:4919: opt_else: k_else compstmt_stmts */ -opt_else: k_else compstmt_stmts { $$ = $2 }; -/* upstream parse.y:4925: for_var: lhs */ -for_var: lhs { $$ = $1 }; -/* upstream parse.y:4926: for_var: mlhs */ -for_var: mlhs { $$ = $1 }; -/* upstream parse.y:4930: f_marg: f_norm_arg */ -f_marg: f_norm_arg { $$ = $1 }; -/* upstream parse.y:4935: f_marg: "(" f_margs rparen */ -f_marg: tLPAREN f_margs rparen %prec tLPAREN { $$ = $2 }; -/* upstream parse.y:3048: mlhs_items_f_marg: f_marg */ -mlhs_items_f_marg: f_marg { $$ = $1 }; -/* upstream parse.y:3053: mlhs_items_f_marg: mlhs_items_f_marg ',' f_marg */ -mlhs_items_f_marg: mlhs_items_f_marg ',' f_marg %prec ',' { $$ = $1 }; -/* upstream parse.y:4943: f_margs: mlhs_items_f_marg */ -f_margs: mlhs_items_f_marg { $$ = $1 }; -/* upstream parse.y:4948: f_margs: mlhs_items_f_marg ',' f_rest_marg */ -f_margs: mlhs_items_f_marg ',' f_rest_marg %prec ',' { $$ = $1 }; -/* upstream parse.y:4953: f_margs: mlhs_items_f_marg ',' f_rest_marg ',' mlhs_items_f_marg */ -f_margs: mlhs_items_f_marg ',' f_rest_marg ',' mlhs_items_f_marg %prec ',' { $$ = $1 }; -/* upstream parse.y:4958: f_margs: f_rest_marg */ -f_margs: f_rest_marg { $$ = $1 }; -/* upstream parse.y:4963: f_margs: f_rest_marg ',' mlhs_items_f_marg */ -f_margs: f_rest_marg ',' mlhs_items_f_marg %prec ',' { $$ = $1 }; -/* upstream parse.y:4970: f_rest_marg: "*" f_norm_arg */ -f_rest_marg: tSTAR f_norm_arg %prec tSTAR { $$ = $2 }; -/* upstream parse.y:4976: f_rest_marg: "*" */ -f_rest_marg: tSTAR %prec tSTAR { $$ = nil }; -/* upstream parse.y:4982: f_any_kwrest: f_kwrest */ -f_any_kwrest: f_kwrest { $$ = $1 }; -/* upstream parse.y:4984: f_any_kwrest: f_no_kwarg */ -f_any_kwrest: f_no_kwarg { $$ = $1 }; -/* upstream parse.y:4990: $@25: %empty */ -midrule_25: %empty { $$ = nil }; -/* upstream parse.y:4990: f_eq: $@25 '=' */ -f_eq: midrule_25 '=' %prec '=' { $$ = nil }; -/* upstream parse.y:3020: f_kw_primary_value: f_label primary_value */ -f_kw_primary_value: f_label primary_value { $$ = $1 }; -/* upstream parse.y:3026: f_kw_primary_value: f_label */ -f_kw_primary_value: f_label { $$ = $1 }; -/* upstream parse.y:3035: f_kwarg_primary_value: f_kw_primary_value */ -f_kwarg_primary_value: f_kw_primary_value { $$ = $1 }; -/* upstream parse.y:3040: f_kwarg_primary_value: f_kwarg_primary_value ',' f_kw_primary_value */ -f_kwarg_primary_value: f_kwarg_primary_value ',' f_kw_primary_value %prec ',' { $$ = $1 }; -/* upstream parse.y:2957: opt_f_block_arg_none: ',' f_block_arg */ -opt_f_block_arg_none: ',' f_block_arg %prec ',' { $$ = $2 }; -/* upstream parse.y:4992: opt_f_block_arg_none: none */ -opt_f_block_arg_none: none { $$ = nil }; -/* upstream parse.y:2934: args_tail_basic_primary_value_none: f_kwarg_primary_value ',' f_kwrest opt_f_block_arg_none */ -args_tail_basic_primary_value_none: f_kwarg_primary_value ',' f_kwrest opt_f_block_arg_none %prec ',' { $$ = $1 }; -/* upstream parse.y:2939: args_tail_basic_primary_value_none: f_kwarg_primary_value opt_f_block_arg_none */ -args_tail_basic_primary_value_none: f_kwarg_primary_value opt_f_block_arg_none { $$ = $1 }; -/* upstream parse.y:2944: args_tail_basic_primary_value_none: f_any_kwrest opt_f_block_arg_none */ -args_tail_basic_primary_value_none: f_any_kwrest opt_f_block_arg_none { $$ = $1 }; -/* upstream parse.y:2949: args_tail_basic_primary_value_none: f_block_arg */ -args_tail_basic_primary_value_none: f_block_arg { $$ = $1 }; -/* upstream parse.y:4992: block_args_tail: args_tail_basic_primary_value_none */ -block_args_tail: args_tail_basic_primary_value_none { $$ = $1 }; -/* upstream parse.y:4996: excessed_comma: ',' */ -excessed_comma: ',' %prec ',' { $$ = nil }; -/* upstream parse.y:2998: f_opt_primary_value: f_arg_asgn f_eq primary_value */ -f_opt_primary_value: f_arg_asgn f_eq primary_value { $$ = $1 }; -/* upstream parse.y:3007: f_opt_arg_primary_value: f_opt_primary_value */ -f_opt_arg_primary_value: f_opt_primary_value { $$ = $1 }; -/* upstream parse.y:3012: f_opt_arg_primary_value: f_opt_arg_primary_value ',' f_opt_primary_value */ -f_opt_arg_primary_value: f_opt_arg_primary_value ',' f_opt_primary_value %prec ',' { $$ = $1 }; -/* upstream parse.y:3107: opt_args_tail_block_args_tail_none: ',' block_args_tail */ -opt_args_tail_block_args_tail_none: ',' block_args_tail %prec ',' { $$ = $2 }; -/* upstream parse.y:3112: opt_args_tail_block_args_tail_none: none */ -opt_args_tail_block_args_tail_none: none { $$ = nil }; -/* upstream parse.y:6277: args-list_primary_value_opt_args_tail_block_args_tail_none: f_arg ',' f_opt_arg_primary_value ',' f_rest_arg opt_args_tail_block_args_tail_none */ -args_list_primary_value_opt_args_tail_block_args_tail_none: f_arg ',' f_opt_arg_primary_value ',' f_rest_arg opt_args_tail_block_args_tail_none %prec ',' { $$ = $1 }; -/* upstream parse.y:6282: args-list_primary_value_opt_args_tail_block_args_tail_none: f_arg ',' f_opt_arg_primary_value ',' f_rest_arg ',' f_arg opt_args_tail_block_args_tail_none */ -args_list_primary_value_opt_args_tail_block_args_tail_none: f_arg ',' f_opt_arg_primary_value ',' f_rest_arg ',' f_arg opt_args_tail_block_args_tail_none %prec ',' { $$ = $1 }; -/* upstream parse.y:6287: args-list_primary_value_opt_args_tail_block_args_tail_none: f_arg ',' f_opt_arg_primary_value opt_args_tail_block_args_tail_none */ -args_list_primary_value_opt_args_tail_block_args_tail_none: f_arg ',' f_opt_arg_primary_value opt_args_tail_block_args_tail_none %prec ',' { $$ = $1 }; -/* upstream parse.y:6292: args-list_primary_value_opt_args_tail_block_args_tail_none: f_arg ',' f_opt_arg_primary_value ',' f_arg opt_args_tail_block_args_tail_none */ -args_list_primary_value_opt_args_tail_block_args_tail_none: f_arg ',' f_opt_arg_primary_value ',' f_arg opt_args_tail_block_args_tail_none %prec ',' { $$ = $1 }; -/* upstream parse.y:6297: args-list_primary_value_opt_args_tail_block_args_tail_none: f_arg ',' f_rest_arg opt_args_tail_block_args_tail_none */ -args_list_primary_value_opt_args_tail_block_args_tail_none: f_arg ',' f_rest_arg opt_args_tail_block_args_tail_none %prec ',' { $$ = $1 }; -/* upstream parse.y:6302: args-list_primary_value_opt_args_tail_block_args_tail_none: f_arg ',' f_rest_arg ',' f_arg opt_args_tail_block_args_tail_none */ -args_list_primary_value_opt_args_tail_block_args_tail_none: f_arg ',' f_rest_arg ',' f_arg opt_args_tail_block_args_tail_none %prec ',' { $$ = $1 }; -/* upstream parse.y:6307: args-list_primary_value_opt_args_tail_block_args_tail_none: f_opt_arg_primary_value ',' f_rest_arg opt_args_tail_block_args_tail_none */ -args_list_primary_value_opt_args_tail_block_args_tail_none: f_opt_arg_primary_value ',' f_rest_arg opt_args_tail_block_args_tail_none %prec ',' { $$ = $1 }; -/* upstream parse.y:6312: args-list_primary_value_opt_args_tail_block_args_tail_none: f_opt_arg_primary_value ',' f_rest_arg ',' f_arg opt_args_tail_block_args_tail_none */ -args_list_primary_value_opt_args_tail_block_args_tail_none: f_opt_arg_primary_value ',' f_rest_arg ',' f_arg opt_args_tail_block_args_tail_none %prec ',' { $$ = $1 }; -/* upstream parse.y:6317: args-list_primary_value_opt_args_tail_block_args_tail_none: f_opt_arg_primary_value opt_args_tail_block_args_tail_none */ -args_list_primary_value_opt_args_tail_block_args_tail_none: f_opt_arg_primary_value opt_args_tail_block_args_tail_none { $$ = $1 }; -/* upstream parse.y:6322: args-list_primary_value_opt_args_tail_block_args_tail_none: f_opt_arg_primary_value ',' f_arg opt_args_tail_block_args_tail_none */ -args_list_primary_value_opt_args_tail_block_args_tail_none: f_opt_arg_primary_value ',' f_arg opt_args_tail_block_args_tail_none %prec ',' { $$ = $1 }; -/* upstream parse.y:6327: args-list_primary_value_opt_args_tail_block_args_tail_none: f_rest_arg opt_args_tail_block_args_tail_none */ -args_list_primary_value_opt_args_tail_block_args_tail_none: f_rest_arg opt_args_tail_block_args_tail_none { $$ = $1 }; -/* upstream parse.y:6332: args-list_primary_value_opt_args_tail_block_args_tail_none: f_rest_arg ',' f_arg opt_args_tail_block_args_tail_none */ -args_list_primary_value_opt_args_tail_block_args_tail_none: f_rest_arg ',' f_arg opt_args_tail_block_args_tail_none %prec ',' { $$ = $1 }; -/* upstream parse.y:5003: block_param: args-list_primary_value_opt_args_tail_block_args_tail_none */ -block_param: args_list_primary_value_opt_args_tail_block_args_tail_none { $$ = $1 }; -/* upstream parse.y:5005: block_param: f_arg excessed_comma */ -block_param: f_arg excessed_comma { $$ = $1 }; -/* upstream parse.y:5011: block_param: f_arg opt_args_tail_block_args_tail_none */ -block_param: f_arg opt_args_tail_block_args_tail_none { $$ = $1 }; -/* upstream parse.y:6340: tail-only-args_block_args_tail: block_args_tail */ -tail_only_args_block_args_tail: block_args_tail { $$ = $1 }; -/* upstream parse.y:5015: block_param: tail-only-args_block_args_tail */ -block_param: tail_only_args_block_args_tail { $$ = $1 }; -/* upstream parse.y:5018: opt_block_param_def: none */ -opt_block_param_def: none { $$ = nil }; -/* upstream parse.y:5020: opt_block_param_def: block_param_def */ -opt_block_param_def: block_param_def { $$ = $1 }; -/* upstream parse.y:5026: block_param_def: '|' opt_block_param opt_bv_decl '|' */ -block_param_def: '|' opt_block_param opt_bv_decl '|' %prec '|' { $$ = $2 }; -/* upstream parse.y:5035: opt_block_param: %empty */ -opt_block_param: %empty { $$ = nil }; -/* upstream parse.y:5039: opt_block_param: block_param */ -opt_block_param: block_param { $$ = $1 }; -/* upstream parse.y:5043: opt_bv_decl: option_'\n' */ -opt_bv_decl: option_newline { $$ = $1 }; -/* upstream parse.y:5048: opt_bv_decl: option_'\n' ';' bv_decls option_'\n' */ -opt_bv_decl: option_newline ';' bv_decls option_newline %prec ';' { $$ = $3 }; -/* upstream parse.y:5054: bv_decls: bvar */ -bv_decls: bvar { $$ = $1 }; -/* upstream parse.y:5056: bv_decls: bv_decls ',' bvar */ -bv_decls: bv_decls ',' bvar %prec ',' { $$ = $1 }; -/* upstream parse.y:5061: bvar: "local variable or method" */ -bvar: tIDENTIFIER %prec tIDENTIFIER { $$ = $1 }; -/* upstream parse.y:5065: bvar: f_bad_arg */ -bvar: f_bad_arg { $$ = $1 }; -/* upstream parse.y:5068: max_numparam: %empty */ -max_numparam: %empty { $$ = nil }; -/* upstream parse.y:5074: numparam: %empty */ -numparam: %empty { $$ = nil }; -/* upstream parse.y:5079: it_id: %empty */ -it_id: %empty { $$ = nil }; -/* upstream parse.y:5086: @26: %empty */ -midrule_26: %empty { $$ = nil }; -/* upstream parse.y:5092: $@27: %empty */ -midrule_27: %empty { $$ = nil }; -/* upstream parse.y:5096: lambda: "->" @26 max_numparam numparam it_id allow_exits f_larglist $@27 lambda_body */ -lambda: tLAMBDA midrule_26 max_numparam numparam it_id allow_exits f_larglist midrule_27 lambda_body %prec tLAMBDA { $$ = @builder.lambda_node($7, $8) }; -/* upstream parse.y:5120: f_larglist: '(' f_largs opt_bv_decl ')' */ -f_larglist: '(' f_largs opt_bv_decl ')' %prec ')' { $$ = $2 }; -/* upstream parse.y:5127: f_larglist: f_largs */ -f_larglist: f_largs { $$ = $1 }; -/* upstream parse.y:5136: lambda_body: tLAMBEG compstmt_stmts '}' */ -lambda_body: tLAMBEG compstmt_stmts '}' %prec '}' { $$ = $2 }; -/* upstream parse.y:5142: $@28: %empty */ -midrule_28: %empty { $$ = nil }; -/* upstream parse.y:5146: lambda_body: "'do' for lambda" $@28 bodystmt k_end */ -lambda_body: keyword_do_LAMBDA midrule_28 bodystmt k_end %prec keyword_do_LAMBDA { $$ = $3 }; -/* upstream parse.y:5153: do_block: k_do_block do_body k_end */ -do_block: k_do_block do_body k_end { $$ = $2 }; -/* upstream parse.y:5161: block_call: command do_block */ -block_call: command do_block { $$ = @builder.block_call($1, $2) }; -/* upstream parse.y:5167: block_call: block_call call_op2 operation2 opt_paren_args */ -block_call: block_call call_op2 operation2 opt_paren_args { $$ = $1 }; -/* upstream parse.y:5177: block_call: block_call call_op2 operation2 opt_paren_args brace_block */ -block_call: block_call call_op2 operation2 opt_paren_args brace_block { $$ = $1 }; -/* upstream parse.y:5183: block_call: block_call call_op2 operation2 command_args do_block */ -block_call: block_call call_op2 operation2 command_args do_block { $$ = $1 }; -/* upstream parse.y:5188: block_call: block_call call_op2 paren_args */ -block_call: block_call call_op2 paren_args { $$ = $1 }; -/* upstream parse.y:5196: method_call: fcall paren_args */ -method_call: fcall paren_args { $$ = @builder.call($1, $2 || []) }; -/* upstream parse.y:5203: method_call: primary_value call_op operation2 opt_paren_args */ -method_call: primary_value call_op operation2 opt_paren_args { $$ = @builder.receiver_call($1, $2, $3, $4) }; -/* upstream parse.y:5214: method_call: primary_value "::" operation2 paren_args */ -method_call: primary_value tCOLON2 operation2 paren_args %prec tCOLON2 { $$ = @builder.receiver_call($1, :"::", $3, $4) }; -/* upstream parse.y:5220: method_call: primary_value "::" operation3 */ -method_call: primary_value tCOLON2 operation3 %prec tCOLON2 { $$ = @builder.receiver_call($1, :"::", $3, []) }; -/* upstream parse.y:5225: method_call: primary_value call_op2 paren_args */ -method_call: primary_value call_op2 paren_args { $$ = @builder.receiver_call($1, $2, :call, $3) }; -/* upstream parse.y:5231: method_call: "'super'" paren_args */ -method_call: keyword_super paren_args %prec keyword_super { $$ = @builder.call(:super, $2 || []) }; -/* upstream parse.y:5241: method_call: "'super'" */ -method_call: keyword_super %prec keyword_super { $$ = @builder.call(:super, []) }; -/* upstream parse.y:5246: method_call: primary_value '[' opt_call_args rbracket */ -method_call: primary_value '[' opt_call_args rbracket %prec '[' { $$ = @builder.index($1, $3 || []) }; -/* upstream parse.y:5254: brace_block: '{' brace_body '}' */ -brace_block: '{' brace_body '}' %prec '}' { $$ = $2 }; -/* upstream parse.y:5260: brace_block: k_do do_body k_end */ -brace_block: k_do do_body k_end { $$ = $2 }; -/* upstream parse.y:5267: @29: %empty */ -midrule_29: %empty { $$ = nil }; -/* upstream parse.y:5270: brace_body: @29 max_numparam numparam it_id allow_exits opt_block_param_def compstmt_stmts */ -brace_body: midrule_29 max_numparam numparam it_id allow_exits opt_block_param_def compstmt_stmts { $$ = $7 }; -/* upstream parse.y:5284: @30: %empty */ -midrule_30: %empty { $$ = nil }; -/* upstream parse.y:5290: do_body: @30 max_numparam numparam it_id allow_exits opt_block_param_def bodystmt */ -do_body: midrule_30 max_numparam numparam it_id allow_exits opt_block_param_def bodystmt { $$ = $7 }; -/* upstream parse.y:5306: case_args: arg_value */ -case_args: arg_value { $$ = [$1].freeze }; -/* upstream parse.y:5312: case_args: "*" arg_value */ -case_args: tSTAR arg_value %prec tSTAR { $$ = $2 }; -/* upstream parse.y:5317: case_args: case_args ',' arg_value */ -case_args: case_args ',' arg_value %prec ',' { $$ = ($1 + [$3]).freeze }; -/* upstream parse.y:5323: case_args: case_args ',' "*" arg_value */ -case_args: case_args ',' tSTAR arg_value %prec tSTAR { $$ = $1 }; -/* upstream parse.y:5332: case_body: k_when case_args then compstmt_stmts cases */ -case_body: k_when case_args then compstmt_stmts cases { $$ = @builder.case_chain(@builder.when_node($2, $4), $5) }; -/* upstream parse.y:5339: cases: opt_else */ -cases: opt_else { $$ = $1 }; -/* upstream parse.y:5340: cases: case_body */ -cases: case_body { $$ = [$1].freeze }; -/* upstream parse.y:5343: p_pvtbl: %empty */ -p_pvtbl: %empty { $$ = nil }; -/* upstream parse.y:5344: p_pktbl: %empty */ -p_pktbl: %empty { $$ = nil }; -/* upstream parse.y:5346: p_in_kwarg: %empty */ -p_in_kwarg: %empty { $$ = nil }; -/* upstream parse.y:5359: $@31: %empty */ -midrule_31: %empty { $$ = nil }; -/* upstream parse.y:5368: p_case_body: "'in'" p_in_kwarg p_pvtbl p_pktbl p_top_expr then $@31 compstmt_stmts p_cases */ -p_case_body: keyword_in p_in_kwarg p_pvtbl p_pktbl p_top_expr then midrule_31 compstmt_stmts p_cases %prec keyword_in { $$ = $1 }; -/* upstream parse.y:5374: p_cases: opt_else */ -p_cases: opt_else { $$ = $1 }; -/* upstream parse.y:5375: p_cases: p_case_body */ -p_cases: p_case_body { $$ = $1 }; -/* upstream parse.y:5378: p_top_expr: p_top_expr_body */ -p_top_expr: p_top_expr_body { $$ = $1 }; -/* upstream parse.y:5380: p_top_expr: p_top_expr_body "'if' modifier" expr_value */ -p_top_expr: p_top_expr_body modifier_if expr_value %prec modifier_if { $$ = $1 }; -/* upstream parse.y:5386: p_top_expr: p_top_expr_body "'unless' modifier" expr_value */ -p_top_expr: p_top_expr_body modifier_unless expr_value %prec modifier_unless { $$ = $1 }; -/* upstream parse.y:5393: p_top_expr_body: p_expr */ -p_top_expr_body: p_expr { $$ = $1 }; -/* upstream parse.y:5395: p_top_expr_body: p_expr ',' */ -p_top_expr_body: p_expr ',' %prec ',' { $$ = $1 }; -/* upstream parse.y:5401: p_top_expr_body: p_expr ',' p_args */ -p_top_expr_body: p_expr ',' p_args %prec ',' { $$ = $1 }; -/* upstream parse.y:5407: p_top_expr_body: p_find */ -p_top_expr_body: p_find { $$ = $1 }; -/* upstream parse.y:5412: p_top_expr_body: p_args_tail */ -p_top_expr_body: p_args_tail { $$ = $1 }; -/* upstream parse.y:5417: p_top_expr_body: p_kwargs */ -p_top_expr_body: p_kwargs { $$ = $1 }; -/* upstream parse.y:5423: p_expr: p_as */ -p_expr: p_as { $$ = $1 }; -/* upstream parse.y:5427: p_as: p_expr "=>" p_variable */ -p_as: p_expr tASSOC p_variable %prec tASSOC { $$ = $1 }; -/* upstream parse.y:5433: p_as: p_alt */ -p_as: p_alt { $$ = $1 }; -/* upstream parse.y:5437: $@32: %empty */ -midrule_32: %empty { $$ = nil }; -/* upstream parse.y:5441: p_alt: p_alt '|' $@32 p_expr_basic */ -p_alt: p_alt '|' midrule_32 p_expr_basic %prec '|' { $$ = $1 }; -/* upstream parse.y:5449: p_alt: p_expr_basic */ -p_alt: p_expr_basic { $$ = $1 }; -/* upstream parse.y:5453: p_lparen: '(' p_pktbl */ -p_lparen: '(' p_pktbl %prec '(' { $$ = $2 }; -/* upstream parse.y:5460: p_lbracket: '[' p_pktbl */ -p_lbracket: '[' p_pktbl %prec '[' { $$ = $2 }; -/* upstream parse.y:5466: p_expr_basic: p_value */ -p_expr_basic: p_value { $$ = $1 }; -/* upstream parse.y:5467: p_expr_basic: p_variable */ -p_expr_basic: p_variable { $$ = $1 }; -/* upstream parse.y:5469: p_expr_basic: p_const p_lparen p_args rparen */ -p_expr_basic: p_const p_lparen p_args rparen { $$ = $1 }; -/* upstream parse.y:5476: p_expr_basic: p_const p_lparen p_find rparen */ -p_expr_basic: p_const p_lparen p_find rparen { $$ = $1 }; -/* upstream parse.y:5483: p_expr_basic: p_const p_lparen p_kwargs rparen */ -p_expr_basic: p_const p_lparen p_kwargs rparen { $$ = $1 }; -/* upstream parse.y:5490: p_expr_basic: p_const '(' rparen */ -p_expr_basic: p_const '(' rparen %prec '(' { $$ = $1 }; -/* upstream parse.y:5496: p_expr_basic: p_const p_lbracket p_args rbracket */ -p_expr_basic: p_const p_lbracket p_args rbracket { $$ = $1 }; -/* upstream parse.y:5503: p_expr_basic: p_const p_lbracket p_find rbracket */ -p_expr_basic: p_const p_lbracket p_find rbracket { $$ = $1 }; -/* upstream parse.y:5510: p_expr_basic: p_const p_lbracket p_kwargs rbracket */ -p_expr_basic: p_const p_lbracket p_kwargs rbracket { $$ = $1 }; -/* upstream parse.y:5517: p_expr_basic: p_const '[' rbracket */ -p_expr_basic: p_const '[' rbracket %prec '[' { $$ = $1 }; -/* upstream parse.y:5523: p_expr_basic: "[" p_args rbracket */ -p_expr_basic: tLBRACK p_args rbracket %prec tLBRACK { $$ = $2 }; -/* upstream parse.y:5528: p_expr_basic: "[" p_find rbracket */ -p_expr_basic: tLBRACK p_find rbracket %prec tLBRACK { $$ = $2 }; -/* upstream parse.y:5533: p_expr_basic: "[" rbracket */ -p_expr_basic: tLBRACK rbracket %prec tLBRACK { $$ = nil }; -/* upstream parse.y:5539: $@33: %empty */ -midrule_33: %empty { $$ = nil }; -/* upstream parse.y:5543: p_expr_basic: "{" p_pktbl lex_ctxt $@33 p_kwargs rbrace */ -p_expr_basic: tLBRACE p_pktbl lex_ctxt midrule_33 p_kwargs rbrace %prec tLBRACE { $$ = $1 }; -/* upstream parse.y:5550: p_expr_basic: "{" rbrace */ -p_expr_basic: tLBRACE rbrace %prec tLBRACE { $$ = nil }; -/* upstream parse.y:5556: p_expr_basic: "(" p_pktbl p_expr rparen */ -p_expr_basic: tLPAREN p_pktbl p_expr rparen %prec tLPAREN { $$ = $1 }; -/* upstream parse.y:5564: p_args: p_expr */ -p_args: p_expr { $$ = $1 }; -/* upstream parse.y:5570: p_args: p_args_head */ -p_args: p_args_head { $$ = $1 }; -/* upstream parse.y:5575: p_args: p_args_head p_arg */ -p_args: p_args_head p_arg { $$ = $1 }; -/* upstream parse.y:5580: p_args: p_args_head p_rest */ -p_args: p_args_head p_rest { $$ = $1 }; -/* upstream parse.y:5585: p_args: p_args_head p_rest ',' p_args_post */ -p_args: p_args_head p_rest ',' p_args_post %prec ',' { $$ = $1 }; -/* upstream parse.y:5589: p_args: p_args_tail */ -p_args: p_args_tail { $$ = $1 }; -/* upstream parse.y:5592: p_args_head: p_arg ',' */ -p_args_head: p_arg ',' %prec ',' { $$ = $1 }; -/* upstream parse.y:5594: p_args_head: p_args_head p_arg ',' */ -p_args_head: p_args_head p_arg ',' %prec ',' { $$ = $1 }; -/* upstream parse.y:5601: p_args_tail: p_rest */ -p_args_tail: p_rest { $$ = $1 }; -/* upstream parse.y:5606: p_args_tail: p_rest ',' p_args_post */ -p_args_tail: p_rest ',' p_args_post %prec ',' { $$ = $1 }; -/* upstream parse.y:5613: p_find: p_rest ',' p_args_post ',' p_rest */ -p_find: p_rest ',' p_args_post ',' p_rest %prec ',' { $$ = $1 }; -/* upstream parse.y:5621: p_rest: "*" "local variable or method" */ -p_rest: tSTAR tIDENTIFIER %prec tIDENTIFIER { $$ = $2 }; -/* upstream parse.y:5627: p_rest: "*" */ -p_rest: tSTAR %prec tSTAR { $$ = nil }; -/* upstream parse.y:5633: p_args_post: p_arg */ -p_args_post: p_arg { $$ = $1 }; -/* upstream parse.y:5635: p_args_post: p_args_post ',' p_arg */ -p_args_post: p_args_post ',' p_arg %prec ',' { $$ = $1 }; -/* upstream parse.y:5642: p_arg: p_expr */ -p_arg: p_expr { $$ = $1 }; -/* upstream parse.y:5649: p_kwargs: p_kwarg ',' p_any_kwrest */ -p_kwargs: p_kwarg ',' p_any_kwrest %prec ',' { $$ = $1 }; -/* upstream parse.y:5654: p_kwargs: p_kwarg */ -p_kwargs: p_kwarg { $$ = $1 }; -/* upstream parse.y:5659: p_kwargs: p_kwarg ',' */ -p_kwargs: p_kwarg ',' %prec ',' { $$ = $1 }; -/* upstream parse.y:5664: p_kwargs: p_any_kwrest */ -p_kwargs: p_any_kwrest { $$ = $1 }; -/* upstream parse.y:5670: p_kwarg: p_kw */ -p_kwarg: p_kw { $$ = $1 }; -/* upstream parse.y:5673: p_kwarg: p_kwarg ',' p_kw */ -p_kwarg: p_kwarg ',' p_kw %prec ',' { $$ = $1 }; -/* upstream parse.y:5680: p_kw: p_kw_label p_expr */ -p_kw: p_kw_label p_expr { $$ = $1 }; -/* upstream parse.y:5686: p_kw: p_kw_label */ -p_kw: p_kw_label { $$ = $1 }; -/* upstream parse.y:5697: p_kw_label: "label" */ -p_kw_label: tLABEL %prec tLABEL { $$ = $1 }; -/* upstream parse.y:5699: p_kw_label: "string literal" string_contents tLABEL_END */ -p_kw_label: tSTRING_BEG string_contents tLABEL_END %prec tLABEL_END { $$ = $1 }; -/* upstream parse.y:5714: p_kwrest: kwrest_mark "local variable or method" */ -p_kwrest: kwrest_mark tIDENTIFIER %prec tIDENTIFIER { $$ = $1 }; -/* upstream parse.y:5719: p_kwrest: kwrest_mark */ -p_kwrest: kwrest_mark { $$ = nil }; -/* upstream parse.y:5726: p_kwnorest: kwrest_mark "'nil'" */ -p_kwnorest: kwrest_mark keyword_nil %prec keyword_nil { $$ = $1 }; -/* upstream parse.y:5731: p_any_kwrest: p_kwrest */ -p_any_kwrest: p_kwrest { $$ = $1 }; -/* upstream parse.y:5733: p_any_kwrest: p_kwnorest */ -p_any_kwrest: p_kwnorest { $$ = $1 }; -/* upstream parse.y:5739: p_value: p_primitive */ -p_value: p_primitive { $$ = $1 }; -/* upstream parse.y:3120: range_expr_p_primitive: p_primitive ".." p_primitive */ -range_expr_p_primitive: p_primitive tDOT2 p_primitive %prec tDOT2 { $$ = $1 }; -/* upstream parse.y:3127: range_expr_p_primitive: p_primitive "..." p_primitive */ -range_expr_p_primitive: p_primitive tDOT3 p_primitive %prec tDOT3 { $$ = $1 }; -/* upstream parse.y:3134: range_expr_p_primitive: p_primitive ".." */ -range_expr_p_primitive: p_primitive tDOT2 %prec tDOT2 { $$ = @builder.range(:"..", $1, nil) }; -/* upstream parse.y:3140: range_expr_p_primitive: p_primitive "..." */ -range_expr_p_primitive: p_primitive tDOT3 %prec tDOT3 { $$ = @builder.range(:"...", $1, nil) }; -/* upstream parse.y:3146: range_expr_p_primitive: "(.." p_primitive */ -range_expr_p_primitive: tBDOT2 p_primitive %prec tBDOT2 { $$ = @builder.range(:"..", nil, $2) }; -/* upstream parse.y:3152: range_expr_p_primitive: "(..." p_primitive */ -range_expr_p_primitive: tBDOT3 p_primitive %prec tBDOT3 { $$ = @builder.range(:"...", nil, $2) }; -/* upstream parse.y:5740: p_value: range_expr_p_primitive */ -p_value: range_expr_p_primitive { $$ = $1 }; -/* upstream parse.y:5741: p_value: p_var_ref */ -p_value: p_var_ref { $$ = $1 }; -/* upstream parse.y:5742: p_value: p_expr_ref */ -p_value: p_expr_ref { $$ = $1 }; -/* upstream parse.y:5743: p_value: p_const */ -p_value: p_const { $$ = $1 }; -/* upstream parse.y:5746: p_primitive: literal */ -p_primitive: literal { $$ = $1 }; -/* upstream parse.y:5746: p_primitive: strings */ -p_primitive: strings { $$ = $1 }; -/* upstream parse.y:5746: p_primitive: xstring */ -p_primitive: xstring { $$ = $1 }; -/* upstream parse.y:5746: p_primitive: regexp */ -p_primitive: regexp { $$ = $1 }; -/* upstream parse.y:5746: p_primitive: words */ -p_primitive: words { $$ = $1 }; -/* upstream parse.y:5746: p_primitive: qwords */ -p_primitive: qwords { $$ = $1 }; -/* upstream parse.y:5746: p_primitive: symbols */ -p_primitive: symbols { $$ = $1 }; -/* upstream parse.y:5746: p_primitive: qsymbols */ -p_primitive: qsymbols { $$ = $1 }; -/* upstream parse.y:5748: p_primitive: keyword_variable */ -p_primitive: keyword_variable { $$ = $1 }; -/* upstream parse.y:5752: p_primitive: lambda */ -p_primitive: lambda { $$ = $1 }; -/* upstream parse.y:5756: p_variable: "local variable or method" */ -p_variable: tIDENTIFIER %prec tIDENTIFIER { $$ = $1 }; -/* upstream parse.y:5764: p_var_ref: '^' "local variable or method" */ -p_var_ref: '^' tIDENTIFIER %prec tIDENTIFIER { $$ = $2 }; -/* upstream parse.y:5776: p_var_ref: '^' nonlocal_var */ -p_var_ref: '^' nonlocal_var %prec '^' { $$ = $2 }; -/* upstream parse.y:5783: p_expr_ref: '^' "(" expr_value rparen */ -p_expr_ref: '^' tLPAREN expr_value rparen %prec tLPAREN { $$ = $3 }; -/* upstream parse.y:5790: p_const: ":: at EXPR_BEG" cname */ -p_const: tCOLON3 cname %prec tCOLON3 { $$ = $2 }; -/* upstream parse.y:5795: p_const: p_const "::" cname */ -p_const: p_const tCOLON2 cname %prec tCOLON2 { $$ = $1 }; -/* upstream parse.y:5800: p_const: "constant" */ -p_const: tCONSTANT %prec tCONSTANT { $$ = $1 }; -/* upstream parse.y:5809: opt_rescue: k_rescue exc_list exc_var then compstmt_stmts opt_rescue */ -opt_rescue: k_rescue exc_list exc_var then compstmt_stmts opt_rescue { $$ = $1 }; -/* upstream parse.y:5827: opt_rescue: none */ -opt_rescue: none { $$ = nil }; -/* upstream parse.y:5831: exc_list: arg_value */ -exc_list: arg_value { $$ = $1 }; -/* upstream parse.y:5836: exc_list: mrhs */ -exc_list: mrhs { $$ = $1 }; -/* upstream parse.y:5839: exc_list: none */ -exc_list: none { $$ = nil }; -/* upstream parse.y:5843: exc_var: "=>" lhs */ -exc_var: tASSOC lhs %prec tASSOC { $$ = $2 }; -/* upstream parse.y:5847: exc_var: none */ -exc_var: none { $$ = nil }; -/* upstream parse.y:5851: opt_ensure: k_ensure stmts option_terms */ -opt_ensure: k_ensure stmts option_terms { $$ = $2 }; -/* upstream parse.y:5857: opt_ensure: none */ -opt_ensure: none { $$ = nil }; -/* upstream parse.y:5860: literal: numeric */ -literal: numeric { $$ = $1 }; -/* upstream parse.y:5861: literal: symbol */ -literal: symbol { $$ = $1 }; -/* upstream parse.y:5865: strings: string */ -strings: string { $$ = $1 }; -/* upstream parse.y:5876: string: "char literal" */ -string: tCHAR %prec tCHAR { $$ = @builder.literal($1) }; -/* upstream parse.y:5877: string: string1 */ -string: string1 { $$ = $1 }; -/* upstream parse.y:5879: string: string string1 */ -string: string string1 { $$ = @builder.concat_strings($1, $2) }; -/* upstream parse.y:5886: string1: "string literal" string_contents "terminator" */ -string1: tSTRING_BEG string_contents tSTRING_END %prec tSTRING_END { $$ = @builder.string(@builder.join_strings($2)) }; -/* upstream parse.y:5899: xstring: "backtick literal" xstring_contents "terminator" */ -xstring: tXSTRING_BEG xstring_contents tSTRING_END %prec tSTRING_END { $$ = @builder.xstring(@builder.join_strings($2)) }; -/* upstream parse.y:5911: regexp: "regexp literal" regexp_contents tREGEXP_END */ -regexp: tREGEXP_BEG regexp_contents tREGEXP_END %prec tREGEXP_END { $$ = @builder.regexp(@builder.join_strings($2)) }; -/* upstream parse.y:5917: nonempty_list_' ': ' ' */ -nonempty_list____: ' ' %prec ' ' { $$ = nil }; -/* upstream parse.y:5917: nonempty_list_' ': nonempty_list_' ' ' ' */ -nonempty_list____: nonempty_list____ ' ' %prec ' ' { $$ = nil }; -/* upstream parse.y:3169: words_tWORDS_BEG_word_list: "word list" nonempty_list_' ' word_list "terminator" */ -words_tWORDS_BEG_word_list: tWORDS_BEG nonempty_list____ word_list tSTRING_END %prec tSTRING_END { $$ = $3 }; -/* upstream parse.y:5917: words: words_tWORDS_BEG_word_list */ -words: words_tWORDS_BEG_word_list { $$ = $1 }; -/* upstream parse.y:5921: word_list: %empty */ -word_list: %empty { $$ = [] }; -/* upstream parse.y:5926: word_list: word_list word nonempty_list_' ' */ -word_list: word_list word nonempty_list____ { $$ = ($1 + [$2]).freeze }; -/* upstream parse.y:5932: word: string_content */ -word: string_content { $$ = $1 }; -/* upstream parse.y:5935: word: word string_content */ -word: word string_content { $$ = $1.to_s + $2.to_s }; -/* upstream parse.y:3169: words_tSYMBOLS_BEG_symbol_list: "symbol list" nonempty_list_' ' symbol_list "terminator" */ -words_tSYMBOLS_BEG_symbol_list: tSYMBOLS_BEG nonempty_list____ symbol_list tSTRING_END %prec tSTRING_END { $$ = $3 }; -/* upstream parse.y:5941: symbols: words_tSYMBOLS_BEG_symbol_list */ -symbols: words_tSYMBOLS_BEG_symbol_list { $$ = @builder.word_array($1, symbols: true) }; -/* upstream parse.y:5945: symbol_list: %empty */ -symbol_list: %empty { $$ = [] }; -/* upstream parse.y:5950: symbol_list: symbol_list word nonempty_list_' ' */ -symbol_list: symbol_list word nonempty_list____ { $$ = ($1 + [$2]).freeze }; -/* upstream parse.y:3169: words_tQWORDS_BEG_qword_list: "verbatim word list" nonempty_list_' ' qword_list "terminator" */ -words_tQWORDS_BEG_qword_list: tQWORDS_BEG nonempty_list____ qword_list tSTRING_END %prec tSTRING_END { $$ = $3 }; -/* upstream parse.y:5956: qwords: words_tQWORDS_BEG_qword_list */ -qwords: words_tQWORDS_BEG_qword_list { $$ = @builder.word_array($1) }; -/* upstream parse.y:3169: words_tQSYMBOLS_BEG_qsym_list: "verbatim symbol list" nonempty_list_' ' qsym_list "terminator" */ -words_tQSYMBOLS_BEG_qsym_list: tQSYMBOLS_BEG nonempty_list____ qsym_list tSTRING_END %prec tSTRING_END { $$ = $3 }; -/* upstream parse.y:5959: qsymbols: words_tQSYMBOLS_BEG_qsym_list */ -qsymbols: words_tQSYMBOLS_BEG_qsym_list { $$ = @builder.word_array($1, symbols: true) }; -/* upstream parse.y:5963: qword_list: %empty */ -qword_list: %empty { $$ = [] }; -/* upstream parse.y:5968: qword_list: qword_list "literal content" nonempty_list_' ' */ -qword_list: qword_list tSTRING_CONTENT nonempty_list____ %prec tSTRING_CONTENT { $$ = ($1 + [$2]).freeze }; -/* upstream parse.y:5975: qsym_list: %empty */ -qsym_list: %empty { $$ = nil }; -/* upstream parse.y:5980: qsym_list: qsym_list "literal content" nonempty_list_' ' */ -qsym_list: qsym_list tSTRING_CONTENT nonempty_list____ %prec tSTRING_CONTENT { $$ = $1 }; -/* upstream parse.y:5987: string_contents: %empty */ -string_contents: %empty { $$ = [] }; -/* upstream parse.y:5992: string_contents: string_contents string_content */ -string_contents: string_contents string_content { $$ = ($1 + [$2]).freeze }; -/* upstream parse.y:5999: xstring_contents: %empty */ -xstring_contents: %empty { $$ = [] }; -/* upstream parse.y:6004: xstring_contents: xstring_contents string_content */ -xstring_contents: xstring_contents string_content { $$ = ($1 + [$2]).freeze }; -/* upstream parse.y:6011: regexp_contents: %empty */ -regexp_contents: %empty { $$ = [] }; -/* upstream parse.y:6016: regexp_contents: regexp_contents string_content */ -regexp_contents: regexp_contents string_content { $$ = ($1 + [$2]).freeze }; -/* upstream parse.y:6041: string_content: "literal content" */ -string_content: tSTRING_CONTENT %prec tSTRING_CONTENT { $$ = $1 }; -/* upstream parse.y:6044: @34: %empty */ -midrule_34: %empty { $$ = nil }; -/* upstream parse.y:6051: string_content: tSTRING_DVAR @34 string_dvar */ -string_content: tSTRING_DVAR midrule_34 string_dvar %prec tSTRING_DVAR { $$ = $1 }; -/* upstream parse.y:6058: @35: %empty */ -midrule_35: %empty { $$ = nil }; -/* upstream parse.y:6066: @36: %empty */ -midrule_36: %empty { $$ = nil }; -/* upstream parse.y:6070: @37: %empty */ -midrule_37: %empty { $$ = nil }; -/* upstream parse.y:6074: @38: %empty */ -midrule_38: %empty { $$ = nil }; -/* upstream parse.y:6079: string_content: "'#{'" @35 @36 @37 @38 compstmt_stmts string_dend */ -string_content: tSTRING_DBEG midrule_35 midrule_36 midrule_37 midrule_38 compstmt_stmts string_dend %prec tSTRING_DBEG { $$ = @builder.interpolation($6) }; -/* upstream parse.y:6094: string_dend: "'}'" */ -string_dend: tSTRING_DEND %prec tSTRING_DEND { $$ = nil }; -/* upstream parse.y:6095: string_dend: "end-of-input" */ -string_dend: END_OF_INPUT %prec END_OF_INPUT { $$ = nil }; -/* upstream parse.y:6099: string_dvar: nonlocal_var */ -string_dvar: nonlocal_var { $$ = $1 }; -/* upstream parse.y:6103: string_dvar: backref */ -string_dvar: backref { $$ = $1 }; -/* upstream parse.y:6106: symbol: ssym */ -symbol: ssym { $$ = $1 }; -/* upstream parse.y:6107: symbol: dsym */ -symbol: dsym { $$ = $1 }; -/* upstream parse.y:6111: ssym: "symbol literal" sym */ -ssym: tSYMBEG sym %prec tSYMBEG { $$ = @builder.symbol($2) }; -/* upstream parse.y:6126: sym: fname */ -sym: fname { $$ = $1 }; -/* upstream parse.y:6127: sym: nonlocal_var */ -sym: nonlocal_var { $$ = $1 }; -/* upstream parse.y:6131: dsym: "symbol literal" string_contents "terminator" */ -dsym: tSYMBEG string_contents tSTRING_END %prec tSTRING_END { $$ = @builder.symbol(@builder.join_strings($2)) }; -/* upstream parse.y:6138: numeric: simple_numeric */ -numeric: simple_numeric { $$ = $1 }; -/* upstream parse.y:6140: numeric: tUMINUS_NUM simple_numeric */ -numeric: tUMINUS_NUM simple_numeric %prec tLOWEST { $$ = @builder.unary(:-, $2) }; -/* upstream parse.y:6147: simple_numeric: "integer literal" */ -simple_numeric: tINTEGER %prec tINTEGER { $$ = @builder.integer($1) }; -/* upstream parse.y:6148: simple_numeric: "float literal" */ -simple_numeric: tFLOAT %prec tFLOAT { $$ = @builder.float($1) }; -/* upstream parse.y:6149: simple_numeric: "rational literal" */ -simple_numeric: tRATIONAL %prec tRATIONAL { $$ = @builder.literal($1) }; -/* upstream parse.y:6150: simple_numeric: "imaginary literal" */ -simple_numeric: tIMAGINARY %prec tIMAGINARY { $$ = @builder.literal($1) }; -/* upstream parse.y:6153: nonlocal_var: "instance variable" */ -nonlocal_var: tIVAR %prec tIVAR { $$ = @builder.variable(:instance, $1) }; -/* upstream parse.y:6154: nonlocal_var: "global variable" */ -nonlocal_var: tGVAR %prec tGVAR { $$ = @builder.variable(:global, $1) }; -/* upstream parse.y:6155: nonlocal_var: "class variable" */ -nonlocal_var: tCVAR %prec tCVAR { $$ = @builder.variable(:class, $1) }; -/* upstream parse.y:6158: user_variable: "local variable or method" */ -user_variable: tIDENTIFIER %prec tIDENTIFIER { $$ = @builder.identifier($1) }; -/* upstream parse.y:6158: user_variable: "constant" */ -user_variable: tCONSTANT %prec tCONSTANT { $$ = @builder.variable(:constant, $1) }; -/* upstream parse.y:6159: user_variable: nonlocal_var */ -user_variable: nonlocal_var { $$ = $1 }; -/* upstream parse.y:6162: keyword_variable: "'nil'" */ -keyword_variable: keyword_nil %prec keyword_nil { $$ = @builder.literal(nil) }; -/* upstream parse.y:6163: keyword_variable: "'self'" */ -keyword_variable: keyword_self %prec keyword_self { $$ = @builder.literal(:self) }; -/* upstream parse.y:6164: keyword_variable: "'true'" */ -keyword_variable: keyword_true %prec keyword_true { $$ = @builder.literal(true) }; -/* upstream parse.y:6165: keyword_variable: "'false'" */ -keyword_variable: keyword_false %prec keyword_false { $$ = @builder.literal(false) }; -/* upstream parse.y:6166: keyword_variable: "'__FILE__'" */ -keyword_variable: keyword__FILE__ %prec keyword__FILE__ { $$ = @builder.literal(:__FILE__) }; -/* upstream parse.y:6167: keyword_variable: "'__LINE__'" */ -keyword_variable: keyword__LINE__ %prec keyword__LINE__ { $$ = @builder.literal(:__LINE__) }; -/* upstream parse.y:6168: keyword_variable: "'__ENCODING__'" */ -keyword_variable: keyword__ENCODING__ %prec keyword__ENCODING__ { $$ = @builder.literal(:__ENCODING__) }; -/* upstream parse.y:6172: var_ref: user_variable */ -var_ref: user_variable { $$ = @builder.read_local($1) }; -/* upstream parse.y:6182: var_ref: keyword_variable */ -var_ref: keyword_variable { $$ = $1 }; -/* upstream parse.y:6189: var_lhs: user_variable */ -var_lhs: user_variable { $$ = $1 }; -/* upstream parse.y:6189: var_lhs: keyword_variable */ -var_lhs: keyword_variable { $$ = $1 }; -/* upstream parse.y:6195: backref: "numbered reference" */ -backref: tNTH_REF %prec tNTH_REF { $$ = @builder.variable(:backref, $1) }; -/* upstream parse.y:6196: backref: "back reference" */ -backref: tBACK_REF %prec tBACK_REF { $$ = @builder.variable(:backref, $1) }; -/* upstream parse.y:6200: $@39: %empty */ -midrule_39: %empty { $$ = nil }; -/* upstream parse.y:6205: superclass: '<' $@39 expr_value term */ -superclass: '<' midrule_39 expr_value term %prec '<' { $$ = $3 }; -/* upstream parse.y:6209: superclass: none */ -superclass: none { $$ = nil }; -/* upstream parse.y:6212: f_opt_paren_args: f_paren_args */ -f_opt_paren_args: f_paren_args { $$ = $1 }; -/* upstream parse.y:6214: f_opt_paren_args: f_empty_arg */ -f_opt_paren_args: f_empty_arg { $$ = nil }; -/* upstream parse.y:6220: f_empty_arg: %empty */ -f_empty_arg: %empty { $$ = nil }; -/* upstream parse.y:6228: f_paren_args: '(' f_args rparen */ -f_paren_args: '(' f_args rparen %prec '(' { $$ = $2 }; -/* upstream parse.y:6237: f_arglist: f_paren_args */ -f_arglist: f_paren_args { $$ = $1 }; -/* upstream parse.y:6238: @40: %empty */ -midrule_40: %empty { $$ = nil }; -/* upstream parse.y:6245: f_arglist: @40 f_args term */ -f_arglist: midrule_40 f_args term { $$ = $2 }; -/* upstream parse.y:3020: f_kw_arg_value: f_label arg_value */ -f_kw_arg_value: f_label arg_value { $$ = $1 }; -/* upstream parse.y:3026: f_kw_arg_value: f_label */ -f_kw_arg_value: f_label { $$ = $1 }; -/* upstream parse.y:3035: f_kwarg_arg_value: f_kw_arg_value */ -f_kwarg_arg_value: f_kw_arg_value { $$ = $1 }; -/* upstream parse.y:3040: f_kwarg_arg_value: f_kwarg_arg_value ',' f_kw_arg_value */ -f_kwarg_arg_value: f_kwarg_arg_value ',' f_kw_arg_value %prec ',' { $$ = $1 }; -/* upstream parse.y:2957: opt_f_block_arg_opt_comma: ',' f_block_arg */ -opt_f_block_arg_opt_comma: ',' f_block_arg %prec ',' { $$ = $2 }; -/* upstream parse.y:6255: opt_f_block_arg_opt_comma: opt_comma */ -opt_f_block_arg_opt_comma: opt_comma { $$ = $1 }; -/* upstream parse.y:2934: args_tail_basic_arg_value_opt_comma: f_kwarg_arg_value ',' f_kwrest opt_f_block_arg_opt_comma */ -args_tail_basic_arg_value_opt_comma: f_kwarg_arg_value ',' f_kwrest opt_f_block_arg_opt_comma %prec ',' { $$ = $1 }; -/* upstream parse.y:2939: args_tail_basic_arg_value_opt_comma: f_kwarg_arg_value opt_f_block_arg_opt_comma */ -args_tail_basic_arg_value_opt_comma: f_kwarg_arg_value opt_f_block_arg_opt_comma { $$ = $1 }; -/* upstream parse.y:2944: args_tail_basic_arg_value_opt_comma: f_any_kwrest opt_f_block_arg_opt_comma */ -args_tail_basic_arg_value_opt_comma: f_any_kwrest opt_f_block_arg_opt_comma { $$ = $1 }; -/* upstream parse.y:2949: args_tail_basic_arg_value_opt_comma: f_block_arg */ -args_tail_basic_arg_value_opt_comma: f_block_arg { $$ = $1 }; -/* upstream parse.y:6255: args_tail: args_tail_basic_arg_value_opt_comma */ -args_tail: args_tail_basic_arg_value_opt_comma { $$ = $1 }; -/* upstream parse.y:6257: args_tail: args_forward */ -args_tail: args_forward { $$ = $1 }; -/* upstream parse.y:2934: args_tail_basic_arg_value_none: f_kwarg_arg_value ',' f_kwrest opt_f_block_arg_none */ -args_tail_basic_arg_value_none: f_kwarg_arg_value ',' f_kwrest opt_f_block_arg_none %prec ',' { $$ = $1 }; -/* upstream parse.y:2939: args_tail_basic_arg_value_none: f_kwarg_arg_value opt_f_block_arg_none */ -args_tail_basic_arg_value_none: f_kwarg_arg_value opt_f_block_arg_none { $$ = $1 }; -/* upstream parse.y:2944: args_tail_basic_arg_value_none: f_any_kwrest opt_f_block_arg_none */ -args_tail_basic_arg_value_none: f_any_kwrest opt_f_block_arg_none { $$ = $1 }; -/* upstream parse.y:2949: args_tail_basic_arg_value_none: f_block_arg */ -args_tail_basic_arg_value_none: f_block_arg { $$ = $1 }; -/* upstream parse.y:6265: largs_tail: args_tail_basic_arg_value_none */ -largs_tail: args_tail_basic_arg_value_none { $$ = $1 }; -/* upstream parse.y:6267: largs_tail: args_forward */ -largs_tail: args_forward { $$ = $1 }; -/* upstream parse.y:2998: f_opt_arg_value: f_arg_asgn f_eq arg_value */ -f_opt_arg_value: f_arg_asgn f_eq arg_value { $$ = $1 }; -/* upstream parse.y:3007: f_opt_arg_arg_value: f_opt_arg_value */ -f_opt_arg_arg_value: f_opt_arg_value { $$ = $1 }; -/* upstream parse.y:3012: f_opt_arg_arg_value: f_opt_arg_arg_value ',' f_opt_arg_value */ -f_opt_arg_arg_value: f_opt_arg_arg_value ',' f_opt_arg_value %prec ',' { $$ = $1 }; -/* upstream parse.y:3107: opt_args_tail_args_tail_opt_comma: ',' args_tail */ -opt_args_tail_args_tail_opt_comma: ',' args_tail %prec ',' { $$ = $2 }; -/* upstream parse.y:3112: opt_args_tail_args_tail_opt_comma: opt_comma */ -opt_args_tail_args_tail_opt_comma: opt_comma { $$ = $1 }; -/* upstream parse.y:6277: args-list_arg_value_opt_args_tail_args_tail_opt_comma: f_arg ',' f_opt_arg_arg_value ',' f_rest_arg opt_args_tail_args_tail_opt_comma */ -args_list_arg_value_opt_args_tail_args_tail_opt_comma: f_arg ',' f_opt_arg_arg_value ',' f_rest_arg opt_args_tail_args_tail_opt_comma %prec ',' { $$ = $1 }; -/* upstream parse.y:6282: args-list_arg_value_opt_args_tail_args_tail_opt_comma: f_arg ',' f_opt_arg_arg_value ',' f_rest_arg ',' f_arg opt_args_tail_args_tail_opt_comma */ -args_list_arg_value_opt_args_tail_args_tail_opt_comma: f_arg ',' f_opt_arg_arg_value ',' f_rest_arg ',' f_arg opt_args_tail_args_tail_opt_comma %prec ',' { $$ = $1 }; -/* upstream parse.y:6287: args-list_arg_value_opt_args_tail_args_tail_opt_comma: f_arg ',' f_opt_arg_arg_value opt_args_tail_args_tail_opt_comma */ -args_list_arg_value_opt_args_tail_args_tail_opt_comma: f_arg ',' f_opt_arg_arg_value opt_args_tail_args_tail_opt_comma %prec ',' { $$ = $1 }; -/* upstream parse.y:6292: args-list_arg_value_opt_args_tail_args_tail_opt_comma: f_arg ',' f_opt_arg_arg_value ',' f_arg opt_args_tail_args_tail_opt_comma */ -args_list_arg_value_opt_args_tail_args_tail_opt_comma: f_arg ',' f_opt_arg_arg_value ',' f_arg opt_args_tail_args_tail_opt_comma %prec ',' { $$ = $1 }; -/* upstream parse.y:6297: args-list_arg_value_opt_args_tail_args_tail_opt_comma: f_arg ',' f_rest_arg opt_args_tail_args_tail_opt_comma */ -args_list_arg_value_opt_args_tail_args_tail_opt_comma: f_arg ',' f_rest_arg opt_args_tail_args_tail_opt_comma %prec ',' { $$ = $1 }; -/* upstream parse.y:6302: args-list_arg_value_opt_args_tail_args_tail_opt_comma: f_arg ',' f_rest_arg ',' f_arg opt_args_tail_args_tail_opt_comma */ -args_list_arg_value_opt_args_tail_args_tail_opt_comma: f_arg ',' f_rest_arg ',' f_arg opt_args_tail_args_tail_opt_comma %prec ',' { $$ = $1 }; -/* upstream parse.y:6307: args-list_arg_value_opt_args_tail_args_tail_opt_comma: f_opt_arg_arg_value ',' f_rest_arg opt_args_tail_args_tail_opt_comma */ -args_list_arg_value_opt_args_tail_args_tail_opt_comma: f_opt_arg_arg_value ',' f_rest_arg opt_args_tail_args_tail_opt_comma %prec ',' { $$ = $1 }; -/* upstream parse.y:6312: args-list_arg_value_opt_args_tail_args_tail_opt_comma: f_opt_arg_arg_value ',' f_rest_arg ',' f_arg opt_args_tail_args_tail_opt_comma */ -args_list_arg_value_opt_args_tail_args_tail_opt_comma: f_opt_arg_arg_value ',' f_rest_arg ',' f_arg opt_args_tail_args_tail_opt_comma %prec ',' { $$ = $1 }; -/* upstream parse.y:6317: args-list_arg_value_opt_args_tail_args_tail_opt_comma: f_opt_arg_arg_value opt_args_tail_args_tail_opt_comma */ -args_list_arg_value_opt_args_tail_args_tail_opt_comma: f_opt_arg_arg_value opt_args_tail_args_tail_opt_comma { $$ = $1 }; -/* upstream parse.y:6322: args-list_arg_value_opt_args_tail_args_tail_opt_comma: f_opt_arg_arg_value ',' f_arg opt_args_tail_args_tail_opt_comma */ -args_list_arg_value_opt_args_tail_args_tail_opt_comma: f_opt_arg_arg_value ',' f_arg opt_args_tail_args_tail_opt_comma %prec ',' { $$ = $1 }; -/* upstream parse.y:6327: args-list_arg_value_opt_args_tail_args_tail_opt_comma: f_rest_arg opt_args_tail_args_tail_opt_comma */ -args_list_arg_value_opt_args_tail_args_tail_opt_comma: f_rest_arg opt_args_tail_args_tail_opt_comma { $$ = $1 }; -/* upstream parse.y:6332: args-list_arg_value_opt_args_tail_args_tail_opt_comma: f_rest_arg ',' f_arg opt_args_tail_args_tail_opt_comma */ -args_list_arg_value_opt_args_tail_args_tail_opt_comma: f_rest_arg ',' f_arg opt_args_tail_args_tail_opt_comma %prec ',' { $$ = $1 }; -/* upstream parse.y:6357: f_args-list_args_tail_opt_comma: args-list_arg_value_opt_args_tail_args_tail_opt_comma */ -f_args_list_args_tail_opt_comma: args_list_arg_value_opt_args_tail_args_tail_opt_comma { $$ = $1 }; -/* upstream parse.y:6349: f_args-list_args_tail_opt_comma: f_arg opt_args_tail_args_tail_opt_comma */ -f_args_list_args_tail_opt_comma: f_arg opt_args_tail_args_tail_opt_comma { $$ = $1 }; -/* upstream parse.y:6340: tail-only-args_args_tail: args_tail */ -tail_only_args_args_tail: args_tail { $$ = $1 }; -/* upstream parse.y:6357: f_args-list_args_tail_opt_comma: tail-only-args_args_tail */ -f_args_list_args_tail_opt_comma: tail_only_args_args_tail { $$ = $1 }; -/* upstream parse.y:6357: f_args-list_args_tail_opt_comma: f_empty_arg */ -f_args_list_args_tail_opt_comma: f_empty_arg { $$ = [] }; -/* upstream parse.y:6357: f_args: f_args-list_args_tail_opt_comma */ -f_args: f_args_list_args_tail_opt_comma { $$ = $1 }; -/* upstream parse.y:3107: opt_args_tail_largs_tail_none: ',' largs_tail */ -opt_args_tail_largs_tail_none: ',' largs_tail %prec ',' { $$ = $2 }; -/* upstream parse.y:3112: opt_args_tail_largs_tail_none: none */ -opt_args_tail_largs_tail_none: none { $$ = nil }; -/* upstream parse.y:6277: args-list_arg_value_opt_args_tail_largs_tail_none: f_arg ',' f_opt_arg_arg_value ',' f_rest_arg opt_args_tail_largs_tail_none */ -args_list_arg_value_opt_args_tail_largs_tail_none: f_arg ',' f_opt_arg_arg_value ',' f_rest_arg opt_args_tail_largs_tail_none %prec ',' { $$ = $1 }; -/* upstream parse.y:6282: args-list_arg_value_opt_args_tail_largs_tail_none: f_arg ',' f_opt_arg_arg_value ',' f_rest_arg ',' f_arg opt_args_tail_largs_tail_none */ -args_list_arg_value_opt_args_tail_largs_tail_none: f_arg ',' f_opt_arg_arg_value ',' f_rest_arg ',' f_arg opt_args_tail_largs_tail_none %prec ',' { $$ = $1 }; -/* upstream parse.y:6287: args-list_arg_value_opt_args_tail_largs_tail_none: f_arg ',' f_opt_arg_arg_value opt_args_tail_largs_tail_none */ -args_list_arg_value_opt_args_tail_largs_tail_none: f_arg ',' f_opt_arg_arg_value opt_args_tail_largs_tail_none %prec ',' { $$ = $1 }; -/* upstream parse.y:6292: args-list_arg_value_opt_args_tail_largs_tail_none: f_arg ',' f_opt_arg_arg_value ',' f_arg opt_args_tail_largs_tail_none */ -args_list_arg_value_opt_args_tail_largs_tail_none: f_arg ',' f_opt_arg_arg_value ',' f_arg opt_args_tail_largs_tail_none %prec ',' { $$ = $1 }; -/* upstream parse.y:6297: args-list_arg_value_opt_args_tail_largs_tail_none: f_arg ',' f_rest_arg opt_args_tail_largs_tail_none */ -args_list_arg_value_opt_args_tail_largs_tail_none: f_arg ',' f_rest_arg opt_args_tail_largs_tail_none %prec ',' { $$ = $1 }; -/* upstream parse.y:6302: args-list_arg_value_opt_args_tail_largs_tail_none: f_arg ',' f_rest_arg ',' f_arg opt_args_tail_largs_tail_none */ -args_list_arg_value_opt_args_tail_largs_tail_none: f_arg ',' f_rest_arg ',' f_arg opt_args_tail_largs_tail_none %prec ',' { $$ = $1 }; -/* upstream parse.y:6307: args-list_arg_value_opt_args_tail_largs_tail_none: f_opt_arg_arg_value ',' f_rest_arg opt_args_tail_largs_tail_none */ -args_list_arg_value_opt_args_tail_largs_tail_none: f_opt_arg_arg_value ',' f_rest_arg opt_args_tail_largs_tail_none %prec ',' { $$ = $1 }; -/* upstream parse.y:6312: args-list_arg_value_opt_args_tail_largs_tail_none: f_opt_arg_arg_value ',' f_rest_arg ',' f_arg opt_args_tail_largs_tail_none */ -args_list_arg_value_opt_args_tail_largs_tail_none: f_opt_arg_arg_value ',' f_rest_arg ',' f_arg opt_args_tail_largs_tail_none %prec ',' { $$ = $1 }; -/* upstream parse.y:6317: args-list_arg_value_opt_args_tail_largs_tail_none: f_opt_arg_arg_value opt_args_tail_largs_tail_none */ -args_list_arg_value_opt_args_tail_largs_tail_none: f_opt_arg_arg_value opt_args_tail_largs_tail_none { $$ = $1 }; -/* upstream parse.y:6322: args-list_arg_value_opt_args_tail_largs_tail_none: f_opt_arg_arg_value ',' f_arg opt_args_tail_largs_tail_none */ -args_list_arg_value_opt_args_tail_largs_tail_none: f_opt_arg_arg_value ',' f_arg opt_args_tail_largs_tail_none %prec ',' { $$ = $1 }; -/* upstream parse.y:6327: args-list_arg_value_opt_args_tail_largs_tail_none: f_rest_arg opt_args_tail_largs_tail_none */ -args_list_arg_value_opt_args_tail_largs_tail_none: f_rest_arg opt_args_tail_largs_tail_none { $$ = $1 }; -/* upstream parse.y:6332: args-list_arg_value_opt_args_tail_largs_tail_none: f_rest_arg ',' f_arg opt_args_tail_largs_tail_none */ -args_list_arg_value_opt_args_tail_largs_tail_none: f_rest_arg ',' f_arg opt_args_tail_largs_tail_none %prec ',' { $$ = $1 }; -/* upstream parse.y:6360: f_args-list_largs_tail_none: args-list_arg_value_opt_args_tail_largs_tail_none */ -f_args_list_largs_tail_none: args_list_arg_value_opt_args_tail_largs_tail_none { $$ = $1 }; -/* upstream parse.y:6349: f_args-list_largs_tail_none: f_arg opt_args_tail_largs_tail_none */ -f_args_list_largs_tail_none: f_arg opt_args_tail_largs_tail_none { $$ = $1 }; -/* upstream parse.y:6340: tail-only-args_largs_tail: largs_tail */ -tail_only_args_largs_tail: largs_tail { $$ = $1 }; -/* upstream parse.y:6360: f_args-list_largs_tail_none: tail-only-args_largs_tail */ -f_args_list_largs_tail_none: tail_only_args_largs_tail { $$ = $1 }; -/* upstream parse.y:6360: f_args-list_largs_tail_none: f_empty_arg */ -f_args_list_largs_tail_none: f_empty_arg { $$ = nil }; -/* upstream parse.y:6360: f_largs: f_args-list_largs_tail_none */ -f_largs: f_args_list_largs_tail_none { $$ = $1 }; -/* upstream parse.y:6364: args_forward: "(..." */ -args_forward: tBDOT3 %prec tBDOT3 { $$ = nil }; -/* upstream parse.y:6371: f_bad_arg: "constant" */ -f_bad_arg: tCONSTANT %prec tCONSTANT { $$ = $1 }; -/* upstream parse.y:6380: f_bad_arg: "instance variable" */ -f_bad_arg: tIVAR %prec tIVAR { $$ = nil }; -/* upstream parse.y:6389: f_bad_arg: "global variable" */ -f_bad_arg: tGVAR %prec tGVAR { $$ = nil }; -/* upstream parse.y:6398: f_bad_arg: "class variable" */ -f_bad_arg: tCVAR %prec tCVAR { $$ = nil }; -/* upstream parse.y:6408: f_norm_arg: f_bad_arg */ -f_norm_arg: f_bad_arg { $$ = $1 }; -/* upstream parse.y:6410: f_norm_arg: "local variable or method" */ -f_norm_arg: tIDENTIFIER %prec tIDENTIFIER { $$ = $1 }; -/* upstream parse.y:6420: f_arg_asgn: f_norm_arg */ -f_arg_asgn: f_norm_arg { $$ = $1 }; -/* upstream parse.y:6427: f_arg_item: f_arg_asgn */ -f_arg_item: f_arg_asgn { $$ = $1 }; -/* upstream parse.y:6432: f_arg_item: "(" f_margs rparen */ -f_arg_item: tLPAREN f_margs rparen %prec tLPAREN { $$ = $1 }; -/* upstream parse.y:6450: f_arg: f_arg_item */ -f_arg: f_arg_item { $$ = $1 }; -/* upstream parse.y:6453: f_arg: f_arg ',' f_arg_item */ -f_arg: f_arg ',' f_arg_item %prec ',' { $$ = $1 }; -/* upstream parse.y:6464: f_label: "label" */ -f_label: tLABEL %prec tLABEL { $$ = $1 }; -/* upstream parse.y:6483: kwrest_mark: "**" */ -kwrest_mark: tPOW %prec tPOW { $$ = nil }; -/* upstream parse.y:6484: kwrest_mark: "**arg" */ -kwrest_mark: tDSTAR %prec tDSTAR { $$ = nil }; -/* upstream parse.y:6488: f_no_kwarg: p_kwnorest */ -f_no_kwarg: p_kwnorest { $$ = $1 }; -/* upstream parse.y:6494: f_kwrest: kwrest_mark "local variable or method" */ -f_kwrest: kwrest_mark tIDENTIFIER %prec tIDENTIFIER { $$ = $2 }; -/* upstream parse.y:6500: f_kwrest: kwrest_mark */ -f_kwrest: kwrest_mark { $$ = $1 }; -/* upstream parse.y:6507: restarg_mark: '*' */ -restarg_mark: '*' %prec '*' { $$ = nil }; -/* upstream parse.y:6508: restarg_mark: "*" */ -restarg_mark: tSTAR %prec tSTAR { $$ = nil }; -/* upstream parse.y:6512: f_rest_arg: restarg_mark "local variable or method" */ -f_rest_arg: restarg_mark tIDENTIFIER %prec tIDENTIFIER { $$ = [$2].freeze }; -/* upstream parse.y:6518: f_rest_arg: restarg_mark */ -f_rest_arg: restarg_mark { $$ = [] }; -/* upstream parse.y:6525: blkarg_mark: '&' */ -blkarg_mark: '&' %prec '&' { $$ = nil }; -/* upstream parse.y:6526: blkarg_mark: "&" */ -blkarg_mark: tAMPER %prec tAMPER { $$ = nil }; -/* upstream parse.y:6530: f_block_arg: blkarg_mark "local variable or method" */ -f_block_arg: blkarg_mark tIDENTIFIER %prec tIDENTIFIER { $$ = $2 }; -/* upstream parse.y:6536: f_block_arg: blkarg_mark "'nil'" */ -f_block_arg: blkarg_mark keyword_nil %prec keyword_nil { $$ = nil }; -/* upstream parse.y:6541: f_block_arg: blkarg_mark */ -f_block_arg: blkarg_mark { $$ = $1 }; -/* upstream parse.y:6548: option_',': %empty */ -option____: %empty { $$ = nil }; -/* upstream parse.y:6548: option_',': ',' */ -option____: ',' %prec ',' { $$ = $1 }; -/* upstream parse.y:6549: opt_comma: option_',' */ -opt_comma: option____ { $$ = $1 }; -/* upstream parse.y:3161: value_expr_singleton_expr: singleton_expr */ -value_expr_singleton_expr: singleton_expr { $$ = $1 }; -/* upstream parse.y:6557: singleton: value_expr_singleton_expr */ -singleton: value_expr_singleton_expr { $$ = $1 }; -/* upstream parse.y:6586: singleton_expr: var_ref */ -singleton_expr: var_ref { $$ = $1 }; -/* upstream parse.y:6588: $@41: %empty */ -midrule_41: %empty { $$ = nil }; -/* upstream parse.y:6593: singleton_expr: '(' $@41 expr rparen */ -singleton_expr: '(' midrule_41 expr rparen %prec '(' { $$ = $3 }; -/* upstream parse.y:6600: assoc_list: none */ -assoc_list: none { $$ = [].freeze }; -/* upstream parse.y:6602: assoc_list: assocs trailer */ -assoc_list: assocs trailer { $$ = $1 }; -/* upstream parse.y:6608: assocs: assoc */ -assocs: assoc { $$ = [$1].freeze }; -/* upstream parse.y:6611: assocs: assocs ',' assoc */ -assocs: assocs ',' assoc %prec ',' { $$ = ($1 + [$3]).freeze }; -/* upstream parse.y:6634: assoc: arg_value "=>" arg_value */ -assoc: arg_value tASSOC arg_value %prec tASSOC { $$ = @builder.pair($1, $3) }; -/* upstream parse.y:6639: assoc: "label" arg_value */ -assoc: tLABEL arg_value %prec tLABEL { $$ = @builder.pair(@builder.symbol($1), $2) }; -/* upstream parse.y:6644: assoc: "label" */ -assoc: tLABEL %prec tLABEL { $$ = $1 }; -/* upstream parse.y:6651: assoc: "string literal" string_contents tLABEL_END arg_value */ -assoc: tSTRING_BEG string_contents tLABEL_END arg_value %prec tLABEL_END { $$ = $4 }; -/* upstream parse.y:6657: assoc: "**arg" arg_value */ -assoc: tDSTAR arg_value %prec tDSTAR { $$ = $2 }; -/* upstream parse.y:6662: assoc: "**arg" */ -assoc: tDSTAR %prec tDSTAR { $$ = nil }; -/* upstream parse.y:6674: operation2: "local variable or method" */ -operation2: tIDENTIFIER %prec tIDENTIFIER { $$ = $1 }; -/* upstream parse.y:6674: operation2: "constant" */ -operation2: tCONSTANT %prec tCONSTANT { $$ = $1 }; -/* upstream parse.y:6674: operation2: "method" */ -operation2: tFID %prec tFID { $$ = $1 }; -/* upstream parse.y:6675: operation2: op */ -operation2: op { $$ = $1 }; -/* upstream parse.y:6678: operation3: "local variable or method" */ -operation3: tIDENTIFIER %prec tIDENTIFIER { $$ = $1 }; -/* upstream parse.y:6679: operation3: "method" */ -operation3: tFID %prec tFID { $$ = $1 }; -/* upstream parse.y:6680: operation3: op */ -operation3: op { $$ = $1 }; -/* upstream parse.y:6683: dot_or_colon: '.' */ -dot_or_colon: '.' %prec '.' { $$ = @builder.operator(".") }; -/* upstream parse.y:6684: dot_or_colon: "::" */ -dot_or_colon: tCOLON2 %prec tCOLON2 { $$ = @builder.operator("::") }; -/* upstream parse.y:6687: call_op: '.' */ -call_op: '.' %prec '.' { $$ = @builder.operator(".") }; -/* upstream parse.y:6688: call_op: "&." */ -call_op: tANDDOT %prec tANDDOT { $$ = @builder.operator("&.") }; -/* upstream parse.y:6691: call_op2: call_op */ -call_op2: call_op { $$ = $1 }; -/* upstream parse.y:6692: call_op2: "::" */ -call_op2: tCOLON2 %prec tCOLON2 { $$ = @builder.operator("::") }; -/* upstream parse.y:6695: rparen: option_'\n' ')' */ -rparen: option_newline ')' %prec ')' { $$ = nil }; -/* upstream parse.y:6698: rbracket: option_'\n' ']' */ -rbracket: option_newline ']' %prec ']' { $$ = nil }; -/* upstream parse.y:6701: rbrace: option_'\n' '}' */ -rbrace: option_newline '}' %prec '}' { $$ = nil }; -/* upstream parse.y:6704: trailer: option_'\n' */ -trailer: option_newline { $$ = nil }; -/* upstream parse.y:6705: trailer: ',' */ -trailer: ',' %prec ',' { $$ = nil }; -/* upstream parse.y:6709: term: ';' */ -term: ';' %prec ';' { $$ = nil }; -/* upstream parse.y:6717: term: '\n' */ -term: '\n' %prec '\n' { $$ = nil }; -/* upstream parse.y:6723: terms: term */ -terms: term { $$ = nil }; -/* upstream parse.y:6724: terms: terms ';' */ -terms: terms ';' %prec ';' { $$ = nil }; -/* upstream parse.y:6728: none: %empty */ -none: %empty { $$ = nil }; -/* arpaka extension: block call as a parenthesized argument */ -call_args: block_call { $$ = [$1].freeze }; -call_args: args ',' block_call %prec tLOWEST { $$ = ($1 + [$3]).freeze }; diff --git a/lib/lrama/ruby/languages/ruby/rules.json b/lib/lrama/ruby/languages/ruby/rules.json deleted file mode 100644 index 09fa3e2..0000000 --- a/lib/lrama/ruby/languages/ruby/rules.json +++ /dev/null @@ -1,5120 +0,0 @@ -[ - { - "id": 1, - "rule": "$@1: %empty", - "line": 3176, - "status": "empty" - }, - { - "id": 2, - "rule": "option_terms: %empty", - "line": 3182, - "status": "empty" - }, - { - "id": 3, - "rule": "option_terms: terms", - "line": 3182, - "status": "empty" - }, - { - "id": 4, - "rule": "compstmt_top_stmts: top_stmts option_terms", - "line": 2991, - "status": "pass_through" - }, - { - "id": 5, - "rule": "program: $@1 compstmt_top_stmts", - "line": 3183, - "status": "ported" - }, - { - "id": 6, - "rule": "top_stmts: none", - "line": 3203, - "status": "ported" - }, - { - "id": 7, - "rule": "top_stmts: top_stmt", - "line": 3208, - "status": "ported" - }, - { - "id": 8, - "rule": "top_stmts: top_stmts terms top_stmt", - "line": 3213, - "status": "ported" - }, - { - "id": 9, - "rule": "top_stmt: stmt", - "line": 3220, - "status": "pass_through" - }, - { - "id": 10, - "rule": "top_stmt: \"'BEGIN'\" begin_block", - "line": 3225, - "status": "pass_through" - }, - { - "id": 11, - "rule": "block_open: '{'", - "line": 3231, - "status": "empty" - }, - { - "id": 12, - "rule": "begin_block: block_open compstmt_top_stmts '}'", - "line": 3234, - "status": "pass_through" - }, - { - "id": 13, - "rule": "compstmt_stmts: stmts option_terms", - "line": 2991, - "status": "pass_through" - }, - { - "id": 14, - "rule": "$@2: %empty", - "line": 3247, - "status": "empty" - }, - { - "id": 15, - "rule": "$@3: %empty", - "line": 3252, - "status": "empty" - }, - { - "id": 16, - "rule": "bodystmt: compstmt_stmts lex_ctxt opt_rescue k_else $@2 compstmt_stmts $@3 opt_ensure", - "line": 3256, - "status": "ported" - }, - { - "id": 17, - "rule": "$@4: %empty", - "line": 3263, - "status": "empty" - }, - { - "id": 18, - "rule": "bodystmt: compstmt_stmts lex_ctxt opt_rescue $@4 opt_ensure", - "line": 3267, - "status": "ported" - }, - { - "id": 19, - "rule": "stmts: none", - "line": 3274, - "status": "ported" - }, - { - "id": 20, - "rule": "stmts: stmt_or_begin", - "line": 3279, - "status": "ported" - }, - { - "id": 21, - "rule": "stmts: stmts terms stmt_or_begin", - "line": 3284, - "status": "ported" - }, - { - "id": 22, - "rule": "stmt_or_begin: stmt", - "line": 3290, - "status": "pass_through" - }, - { - "id": 23, - "rule": "$@5: %empty", - "line": 3292, - "status": "empty" - }, - { - "id": 24, - "rule": "stmt_or_begin: \"'BEGIN'\" $@5 begin_block", - "line": 3296, - "status": "pass_through" - }, - { - "id": 25, - "rule": "allow_exits: %empty", - "line": 3301, - "status": "empty" - }, - { - "id": 26, - "rule": "k_END: \"'END'\" lex_ctxt", - "line": 3304, - "status": "pass_through" - }, - { - "id": 27, - "rule": "$@6: %empty", - "line": 3313, - "status": "empty" - }, - { - "id": 28, - "rule": "stmt: \"'alias'\" fitem $@6 fitem", - "line": 3314, - "status": "pass_through" - }, - { - "id": 29, - "rule": "stmt: \"'alias'\" \"global variable\" \"global variable\"", - "line": 3319, - "status": "pass_through" - }, - { - "id": 30, - "rule": "stmt: \"'alias'\" \"global variable\" \"back reference\"", - "line": 3324, - "status": "pass_through" - }, - { - "id": 31, - "rule": "stmt: \"'alias'\" \"global variable\" \"numbered reference\"", - "line": 3332, - "status": "pass_through" - }, - { - "id": 32, - "rule": "stmt: \"'undef'\" undef_list", - "line": 3341, - "status": "pass_through" - }, - { - "id": 33, - "rule": "stmt: stmt \"'if' modifier\" expr_value", - "line": 3348, - "status": "ported" - }, - { - "id": 34, - "rule": "stmt: stmt \"'unless' modifier\" expr_value", - "line": 3354, - "status": "ported" - }, - { - "id": 35, - "rule": "stmt: stmt \"'while' modifier\" expr_value", - "line": 3360, - "status": "ported" - }, - { - "id": 36, - "rule": "stmt: stmt \"'until' modifier\" expr_value", - "line": 3371, - "status": "ported" - }, - { - "id": 37, - "rule": "stmt: stmt \"'rescue' modifier\" after_rescue stmt", - "line": 3382, - "status": "ported" - }, - { - "id": 38, - "rule": "stmt: k_END block_open compstmt_stmts '}'", - "line": 3391, - "status": "pass_through" - }, - { - "id": 39, - "rule": "stmt: command_asgn", - "line": 3402, - "status": "pass_through" - }, - { - "id": 40, - "rule": "stmt: mlhs '=' lex_ctxt command_call_value", - "line": 3404, - "status": "pass_through" - }, - { - "id": 41, - "rule": "asgn_mrhs: lhs '=' lex_ctxt mrhs", - "line": 2926, - "status": "pass_through" - }, - { - "id": 42, - "rule": "stmt: asgn_mrhs", - "line": 3408, - "status": "pass_through" - }, - { - "id": 43, - "rule": "stmt: mlhs '=' lex_ctxt mrhs_arg \"'rescue' modifier\" after_rescue stmt", - "line": 3411, - "status": "ported" - }, - { - "id": 44, - "rule": "stmt: mlhs '=' lex_ctxt mrhs_arg", - "line": 3421, - "status": "pass_through" - }, - { - "id": 45, - "rule": "stmt: expr", - "line": 3425, - "status": "pass_through" - }, - { - "id": 46, - "rule": "stmt: error", - "line": 3427, - "status": "empty" - }, - { - "id": 47, - "rule": "asgn_command_rhs: lhs '=' lex_ctxt command_rhs", - "line": 2926, - "status": "pass_through" - }, - { - "id": 48, - "rule": "command_asgn: asgn_command_rhs", - "line": 3433, - "status": "pass_through" - }, - { - "id": 49, - "rule": "op_asgn_command_rhs: var_lhs \"operator-assignment\" lex_ctxt command_rhs", - "line": 3061, - "status": "pass_through" - }, - { - "id": 50, - "rule": "op_asgn_command_rhs: primary_value '[' opt_call_args rbracket \"operator-assignment\" lex_ctxt command_rhs", - "line": 3066, - "status": "pass_through" - }, - { - "id": 51, - "rule": "op_asgn_command_rhs: primary_value call_op \"local variable or method\" \"operator-assignment\" lex_ctxt command_rhs", - "line": 3071, - "status": "pass_through" - }, - { - "id": 52, - "rule": "op_asgn_command_rhs: primary_value call_op \"constant\" \"operator-assignment\" lex_ctxt command_rhs", - "line": 3076, - "status": "pass_through" - }, - { - "id": 53, - "rule": "op_asgn_command_rhs: primary_value \"::\" \"local variable or method\" \"operator-assignment\" lex_ctxt command_rhs", - "line": 3081, - "status": "pass_through" - }, - { - "id": 54, - "rule": "op_asgn_command_rhs: primary_value \"::\" \"constant\" \"operator-assignment\" lex_ctxt command_rhs", - "line": 3086, - "status": "pass_through" - }, - { - "id": 55, - "rule": "op_asgn_command_rhs: \":: at EXPR_BEG\" \"constant\" \"operator-assignment\" lex_ctxt command_rhs", - "line": 3092, - "status": "pass_through" - }, - { - "id": 56, - "rule": "op_asgn_command_rhs: backref \"operator-assignment\" lex_ctxt command_rhs", - "line": 3098, - "status": "pass_through" - }, - { - "id": 57, - "rule": "command_asgn: op_asgn_command_rhs", - "line": 3434, - "status": "pass_through" - }, - { - "id": 58, - "rule": "def_endless_method_endless_command: defn_head f_opt_paren_args '=' endless_command", - "line": 2966, - "status": "pass_through" - }, - { - "id": 59, - "rule": "def_endless_method_endless_command: defs_head f_opt_paren_args '=' endless_command", - "line": 2977, - "status": "pass_through" - }, - { - "id": 60, - "rule": "command_asgn: def_endless_method_endless_command", - "line": 3435, - "status": "pass_through" - }, - { - "id": 61, - "rule": "endless_command: command", - "line": 3438, - "status": "pass_through" - }, - { - "id": 62, - "rule": "endless_command: endless_command \"'rescue' modifier\" after_rescue arg", - "line": 3440, - "status": "pass_through" - }, - { - "id": 63, - "rule": "option_'\\n': %empty", - "line": 3445, - "status": "empty" - }, - { - "id": 64, - "rule": "option_'\\n': '\\n'", - "line": 3445, - "status": "pass_through" - }, - { - "id": 65, - "rule": "endless_command: \"'not'\" option_'\\n' endless_command", - "line": 3446, - "status": "pass_through" - }, - { - "id": 66, - "rule": "command_rhs: command_call_value", - "line": 3452, - "status": "pass_through" - }, - { - "id": 67, - "rule": "command_rhs: command_call_value \"'rescue' modifier\" after_rescue stmt", - "line": 3454, - "status": "pass_through" - }, - { - "id": 68, - "rule": "command_rhs: command_asgn", - "line": 3460, - "status": "pass_through" - }, - { - "id": 69, - "rule": "expr: command_call", - "line": 3463, - "status": "pass_through" - }, - { - "id": 70, - "rule": "expr: expr \"'and'\" expr", - "line": 3465, - "status": "ported" - }, - { - "id": 71, - "rule": "expr: expr \"'or'\" expr", - "line": 3470, - "status": "ported" - }, - { - "id": 72, - "rule": "expr: \"'not'\" option_'\\n' expr", - "line": 3475, - "status": "ported" - }, - { - "id": 73, - "rule": "expr: '!' command_call", - "line": 3480, - "status": "ported" - }, - { - "id": 74, - "rule": "$@7: %empty", - "line": 3485, - "status": "empty" - }, - { - "id": 75, - "rule": "expr: arg \"=>\" $@7 p_in_kwarg p_pvtbl p_pktbl p_top_expr_body", - "line": 3490, - "status": "pass_through" - }, - { - "id": 76, - "rule": "$@8: %empty", - "line": 3500, - "status": "empty" - }, - { - "id": 77, - "rule": "expr: arg \"'in'\" $@8 p_in_kwarg p_pvtbl p_pktbl p_top_expr_body", - "line": 3505, - "status": "pass_through" - }, - { - "id": 78, - "rule": "expr: arg", - "line": 3514, - "status": "pass_through" - }, - { - "id": 79, - "rule": "def_name: fname", - "line": 3518, - "status": "pass_through" - }, - { - "id": 80, - "rule": "defn_head: k_def def_name", - "line": 3529, - "status": "pass_through" - }, - { - "id": 81, - "rule": "$@9: %empty", - "line": 3538, - "status": "empty" - }, - { - "id": 82, - "rule": "defs_head: k_def singleton dot_or_colon $@9 def_name", - "line": 3542, - "status": "pass_through" - }, - { - "id": 83, - "rule": "value_expr_expr: expr", - "line": 3161, - "status": "pass_through" - }, - { - "id": 84, - "rule": "expr_value: value_expr_expr", - "line": 3551, - "status": "pass_through" - }, - { - "id": 85, - "rule": "expr_value: error", - "line": 3553, - "status": "empty" - }, - { - "id": 86, - "rule": "$@10: %empty", - "line": 3558, - "status": "empty" - }, - { - "id": 87, - "rule": "$@11: %empty", - "line": 3558, - "status": "empty" - }, - { - "id": 88, - "rule": "expr_value_do: $@10 expr_value do $@11", - "line": 3559, - "status": "pass_through" - }, - { - "id": 89, - "rule": "command_call: command", - "line": 3565, - "status": "pass_through" - }, - { - "id": 90, - "rule": "command_call: block_command", - "line": 3566, - "status": "pass_through" - }, - { - "id": 91, - "rule": "value_expr_command_call: command_call", - "line": 3161, - "status": "pass_through" - }, - { - "id": 92, - "rule": "command_call_value: value_expr_command_call", - "line": 3569, - "status": "pass_through" - }, - { - "id": 93, - "rule": "block_command: block_call", - "line": 3572, - "status": "pass_through" - }, - { - "id": 94, - "rule": "block_command: block_call call_op2 operation2 command_args", - "line": 3574, - "status": "pass_through" - }, - { - "id": 95, - "rule": "cmd_brace_block: \"{ arg\" brace_body '}'", - "line": 3581, - "status": "ported" - }, - { - "id": 96, - "rule": "fcall: \"local variable or method\"", - "line": 3589, - "status": "pass_through" - }, - { - "id": 97, - "rule": "fcall: \"constant\"", - "line": 3589, - "status": "pass_through" - }, - { - "id": 98, - "rule": "fcall: \"method\"", - "line": 3589, - "status": "pass_through" - }, - { - "id": 99, - "rule": "command: fcall command_args", - "line": 3596, - "status": "ported" - }, - { - "id": 100, - "rule": "command: fcall command_args cmd_brace_block", - "line": 3603, - "status": "ported" - }, - { - "id": 101, - "rule": "command: primary_value call_op operation2 command_args", - "line": 3612, - "status": "ported" - }, - { - "id": 102, - "rule": "command: primary_value call_op operation2 command_args cmd_brace_block", - "line": 3617, - "status": "ported" - }, - { - "id": 103, - "rule": "command: primary_value \"::\" operation2 command_args", - "line": 3622, - "status": "ported" - }, - { - "id": 104, - "rule": "command: primary_value \"::\" operation2 command_args cmd_brace_block", - "line": 3627, - "status": "ported" - }, - { - "id": 105, - "rule": "command: primary_value \"::\" \"constant\" '{' brace_body '}'", - "line": 3632, - "status": "ported" - }, - { - "id": 106, - "rule": "command: \"'super'\" command_args", - "line": 3638, - "status": "ported" - }, - { - "id": 107, - "rule": "command: k_yield command_args", - "line": 3644, - "status": "ported" - }, - { - "id": 108, - "rule": "command: k_return call_args", - "line": 3650, - "status": "ported" - }, - { - "id": 109, - "rule": "command: \"'break'\" call_args", - "line": 3655, - "status": "ported" - }, - { - "id": 110, - "rule": "command: \"'next'\" call_args", - "line": 3662, - "status": "ported" - }, - { - "id": 111, - "rule": "mlhs: mlhs_basic", - "line": 3670, - "status": "pass_through" - }, - { - "id": 112, - "rule": "mlhs: \"(\" mlhs_inner rparen", - "line": 3672, - "status": "pass_through" - }, - { - "id": 113, - "rule": "mlhs_inner: mlhs_basic", - "line": 3678, - "status": "pass_through" - }, - { - "id": 114, - "rule": "mlhs_inner: \"(\" mlhs_inner rparen", - "line": 3680, - "status": "pass_through" - }, - { - "id": 115, - "rule": "mlhs_basic: mlhs_head", - "line": 3687, - "status": "pass_through" - }, - { - "id": 116, - "rule": "mlhs_basic: mlhs_head mlhs_item", - "line": 3692, - "status": "ported" - }, - { - "id": 117, - "rule": "mlhs_basic: mlhs_head \"*\" mlhs_node", - "line": 3697, - "status": "ported" - }, - { - "id": 118, - "rule": "mlhs_items_mlhs_item: mlhs_item", - "line": 3048, - "status": "pass_through" - }, - { - "id": 119, - "rule": "mlhs_items_mlhs_item: mlhs_items_mlhs_item ',' mlhs_item", - "line": 3053, - "status": "ported" - }, - { - "id": 120, - "rule": "mlhs_basic: mlhs_head \"*\" mlhs_node ',' mlhs_items_mlhs_item", - "line": 3702, - "status": "ported" - }, - { - "id": 121, - "rule": "mlhs_basic: mlhs_head \"*\"", - "line": 3707, - "status": "pass_through" - }, - { - "id": 122, - "rule": "mlhs_basic: mlhs_head \"*\" ',' mlhs_items_mlhs_item", - "line": 3712, - "status": "pass_through" - }, - { - "id": 123, - "rule": "mlhs_basic: \"*\" mlhs_node", - "line": 3717, - "status": "pass_through" - }, - { - "id": 124, - "rule": "mlhs_basic: \"*\" mlhs_node ',' mlhs_items_mlhs_item", - "line": 3722, - "status": "pass_through" - }, - { - "id": 125, - "rule": "mlhs_basic: \"*\"", - "line": 3727, - "status": "empty" - }, - { - "id": 126, - "rule": "mlhs_basic: \"*\" ',' mlhs_items_mlhs_item", - "line": 3732, - "status": "pass_through" - }, - { - "id": 127, - "rule": "mlhs_item: mlhs_node", - "line": 3738, - "status": "pass_through" - }, - { - "id": 128, - "rule": "mlhs_item: \"(\" mlhs_inner rparen", - "line": 3740, - "status": "pass_through" - }, - { - "id": 129, - "rule": "mlhs_head: mlhs_item ','", - "line": 3747, - "status": "ported" - }, - { - "id": 130, - "rule": "mlhs_head: mlhs_head mlhs_item ','", - "line": 3752, - "status": "ported" - }, - { - "id": 131, - "rule": "mlhs_node: user_variable", - "line": 3760, - "status": "pass_through" - }, - { - "id": 132, - "rule": "mlhs_node: keyword_variable", - "line": 3760, - "status": "pass_through" - }, - { - "id": 133, - "rule": "mlhs_node: primary_value '[' opt_call_args rbracket", - "line": 3765, - "status": "ported" - }, - { - "id": 134, - "rule": "mlhs_node: primary_value call_op \"local variable or method\"", - "line": 3770, - "status": "pass_through" - }, - { - "id": 135, - "rule": "mlhs_node: primary_value call_op \"constant\"", - "line": 3770, - "status": "pass_through" - }, - { - "id": 136, - "rule": "mlhs_node: primary_value \"::\" \"local variable or method\"", - "line": 3776, - "status": "pass_through" - }, - { - "id": 137, - "rule": "mlhs_node: primary_value \"::\" \"constant\"", - "line": 3781, - "status": "ported" - }, - { - "id": 138, - "rule": "mlhs_node: \":: at EXPR_BEG\" \"constant\"", - "line": 3786, - "status": "ported" - }, - { - "id": 139, - "rule": "mlhs_node: backref", - "line": 3791, - "status": "pass_through" - }, - { - "id": 140, - "rule": "lhs: user_variable", - "line": 3799, - "status": "ported" - }, - { - "id": 141, - "rule": "lhs: keyword_variable", - "line": 3799, - "status": "pass_through" - }, - { - "id": 142, - "rule": "lhs: primary_value '[' opt_call_args rbracket", - "line": 3804, - "status": "ported" - }, - { - "id": 143, - "rule": "lhs: primary_value call_op \"local variable or method\"", - "line": 3809, - "status": "pass_through" - }, - { - "id": 144, - "rule": "lhs: primary_value call_op \"constant\"", - "line": 3809, - "status": "pass_through" - }, - { - "id": 145, - "rule": "lhs: primary_value \"::\" \"local variable or method\"", - "line": 3814, - "status": "pass_through" - }, - { - "id": 146, - "rule": "lhs: primary_value \"::\" \"constant\"", - "line": 3819, - "status": "ported" - }, - { - "id": 147, - "rule": "lhs: \":: at EXPR_BEG\" \"constant\"", - "line": 3824, - "status": "ported" - }, - { - "id": 148, - "rule": "lhs: backref", - "line": 3829, - "status": "pass_through" - }, - { - "id": 149, - "rule": "cname: \"local variable or method\"", - "line": 3837, - "status": "pass_through" - }, - { - "id": 150, - "rule": "cname: \"constant\"", - "line": 3844, - "status": "pass_through" - }, - { - "id": 151, - "rule": "cpath: \":: at EXPR_BEG\" cname", - "line": 3848, - "status": "pass_through" - }, - { - "id": 152, - "rule": "cpath: cname", - "line": 3853, - "status": "pass_through" - }, - { - "id": 153, - "rule": "cpath: primary_value \"::\" cname", - "line": 3858, - "status": "pass_through" - }, - { - "id": 154, - "rule": "fname: \"local variable or method\"", - "line": 3864, - "status": "pass_through" - }, - { - "id": 155, - "rule": "fname: \"constant\"", - "line": 3864, - "status": "pass_through" - }, - { - "id": 156, - "rule": "fname: \"method\"", - "line": 3864, - "status": "pass_through" - }, - { - "id": 157, - "rule": "fname: op", - "line": 3866, - "status": "pass_through" - }, - { - "id": 158, - "rule": "fname: reswords", - "line": 3870, - "status": "pass_through" - }, - { - "id": 159, - "rule": "fitem: fname", - "line": 3874, - "status": "pass_through" - }, - { - "id": 160, - "rule": "fitem: symbol", - "line": 3878, - "status": "pass_through" - }, - { - "id": 161, - "rule": "undef_list: fitem", - "line": 3882, - "status": "pass_through" - }, - { - "id": 162, - "rule": "$@12: %empty", - "line": 3886, - "status": "empty" - }, - { - "id": 163, - "rule": "undef_list: undef_list ',' $@12 fitem", - "line": 3887, - "status": "ported" - }, - { - "id": 164, - "rule": "op: '|'", - "line": 3894, - "status": "ported" - }, - { - "id": 165, - "rule": "op: '^'", - "line": 3895, - "status": "ported" - }, - { - "id": 166, - "rule": "op: '&'", - "line": 3896, - "status": "ported" - }, - { - "id": 167, - "rule": "op: \"<=>\"", - "line": 3897, - "status": "ported" - }, - { - "id": 168, - "rule": "op: \"==\"", - "line": 3898, - "status": "ported" - }, - { - "id": 169, - "rule": "op: \"===\"", - "line": 3899, - "status": "ported" - }, - { - "id": 170, - "rule": "op: \"=~\"", - "line": 3900, - "status": "ported" - }, - { - "id": 171, - "rule": "op: \"!~\"", - "line": 3901, - "status": "ported" - }, - { - "id": 172, - "rule": "op: '>'", - "line": 3902, - "status": "ported" - }, - { - "id": 173, - "rule": "op: \">=\"", - "line": 3903, - "status": "ported" - }, - { - "id": 174, - "rule": "op: '<'", - "line": 3904, - "status": "ported" - }, - { - "id": 175, - "rule": "op: \"<=\"", - "line": 3905, - "status": "ported" - }, - { - "id": 176, - "rule": "op: \"!=\"", - "line": 3906, - "status": "ported" - }, - { - "id": 177, - "rule": "op: \"<<\"", - "line": 3907, - "status": "ported" - }, - { - "id": 178, - "rule": "op: \">>\"", - "line": 3908, - "status": "ported" - }, - { - "id": 179, - "rule": "op: '+'", - "line": 3909, - "status": "ported" - }, - { - "id": 180, - "rule": "op: '-'", - "line": 3910, - "status": "ported" - }, - { - "id": 181, - "rule": "op: '*'", - "line": 3911, - "status": "ported" - }, - { - "id": 182, - "rule": "op: \"*\"", - "line": 3912, - "status": "ported" - }, - { - "id": 183, - "rule": "op: '/'", - "line": 3913, - "status": "ported" - }, - { - "id": 184, - "rule": "op: '%'", - "line": 3914, - "status": "ported" - }, - { - "id": 185, - "rule": "op: \"**\"", - "line": 3915, - "status": "ported" - }, - { - "id": 186, - "rule": "op: \"**arg\"", - "line": 3916, - "status": "ported" - }, - { - "id": 187, - "rule": "op: '!'", - "line": 3917, - "status": "ported" - }, - { - "id": 188, - "rule": "op: '~'", - "line": 3918, - "status": "ported" - }, - { - "id": 189, - "rule": "op: \"unary+\"", - "line": 3919, - "status": "ported" - }, - { - "id": 190, - "rule": "op: \"unary-\"", - "line": 3920, - "status": "ported" - }, - { - "id": 191, - "rule": "op: \"[]\"", - "line": 3921, - "status": "ported" - }, - { - "id": 192, - "rule": "op: \"[]=\"", - "line": 3922, - "status": "ported" - }, - { - "id": 193, - "rule": "op: '`'", - "line": 3923, - "status": "ported" - }, - { - "id": 194, - "rule": "reswords: \"'__LINE__'\"", - "line": 3926, - "status": "ported" - }, - { - "id": 195, - "rule": "reswords: \"'__FILE__'\"", - "line": 3926, - "status": "ported" - }, - { - "id": 196, - "rule": "reswords: \"'__ENCODING__'\"", - "line": 3926, - "status": "ported" - }, - { - "id": 197, - "rule": "reswords: \"'BEGIN'\"", - "line": 3927, - "status": "ported" - }, - { - "id": 198, - "rule": "reswords: \"'END'\"", - "line": 3927, - "status": "ported" - }, - { - "id": 199, - "rule": "reswords: \"'alias'\"", - "line": 3928, - "status": "ported" - }, - { - "id": 200, - "rule": "reswords: \"'and'\"", - "line": 3928, - "status": "ported" - }, - { - "id": 201, - "rule": "reswords: \"'begin'\"", - "line": 3928, - "status": "ported" - }, - { - "id": 202, - "rule": "reswords: \"'break'\"", - "line": 3929, - "status": "ported" - }, - { - "id": 203, - "rule": "reswords: \"'case'\"", - "line": 3929, - "status": "ported" - }, - { - "id": 204, - "rule": "reswords: \"'class'\"", - "line": 3929, - "status": "ported" - }, - { - "id": 205, - "rule": "reswords: \"'def'\"", - "line": 3929, - "status": "ported" - }, - { - "id": 206, - "rule": "reswords: \"'defined?'\"", - "line": 3930, - "status": "ported" - }, - { - "id": 207, - "rule": "reswords: \"'do'\"", - "line": 3930, - "status": "ported" - }, - { - "id": 208, - "rule": "reswords: \"'else'\"", - "line": 3930, - "status": "ported" - }, - { - "id": 209, - "rule": "reswords: \"'elsif'\"", - "line": 3930, - "status": "ported" - }, - { - "id": 210, - "rule": "reswords: \"'end'\"", - "line": 3931, - "status": "ported" - }, - { - "id": 211, - "rule": "reswords: \"'ensure'\"", - "line": 3931, - "status": "ported" - }, - { - "id": 212, - "rule": "reswords: \"'false'\"", - "line": 3931, - "status": "ported" - }, - { - "id": 213, - "rule": "reswords: \"'for'\"", - "line": 3932, - "status": "ported" - }, - { - "id": 214, - "rule": "reswords: \"'in'\"", - "line": 3932, - "status": "ported" - }, - { - "id": 215, - "rule": "reswords: \"'module'\"", - "line": 3932, - "status": "ported" - }, - { - "id": 216, - "rule": "reswords: \"'next'\"", - "line": 3932, - "status": "ported" - }, - { - "id": 217, - "rule": "reswords: \"'nil'\"", - "line": 3933, - "status": "ported" - }, - { - "id": 218, - "rule": "reswords: \"'not'\"", - "line": 3933, - "status": "ported" - }, - { - "id": 219, - "rule": "reswords: \"'or'\"", - "line": 3933, - "status": "ported" - }, - { - "id": 220, - "rule": "reswords: \"'redo'\"", - "line": 3933, - "status": "ported" - }, - { - "id": 221, - "rule": "reswords: \"'rescue'\"", - "line": 3934, - "status": "ported" - }, - { - "id": 222, - "rule": "reswords: \"'retry'\"", - "line": 3934, - "status": "ported" - }, - { - "id": 223, - "rule": "reswords: \"'return'\"", - "line": 3934, - "status": "ported" - }, - { - "id": 224, - "rule": "reswords: \"'self'\"", - "line": 3934, - "status": "ported" - }, - { - "id": 225, - "rule": "reswords: \"'super'\"", - "line": 3935, - "status": "ported" - }, - { - "id": 226, - "rule": "reswords: \"'then'\"", - "line": 3935, - "status": "ported" - }, - { - "id": 227, - "rule": "reswords: \"'true'\"", - "line": 3935, - "status": "ported" - }, - { - "id": 228, - "rule": "reswords: \"'undef'\"", - "line": 3935, - "status": "ported" - }, - { - "id": 229, - "rule": "reswords: \"'when'\"", - "line": 3936, - "status": "ported" - }, - { - "id": 230, - "rule": "reswords: \"'yield'\"", - "line": 3936, - "status": "ported" - }, - { - "id": 231, - "rule": "reswords: \"'if'\"", - "line": 3936, - "status": "ported" - }, - { - "id": 232, - "rule": "reswords: \"'unless'\"", - "line": 3936, - "status": "ported" - }, - { - "id": 233, - "rule": "reswords: \"'while'\"", - "line": 3937, - "status": "ported" - }, - { - "id": 234, - "rule": "reswords: \"'until'\"", - "line": 3937, - "status": "ported" - }, - { - "id": 235, - "rule": "asgn_arg_rhs: lhs '=' lex_ctxt arg_rhs", - "line": 2926, - "status": "ported" - }, - { - "id": 236, - "rule": "arg: asgn_arg_rhs", - "line": 3940, - "status": "pass_through" - }, - { - "id": 237, - "rule": "op_asgn_arg_rhs: var_lhs \"operator-assignment\" lex_ctxt arg_rhs", - "line": 3061, - "status": "pass_through" - }, - { - "id": 238, - "rule": "op_asgn_arg_rhs: primary_value '[' opt_call_args rbracket \"operator-assignment\" lex_ctxt arg_rhs", - "line": 3066, - "status": "pass_through" - }, - { - "id": 239, - "rule": "op_asgn_arg_rhs: primary_value call_op \"local variable or method\" \"operator-assignment\" lex_ctxt arg_rhs", - "line": 3071, - "status": "pass_through" - }, - { - "id": 240, - "rule": "op_asgn_arg_rhs: primary_value call_op \"constant\" \"operator-assignment\" lex_ctxt arg_rhs", - "line": 3076, - "status": "pass_through" - }, - { - "id": 241, - "rule": "op_asgn_arg_rhs: primary_value \"::\" \"local variable or method\" \"operator-assignment\" lex_ctxt arg_rhs", - "line": 3081, - "status": "pass_through" - }, - { - "id": 242, - "rule": "op_asgn_arg_rhs: primary_value \"::\" \"constant\" \"operator-assignment\" lex_ctxt arg_rhs", - "line": 3086, - "status": "pass_through" - }, - { - "id": 243, - "rule": "op_asgn_arg_rhs: \":: at EXPR_BEG\" \"constant\" \"operator-assignment\" lex_ctxt arg_rhs", - "line": 3092, - "status": "pass_through" - }, - { - "id": 244, - "rule": "op_asgn_arg_rhs: backref \"operator-assignment\" lex_ctxt arg_rhs", - "line": 3098, - "status": "pass_through" - }, - { - "id": 245, - "rule": "arg: op_asgn_arg_rhs", - "line": 3941, - "status": "pass_through" - }, - { - "id": 246, - "rule": "range_expr_arg: arg \"..\" arg", - "line": 3120, - "status": "ported" - }, - { - "id": 247, - "rule": "range_expr_arg: arg \"...\" arg", - "line": 3127, - "status": "ported" - }, - { - "id": 248, - "rule": "range_expr_arg: arg \"..\"", - "line": 3134, - "status": "ported" - }, - { - "id": 249, - "rule": "range_expr_arg: arg \"...\"", - "line": 3140, - "status": "ported" - }, - { - "id": 250, - "rule": "range_expr_arg: \"(..\" arg", - "line": 3146, - "status": "ported" - }, - { - "id": 251, - "rule": "range_expr_arg: \"(...\" arg", - "line": 3152, - "status": "ported" - }, - { - "id": 252, - "rule": "arg: range_expr_arg", - "line": 3942, - "status": "pass_through" - }, - { - "id": 253, - "rule": "arg: arg '+' arg", - "line": 3944, - "status": "ported" - }, - { - "id": 254, - "rule": "arg: arg '-' arg", - "line": 3949, - "status": "ported" - }, - { - "id": 255, - "rule": "arg: arg '*' arg", - "line": 3954, - "status": "ported" - }, - { - "id": 256, - "rule": "arg: arg '/' arg", - "line": 3959, - "status": "ported" - }, - { - "id": 257, - "rule": "arg: arg '%' arg", - "line": 3964, - "status": "ported" - }, - { - "id": 258, - "rule": "arg: arg \"**\" arg", - "line": 3969, - "status": "ported" - }, - { - "id": 259, - "rule": "arg: tUMINUS_NUM simple_numeric \"**\" arg", - "line": 3974, - "status": "ported" - }, - { - "id": 260, - "rule": "arg: \"unary+\" arg", - "line": 3979, - "status": "ported" - }, - { - "id": 261, - "rule": "arg: \"unary-\" arg", - "line": 3984, - "status": "ported" - }, - { - "id": 262, - "rule": "arg: arg '|' arg", - "line": 3989, - "status": "ported" - }, - { - "id": 263, - "rule": "arg: arg '^' arg", - "line": 3994, - "status": "ported" - }, - { - "id": 264, - "rule": "arg: arg '&' arg", - "line": 3999, - "status": "ported" - }, - { - "id": 265, - "rule": "arg: arg \"<=>\" arg", - "line": 4004, - "status": "ported" - }, - { - "id": 266, - "rule": "arg: rel_expr", - "line": 4008, - "status": "pass_through" - }, - { - "id": 267, - "rule": "arg: arg \"==\" arg", - "line": 4010, - "status": "ported" - }, - { - "id": 268, - "rule": "arg: arg \"===\" arg", - "line": 4015, - "status": "ported" - }, - { - "id": 269, - "rule": "arg: arg \"!=\" arg", - "line": 4020, - "status": "ported" - }, - { - "id": 270, - "rule": "arg: arg \"=~\" arg", - "line": 4025, - "status": "ported" - }, - { - "id": 271, - "rule": "arg: arg \"!~\" arg", - "line": 4030, - "status": "ported" - }, - { - "id": 272, - "rule": "arg: '!' arg", - "line": 4035, - "status": "ported" - }, - { - "id": 273, - "rule": "arg: '~' arg", - "line": 4040, - "status": "ported" - }, - { - "id": 274, - "rule": "arg: arg \"<<\" arg", - "line": 4045, - "status": "ported" - }, - { - "id": 275, - "rule": "arg: arg \">>\" arg", - "line": 4050, - "status": "ported" - }, - { - "id": 276, - "rule": "arg: arg \"&&\" arg", - "line": 4055, - "status": "ported" - }, - { - "id": 277, - "rule": "arg: arg \"||\" arg", - "line": 4060, - "status": "ported" - }, - { - "id": 278, - "rule": "arg: \"'defined?'\" option_'\\n' begin_defined arg", - "line": 4065, - "status": "ported" - }, - { - "id": 279, - "rule": "def_endless_method_endless_arg: defn_head f_opt_paren_args '=' endless_arg", - "line": 2966, - "status": "pass_through" - }, - { - "id": 280, - "rule": "def_endless_method_endless_arg: defs_head f_opt_paren_args '=' endless_arg", - "line": 2977, - "status": "pass_through" - }, - { - "id": 281, - "rule": "arg: def_endless_method_endless_arg", - "line": 4071, - "status": "pass_through" - }, - { - "id": 282, - "rule": "arg: ternary", - "line": 4072, - "status": "pass_through" - }, - { - "id": 283, - "rule": "arg: primary", - "line": 4073, - "status": "pass_through" - }, - { - "id": 284, - "rule": "ternary: arg '?' arg option_'\\n' ':' arg", - "line": 4077, - "status": "ported" - }, - { - "id": 285, - "rule": "endless_arg: arg", - "line": 4085, - "status": "pass_through" - }, - { - "id": 286, - "rule": "endless_arg: endless_arg \"'rescue' modifier\" after_rescue arg", - "line": 4087, - "status": "ported" - }, - { - "id": 287, - "rule": "endless_arg: \"'not'\" option_'\\n' endless_arg", - "line": 4093, - "status": "ported" - }, - { - "id": 288, - "rule": "relop: '>'", - "line": 4099, - "status": "ported" - }, - { - "id": 289, - "rule": "relop: '<'", - "line": 4100, - "status": "ported" - }, - { - "id": 290, - "rule": "relop: \">=\"", - "line": 4101, - "status": "ported" - }, - { - "id": 291, - "rule": "relop: \"<=\"", - "line": 4102, - "status": "ported" - }, - { - "id": 292, - "rule": "rel_expr: arg relop arg", - "line": 4106, - "status": "ported" - }, - { - "id": 293, - "rule": "rel_expr: rel_expr relop arg", - "line": 4111, - "status": "ported" - }, - { - "id": 294, - "rule": "lex_ctxt: none", - "line": 4119, - "status": "empty" - }, - { - "id": 295, - "rule": "begin_defined: lex_ctxt", - "line": 4125, - "status": "pass_through" - }, - { - "id": 296, - "rule": "after_rescue: lex_ctxt", - "line": 4132, - "status": "pass_through" - }, - { - "id": 297, - "rule": "value_expr_arg: arg", - "line": 3161, - "status": "pass_through" - }, - { - "id": 298, - "rule": "arg_value: value_expr_arg", - "line": 4138, - "status": "pass_through" - }, - { - "id": 299, - "rule": "aref_args: none", - "line": 4141, - "status": "ported" - }, - { - "id": 300, - "rule": "aref_args: args trailer", - "line": 4142, - "status": "pass_through" - }, - { - "id": 301, - "rule": "aref_args: args ',' assocs trailer", - "line": 4144, - "status": "ported" - }, - { - "id": 302, - "rule": "aref_args: assocs trailer", - "line": 4149, - "status": "pass_through" - }, - { - "id": 303, - "rule": "arg_rhs: arg", - "line": 4156, - "status": "pass_through" - }, - { - "id": 304, - "rule": "arg_rhs: arg \"'rescue' modifier\" after_rescue arg", - "line": 4161, - "status": "ported" - }, - { - "id": 305, - "rule": "paren_args: '(' opt_call_args rparen", - "line": 4170, - "status": "pass_through" - }, - { - "id": 306, - "rule": "paren_args: '(' args ',' args_forward rparen", - "line": 4175, - "status": "ported" - }, - { - "id": 307, - "rule": "paren_args: '(' args_forward rparen", - "line": 4185, - "status": "pass_through" - }, - { - "id": 308, - "rule": "opt_paren_args: none", - "line": 4196, - "status": "empty" - }, - { - "id": 309, - "rule": "opt_paren_args: paren_args", - "line": 4198, - "status": "pass_through" - }, - { - "id": 310, - "rule": "opt_call_args: none", - "line": 4203, - "status": "empty" - }, - { - "id": 311, - "rule": "opt_call_args: call_args", - "line": 4204, - "status": "pass_through" - }, - { - "id": 312, - "rule": "opt_call_args: args ','", - "line": 4205, - "status": "pass_through" - }, - { - "id": 313, - "rule": "opt_call_args: args ',' assocs ','", - "line": 4207, - "status": "pass_through" - }, - { - "id": 314, - "rule": "opt_call_args: assocs ','", - "line": 4212, - "status": "pass_through" - }, - { - "id": 315, - "rule": "value_expr_command: command", - "line": 3161, - "status": "pass_through" - }, - { - "id": 316, - "rule": "call_args: value_expr_command", - "line": 4219, - "status": "pass_through" - }, - { - "id": 317, - "rule": "call_args: def_endless_method_endless_command", - "line": 4224, - "status": "pass_through" - }, - { - "id": 318, - "rule": "call_args: args opt_block_arg", - "line": 4229, - "status": "pass_through" - }, - { - "id": 319, - "rule": "call_args: assocs opt_block_arg", - "line": 4234, - "status": "pass_through" - }, - { - "id": 320, - "rule": "call_args: args ',' assocs opt_block_arg", - "line": 4240, - "status": "ported" - }, - { - "id": 321, - "rule": "call_args: block_arg", - "line": 4245, - "status": "pass_through" - }, - { - "id": 322, - "rule": "$@13: %empty", - "line": 4249, - "status": "empty" - }, - { - "id": 323, - "rule": "command_args: $@13 call_args", - "line": 4267, - "status": "pass_through" - }, - { - "id": 324, - "rule": "block_arg: \"&\" arg_value", - "line": 4288, - "status": "pass_through" - }, - { - "id": 325, - "rule": "block_arg: \"&\"", - "line": 4293, - "status": "empty" - }, - { - "id": 326, - "rule": "opt_block_arg: ',' block_arg", - "line": 4301, - "status": "pass_through" - }, - { - "id": 327, - "rule": "opt_block_arg: none", - "line": 4306, - "status": "empty" - }, - { - "id": 328, - "rule": "args: arg_value", - "line": 4314, - "status": "ported" - }, - { - "id": 329, - "rule": "args: arg_splat", - "line": 4319, - "status": "pass_through" - }, - { - "id": 330, - "rule": "args: args ',' arg_value", - "line": 4324, - "status": "ported" - }, - { - "id": 331, - "rule": "args: args ',' arg_splat", - "line": 4329, - "status": "ported" - }, - { - "id": 332, - "rule": "arg_splat: \"*\" arg_value", - "line": 4337, - "status": "ported" - }, - { - "id": 333, - "rule": "arg_splat: \"*\"", - "line": 4342, - "status": "empty" - }, - { - "id": 334, - "rule": "mrhs_arg: mrhs", - "line": 4350, - "status": "pass_through" - }, - { - "id": 335, - "rule": "mrhs_arg: arg_value", - "line": 4351, - "status": "pass_through" - }, - { - "id": 336, - "rule": "mrhs: args ',' arg_value", - "line": 4356, - "status": "ported" - }, - { - "id": 337, - "rule": "mrhs: args ',' \"*\" arg_value", - "line": 4361, - "status": "ported" - }, - { - "id": 338, - "rule": "mrhs: \"*\" arg_value", - "line": 4366, - "status": "ported" - }, - { - "id": 339, - "rule": "primary: literal", - "line": 4383, - "status": "pass_through" - }, - { - "id": 340, - "rule": "primary: strings", - "line": 4383, - "status": "pass_through" - }, - { - "id": 341, - "rule": "primary: xstring", - "line": 4383, - "status": "pass_through" - }, - { - "id": 342, - "rule": "primary: regexp", - "line": 4383, - "status": "pass_through" - }, - { - "id": 343, - "rule": "primary: words", - "line": 4383, - "status": "ported" - }, - { - "id": 344, - "rule": "primary: qwords", - "line": 4383, - "status": "ported" - }, - { - "id": 345, - "rule": "primary: symbols", - "line": 4383, - "status": "pass_through" - }, - { - "id": 346, - "rule": "primary: qsymbols", - "line": 4383, - "status": "pass_through" - }, - { - "id": 347, - "rule": "primary: var_ref", - "line": 4384, - "status": "pass_through" - }, - { - "id": 348, - "rule": "primary: backref", - "line": 4385, - "status": "pass_through" - }, - { - "id": 349, - "rule": "primary: \"method\"", - "line": 4387, - "status": "ported" - }, - { - "id": 350, - "rule": "$@14: %empty", - "line": 4392, - "status": "empty" - }, - { - "id": 351, - "rule": "primary: k_begin $@14 bodystmt k_end", - "line": 4397, - "status": "ported" - }, - { - "id": 352, - "rule": "$@15: %empty", - "line": 4404, - "status": "empty" - }, - { - "id": 353, - "rule": "primary: \"( arg\" compstmt_stmts $@15 ')'", - "line": 4405, - "status": "ported" - }, - { - "id": 354, - "rule": "primary: \"(\" compstmt_stmts ')'", - "line": 4411, - "status": "ported" - }, - { - "id": 355, - "rule": "primary: primary_value \"::\" \"constant\"", - "line": 4417, - "status": "ported" - }, - { - "id": 356, - "rule": "primary: \":: at EXPR_BEG\" \"constant\"", - "line": 4422, - "status": "pass_through" - }, - { - "id": 357, - "rule": "primary: \"[\" aref_args ']'", - "line": 4427, - "status": "ported" - }, - { - "id": 358, - "rule": "primary: \"{\" assoc_list '}'", - "line": 4432, - "status": "ported" - }, - { - "id": 359, - "rule": "primary: k_return", - "line": 4438, - "status": "ported" - }, - { - "id": 360, - "rule": "primary: k_yield '(' call_args rparen", - "line": 4443, - "status": "ported" - }, - { - "id": 361, - "rule": "primary: k_yield '(' rparen", - "line": 4448, - "status": "ported" - }, - { - "id": 362, - "rule": "primary: k_yield", - "line": 4453, - "status": "ported" - }, - { - "id": 363, - "rule": "primary: \"'defined?'\" option_'\\n' '(' begin_defined expr rparen", - "line": 4458, - "status": "ported" - }, - { - "id": 364, - "rule": "primary: \"'not'\" '(' expr rparen", - "line": 4465, - "status": "ported" - }, - { - "id": 365, - "rule": "primary: \"'not'\" '(' rparen", - "line": 4470, - "status": "ported" - }, - { - "id": 366, - "rule": "primary: fcall brace_block", - "line": 4475, - "status": "ported" - }, - { - "id": 367, - "rule": "primary: method_call", - "line": 4479, - "status": "pass_through" - }, - { - "id": 368, - "rule": "primary: method_call brace_block", - "line": 4481, - "status": "ported" - }, - { - "id": 369, - "rule": "primary: lambda", - "line": 4486, - "status": "pass_through" - }, - { - "id": 370, - "rule": "primary: k_if expr_value then compstmt_stmts if_tail k_end", - "line": 4491, - "status": "ported" - }, - { - "id": 371, - "rule": "primary: k_unless expr_value then compstmt_stmts opt_else k_end", - "line": 4503, - "status": "ported" - }, - { - "id": 372, - "rule": "primary: k_while expr_value_do compstmt_stmts k_end", - "line": 4511, - "status": "ported" - }, - { - "id": 373, - "rule": "primary: k_until expr_value_do compstmt_stmts k_end", - "line": 4520, - "status": "ported" - }, - { - "id": 374, - "rule": "@16: %empty", - "line": 4527, - "status": "empty" - }, - { - "id": 375, - "rule": "primary: k_case expr_value option_terms @16 case_body k_end", - "line": 4533, - "status": "ported" - }, - { - "id": 376, - "rule": "@17: %empty", - "line": 4541, - "status": "empty" - }, - { - "id": 377, - "rule": "primary: k_case option_terms @17 case_body k_end", - "line": 4547, - "status": "ported" - }, - { - "id": 378, - "rule": "primary: k_case expr_value option_terms p_case_body k_end", - "line": 4556, - "status": "pass_through" - }, - { - "id": 379, - "rule": "$@18: %empty", - "line": 4561, - "status": "empty" - }, - { - "id": 380, - "rule": "$@19: %empty", - "line": 4561, - "status": "empty" - }, - { - "id": 381, - "rule": "primary: k_for for_var \"'in'\" $@18 expr_value do $@19 compstmt_stmts k_end", - "line": 4564, - "status": "ported" - }, - { - "id": 382, - "rule": "$@20: %empty", - "line": 4606, - "status": "empty" - }, - { - "id": 383, - "rule": "primary: k_class cpath superclass $@20 bodystmt k_end", - "line": 4611, - "status": "ported" - }, - { - "id": 384, - "rule": "$@21: %empty", - "line": 4628, - "status": "empty" - }, - { - "id": 385, - "rule": "primary: k_class \"<<\" expr_value $@21 term bodystmt k_end", - "line": 4634, - "status": "pass_through" - }, - { - "id": 386, - "rule": "$@22: %empty", - "line": 4647, - "status": "empty" - }, - { - "id": 387, - "rule": "primary: k_module cpath $@22 bodystmt k_end", - "line": 4652, - "status": "ported" - }, - { - "id": 388, - "rule": "$@23: %empty", - "line": 4665, - "status": "empty" - }, - { - "id": 389, - "rule": "primary: defn_head f_arglist $@23 bodystmt k_end", - "line": 4670, - "status": "ported" - }, - { - "id": 390, - "rule": "$@24: %empty", - "line": 4680, - "status": "empty" - }, - { - "id": 391, - "rule": "primary: defs_head f_arglist $@24 bodystmt k_end", - "line": 4685, - "status": "pass_through" - }, - { - "id": 392, - "rule": "primary: \"'break'\"", - "line": 4694, - "status": "ported" - }, - { - "id": 393, - "rule": "primary: \"'next'\"", - "line": 4699, - "status": "ported" - }, - { - "id": 394, - "rule": "primary: \"'redo'\"", - "line": 4704, - "status": "ported" - }, - { - "id": 395, - "rule": "primary: \"'retry'\"", - "line": 4709, - "status": "ported" - }, - { - "id": 396, - "rule": "value_expr_primary: primary", - "line": 3161, - "status": "pass_through" - }, - { - "id": 397, - "rule": "primary_value: value_expr_primary", - "line": 4723, - "status": "pass_through" - }, - { - "id": 398, - "rule": "k_begin: \"'begin'\"", - "line": 4727, - "status": "empty" - }, - { - "id": 399, - "rule": "k_if: \"'if'\"", - "line": 4734, - "status": "pass_through" - }, - { - "id": 400, - "rule": "k_unless: \"'unless'\"", - "line": 4752, - "status": "pass_through" - }, - { - "id": 401, - "rule": "k_while: \"'while'\" allow_exits", - "line": 4759, - "status": "pass_through" - }, - { - "id": 402, - "rule": "k_until: \"'until'\" allow_exits", - "line": 4767, - "status": "pass_through" - }, - { - "id": 403, - "rule": "k_case: \"'case'\"", - "line": 4775, - "status": "pass_through" - }, - { - "id": 404, - "rule": "k_for: \"'for'\" allow_exits", - "line": 4782, - "status": "pass_through" - }, - { - "id": 405, - "rule": "k_class: \"'class'\"", - "line": 4790, - "status": "empty" - }, - { - "id": 406, - "rule": "k_module: \"'module'\"", - "line": 4799, - "status": "empty" - }, - { - "id": 407, - "rule": "k_def: \"'def'\"", - "line": 4808, - "status": "empty" - }, - { - "id": 408, - "rule": "k_do: \"'do'\"", - "line": 4816, - "status": "empty" - }, - { - "id": 409, - "rule": "k_do_block: \"'do' for block\"", - "line": 4823, - "status": "empty" - }, - { - "id": 410, - "rule": "k_rescue: \"'rescue'\"", - "line": 4830, - "status": "empty" - }, - { - "id": 411, - "rule": "k_ensure: \"'ensure'\"", - "line": 4838, - "status": "empty" - }, - { - "id": 412, - "rule": "k_when: \"'when'\"", - "line": 4845, - "status": "pass_through" - }, - { - "id": 413, - "rule": "k_else: \"'else'\"", - "line": 4851, - "status": "pass_through" - }, - { - "id": 414, - "rule": "k_elsif: \"'elsif'\"", - "line": 4866, - "status": "pass_through" - }, - { - "id": 415, - "rule": "k_end: \"'end'\"", - "line": 4873, - "status": "pass_through" - }, - { - "id": 416, - "rule": "k_end: \"dummy end\"", - "line": 4878, - "status": "empty" - }, - { - "id": 417, - "rule": "k_return: \"'return'\"", - "line": 4884, - "status": "pass_through" - }, - { - "id": 418, - "rule": "k_yield: \"'yield'\"", - "line": 4891, - "status": "pass_through" - }, - { - "id": 419, - "rule": "then: term", - "line": 4897, - "status": "pass_through" - }, - { - "id": 420, - "rule": "then: \"'then'\"", - "line": 4898, - "status": "pass_through" - }, - { - "id": 421, - "rule": "then: term \"'then'\"", - "line": 4899, - "status": "pass_through" - }, - { - "id": 422, - "rule": "do: term", - "line": 4902, - "status": "pass_through" - }, - { - "id": 423, - "rule": "do: \"'do' for condition\"", - "line": 4903, - "status": "pass_through" - }, - { - "id": 424, - "rule": "if_tail: opt_else", - "line": 4906, - "status": "pass_through" - }, - { - "id": 425, - "rule": "if_tail: k_elsif expr_value then compstmt_stmts if_tail", - "line": 4910, - "status": "ported" - }, - { - "id": 426, - "rule": "opt_else: none", - "line": 4917, - "status": "empty" - }, - { - "id": 427, - "rule": "opt_else: k_else compstmt_stmts", - "line": 4919, - "status": "pass_through" - }, - { - "id": 428, - "rule": "for_var: lhs", - "line": 4925, - "status": "pass_through" - }, - { - "id": 429, - "rule": "for_var: mlhs", - "line": 4926, - "status": "pass_through" - }, - { - "id": 430, - "rule": "f_marg: f_norm_arg", - "line": 4930, - "status": "pass_through" - }, - { - "id": 431, - "rule": "f_marg: \"(\" f_margs rparen", - "line": 4935, - "status": "pass_through" - }, - { - "id": 432, - "rule": "mlhs_items_f_marg: f_marg", - "line": 3048, - "status": "pass_through" - }, - { - "id": 433, - "rule": "mlhs_items_f_marg: mlhs_items_f_marg ',' f_marg", - "line": 3053, - "status": "pass_through" - }, - { - "id": 434, - "rule": "f_margs: mlhs_items_f_marg", - "line": 4943, - "status": "pass_through" - }, - { - "id": 435, - "rule": "f_margs: mlhs_items_f_marg ',' f_rest_marg", - "line": 4948, - "status": "pass_through" - }, - { - "id": 436, - "rule": "f_margs: mlhs_items_f_marg ',' f_rest_marg ',' mlhs_items_f_marg", - "line": 4953, - "status": "pass_through" - }, - { - "id": 437, - "rule": "f_margs: f_rest_marg", - "line": 4958, - "status": "pass_through" - }, - { - "id": 438, - "rule": "f_margs: f_rest_marg ',' mlhs_items_f_marg", - "line": 4963, - "status": "pass_through" - }, - { - "id": 439, - "rule": "f_rest_marg: \"*\" f_norm_arg", - "line": 4970, - "status": "pass_through" - }, - { - "id": 440, - "rule": "f_rest_marg: \"*\"", - "line": 4976, - "status": "empty" - }, - { - "id": 441, - "rule": "f_any_kwrest: f_kwrest", - "line": 4982, - "status": "pass_through" - }, - { - "id": 442, - "rule": "f_any_kwrest: f_no_kwarg", - "line": 4984, - "status": "pass_through" - }, - { - "id": 443, - "rule": "$@25: %empty", - "line": 4990, - "status": "empty" - }, - { - "id": 444, - "rule": "f_eq: $@25 '='", - "line": 4990, - "status": "empty" - }, - { - "id": 445, - "rule": "f_kw_primary_value: f_label primary_value", - "line": 3020, - "status": "pass_through" - }, - { - "id": 446, - "rule": "f_kw_primary_value: f_label", - "line": 3026, - "status": "pass_through" - }, - { - "id": 447, - "rule": "f_kwarg_primary_value: f_kw_primary_value", - "line": 3035, - "status": "pass_through" - }, - { - "id": 448, - "rule": "f_kwarg_primary_value: f_kwarg_primary_value ',' f_kw_primary_value", - "line": 3040, - "status": "pass_through" - }, - { - "id": 449, - "rule": "opt_f_block_arg_none: ',' f_block_arg", - "line": 2957, - "status": "pass_through" - }, - { - "id": 450, - "rule": "opt_f_block_arg_none: none", - "line": 4992, - "status": "empty" - }, - { - "id": 451, - "rule": "args_tail_basic_primary_value_none: f_kwarg_primary_value ',' f_kwrest opt_f_block_arg_none", - "line": 2934, - "status": "pass_through" - }, - { - "id": 452, - "rule": "args_tail_basic_primary_value_none: f_kwarg_primary_value opt_f_block_arg_none", - "line": 2939, - "status": "pass_through" - }, - { - "id": 453, - "rule": "args_tail_basic_primary_value_none: f_any_kwrest opt_f_block_arg_none", - "line": 2944, - "status": "pass_through" - }, - { - "id": 454, - "rule": "args_tail_basic_primary_value_none: f_block_arg", - "line": 2949, - "status": "pass_through" - }, - { - "id": 455, - "rule": "block_args_tail: args_tail_basic_primary_value_none", - "line": 4992, - "status": "pass_through" - }, - { - "id": 456, - "rule": "excessed_comma: ','", - "line": 4996, - "status": "empty" - }, - { - "id": 457, - "rule": "f_opt_primary_value: f_arg_asgn f_eq primary_value", - "line": 2998, - "status": "pass_through" - }, - { - "id": 458, - "rule": "f_opt_arg_primary_value: f_opt_primary_value", - "line": 3007, - "status": "pass_through" - }, - { - "id": 459, - "rule": "f_opt_arg_primary_value: f_opt_arg_primary_value ',' f_opt_primary_value", - "line": 3012, - "status": "pass_through" - }, - { - "id": 460, - "rule": "opt_args_tail_block_args_tail_none: ',' block_args_tail", - "line": 3107, - "status": "pass_through" - }, - { - "id": 461, - "rule": "opt_args_tail_block_args_tail_none: none", - "line": 3112, - "status": "empty" - }, - { - "id": 462, - "rule": "args-list_primary_value_opt_args_tail_block_args_tail_none: f_arg ',' f_opt_arg_primary_value ',' f_rest_arg opt_args_tail_block_args_tail_none", - "line": 6277, - "status": "pass_through" - }, - { - "id": 463, - "rule": "args-list_primary_value_opt_args_tail_block_args_tail_none: f_arg ',' f_opt_arg_primary_value ',' f_rest_arg ',' f_arg opt_args_tail_block_args_tail_none", - "line": 6282, - "status": "pass_through" - }, - { - "id": 464, - "rule": "args-list_primary_value_opt_args_tail_block_args_tail_none: f_arg ',' f_opt_arg_primary_value opt_args_tail_block_args_tail_none", - "line": 6287, - "status": "pass_through" - }, - { - "id": 465, - "rule": "args-list_primary_value_opt_args_tail_block_args_tail_none: f_arg ',' f_opt_arg_primary_value ',' f_arg opt_args_tail_block_args_tail_none", - "line": 6292, - "status": "pass_through" - }, - { - "id": 466, - "rule": "args-list_primary_value_opt_args_tail_block_args_tail_none: f_arg ',' f_rest_arg opt_args_tail_block_args_tail_none", - "line": 6297, - "status": "pass_through" - }, - { - "id": 467, - "rule": "args-list_primary_value_opt_args_tail_block_args_tail_none: f_arg ',' f_rest_arg ',' f_arg opt_args_tail_block_args_tail_none", - "line": 6302, - "status": "pass_through" - }, - { - "id": 468, - "rule": "args-list_primary_value_opt_args_tail_block_args_tail_none: f_opt_arg_primary_value ',' f_rest_arg opt_args_tail_block_args_tail_none", - "line": 6307, - "status": "pass_through" - }, - { - "id": 469, - "rule": "args-list_primary_value_opt_args_tail_block_args_tail_none: f_opt_arg_primary_value ',' f_rest_arg ',' f_arg opt_args_tail_block_args_tail_none", - "line": 6312, - "status": "pass_through" - }, - { - "id": 470, - "rule": "args-list_primary_value_opt_args_tail_block_args_tail_none: f_opt_arg_primary_value opt_args_tail_block_args_tail_none", - "line": 6317, - "status": "pass_through" - }, - { - "id": 471, - "rule": "args-list_primary_value_opt_args_tail_block_args_tail_none: f_opt_arg_primary_value ',' f_arg opt_args_tail_block_args_tail_none", - "line": 6322, - "status": "pass_through" - }, - { - "id": 472, - "rule": "args-list_primary_value_opt_args_tail_block_args_tail_none: f_rest_arg opt_args_tail_block_args_tail_none", - "line": 6327, - "status": "pass_through" - }, - { - "id": 473, - "rule": "args-list_primary_value_opt_args_tail_block_args_tail_none: f_rest_arg ',' f_arg opt_args_tail_block_args_tail_none", - "line": 6332, - "status": "pass_through" - }, - { - "id": 474, - "rule": "block_param: args-list_primary_value_opt_args_tail_block_args_tail_none", - "line": 5003, - "status": "pass_through" - }, - { - "id": 475, - "rule": "block_param: f_arg excessed_comma", - "line": 5005, - "status": "pass_through" - }, - { - "id": 476, - "rule": "block_param: f_arg opt_args_tail_block_args_tail_none", - "line": 5011, - "status": "pass_through" - }, - { - "id": 477, - "rule": "tail-only-args_block_args_tail: block_args_tail", - "line": 6340, - "status": "pass_through" - }, - { - "id": 478, - "rule": "block_param: tail-only-args_block_args_tail", - "line": 5015, - "status": "pass_through" - }, - { - "id": 479, - "rule": "opt_block_param_def: none", - "line": 5018, - "status": "empty" - }, - { - "id": 480, - "rule": "opt_block_param_def: block_param_def", - "line": 5020, - "status": "pass_through" - }, - { - "id": 481, - "rule": "block_param_def: '|' opt_block_param opt_bv_decl '|'", - "line": 5026, - "status": "pass_through" - }, - { - "id": 482, - "rule": "opt_block_param: %empty", - "line": 5035, - "status": "empty" - }, - { - "id": 483, - "rule": "opt_block_param: block_param", - "line": 5039, - "status": "pass_through" - }, - { - "id": 484, - "rule": "opt_bv_decl: option_'\\n'", - "line": 5043, - "status": "pass_through" - }, - { - "id": 485, - "rule": "opt_bv_decl: option_'\\n' ';' bv_decls option_'\\n'", - "line": 5048, - "status": "pass_through" - }, - { - "id": 486, - "rule": "bv_decls: bvar", - "line": 5054, - "status": "pass_through" - }, - { - "id": 487, - "rule": "bv_decls: bv_decls ',' bvar", - "line": 5056, - "status": "pass_through" - }, - { - "id": 488, - "rule": "bvar: \"local variable or method\"", - "line": 5061, - "status": "pass_through" - }, - { - "id": 489, - "rule": "bvar: f_bad_arg", - "line": 5065, - "status": "pass_through" - }, - { - "id": 490, - "rule": "max_numparam: %empty", - "line": 5068, - "status": "empty" - }, - { - "id": 491, - "rule": "numparam: %empty", - "line": 5074, - "status": "empty" - }, - { - "id": 492, - "rule": "it_id: %empty", - "line": 5079, - "status": "empty" - }, - { - "id": 493, - "rule": "@26: %empty", - "line": 5086, - "status": "empty" - }, - { - "id": 494, - "rule": "$@27: %empty", - "line": 5092, - "status": "empty" - }, - { - "id": 495, - "rule": "lambda: \"->\" @26 max_numparam numparam it_id allow_exits f_larglist $@27 lambda_body", - "line": 5096, - "status": "ported" - }, - { - "id": 496, - "rule": "f_larglist: '(' f_largs opt_bv_decl ')'", - "line": 5120, - "status": "pass_through" - }, - { - "id": 497, - "rule": "f_larglist: f_largs", - "line": 5127, - "status": "pass_through" - }, - { - "id": 498, - "rule": "lambda_body: tLAMBEG compstmt_stmts '}'", - "line": 5136, - "status": "pass_through" - }, - { - "id": 499, - "rule": "$@28: %empty", - "line": 5142, - "status": "empty" - }, - { - "id": 500, - "rule": "lambda_body: \"'do' for lambda\" $@28 bodystmt k_end", - "line": 5146, - "status": "pass_through" - }, - { - "id": 501, - "rule": "do_block: k_do_block do_body k_end", - "line": 5153, - "status": "pass_through" - }, - { - "id": 502, - "rule": "block_call: command do_block", - "line": 5161, - "status": "ported" - }, - { - "id": 503, - "rule": "block_call: block_call call_op2 operation2 opt_paren_args", - "line": 5167, - "status": "pass_through" - }, - { - "id": 504, - "rule": "block_call: block_call call_op2 operation2 opt_paren_args brace_block", - "line": 5177, - "status": "pass_through" - }, - { - "id": 505, - "rule": "block_call: block_call call_op2 operation2 command_args do_block", - "line": 5183, - "status": "pass_through" - }, - { - "id": 506, - "rule": "block_call: block_call call_op2 paren_args", - "line": 5188, - "status": "pass_through" - }, - { - "id": 507, - "rule": "method_call: fcall paren_args", - "line": 5196, - "status": "ported" - }, - { - "id": 508, - "rule": "method_call: primary_value call_op operation2 opt_paren_args", - "line": 5203, - "status": "ported" - }, - { - "id": 509, - "rule": "method_call: primary_value \"::\" operation2 paren_args", - "line": 5214, - "status": "ported" - }, - { - "id": 510, - "rule": "method_call: primary_value \"::\" operation3", - "line": 5220, - "status": "ported" - }, - { - "id": 511, - "rule": "method_call: primary_value call_op2 paren_args", - "line": 5225, - "status": "ported" - }, - { - "id": 512, - "rule": "method_call: \"'super'\" paren_args", - "line": 5231, - "status": "ported" - }, - { - "id": 513, - "rule": "method_call: \"'super'\"", - "line": 5241, - "status": "ported" - }, - { - "id": 514, - "rule": "method_call: primary_value '[' opt_call_args rbracket", - "line": 5246, - "status": "ported" - }, - { - "id": 515, - "rule": "brace_block: '{' brace_body '}'", - "line": 5254, - "status": "pass_through" - }, - { - "id": 516, - "rule": "brace_block: k_do do_body k_end", - "line": 5260, - "status": "pass_through" - }, - { - "id": 517, - "rule": "@29: %empty", - "line": 5267, - "status": "empty" - }, - { - "id": 518, - "rule": "brace_body: @29 max_numparam numparam it_id allow_exits opt_block_param_def compstmt_stmts", - "line": 5270, - "status": "pass_through" - }, - { - "id": 519, - "rule": "@30: %empty", - "line": 5284, - "status": "empty" - }, - { - "id": 520, - "rule": "do_body: @30 max_numparam numparam it_id allow_exits opt_block_param_def bodystmt", - "line": 5290, - "status": "pass_through" - }, - { - "id": 521, - "rule": "case_args: arg_value", - "line": 5306, - "status": "ported" - }, - { - "id": 522, - "rule": "case_args: \"*\" arg_value", - "line": 5312, - "status": "pass_through" - }, - { - "id": 523, - "rule": "case_args: case_args ',' arg_value", - "line": 5317, - "status": "ported" - }, - { - "id": 524, - "rule": "case_args: case_args ',' \"*\" arg_value", - "line": 5323, - "status": "pass_through" - }, - { - "id": 525, - "rule": "case_body: k_when case_args then compstmt_stmts cases", - "line": 5332, - "status": "ported" - }, - { - "id": 526, - "rule": "cases: opt_else", - "line": 5339, - "status": "pass_through" - }, - { - "id": 527, - "rule": "cases: case_body", - "line": 5340, - "status": "ported" - }, - { - "id": 528, - "rule": "p_pvtbl: %empty", - "line": 5343, - "status": "empty" - }, - { - "id": 529, - "rule": "p_pktbl: %empty", - "line": 5344, - "status": "empty" - }, - { - "id": 530, - "rule": "p_in_kwarg: %empty", - "line": 5346, - "status": "empty" - }, - { - "id": 531, - "rule": "$@31: %empty", - "line": 5359, - "status": "empty" - }, - { - "id": 532, - "rule": "p_case_body: \"'in'\" p_in_kwarg p_pvtbl p_pktbl p_top_expr then $@31 compstmt_stmts p_cases", - "line": 5368, - "status": "pass_through" - }, - { - "id": 533, - "rule": "p_cases: opt_else", - "line": 5374, - "status": "pass_through" - }, - { - "id": 534, - "rule": "p_cases: p_case_body", - "line": 5375, - "status": "pass_through" - }, - { - "id": 535, - "rule": "p_top_expr: p_top_expr_body", - "line": 5378, - "status": "pass_through" - }, - { - "id": 536, - "rule": "p_top_expr: p_top_expr_body \"'if' modifier\" expr_value", - "line": 5380, - "status": "pass_through" - }, - { - "id": 537, - "rule": "p_top_expr: p_top_expr_body \"'unless' modifier\" expr_value", - "line": 5386, - "status": "pass_through" - }, - { - "id": 538, - "rule": "p_top_expr_body: p_expr", - "line": 5393, - "status": "pass_through" - }, - { - "id": 539, - "rule": "p_top_expr_body: p_expr ','", - "line": 5395, - "status": "pass_through" - }, - { - "id": 540, - "rule": "p_top_expr_body: p_expr ',' p_args", - "line": 5401, - "status": "pass_through" - }, - { - "id": 541, - "rule": "p_top_expr_body: p_find", - "line": 5407, - "status": "pass_through" - }, - { - "id": 542, - "rule": "p_top_expr_body: p_args_tail", - "line": 5412, - "status": "pass_through" - }, - { - "id": 543, - "rule": "p_top_expr_body: p_kwargs", - "line": 5417, - "status": "pass_through" - }, - { - "id": 544, - "rule": "p_expr: p_as", - "line": 5423, - "status": "pass_through" - }, - { - "id": 545, - "rule": "p_as: p_expr \"=>\" p_variable", - "line": 5427, - "status": "pass_through" - }, - { - "id": 546, - "rule": "p_as: p_alt", - "line": 5433, - "status": "pass_through" - }, - { - "id": 547, - "rule": "$@32: %empty", - "line": 5437, - "status": "empty" - }, - { - "id": 548, - "rule": "p_alt: p_alt '|' $@32 p_expr_basic", - "line": 5441, - "status": "pass_through" - }, - { - "id": 549, - "rule": "p_alt: p_expr_basic", - "line": 5449, - "status": "pass_through" - }, - { - "id": 550, - "rule": "p_lparen: '(' p_pktbl", - "line": 5453, - "status": "pass_through" - }, - { - "id": 551, - "rule": "p_lbracket: '[' p_pktbl", - "line": 5460, - "status": "pass_through" - }, - { - "id": 552, - "rule": "p_expr_basic: p_value", - "line": 5466, - "status": "pass_through" - }, - { - "id": 553, - "rule": "p_expr_basic: p_variable", - "line": 5467, - "status": "pass_through" - }, - { - "id": 554, - "rule": "p_expr_basic: p_const p_lparen p_args rparen", - "line": 5469, - "status": "pass_through" - }, - { - "id": 555, - "rule": "p_expr_basic: p_const p_lparen p_find rparen", - "line": 5476, - "status": "pass_through" - }, - { - "id": 556, - "rule": "p_expr_basic: p_const p_lparen p_kwargs rparen", - "line": 5483, - "status": "pass_through" - }, - { - "id": 557, - "rule": "p_expr_basic: p_const '(' rparen", - "line": 5490, - "status": "pass_through" - }, - { - "id": 558, - "rule": "p_expr_basic: p_const p_lbracket p_args rbracket", - "line": 5496, - "status": "pass_through" - }, - { - "id": 559, - "rule": "p_expr_basic: p_const p_lbracket p_find rbracket", - "line": 5503, - "status": "pass_through" - }, - { - "id": 560, - "rule": "p_expr_basic: p_const p_lbracket p_kwargs rbracket", - "line": 5510, - "status": "pass_through" - }, - { - "id": 561, - "rule": "p_expr_basic: p_const '[' rbracket", - "line": 5517, - "status": "pass_through" - }, - { - "id": 562, - "rule": "p_expr_basic: \"[\" p_args rbracket", - "line": 5523, - "status": "pass_through" - }, - { - "id": 563, - "rule": "p_expr_basic: \"[\" p_find rbracket", - "line": 5528, - "status": "pass_through" - }, - { - "id": 564, - "rule": "p_expr_basic: \"[\" rbracket", - "line": 5533, - "status": "empty" - }, - { - "id": 565, - "rule": "$@33: %empty", - "line": 5539, - "status": "empty" - }, - { - "id": 566, - "rule": "p_expr_basic: \"{\" p_pktbl lex_ctxt $@33 p_kwargs rbrace", - "line": 5543, - "status": "pass_through" - }, - { - "id": 567, - "rule": "p_expr_basic: \"{\" rbrace", - "line": 5550, - "status": "empty" - }, - { - "id": 568, - "rule": "p_expr_basic: \"(\" p_pktbl p_expr rparen", - "line": 5556, - "status": "pass_through" - }, - { - "id": 569, - "rule": "p_args: p_expr", - "line": 5564, - "status": "pass_through" - }, - { - "id": 570, - "rule": "p_args: p_args_head", - "line": 5570, - "status": "pass_through" - }, - { - "id": 571, - "rule": "p_args: p_args_head p_arg", - "line": 5575, - "status": "pass_through" - }, - { - "id": 572, - "rule": "p_args: p_args_head p_rest", - "line": 5580, - "status": "pass_through" - }, - { - "id": 573, - "rule": "p_args: p_args_head p_rest ',' p_args_post", - "line": 5585, - "status": "pass_through" - }, - { - "id": 574, - "rule": "p_args: p_args_tail", - "line": 5589, - "status": "pass_through" - }, - { - "id": 575, - "rule": "p_args_head: p_arg ','", - "line": 5592, - "status": "pass_through" - }, - { - "id": 576, - "rule": "p_args_head: p_args_head p_arg ','", - "line": 5594, - "status": "pass_through" - }, - { - "id": 577, - "rule": "p_args_tail: p_rest", - "line": 5601, - "status": "pass_through" - }, - { - "id": 578, - "rule": "p_args_tail: p_rest ',' p_args_post", - "line": 5606, - "status": "pass_through" - }, - { - "id": 579, - "rule": "p_find: p_rest ',' p_args_post ',' p_rest", - "line": 5613, - "status": "pass_through" - }, - { - "id": 580, - "rule": "p_rest: \"*\" \"local variable or method\"", - "line": 5621, - "status": "pass_through" - }, - { - "id": 581, - "rule": "p_rest: \"*\"", - "line": 5627, - "status": "empty" - }, - { - "id": 582, - "rule": "p_args_post: p_arg", - "line": 5633, - "status": "pass_through" - }, - { - "id": 583, - "rule": "p_args_post: p_args_post ',' p_arg", - "line": 5635, - "status": "pass_through" - }, - { - "id": 584, - "rule": "p_arg: p_expr", - "line": 5642, - "status": "pass_through" - }, - { - "id": 585, - "rule": "p_kwargs: p_kwarg ',' p_any_kwrest", - "line": 5649, - "status": "pass_through" - }, - { - "id": 586, - "rule": "p_kwargs: p_kwarg", - "line": 5654, - "status": "pass_through" - }, - { - "id": 587, - "rule": "p_kwargs: p_kwarg ','", - "line": 5659, - "status": "pass_through" - }, - { - "id": 588, - "rule": "p_kwargs: p_any_kwrest", - "line": 5664, - "status": "pass_through" - }, - { - "id": 589, - "rule": "p_kwarg: p_kw", - "line": 5670, - "status": "pass_through" - }, - { - "id": 590, - "rule": "p_kwarg: p_kwarg ',' p_kw", - "line": 5673, - "status": "pass_through" - }, - { - "id": 591, - "rule": "p_kw: p_kw_label p_expr", - "line": 5680, - "status": "pass_through" - }, - { - "id": 592, - "rule": "p_kw: p_kw_label", - "line": 5686, - "status": "pass_through" - }, - { - "id": 593, - "rule": "p_kw_label: \"label\"", - "line": 5697, - "status": "pass_through" - }, - { - "id": 594, - "rule": "p_kw_label: \"string literal\" string_contents tLABEL_END", - "line": 5699, - "status": "pass_through" - }, - { - "id": 595, - "rule": "p_kwrest: kwrest_mark \"local variable or method\"", - "line": 5714, - "status": "pass_through" - }, - { - "id": 596, - "rule": "p_kwrest: kwrest_mark", - "line": 5719, - "status": "empty" - }, - { - "id": 597, - "rule": "p_kwnorest: kwrest_mark \"'nil'\"", - "line": 5726, - "status": "pass_through" - }, - { - "id": 598, - "rule": "p_any_kwrest: p_kwrest", - "line": 5731, - "status": "pass_through" - }, - { - "id": 599, - "rule": "p_any_kwrest: p_kwnorest", - "line": 5733, - "status": "pass_through" - }, - { - "id": 600, - "rule": "p_value: p_primitive", - "line": 5739, - "status": "pass_through" - }, - { - "id": 601, - "rule": "range_expr_p_primitive: p_primitive \"..\" p_primitive", - "line": 3120, - "status": "pass_through" - }, - { - "id": 602, - "rule": "range_expr_p_primitive: p_primitive \"...\" p_primitive", - "line": 3127, - "status": "pass_through" - }, - { - "id": 603, - "rule": "range_expr_p_primitive: p_primitive \"..\"", - "line": 3134, - "status": "ported" - }, - { - "id": 604, - "rule": "range_expr_p_primitive: p_primitive \"...\"", - "line": 3140, - "status": "ported" - }, - { - "id": 605, - "rule": "range_expr_p_primitive: \"(..\" p_primitive", - "line": 3146, - "status": "ported" - }, - { - "id": 606, - "rule": "range_expr_p_primitive: \"(...\" p_primitive", - "line": 3152, - "status": "ported" - }, - { - "id": 607, - "rule": "p_value: range_expr_p_primitive", - "line": 5740, - "status": "pass_through" - }, - { - "id": 608, - "rule": "p_value: p_var_ref", - "line": 5741, - "status": "pass_through" - }, - { - "id": 609, - "rule": "p_value: p_expr_ref", - "line": 5742, - "status": "pass_through" - }, - { - "id": 610, - "rule": "p_value: p_const", - "line": 5743, - "status": "pass_through" - }, - { - "id": 611, - "rule": "p_primitive: literal", - "line": 5746, - "status": "pass_through" - }, - { - "id": 612, - "rule": "p_primitive: strings", - "line": 5746, - "status": "pass_through" - }, - { - "id": 613, - "rule": "p_primitive: xstring", - "line": 5746, - "status": "pass_through" - }, - { - "id": 614, - "rule": "p_primitive: regexp", - "line": 5746, - "status": "pass_through" - }, - { - "id": 615, - "rule": "p_primitive: words", - "line": 5746, - "status": "pass_through" - }, - { - "id": 616, - "rule": "p_primitive: qwords", - "line": 5746, - "status": "pass_through" - }, - { - "id": 617, - "rule": "p_primitive: symbols", - "line": 5746, - "status": "pass_through" - }, - { - "id": 618, - "rule": "p_primitive: qsymbols", - "line": 5746, - "status": "pass_through" - }, - { - "id": 619, - "rule": "p_primitive: keyword_variable", - "line": 5748, - "status": "pass_through" - }, - { - "id": 620, - "rule": "p_primitive: lambda", - "line": 5752, - "status": "pass_through" - }, - { - "id": 621, - "rule": "p_variable: \"local variable or method\"", - "line": 5756, - "status": "pass_through" - }, - { - "id": 622, - "rule": "p_var_ref: '^' \"local variable or method\"", - "line": 5764, - "status": "pass_through" - }, - { - "id": 623, - "rule": "p_var_ref: '^' nonlocal_var", - "line": 5776, - "status": "pass_through" - }, - { - "id": 624, - "rule": "p_expr_ref: '^' \"(\" expr_value rparen", - "line": 5783, - "status": "pass_through" - }, - { - "id": 625, - "rule": "p_const: \":: at EXPR_BEG\" cname", - "line": 5790, - "status": "pass_through" - }, - { - "id": 626, - "rule": "p_const: p_const \"::\" cname", - "line": 5795, - "status": "pass_through" - }, - { - "id": 627, - "rule": "p_const: \"constant\"", - "line": 5800, - "status": "pass_through" - }, - { - "id": 628, - "rule": "opt_rescue: k_rescue exc_list exc_var then compstmt_stmts opt_rescue", - "line": 5809, - "status": "pass_through" - }, - { - "id": 629, - "rule": "opt_rescue: none", - "line": 5827, - "status": "empty" - }, - { - "id": 630, - "rule": "exc_list: arg_value", - "line": 5831, - "status": "pass_through" - }, - { - "id": 631, - "rule": "exc_list: mrhs", - "line": 5836, - "status": "pass_through" - }, - { - "id": 632, - "rule": "exc_list: none", - "line": 5839, - "status": "empty" - }, - { - "id": 633, - "rule": "exc_var: \"=>\" lhs", - "line": 5843, - "status": "pass_through" - }, - { - "id": 634, - "rule": "exc_var: none", - "line": 5847, - "status": "empty" - }, - { - "id": 635, - "rule": "opt_ensure: k_ensure stmts option_terms", - "line": 5851, - "status": "pass_through" - }, - { - "id": 636, - "rule": "opt_ensure: none", - "line": 5857, - "status": "empty" - }, - { - "id": 637, - "rule": "literal: numeric", - "line": 5860, - "status": "pass_through" - }, - { - "id": 638, - "rule": "literal: symbol", - "line": 5861, - "status": "pass_through" - }, - { - "id": 639, - "rule": "strings: string", - "line": 5865, - "status": "pass_through" - }, - { - "id": 640, - "rule": "string: \"char literal\"", - "line": 5876, - "status": "ported" - }, - { - "id": 641, - "rule": "string: string1", - "line": 5877, - "status": "pass_through" - }, - { - "id": 642, - "rule": "string: string string1", - "line": 5879, - "status": "ported" - }, - { - "id": 643, - "rule": "string1: \"string literal\" string_contents \"terminator\"", - "line": 5886, - "status": "ported" - }, - { - "id": 644, - "rule": "xstring: \"backtick literal\" xstring_contents \"terminator\"", - "line": 5899, - "status": "ported" - }, - { - "id": 645, - "rule": "regexp: \"regexp literal\" regexp_contents tREGEXP_END", - "line": 5911, - "status": "ported" - }, - { - "id": 646, - "rule": "nonempty_list_' ': ' '", - "line": 5917, - "status": "pass_through" - }, - { - "id": 647, - "rule": "nonempty_list_' ': nonempty_list_' ' ' '", - "line": 5917, - "status": "pass_through" - }, - { - "id": 648, - "rule": "words_tWORDS_BEG_word_list: \"word list\" nonempty_list_' ' word_list \"terminator\"", - "line": 3169, - "status": "pass_through" - }, - { - "id": 649, - "rule": "words: words_tWORDS_BEG_word_list", - "line": 5917, - "status": "pass_through" - }, - { - "id": 650, - "rule": "word_list: %empty", - "line": 5921, - "status": "empty" - }, - { - "id": 651, - "rule": "word_list: word_list word nonempty_list_' '", - "line": 5926, - "status": "ported" - }, - { - "id": 652, - "rule": "word: string_content", - "line": 5932, - "status": "pass_through" - }, - { - "id": 653, - "rule": "word: word string_content", - "line": 5935, - "status": "ported" - }, - { - "id": 654, - "rule": "words_tSYMBOLS_BEG_symbol_list: \"symbol list\" nonempty_list_' ' symbol_list \"terminator\"", - "line": 3169, - "status": "pass_through" - }, - { - "id": 655, - "rule": "symbols: words_tSYMBOLS_BEG_symbol_list", - "line": 5941, - "status": "ported" - }, - { - "id": 656, - "rule": "symbol_list: %empty", - "line": 5945, - "status": "empty" - }, - { - "id": 657, - "rule": "symbol_list: symbol_list word nonempty_list_' '", - "line": 5950, - "status": "ported" - }, - { - "id": 658, - "rule": "words_tQWORDS_BEG_qword_list: \"verbatim word list\" nonempty_list_' ' qword_list \"terminator\"", - "line": 3169, - "status": "pass_through" - }, - { - "id": 659, - "rule": "qwords: words_tQWORDS_BEG_qword_list", - "line": 5956, - "status": "ported" - }, - { - "id": 660, - "rule": "words_tQSYMBOLS_BEG_qsym_list: \"verbatim symbol list\" nonempty_list_' ' qsym_list \"terminator\"", - "line": 3169, - "status": "pass_through" - }, - { - "id": 661, - "rule": "qsymbols: words_tQSYMBOLS_BEG_qsym_list", - "line": 5959, - "status": "ported" - }, - { - "id": 662, - "rule": "qword_list: %empty", - "line": 5963, - "status": "empty" - }, - { - "id": 663, - "rule": "qword_list: qword_list \"literal content\" nonempty_list_' '", - "line": 5968, - "status": "ported" - }, - { - "id": 664, - "rule": "qsym_list: %empty", - "line": 5975, - "status": "empty" - }, - { - "id": 665, - "rule": "qsym_list: qsym_list \"literal content\" nonempty_list_' '", - "line": 5980, - "status": "pass_through" - }, - { - "id": 666, - "rule": "string_contents: %empty", - "line": 5987, - "status": "empty" - }, - { - "id": 667, - "rule": "string_contents: string_contents string_content", - "line": 5992, - "status": "ported" - }, - { - "id": 668, - "rule": "xstring_contents: %empty", - "line": 5999, - "status": "empty" - }, - { - "id": 669, - "rule": "xstring_contents: xstring_contents string_content", - "line": 6004, - "status": "ported" - }, - { - "id": 670, - "rule": "regexp_contents: %empty", - "line": 6011, - "status": "empty" - }, - { - "id": 671, - "rule": "regexp_contents: regexp_contents string_content", - "line": 6016, - "status": "ported" - }, - { - "id": 672, - "rule": "string_content: \"literal content\"", - "line": 6041, - "status": "pass_through" - }, - { - "id": 673, - "rule": "@34: %empty", - "line": 6044, - "status": "empty" - }, - { - "id": 674, - "rule": "string_content: tSTRING_DVAR @34 string_dvar", - "line": 6051, - "status": "pass_through" - }, - { - "id": 675, - "rule": "@35: %empty", - "line": 6058, - "status": "empty" - }, - { - "id": 676, - "rule": "@36: %empty", - "line": 6066, - "status": "empty" - }, - { - "id": 677, - "rule": "@37: %empty", - "line": 6070, - "status": "empty" - }, - { - "id": 678, - "rule": "@38: %empty", - "line": 6074, - "status": "empty" - }, - { - "id": 679, - "rule": "string_content: \"'#{'\" @35 @36 @37 @38 compstmt_stmts string_dend", - "line": 6079, - "status": "ported" - }, - { - "id": 680, - "rule": "string_dend: \"'}'\"", - "line": 6094, - "status": "empty" - }, - { - "id": 681, - "rule": "string_dend: \"end-of-input\"", - "line": 6095, - "status": "empty" - }, - { - "id": 682, - "rule": "string_dvar: nonlocal_var", - "line": 6099, - "status": "pass_through" - }, - { - "id": 683, - "rule": "string_dvar: backref", - "line": 6103, - "status": "pass_through" - }, - { - "id": 684, - "rule": "symbol: ssym", - "line": 6106, - "status": "pass_through" - }, - { - "id": 685, - "rule": "symbol: dsym", - "line": 6107, - "status": "pass_through" - }, - { - "id": 686, - "rule": "ssym: \"symbol literal\" sym", - "line": 6111, - "status": "ported" - }, - { - "id": 687, - "rule": "sym: fname", - "line": 6126, - "status": "pass_through" - }, - { - "id": 688, - "rule": "sym: nonlocal_var", - "line": 6127, - "status": "pass_through" - }, - { - "id": 689, - "rule": "dsym: \"symbol literal\" string_contents \"terminator\"", - "line": 6131, - "status": "ported" - }, - { - "id": 690, - "rule": "numeric: simple_numeric", - "line": 6138, - "status": "pass_through" - }, - { - "id": 691, - "rule": "numeric: tUMINUS_NUM simple_numeric", - "line": 6140, - "status": "ported" - }, - { - "id": 692, - "rule": "simple_numeric: \"integer literal\"", - "line": 6147, - "status": "ported" - }, - { - "id": 693, - "rule": "simple_numeric: \"float literal\"", - "line": 6148, - "status": "ported" - }, - { - "id": 694, - "rule": "simple_numeric: \"rational literal\"", - "line": 6149, - "status": "ported" - }, - { - "id": 695, - "rule": "simple_numeric: \"imaginary literal\"", - "line": 6150, - "status": "ported" - }, - { - "id": 696, - "rule": "nonlocal_var: \"instance variable\"", - "line": 6153, - "status": "ported" - }, - { - "id": 697, - "rule": "nonlocal_var: \"global variable\"", - "line": 6154, - "status": "ported" - }, - { - "id": 698, - "rule": "nonlocal_var: \"class variable\"", - "line": 6155, - "status": "ported" - }, - { - "id": 699, - "rule": "user_variable: \"local variable or method\"", - "line": 6158, - "status": "ported" - }, - { - "id": 700, - "rule": "user_variable: \"constant\"", - "line": 6158, - "status": "ported" - }, - { - "id": 701, - "rule": "user_variable: nonlocal_var", - "line": 6159, - "status": "pass_through" - }, - { - "id": 702, - "rule": "keyword_variable: \"'nil'\"", - "line": 6162, - "status": "ported" - }, - { - "id": 703, - "rule": "keyword_variable: \"'self'\"", - "line": 6163, - "status": "ported" - }, - { - "id": 704, - "rule": "keyword_variable: \"'true'\"", - "line": 6164, - "status": "ported" - }, - { - "id": 705, - "rule": "keyword_variable: \"'false'\"", - "line": 6165, - "status": "ported" - }, - { - "id": 706, - "rule": "keyword_variable: \"'__FILE__'\"", - "line": 6166, - "status": "ported" - }, - { - "id": 707, - "rule": "keyword_variable: \"'__LINE__'\"", - "line": 6167, - "status": "ported" - }, - { - "id": 708, - "rule": "keyword_variable: \"'__ENCODING__'\"", - "line": 6168, - "status": "ported" - }, - { - "id": 709, - "rule": "var_ref: user_variable", - "line": 6172, - "status": "ported" - }, - { - "id": 710, - "rule": "var_ref: keyword_variable", - "line": 6182, - "status": "pass_through" - }, - { - "id": 711, - "rule": "var_lhs: user_variable", - "line": 6189, - "status": "pass_through" - }, - { - "id": 712, - "rule": "var_lhs: keyword_variable", - "line": 6189, - "status": "pass_through" - }, - { - "id": 713, - "rule": "backref: \"numbered reference\"", - "line": 6195, - "status": "ported" - }, - { - "id": 714, - "rule": "backref: \"back reference\"", - "line": 6196, - "status": "ported" - }, - { - "id": 715, - "rule": "$@39: %empty", - "line": 6200, - "status": "empty" - }, - { - "id": 716, - "rule": "superclass: '<' $@39 expr_value term", - "line": 6205, - "status": "pass_through" - }, - { - "id": 717, - "rule": "superclass: none", - "line": 6209, - "status": "empty" - }, - { - "id": 718, - "rule": "f_opt_paren_args: f_paren_args", - "line": 6212, - "status": "pass_through" - }, - { - "id": 719, - "rule": "f_opt_paren_args: f_empty_arg", - "line": 6214, - "status": "empty" - }, - { - "id": 720, - "rule": "f_empty_arg: %empty", - "line": 6220, - "status": "empty" - }, - { - "id": 721, - "rule": "f_paren_args: '(' f_args rparen", - "line": 6228, - "status": "pass_through" - }, - { - "id": 722, - "rule": "f_arglist: f_paren_args", - "line": 6237, - "status": "pass_through" - }, - { - "id": 723, - "rule": "@40: %empty", - "line": 6238, - "status": "empty" - }, - { - "id": 724, - "rule": "f_arglist: @40 f_args term", - "line": 6245, - "status": "pass_through" - }, - { - "id": 725, - "rule": "f_kw_arg_value: f_label arg_value", - "line": 3020, - "status": "pass_through" - }, - { - "id": 726, - "rule": "f_kw_arg_value: f_label", - "line": 3026, - "status": "pass_through" - }, - { - "id": 727, - "rule": "f_kwarg_arg_value: f_kw_arg_value", - "line": 3035, - "status": "pass_through" - }, - { - "id": 728, - "rule": "f_kwarg_arg_value: f_kwarg_arg_value ',' f_kw_arg_value", - "line": 3040, - "status": "pass_through" - }, - { - "id": 729, - "rule": "opt_f_block_arg_opt_comma: ',' f_block_arg", - "line": 2957, - "status": "pass_through" - }, - { - "id": 730, - "rule": "opt_f_block_arg_opt_comma: opt_comma", - "line": 6255, - "status": "pass_through" - }, - { - "id": 731, - "rule": "args_tail_basic_arg_value_opt_comma: f_kwarg_arg_value ',' f_kwrest opt_f_block_arg_opt_comma", - "line": 2934, - "status": "pass_through" - }, - { - "id": 732, - "rule": "args_tail_basic_arg_value_opt_comma: f_kwarg_arg_value opt_f_block_arg_opt_comma", - "line": 2939, - "status": "pass_through" - }, - { - "id": 733, - "rule": "args_tail_basic_arg_value_opt_comma: f_any_kwrest opt_f_block_arg_opt_comma", - "line": 2944, - "status": "pass_through" - }, - { - "id": 734, - "rule": "args_tail_basic_arg_value_opt_comma: f_block_arg", - "line": 2949, - "status": "pass_through" - }, - { - "id": 735, - "rule": "args_tail: args_tail_basic_arg_value_opt_comma", - "line": 6255, - "status": "pass_through" - }, - { - "id": 736, - "rule": "args_tail: args_forward", - "line": 6257, - "status": "pass_through" - }, - { - "id": 737, - "rule": "args_tail_basic_arg_value_none: f_kwarg_arg_value ',' f_kwrest opt_f_block_arg_none", - "line": 2934, - "status": "pass_through" - }, - { - "id": 738, - "rule": "args_tail_basic_arg_value_none: f_kwarg_arg_value opt_f_block_arg_none", - "line": 2939, - "status": "pass_through" - }, - { - "id": 739, - "rule": "args_tail_basic_arg_value_none: f_any_kwrest opt_f_block_arg_none", - "line": 2944, - "status": "pass_through" - }, - { - "id": 740, - "rule": "args_tail_basic_arg_value_none: f_block_arg", - "line": 2949, - "status": "pass_through" - }, - { - "id": 741, - "rule": "largs_tail: args_tail_basic_arg_value_none", - "line": 6265, - "status": "pass_through" - }, - { - "id": 742, - "rule": "largs_tail: args_forward", - "line": 6267, - "status": "pass_through" - }, - { - "id": 743, - "rule": "f_opt_arg_value: f_arg_asgn f_eq arg_value", - "line": 2998, - "status": "pass_through" - }, - { - "id": 744, - "rule": "f_opt_arg_arg_value: f_opt_arg_value", - "line": 3007, - "status": "pass_through" - }, - { - "id": 745, - "rule": "f_opt_arg_arg_value: f_opt_arg_arg_value ',' f_opt_arg_value", - "line": 3012, - "status": "pass_through" - }, - { - "id": 746, - "rule": "opt_args_tail_args_tail_opt_comma: ',' args_tail", - "line": 3107, - "status": "pass_through" - }, - { - "id": 747, - "rule": "opt_args_tail_args_tail_opt_comma: opt_comma", - "line": 3112, - "status": "pass_through" - }, - { - "id": 748, - "rule": "args-list_arg_value_opt_args_tail_args_tail_opt_comma: f_arg ',' f_opt_arg_arg_value ',' f_rest_arg opt_args_tail_args_tail_opt_comma", - "line": 6277, - "status": "pass_through" - }, - { - "id": 749, - "rule": "args-list_arg_value_opt_args_tail_args_tail_opt_comma: f_arg ',' f_opt_arg_arg_value ',' f_rest_arg ',' f_arg opt_args_tail_args_tail_opt_comma", - "line": 6282, - "status": "pass_through" - }, - { - "id": 750, - "rule": "args-list_arg_value_opt_args_tail_args_tail_opt_comma: f_arg ',' f_opt_arg_arg_value opt_args_tail_args_tail_opt_comma", - "line": 6287, - "status": "pass_through" - }, - { - "id": 751, - "rule": "args-list_arg_value_opt_args_tail_args_tail_opt_comma: f_arg ',' f_opt_arg_arg_value ',' f_arg opt_args_tail_args_tail_opt_comma", - "line": 6292, - "status": "pass_through" - }, - { - "id": 752, - "rule": "args-list_arg_value_opt_args_tail_args_tail_opt_comma: f_arg ',' f_rest_arg opt_args_tail_args_tail_opt_comma", - "line": 6297, - "status": "pass_through" - }, - { - "id": 753, - "rule": "args-list_arg_value_opt_args_tail_args_tail_opt_comma: f_arg ',' f_rest_arg ',' f_arg opt_args_tail_args_tail_opt_comma", - "line": 6302, - "status": "pass_through" - }, - { - "id": 754, - "rule": "args-list_arg_value_opt_args_tail_args_tail_opt_comma: f_opt_arg_arg_value ',' f_rest_arg opt_args_tail_args_tail_opt_comma", - "line": 6307, - "status": "pass_through" - }, - { - "id": 755, - "rule": "args-list_arg_value_opt_args_tail_args_tail_opt_comma: f_opt_arg_arg_value ',' f_rest_arg ',' f_arg opt_args_tail_args_tail_opt_comma", - "line": 6312, - "status": "pass_through" - }, - { - "id": 756, - "rule": "args-list_arg_value_opt_args_tail_args_tail_opt_comma: f_opt_arg_arg_value opt_args_tail_args_tail_opt_comma", - "line": 6317, - "status": "pass_through" - }, - { - "id": 757, - "rule": "args-list_arg_value_opt_args_tail_args_tail_opt_comma: f_opt_arg_arg_value ',' f_arg opt_args_tail_args_tail_opt_comma", - "line": 6322, - "status": "pass_through" - }, - { - "id": 758, - "rule": "args-list_arg_value_opt_args_tail_args_tail_opt_comma: f_rest_arg opt_args_tail_args_tail_opt_comma", - "line": 6327, - "status": "pass_through" - }, - { - "id": 759, - "rule": "args-list_arg_value_opt_args_tail_args_tail_opt_comma: f_rest_arg ',' f_arg opt_args_tail_args_tail_opt_comma", - "line": 6332, - "status": "pass_through" - }, - { - "id": 760, - "rule": "f_args-list_args_tail_opt_comma: args-list_arg_value_opt_args_tail_args_tail_opt_comma", - "line": 6357, - "status": "pass_through" - }, - { - "id": 761, - "rule": "f_args-list_args_tail_opt_comma: f_arg opt_args_tail_args_tail_opt_comma", - "line": 6349, - "status": "pass_through" - }, - { - "id": 762, - "rule": "tail-only-args_args_tail: args_tail", - "line": 6340, - "status": "pass_through" - }, - { - "id": 763, - "rule": "f_args-list_args_tail_opt_comma: tail-only-args_args_tail", - "line": 6357, - "status": "pass_through" - }, - { - "id": 764, - "rule": "f_args-list_args_tail_opt_comma: f_empty_arg", - "line": 6357, - "status": "empty" - }, - { - "id": 765, - "rule": "f_args: f_args-list_args_tail_opt_comma", - "line": 6357, - "status": "pass_through" - }, - { - "id": 766, - "rule": "opt_args_tail_largs_tail_none: ',' largs_tail", - "line": 3107, - "status": "pass_through" - }, - { - "id": 767, - "rule": "opt_args_tail_largs_tail_none: none", - "line": 3112, - "status": "empty" - }, - { - "id": 768, - "rule": "args-list_arg_value_opt_args_tail_largs_tail_none: f_arg ',' f_opt_arg_arg_value ',' f_rest_arg opt_args_tail_largs_tail_none", - "line": 6277, - "status": "pass_through" - }, - { - "id": 769, - "rule": "args-list_arg_value_opt_args_tail_largs_tail_none: f_arg ',' f_opt_arg_arg_value ',' f_rest_arg ',' f_arg opt_args_tail_largs_tail_none", - "line": 6282, - "status": "pass_through" - }, - { - "id": 770, - "rule": "args-list_arg_value_opt_args_tail_largs_tail_none: f_arg ',' f_opt_arg_arg_value opt_args_tail_largs_tail_none", - "line": 6287, - "status": "pass_through" - }, - { - "id": 771, - "rule": "args-list_arg_value_opt_args_tail_largs_tail_none: f_arg ',' f_opt_arg_arg_value ',' f_arg opt_args_tail_largs_tail_none", - "line": 6292, - "status": "pass_through" - }, - { - "id": 772, - "rule": "args-list_arg_value_opt_args_tail_largs_tail_none: f_arg ',' f_rest_arg opt_args_tail_largs_tail_none", - "line": 6297, - "status": "pass_through" - }, - { - "id": 773, - "rule": "args-list_arg_value_opt_args_tail_largs_tail_none: f_arg ',' f_rest_arg ',' f_arg opt_args_tail_largs_tail_none", - "line": 6302, - "status": "pass_through" - }, - { - "id": 774, - "rule": "args-list_arg_value_opt_args_tail_largs_tail_none: f_opt_arg_arg_value ',' f_rest_arg opt_args_tail_largs_tail_none", - "line": 6307, - "status": "pass_through" - }, - { - "id": 775, - "rule": "args-list_arg_value_opt_args_tail_largs_tail_none: f_opt_arg_arg_value ',' f_rest_arg ',' f_arg opt_args_tail_largs_tail_none", - "line": 6312, - "status": "pass_through" - }, - { - "id": 776, - "rule": "args-list_arg_value_opt_args_tail_largs_tail_none: f_opt_arg_arg_value opt_args_tail_largs_tail_none", - "line": 6317, - "status": "pass_through" - }, - { - "id": 777, - "rule": "args-list_arg_value_opt_args_tail_largs_tail_none: f_opt_arg_arg_value ',' f_arg opt_args_tail_largs_tail_none", - "line": 6322, - "status": "pass_through" - }, - { - "id": 778, - "rule": "args-list_arg_value_opt_args_tail_largs_tail_none: f_rest_arg opt_args_tail_largs_tail_none", - "line": 6327, - "status": "pass_through" - }, - { - "id": 779, - "rule": "args-list_arg_value_opt_args_tail_largs_tail_none: f_rest_arg ',' f_arg opt_args_tail_largs_tail_none", - "line": 6332, - "status": "pass_through" - }, - { - "id": 780, - "rule": "f_args-list_largs_tail_none: args-list_arg_value_opt_args_tail_largs_tail_none", - "line": 6360, - "status": "pass_through" - }, - { - "id": 781, - "rule": "f_args-list_largs_tail_none: f_arg opt_args_tail_largs_tail_none", - "line": 6349, - "status": "pass_through" - }, - { - "id": 782, - "rule": "tail-only-args_largs_tail: largs_tail", - "line": 6340, - "status": "pass_through" - }, - { - "id": 783, - "rule": "f_args-list_largs_tail_none: tail-only-args_largs_tail", - "line": 6360, - "status": "pass_through" - }, - { - "id": 784, - "rule": "f_args-list_largs_tail_none: f_empty_arg", - "line": 6360, - "status": "empty" - }, - { - "id": 785, - "rule": "f_largs: f_args-list_largs_tail_none", - "line": 6360, - "status": "pass_through" - }, - { - "id": 786, - "rule": "args_forward: \"(...\"", - "line": 6364, - "status": "empty" - }, - { - "id": 787, - "rule": "f_bad_arg: \"constant\"", - "line": 6371, - "status": "pass_through" - }, - { - "id": 788, - "rule": "f_bad_arg: \"instance variable\"", - "line": 6380, - "status": "empty" - }, - { - "id": 789, - "rule": "f_bad_arg: \"global variable\"", - "line": 6389, - "status": "empty" - }, - { - "id": 790, - "rule": "f_bad_arg: \"class variable\"", - "line": 6398, - "status": "empty" - }, - { - "id": 791, - "rule": "f_norm_arg: f_bad_arg", - "line": 6408, - "status": "pass_through" - }, - { - "id": 792, - "rule": "f_norm_arg: \"local variable or method\"", - "line": 6410, - "status": "pass_through" - }, - { - "id": 793, - "rule": "f_arg_asgn: f_norm_arg", - "line": 6420, - "status": "pass_through" - }, - { - "id": 794, - "rule": "f_arg_item: f_arg_asgn", - "line": 6427, - "status": "pass_through" - }, - { - "id": 795, - "rule": "f_arg_item: \"(\" f_margs rparen", - "line": 6432, - "status": "pass_through" - }, - { - "id": 796, - "rule": "f_arg: f_arg_item", - "line": 6450, - "status": "pass_through" - }, - { - "id": 797, - "rule": "f_arg: f_arg ',' f_arg_item", - "line": 6453, - "status": "pass_through" - }, - { - "id": 798, - "rule": "f_label: \"label\"", - "line": 6464, - "status": "pass_through" - }, - { - "id": 799, - "rule": "kwrest_mark: \"**\"", - "line": 6483, - "status": "empty" - }, - { - "id": 800, - "rule": "kwrest_mark: \"**arg\"", - "line": 6484, - "status": "empty" - }, - { - "id": 801, - "rule": "f_no_kwarg: p_kwnorest", - "line": 6488, - "status": "pass_through" - }, - { - "id": 802, - "rule": "f_kwrest: kwrest_mark \"local variable or method\"", - "line": 6494, - "status": "pass_through" - }, - { - "id": 803, - "rule": "f_kwrest: kwrest_mark", - "line": 6500, - "status": "pass_through" - }, - { - "id": 804, - "rule": "restarg_mark: '*'", - "line": 6507, - "status": "empty" - }, - { - "id": 805, - "rule": "restarg_mark: \"*\"", - "line": 6508, - "status": "empty" - }, - { - "id": 806, - "rule": "f_rest_arg: restarg_mark \"local variable or method\"", - "line": 6512, - "status": "ported" - }, - { - "id": 807, - "rule": "f_rest_arg: restarg_mark", - "line": 6518, - "status": "empty" - }, - { - "id": 808, - "rule": "blkarg_mark: '&'", - "line": 6525, - "status": "empty" - }, - { - "id": 809, - "rule": "blkarg_mark: \"&\"", - "line": 6526, - "status": "empty" - }, - { - "id": 810, - "rule": "f_block_arg: blkarg_mark \"local variable or method\"", - "line": 6530, - "status": "pass_through" - }, - { - "id": 811, - "rule": "f_block_arg: blkarg_mark \"'nil'\"", - "line": 6536, - "status": "empty" - }, - { - "id": 812, - "rule": "f_block_arg: blkarg_mark", - "line": 6541, - "status": "pass_through" - }, - { - "id": 813, - "rule": "option_',': %empty", - "line": 6548, - "status": "empty" - }, - { - "id": 814, - "rule": "option_',': ','", - "line": 6548, - "status": "pass_through" - }, - { - "id": 815, - "rule": "opt_comma: option_','", - "line": 6549, - "status": "pass_through" - }, - { - "id": 816, - "rule": "value_expr_singleton_expr: singleton_expr", - "line": 3161, - "status": "pass_through" - }, - { - "id": 817, - "rule": "singleton: value_expr_singleton_expr", - "line": 6557, - "status": "pass_through" - }, - { - "id": 818, - "rule": "singleton_expr: var_ref", - "line": 6586, - "status": "pass_through" - }, - { - "id": 819, - "rule": "$@41: %empty", - "line": 6588, - "status": "empty" - }, - { - "id": 820, - "rule": "singleton_expr: '(' $@41 expr rparen", - "line": 6593, - "status": "pass_through" - }, - { - "id": 821, - "rule": "assoc_list: none", - "line": 6600, - "status": "ported" - }, - { - "id": 822, - "rule": "assoc_list: assocs trailer", - "line": 6602, - "status": "pass_through" - }, - { - "id": 823, - "rule": "assocs: assoc", - "line": 6608, - "status": "ported" - }, - { - "id": 824, - "rule": "assocs: assocs ',' assoc", - "line": 6611, - "status": "ported" - }, - { - "id": 825, - "rule": "assoc: arg_value \"=>\" arg_value", - "line": 6634, - "status": "ported" - }, - { - "id": 826, - "rule": "assoc: \"label\" arg_value", - "line": 6639, - "status": "ported" - }, - { - "id": 827, - "rule": "assoc: \"label\"", - "line": 6644, - "status": "pass_through" - }, - { - "id": 828, - "rule": "assoc: \"string literal\" string_contents tLABEL_END arg_value", - "line": 6651, - "status": "pass_through" - }, - { - "id": 829, - "rule": "assoc: \"**arg\" arg_value", - "line": 6657, - "status": "pass_through" - }, - { - "id": 830, - "rule": "assoc: \"**arg\"", - "line": 6662, - "status": "empty" - }, - { - "id": 831, - "rule": "operation2: \"local variable or method\"", - "line": 6674, - "status": "pass_through" - }, - { - "id": 832, - "rule": "operation2: \"constant\"", - "line": 6674, - "status": "pass_through" - }, - { - "id": 833, - "rule": "operation2: \"method\"", - "line": 6674, - "status": "pass_through" - }, - { - "id": 834, - "rule": "operation2: op", - "line": 6675, - "status": "pass_through" - }, - { - "id": 835, - "rule": "operation3: \"local variable or method\"", - "line": 6678, - "status": "pass_through" - }, - { - "id": 836, - "rule": "operation3: \"method\"", - "line": 6679, - "status": "pass_through" - }, - { - "id": 837, - "rule": "operation3: op", - "line": 6680, - "status": "pass_through" - }, - { - "id": 838, - "rule": "dot_or_colon: '.'", - "line": 6683, - "status": "ported" - }, - { - "id": 839, - "rule": "dot_or_colon: \"::\"", - "line": 6684, - "status": "ported" - }, - { - "id": 840, - "rule": "call_op: '.'", - "line": 6687, - "status": "ported" - }, - { - "id": 841, - "rule": "call_op: \"&.\"", - "line": 6688, - "status": "ported" - }, - { - "id": 842, - "rule": "call_op2: call_op", - "line": 6691, - "status": "pass_through" - }, - { - "id": 843, - "rule": "call_op2: \"::\"", - "line": 6692, - "status": "ported" - }, - { - "id": 844, - "rule": "rparen: option_'\\n' ')'", - "line": 6695, - "status": "pass_through" - }, - { - "id": 845, - "rule": "rbracket: option_'\\n' ']'", - "line": 6698, - "status": "pass_through" - }, - { - "id": 846, - "rule": "rbrace: option_'\\n' '}'", - "line": 6701, - "status": "empty" - }, - { - "id": 847, - "rule": "trailer: option_'\\n'", - "line": 6704, - "status": "pass_through" - }, - { - "id": 848, - "rule": "trailer: ','", - "line": 6705, - "status": "pass_through" - }, - { - "id": 849, - "rule": "term: ';'", - "line": 6709, - "status": "empty" - }, - { - "id": 850, - "rule": "term: '\\n'", - "line": 6717, - "status": "empty" - }, - { - "id": 851, - "rule": "terms: term", - "line": 6723, - "status": "empty" - }, - { - "id": 852, - "rule": "terms: terms ';'", - "line": 6724, - "status": "empty" - }, - { - "id": 853, - "rule": "none: %empty", - "line": 6728, - "status": "empty" - } -] diff --git a/plan.md b/plan.md deleted file mode 100644 index b43af14..0000000 --- a/plan.md +++ /dev/null @@ -1,48 +0,0 @@ -# `arpaka` にRuby frontendを統合する - -## 目的と公開 API - -汎用の Ruby パーサー生成基盤と、対象言語ごとの文法・意味アクション・AST を、この gem 内で提供する。最初に既存の Ruby 用試作を統合する。 - -```ruby -require "arpaka" - -ast = Arpaka.parse( - [[:tIDENTIFIER, :a], ["=", nil], [:tINTEGER, 1]], -) -``` - -- frontend APIはRuby専用のため、`language:` の指定は不要。 -- `generate`/`compile` は引き続き任意の文法を扱う汎用 API として維持する。 -- 入力はトークン列。lexer、C 用アクション、新しい Ruby 構文への対応は今回追加しない。 -- `RubyVM` の AST 機能による代替処理は設けない。 - -## 統合と読みやすさ - -- `~/Program/Ruby/ruby-frontend` の試作をこのリポジトリに取り込み、言語固有コードは `Arpaka` として公開する。既存の別ディレクトリは削除しない。 -- AST は同名前空間の `AST` に置き、既存の `Program`、`Literal`、`Binary`、`Unary`、`LocalRead`、`LocalWrite`、`BareCall` を維持する。将来の C 用 AST に同じ形式を強制しない。 -- 対応範囲は既存の数値・nil/真偽値・四則演算・単項演算・括弧・単純代入・文の並び。代入前の名前は `BareCall`、代入後は `LocalRead` とする。 -- 初回の `parse` で同梱文法を `compile` し、生成クラスをプロセス内でキャッシュする。初期化は排他制御し、失敗時はキャッシュを残さない。呼び出しごとにパーサーとビルダーを新規作成する。 -- 初回生成はメモリ内で行い、利用者のディレクトリへの書き込みや事前ビルドを不要にする。既存の `RUBY_BOX=1` 要件を適用する。 -- 試作の数値 ID による操作振り分けを、`@builder.binary(:+, $1, $3)` などの名前付き呼び出しに置き換える。 -- 文法の非終端名は元の名前を可能な限り保持する。展開された規則にも衝突しない説明的な名前を付け、生成コードの各 reduce 分岐に元の規則をコメントとして出す。 - -## 文法・配布・エラー - -- 上流 Ruby の固定コミット `37d60dd3241d5fdb10ca43f36077f5d556ae1b8d`、原文・前処理ツール・チェックサム・ライセンスを引き継ぐ。 -- 試作と同様、Lrama で展開した全文法を保持する。規則ごとの移植状況と元の位置を記録し、未対応規則も明示的なアクションを持たせる。 -- 文法変換とアクション定義を管理元にし、巨大な全置換パッチをビルド入力にする方式は整理する。配布用の Ruby アクション付き文法と軽量な規則メタデータを再生成可能な成果物として保存する。 -- gem には実行用コード・文法・必要なメタデータとライセンスを含める。上流の全文ファイル、開発ツール、C アクション全文の調査資料は開発用として配布対象から除外する。 -- `Arpaka::ParseError` は token/state、`UnsupportedSyntax` は元の規則名と上流位置を公開する。どちらも既存の `Lrama::Ruby::Error` を継承する。 -- 未対応構文を省略した AST は返さない。既存のエラー回復なしの動作と、予約 error トークンの拒否を維持する。 -- README・AGENTS.md・RBS を新しい位置づけと API に更新する。`DESIGN.md` の別プロジェクト前提も修正するが、引き続きコミット対象外とする。 - -## 検証と完成条件 - -- 試作の8テストを移植し、式の構造、代入・自己参照、裸の名前、失敗後の状態分離、未対応構文を検証する。 -- 公開 API の言語指定、初回生成と再利用、同時初期化、生成失敗後の再試行を検証する。 -- 汎用の電卓文法と Ruby 用 AST 生成を同一プロセスで実行し、名前空間・Box・アクションが干渉しないことを確認する。 -- 文法再生成のチェックで、853規則の構造、途中アクションに対応する空規則、トークン番号、優先順位、移植状況の分類を照合する。 -- 生成 Ruby を構文検証し、代表的な規則名・名前付きアクションが出力されることを確認する。 -- ビルドした gem を一時環境にインストールし、開発用ファイル・ネットワーク・書き込み権限なしで初回 `parse` が成功することを確認する。 -- 通常のテストと CI に統合する。今回はライブラリ統合までとし、未対応805規則の追加移植や Lrama 本体への PR は次の作業とする。 diff --git a/sig/arpaka.rbs b/sig/arpaka.rbs index 28bc2b9..55109a0 100644 --- a/sig/arpaka.rbs +++ b/sig/arpaka.rbs @@ -1,7 +1,15 @@ module Arpaka - def self.parse: (Enumerable[[String | Symbol | Integer, untyped]] tokens) -> ::Lrama::Ruby::Languages::Ruby::AST::Program - def self.parse_source: (String source, ?filename: String) -> ::Lrama::Ruby::Languages::Ruby::AST::Program + interface _Program + def statements: () -> Array[untyped] + end + + interface _Frontend + def parse: (String source, ?filename: String) -> _Program + end + + def self.generate: (ruby_source: String, ?class_name: String) -> String + def self.compile: (ruby_source: String, ?class_name: String) -> _Frontend - class LexerError < ::Lrama::Ruby::Error + class Error < ::Lrama::Ruby::Error end end diff --git a/sig/lrama/ruby.rbs b/sig/lrama/ruby.rbs index ca6c5f3..5c4a352 100644 --- a/sig/lrama/ruby.rbs +++ b/sig/lrama/ruby.rbs @@ -5,8 +5,6 @@ module Lrama class Error < StandardError end - def self.parse: (Enumerable[[String | Symbol | Integer, untyped]] tokens, language: :ruby) -> Languages::Ruby::AST::Program - def self.generate: (String source, ?filename: String, ?class_name: String, ?mode: (:parser | :recognizer), ?allow_error_rules: bool) -> String def self.compile: (String source, ?filename: String, ?class_name: String, ?mode: (:parser | :recognizer), ?allow_error_rules: bool) -> Class @@ -15,255 +13,5 @@ module Lrama def generate: (String source, ?filename: String, ?class_name: String, ?mode: (:parser | :recognizer), ?allow_error_rules: bool) -> String end - module Languages - module Ruby - def self.parse: (Enumerable[[String | Symbol | Integer, untyped]] tokens) -> AST::Program - def self.parse_source: (String source, ?filename: String) -> AST::Program - - class ParseError < ::Lrama::Ruby::Error - attr_reader token: untyped - attr_reader state: Integer - def initialize: (untyped error) -> void - end - - class UnsupportedSyntax < ::Lrama::Ruby::Error - attr_reader rule: String - attr_reader line: Integer - def initialize: (String rule, Integer line) -> void - end - - class LexerError < ::Lrama::Ruby::Error - end - - module AST - type node = Literal | Binary | Unary | If | LocalRead | LocalWrite | BareCall | ArrayLiteral | HashLiteral | RangeLiteral | Ternary | Call | ReceiverCall | BlockCall | StringLiteral | InterpolatedString | RegexpLiteral | XStringLiteral | Index | Loop | Control | Variable | Rescue | Case | When | For | Def | ClassDef | ModuleDef | Lambda - type hash_pair = Pair - - class Program < ::Data - attr_reader statements: Array[node] - def self.new: (Array[node] statements) -> instance - | (statements: Array[node]) -> instance - end - - class Literal < ::Data - attr_reader value: Integer | Float | Symbol | bool | nil - def self.new: (Integer | Float | Symbol | bool | nil value) -> instance - | (value: Integer | Float | Symbol | bool | nil) -> instance - end - - class Binary < ::Data - attr_reader operator: Symbol - attr_reader left: node - attr_reader right: node - def self.new: (Symbol operator, node left, node right) -> instance - | (operator: Symbol, left: node, right: node) -> instance - end - - class Unary < ::Data - attr_reader operator: Symbol - attr_reader operand: node - def self.new: (Symbol operator, node operand) -> instance - | (operator: Symbol, operand: node) -> instance - end - - class If < ::Data - attr_reader condition: node - attr_reader then_body: Array[node] - attr_reader else_body: Array[node]? - def self.new: (node condition, Array[node] then_body, Array[node]? else_body) -> instance - | (condition: node, then_body: Array[node], else_body: Array[node]?) -> instance - end - - class LocalRead < ::Data - attr_reader name: Symbol - def self.new: (Symbol name) -> instance - | (name: Symbol) -> instance - end - - class LocalWrite < ::Data - attr_reader name: Symbol - attr_reader value: node - def self.new: (Symbol name, node value) -> instance - | (name: Symbol, value: node) -> instance - end - - class BareCall < ::Data - attr_reader name: Symbol - def self.new: (Symbol name) -> instance - | (name: Symbol) -> instance - end - - class ArrayLiteral < ::Data - attr_reader elements: Array[node] - def self.new: (Array[node] elements) -> instance - | (elements: Array[node]) -> instance - end - - class HashLiteral < ::Data - attr_reader pairs: Array[Pair] - def self.new: (Array[Pair] pairs) -> instance - | (pairs: Array[Pair]) -> instance - end - - class Pair < ::Data - attr_reader key: node - attr_reader value: node - def self.new: (node key, node value) -> instance - | (key: node, value: node) -> instance - end - - class RangeLiteral < ::Data - attr_reader operator: Symbol - attr_reader begin_value: node - attr_reader end_value: node - def self.new: (Symbol operator, node begin_value, node end_value) -> instance - | (operator: Symbol, begin_value: node, end_value: node) -> instance - end - - class Ternary < ::Data - attr_reader condition: node - attr_reader then_value: node - attr_reader else_value: node - def self.new: (node condition, node then_value, node else_value) -> instance - | (condition: node, then_value: node, else_value: node) -> instance - end - - class Call < ::Data - attr_reader name: Symbol - attr_reader arguments: Array[node] - def self.new: (Symbol name, Array[node] arguments) -> instance - | (name: Symbol, arguments: Array[node]) -> instance - end - - class ReceiverCall < ::Data - attr_reader receiver: node - attr_reader operator: :"." | :"::" | :"&." - attr_reader name: Symbol - attr_reader arguments: Array[node] - def self.new: (node receiver, (:"." | :"::" | :"&.") operator, Symbol name, Array[node] arguments) -> instance - | (receiver: node, operator: (:"." | :"::" | :"&."), name: Symbol, arguments: Array[node]) -> instance - end - - class BlockCall < ::Data - attr_reader call: node - attr_reader body: Array[node] - def self.new: (node call, Array[node] body) -> instance - | (call: node, body: Array[node]) -> instance - end - - class StringLiteral < ::Data - attr_reader value: String - def self.new: (String value) -> instance - | (value: String) -> instance - end - - class InterpolatedString < ::Data - attr_reader parts: Array[node | String] - def self.new: (Array[node | String] parts) -> instance - | (parts: Array[node | String]) -> instance - end - - class RegexpLiteral < ::Data - attr_reader value: String - def self.new: (String value) -> instance - | (value: String) -> instance - end - - class XStringLiteral < ::Data - attr_reader value: String - def self.new: (String value) -> instance - | (value: String) -> instance - end - - class For < ::Data - attr_reader variable: node - attr_reader enumerable: node - attr_reader body: Array[node] - def self.new: (node variable, node enumerable, Array[node] body) -> instance - | (variable: node, enumerable: node, body: Array[node]) -> instance - end - - class Lambda < ::Data - attr_reader arguments: Array[untyped] - attr_reader body: Array[node] - def self.new: (Array[untyped] arguments, Array[node] body) -> instance - | (arguments: Array[untyped], body: Array[node]) -> instance - end - - class Def < ::Data - attr_reader name: Symbol - attr_reader arguments: Array[untyped] - attr_reader body: Array[node] - def self.new: (Symbol name, Array[untyped] arguments, Array[node] body) -> instance - | (name: Symbol, arguments: Array[untyped], body: Array[node]) -> instance - end - - class ClassDef < ::Data - attr_reader name: Symbol - attr_reader superclass: node? - attr_reader body: Array[node] - def self.new: (Symbol name, node? superclass, Array[node] body) -> instance - | (name: Symbol, superclass: node?, body: Array[node]) -> instance - end - - class ModuleDef < ::Data - attr_reader name: Symbol - attr_reader body: Array[node] - def self.new: (Symbol name, Array[node] body) -> instance - | (name: Symbol, body: Array[node]) -> instance - end - - class Index < ::Data - attr_reader receiver: node - attr_reader arguments: Array[node] - def self.new: (node receiver, Array[node] arguments) -> instance - | (receiver: node, arguments: Array[node]) -> instance - end - - class Loop < ::Data - attr_reader kind: Symbol - attr_reader condition: node - attr_reader body: Array[node] - def self.new: (Symbol kind, node condition, Array[node] body) -> instance - | (kind: Symbol, condition: node, body: Array[node]) -> instance - end - - class Control < ::Data - attr_reader kind: Symbol - def self.new: (Symbol kind) -> instance - | (kind: Symbol) -> instance - end - - class Variable < ::Data - attr_reader kind: Symbol - attr_reader name: Symbol | Integer - def self.new: (Symbol kind, Symbol | Integer name) -> instance - | (kind: Symbol, name: Symbol | Integer) -> instance - end - - class Rescue < ::Data - attr_reader expression: node - attr_reader fallback: node - def self.new: (node expression, node fallback) -> instance - | (expression: node, fallback: node) -> instance - end - - class When < ::Data - attr_reader patterns: Array[node] - attr_reader body: Array[node] - def self.new: (Array[node] patterns, Array[node] body) -> instance - | (patterns: Array[node], body: Array[node]) -> instance - end - - class Case < ::Data - attr_reader expression: node - attr_reader whens: Array[When] - attr_reader else_body: Array[node]? - def self.new: (node expression, Array[When] whens, Array[node]? else_body) -> instance - | (expression: node, whens: Array[When], else_body: Array[node]?) -> instance - end - end - end - end end end diff --git a/test/generated_frontend_helper.rb b/test/generated_frontend_helper.rb new file mode 100644 index 0000000..4ae4ef1 --- /dev/null +++ b/test/generated_frontend_helper.rb @@ -0,0 +1,7 @@ +# frozen_string_literal: true + +require "test_helper" + +RUBY_SOURCE = File.expand_path("../vendor/ruby", __dir__) +GeneratedRubyFrontend = Arpaka.compile(ruby_source: RUBY_SOURCE, + class_name: "TestRubyFrontend") diff --git a/test/lrama/frontend_generator_test.rb b/test/lrama/frontend_generator_test.rb new file mode 100644 index 0000000..672cc49 --- /dev/null +++ b/test/lrama/frontend_generator_test.rb @@ -0,0 +1,115 @@ +# frozen_string_literal: true + +require "test_helper" +require_relative "../generated_frontend_helper" +require "arpaka/cli" +require "tmpdir" +require "fileutils" +require "open3" +require "rbconfig" +require "stringio" + +class FrontendGeneratorTest < Test::Unit::TestCase + def generated_source + self.class.instance_variable_get(:@source) || + self.class.instance_variable_set(:@source, + Arpaka.generate(ruby_source: RUBY_SOURCE, class_name: "StandaloneRuby")) + end + + test "one relocated file parses without gems, Box, or source files" do + Dir.mktmpdir do |directory| + path = File.join(directory, "frontend.rb") + File.write(path, generated_source) + script = <<~'RUBY' + require File.expand_path("frontend.rb") + abort "Box enabled" if Ruby::Box.enabled? + abort "unexpected dependency" if defined?(Arpaka) || defined?(Lrama) + tree = StandaloneRuby.parse("a = 1\na + 2") + abort "wrong AST" unless tree.statements.last.left.name == :a + abort "leaked state" unless StandaloneRuby.parse("a").statements.first.is_a?(StandaloneRuby::AST::BareCall) + abort "token API is public" if StandaloneRuby.respond_to?(:parse_tokens) + begin + StandaloneRuby.parse('"unterminated', filename: "broken.rb") + abort "missing lexer error" + rescue StandaloneRuby::LexerError => error + abort error.message unless error.message.include?("broken.rb:1:") + end + RUBY + output, error, result = Open3.capture3( + { "RUBY_BOX" => nil, "RUBYOPT" => nil, "RUBYLIB" => nil }, + RbConfig.ruby, "--disable-gems", "-e", script, chdir: directory) + assert_predicate(result, :success?, output + error) + end + end + + test "independent generated classes coexist without Box" do + namespace = Module.new + namespace.module_eval(generated_source) + namespace.module_eval(Arpaka.generate(ruby_source: RUBY_SOURCE, class_name: "OtherRuby")) + first = namespace.const_get(:StandaloneRuby) + second = namespace.const_get(:OtherRuby) + assert_not_equal(first::AST::Program, second::AST::Program) + assert_equal(2, second.parse("2").statements.first.value) + assert_equal(1, first.parse("1").statements.first.value) + end + + test "generation is deterministic and preserves provenance and notices" do + assert_equal(generated_source, Arpaka.generate(ruby_source: RUBY_SOURCE, class_name: "StandaloneRuby")) + assert_include(generated_source, "parse.y SHA256:") + assert_include(generated_source, "Yukihiro Matsumoto") + assert_include(generated_source, "Redistribution and use in source and binary forms") + end + + test "missing inputs and invalid class names produce generation errors" do + assert_raise(Arpaka::Error) { Arpaka.generate(ruby_source: RUBY_SOURCE, class_name: "A::B") } + Dir.mktmpdir do |directory| + error = assert_raise(Arpaka::Error) { Arpaka.generate(ruby_source: directory) } + assert_include(error.message, "parse.y") + end + end + + test "changed upstream input is rejected before actions are applied" do + Dir.mktmpdir do |directory| + FileUtils.cp_r(File.join(RUBY_SOURCE, "."), directory) + File.open(File.join(directory, "parse.y"), "a") { |file| file.puts("/* changed */") } + error = assert_raise(Arpaka::Error) { Arpaka.generate(ruby_source: directory) } + assert_include(error.message, "Unsupported Ruby source change: parse.y") + assert_include(error.message, "SHA256") + end + end + + test "CLI requires output and handles help" do + out, err = StringIO.new, StringIO.new + assert_equal(0, Arpaka::CLI.run(["--help"], out: out, err: err)) + assert_include(out.string, "--ruby-source") + assert_equal(1, Arpaka::CLI.run(["generate", "--ruby-source", RUBY_SOURCE], out: out, err: err)) + assert_include(err.string, "--output") + end + + test "CLI writes complete output and requires force to replace it" do + Dir.mktmpdir do |directory| + path = File.join(directory, "frontend.rb") + args = ["generate", "--ruby-source", RUBY_SOURCE, "--class-name", "CliRuby", "--output", path] + out, err = StringIO.new, StringIO.new + assert_equal(0, Arpaka::CLI.run(args, out: out, err: err), err.string) + assert_include(File.read(path), "class CliRuby") + File.write(path, "existing\n") + assert_equal(1, Arpaka::CLI.run(args, out: out, err: err)) + assert_equal("existing\n", File.read(path)) + assert_equal(0, Arpaka::CLI.run(args + ["--force"], out: out, err: err), err.string) + assert_include(File.read(path), "class CliRuby") + File.write(path, "keep on failure\n") + bad = ["generate", "--ruby-source", directory, "--output", path, "--force"] + assert_equal(1, Arpaka::CLI.run(bad, out: out, err: err)) + assert_equal("keep on failure\n", File.read(path)) + end + end + + test "compile returns a source parsing frontend and isolates names" do + frontend = Arpaka.compile(ruby_source: RUBY_SOURCE, class_name: "IsolatedRuby") + assert_equal(42, frontend.parse("42").statements.first.value) + assert_false(Object.const_defined?(:IsolatedRuby, false)) + assert_false(Arpaka.respond_to?(:parse)) + assert_false(Lrama::Ruby.respond_to?(:parse)) + end +end diff --git a/test/lrama/rails_regression_test.rb b/test/lrama/rails_regression_test.rb new file mode 100644 index 0000000..4c61216 --- /dev/null +++ b/test/lrama/rails_regression_test.rb @@ -0,0 +1,50 @@ +# frozen_string_literal: true + +require "test_helper" +require_relative "../generated_frontend_helper" + +class RailsRegressionTest < Test::Unit::TestCase + test "parses keyword arguments in ternary branches" do + assert_nothing_raised do + GeneratedRubyFrontend.parse("value = first?(value: true) ? second(value: false) : value\n") + end + end + + test "preserves the method body after unary operator method definitions" do + assert_nothing_raised do + GeneratedRubyFrontend.parse("def -@\n Scalar.new(-value)\nend\n") + end + end + + test "preserves newlines after operator symbols in alias declarations" do + assert_nothing_raised do + GeneratedRubyFrontend.parse("alias_method :minus_without_duration, :-\nalias_method :-, :minus_with_duration\n") + GeneratedRubyFrontend.parse("alias_method :before?, :<\nalias_method :after?, :>\n") + end + end + + test "parses keyword arguments in block parameters" do + assert_nothing_raised do + GeneratedRubyFrontend.parse("proc { |salt, secret_length:| \"\".ljust(secret_length, salt) }\n") + end + end + + test "parses keyword argument forwarding before a block" do + assert_nothing_raised do + GeneratedRubyFrontend.parse("call blob:, variants: [{ resize_to_limit: [2, 2] }] do\nend\n") + end + end + + test "does not hide control-flow terminators inside array literals" do + source = <<~'RUBY' + values = [ + if condition + value + end, + other + ] + RUBY + + assert_nothing_raised { GeneratedRubyFrontend.parse(source) } + end +end diff --git a/test/lrama/ruby_language_test.rb b/test/lrama/ruby_language_test.rb index 319aa89..b89e90c 100644 --- a/test/lrama/ruby_language_test.rb +++ b/test/lrama/ruby_language_test.rb @@ -1,15 +1,15 @@ # frozen_string_literal: true require "test_helper" -require "lrama/ruby/languages/ruby" +require_relative "../generated_frontend_helper" require "open3" require "rbconfig" class ArpakaTest < Test::Unit::TestCase - AST = Arpaka::AST + AST = GeneratedRubyFrontend::AST def parse(*tokens) - Arpaka.parse(tokens) + GeneratedRubyFrontend.send(:parse_tokens, tokens) end def literal(value) @@ -24,40 +24,17 @@ def literal(value) end test "generated reductions retain readable rules and named actions" do - path = File.expand_path("../../lib/lrama/ruby/languages/ruby/parse.y", __dir__) - source = Lrama::Ruby.generate(File.read(path), allow_error_rules: true) + source = Arpaka.generate(ruby_source: RUBY_SOURCE) assert_include(source, "# arg: arg '+' arg") assert_include(source, "@builder.binary(:+, ") assert_not_include(source, "@builder.reduce(") end - test "first generation is synchronized and failed generation can be retried" do - script = <<~'RUBY' - require "arpaka" - original = Lrama::Ruby.method(:compile) - attempts = 0 - Lrama::Ruby.define_singleton_method(:compile) do |*args, **options| - attempts += 1 - raise "generation failed" if attempts == 1 - sleep 0.05 - original.call(*args, **options) - end - begin - Arpaka.parse([]) - abort "failure was swallowed" - rescue RuntimeError => error - raise unless error.message == "generation failed" - end - threads = 4.times.map do - Thread.new { Arpaka.parse([[:tINTEGER, 3]]) } - end - trees = threads.map(&:value) - abort "wrong AST" unless trees.all? { |tree| tree.statements.first.value == 3 } - Arpaka.parse([]) - abort "generated more than once after retry" unless attempts == 2 - RUBY - _output, error, result = Open3.capture3({ "RUBY_BOX" => "1" }, RbConfig.ruby, "-Ilib", "-e", script) - assert_predicate(result, :success?, error) + test "failed parsing and concurrent calls do not share local state" do + assert_raise(GeneratedRubyFrontend::ParseError) { GeneratedRubyFrontend.parse("a =") } + threads = 4.times.map { Thread.new { GeneratedRubyFrontend.parse("a = 3") } } + assert(threads.map(&:value).all? { |tree| tree.statements.first.value.value == 3 }) + assert_kind_of(AST::BareCall, GeneratedRubyFrontend.parse("a").statements.first) end test "empty program and literal statements" do @@ -112,22 +89,22 @@ def literal(value) test "setter method definitions preserve the equals suffix" do expected = AST::Program.new([AST::Def.new(:"value=", :value, [AST::BareCall.new(:value)])]) - assert_equal(expected, Arpaka.parse_source("def value=(value)\n value\nend")) - assert_nothing_raised { Arpaka.parse_source("def self.value=; end") } + assert_equal(expected, GeneratedRubyFrontend.parse("def value=(value)\n value\nend")) + assert_nothing_raised { GeneratedRubyFrontend.parse("def self.value=; end") } end test "alias preserves bracket operator names" do - assert_nothing_raised { Arpaka.parse_source("alias store []=") } + assert_nothing_raised { GeneratedRubyFrontend.parse("alias store []=") } end test "keywords after a receiver are method names" do receiver = AST::ReceiverCall.new(literal(:self), :".", :class, []) assert_equal(AST::Program.new([AST::ReceiverCall.new(receiver, :".", :to_s, [])]), - Arpaka.parse_source("self.class.to_s")) + GeneratedRubyFrontend.parse("self.class.to_s")) end test "operator calls use ordinary argument parentheses" do - tokens = Arpaka.const_get(:Lexer, false).new("handler.(message)").each.to_a + tokens = GeneratedRubyFrontend.const_get(:Lexer, false).new("handler.(message)").each.to_a assert_equal([".", "(", :tIDENTIFIER], tokens[1, 3].map(&:first)) end @@ -137,9 +114,9 @@ def literal(value) assert_equal(AST::Program.new([AST::ClassDef.new(:Foo, nil, [literal(1)])]), parse(*class_tokens)) assert_equal(AST::Program.new([AST::ModuleDef.new(:Bar, [literal(2)])]), parse(*module_tokens)) assert_equal(AST::ClassDef.new(:Error, AST::Variable.new(:constant, :StandardError), []), - Arpaka.parse_source("class Error < StandardError\nend").statements.first) + GeneratedRubyFrontend.parse("class Error < StandardError\nend").statements.first) assert_equal(:TrixEditor, - Arpaka.parse_source("class Editor::TrixEditor\nend").statements.first.name) + GeneratedRubyFrontend.parse("class Editor::TrixEditor\nend").statements.first.name) end test "simple string literals preserve their content" do @@ -173,7 +150,7 @@ def literal(value) expected = AST::Index.new(receiver, [literal(0)]) assert_equal(AST::Program.new([expected]), parse([:tLBRACK, nil], [:tINTEGER, 1], [",", nil], [:tINTEGER, 2], ["]", nil], ["[", nil], [:tINTEGER, 0], ["]", nil])) assert_equal(AST::Index.new(AST::BareCall.new(:values), [literal(0)]), - Arpaka.parse_source("values[0]").statements.first) + GeneratedRubyFrontend.parse("values[0]").statements.first) end test "label hash entries build the same pair AST" do @@ -242,7 +219,7 @@ def literal(value) test "super without arguments becomes a call node" do assert_equal(AST::Program.new([AST::Call.new(:super, [])]), parse([:keyword_super, nil])) - assert_equal(AST::Program.new([AST::Call.new(:super, [])]), Arpaka.parse_source("super()")) + assert_equal(AST::Program.new([AST::Call.new(:super, [])]), GeneratedRubyFrontend.parse("super()")) end test "parenthesized not becomes a logical negation" do @@ -329,26 +306,26 @@ def literal(value) test "command blocks accept block parameters" do assert_equal(AST::Program.new([AST::BlockCall.new(AST::Call.new(:each, []), [AST::BareCall.new(:value)])]), - Arpaka.parse_source("each { |value| value }")) + GeneratedRubyFrontend.parse("each { |value| value }")) assert_equal(AST::Program.new([AST::BlockCall.new(AST::Call.new(:each, []), [AST::BareCall.new(:value)])]), - Arpaka.parse_source("each do |value| value end")) + GeneratedRubyFrontend.parse("each do |value| value end")) end test "method bodies after splat arguments accept hash literals" do assert_nothing_raised do - Arpaka.parse_source('def decode(*) { foo: "decoded" } end') + GeneratedRubyFrontend.parse('def decode(*) { foo: "decoded" } end') end end test "lambda blocks restore an enclosing do block" do assert_nothing_raised do - Arpaka.parse_source("included do\n value = ->(body) { body }\n Mime[:html]\nend") + GeneratedRubyFrontend.parse("included do\n value = ->(body) { body }\n Mime[:html]\nend") end end test "heredoc suffixes retain literal expression context" do assert_nothing_raised do - Arpaka.parse_source("value = <<~TEXT unless condition\n body\nTEXT\n") + GeneratedRubyFrontend.parse("value = <<~TEXT unless condition\n body\nTEXT\n") end end @@ -363,19 +340,19 @@ def parse end end) RUBY - assert_nothing_raised { Arpaka.parse_source(source) } + assert_nothing_raised { GeneratedRubyFrontend.parse(source) } end test "method chains may continue after a newline before a dot" do - assert_nothing_raised { Arpaka.parse_source("value = result\n .first\n .last") } + assert_nothing_raised { GeneratedRubyFrontend.parse("value = result\n .first\n .last") } assert_nothing_raised do - Arpaka.parse_source("value = result\n .first\n # keep chaining\n .last") + GeneratedRubyFrontend.parse("value = result\n .first\n # keep chaining\n .last") end end test "conditional endings preserve enclosing blocks" do assert_nothing_raised do - Arpaka.parse_source("include(Module.new do\n define_method(:value) do\n if condition\n value\n else\n other\n end\n end\nend)") + GeneratedRubyFrontend.parse("include(Module.new do\n define_method(:value) do\n if condition\n value\n else\n other\n end\n end\nend)") end end @@ -386,32 +363,32 @@ def create; end def update; end end) RUBY - assert_nothing_raised { Arpaka.parse_source(source) } + assert_nothing_raised { GeneratedRubyFrontend.parse(source) } end test "negative command arguments use a block do token" do - assert_nothing_raised { Arpaka.parse_source('assert_difference "x", -(value.count) do; value; end') } + assert_nothing_raised { GeneratedRubyFrontend.parse('assert_difference "x", -(value.count) do; value; end') } end test "operator method definitions do not start literals" do - assert_nothing_raised { Arpaka.parse_source("def %(other); end\ndef /(other); end") } + assert_nothing_raised { GeneratedRubyFrontend.parse("def %(other); end\ndef /(other); end") } end test "slash and percent symbols do not start literals" do - assert_nothing_raised { Arpaka.parse_source("calculate(:/, value)\ncalculate(:%, value)") } + assert_nothing_raised { GeneratedRubyFrontend.parse("calculate(:/, value)\ncalculate(:%, value)") } end test "operator symbols end an alias expression" do - assert_nothing_raised { Arpaka.parse_source("alias_method :regular_writer, :[]=\nunless condition\nend") } + assert_nothing_raised { GeneratedRubyFrontend.parse("alias_method :regular_writer, :[]=\nunless condition\nend") } end test "ampersand symbols end an expression" do - assert_nothing_raised { Arpaka.parse_source("name == :&\nname\n") } + assert_nothing_raised { GeneratedRubyFrontend.parse("name == :&\nname\n") } end test "Rails trick syntax remains parseable" do assert_nothing_raised do - Arpaka.parse_source(<<~RUBY) + GeneratedRubyFrontend.parse(<<~RUBY) def source_location if line_number "on line" @@ -420,14 +397,14 @@ def source_location end + file_name end RUBY - Arpaka.parse_source(<<~RUBY) + GeneratedRubyFrontend.parse(<<~RUBY) helper(Module.new do def render_from_helper from_test_case(suffix: "!") end end) RUBY - Arpaka.parse_source(<<~RUBY) + GeneratedRubyFrontend.parse(<<~RUBY) def exec_queries super do |record| set_inverse(record) @@ -435,13 +412,13 @@ def exec_queries end end RUBY - Arpaka.parse_source(<<~RUBY) + GeneratedRubyFrontend.parse(<<~RUBY) result .first # keep chaining .last RUBY - Arpaka.parse_source(<<~RUBY) + GeneratedRubyFrontend.parse(<<~RUBY) def create_table(table_name, **options) if block_given? super { |table| yield compatible_table_definition(table) } @@ -454,15 +431,15 @@ def create_table(table_name, **options) end test "source lexer recognizes lambda as a lambda expression" do - assert_nothing_raised { Arpaka.parse_source("value = lambda do\n work\nend\n") } + assert_nothing_raised { GeneratedRubyFrontend.parse("value = lambda do\n work\nend\n") } end test "method bodies may start with an array literal after arguments" do - assert_nothing_raised { Arpaka.parse_source("def multi(a, b, c) [a, b, c] end") } + assert_nothing_raised { GeneratedRubyFrontend.parse("def multi(a, b, c) [a, b, c] end") } end test "singleton method definitions may parenthesize their receiver" do - assert_nothing_raised { Arpaka.parse_source("def (object.foo).bar(value)\n value\nend") } + assert_nothing_raised { GeneratedRubyFrontend.parse("def (object.foo).bar(value)\n value\nend") } end test "if, unless, elsif and else build conditional AST nodes" do @@ -493,14 +470,14 @@ def create_table(table_name, **options) assert_equal(expected, parse([:tIDENTIFIER, "a"])) parse([:tIDENTIFIER, :a], ["=", nil], [:tINTEGER, 1]) assert_equal(expected, parse([:tIDENTIFIER, :a])) - assert_raise(Arpaka::ParseError) { parse([:tIDENTIFIER, :a], ["=", nil]) } + assert_raise(GeneratedRubyFrontend::ParseError) { parse([:tIDENTIFIER, :a], ["=", nil]) } assert_equal(expected, parse([:tIDENTIFIER, :a])) end test "unsupported syntax reports its upstream rule" do inputs = [] inputs.each do |tokens| - error = assert_raise(Arpaka::UnsupportedSyntax) { Arpaka.parse(tokens) } + error = assert_raise(GeneratedRubyFrontend::UnsupportedSyntax) { GeneratedRubyFrontend.send(:parse_tokens, tokens) } assert_kind_of(String, error.rule) assert_kind_of(Integer, error.line) assert_include(error.message, "upstream parse.y:") @@ -510,7 +487,7 @@ def create_table(table_name, **options) test "invalid token streams stop without recovery" do [[[:UNKNOWN, nil]], [[256, nil]], [[:tINTEGER, 1], ["+", nil]], [[:tINTEGER, 1], [:tINTEGER, 2]]].each do |tokens| - assert_raise(Arpaka::ParseError) { Arpaka.parse(tokens) } + assert_raise(GeneratedRubyFrontend::ParseError) { GeneratedRubyFrontend.send(:parse_tokens, tokens) } end end @@ -519,88 +496,88 @@ def create_table(table_name, **options) AST::LocalWrite.new(:a, literal(1)), AST::Binary.new(:+, AST::LocalRead.new(:a), literal(2)) ]) - assert_equal(expected, Arpaka.parse_source("a = 1\na + 2", filename: "example.rb")) + assert_equal(expected, GeneratedRubyFrontend.parse("a = 1\na + 2", filename: "example.rb")) assert_equal(AST::Program.new([AST::Call.new(:f, [literal(1), literal(2)])]), - Arpaka.parse_source("f(1, 2)")) + GeneratedRubyFrontend.parse("f(1, 2)")) assert_equal(AST::Program.new([AST::StringLiteral.new("hello")]), - Arpaka.parse_source('"hello"')) + GeneratedRubyFrontend.parse('"hello"')) end test "source lexer errors include source position" do - error = assert_raise(Arpaka::LexerError) { Arpaka.parse_source('"unterminated', filename: "broken.rb") } + error = assert_raise(GeneratedRubyFrontend::LexerError) { GeneratedRubyFrontend.parse('"unterminated', filename: "broken.rb") } assert_include(error.message, "broken.rb:1:") end test "source lexer preserves literal kinds" do - assert_equal(AST::XStringLiteral.new("echo"), Arpaka.parse_source("`echo`").statements.first) - assert_equal(AST::RegexpLiteral.new("a[b]"), Arpaka.parse_source("/a[b]/").statements.first) - assert_equal(AST::StringLiteral.new("quiet"), Arpaka.parse_source("%q(quiet)").statements.first) + assert_equal(AST::XStringLiteral.new("echo"), GeneratedRubyFrontend.parse("`echo`").statements.first) + assert_equal(AST::RegexpLiteral.new("a[b]"), GeneratedRubyFrontend.parse("/a[b]/").statements.first) + assert_equal(AST::StringLiteral.new("quiet"), GeneratedRubyFrontend.parse("%q(quiet)").statements.first) assert_equal(AST::ArrayLiteral.new([AST::StringLiteral.new("one"), AST::StringLiteral.new("two")]), - Arpaka.parse_source("%w(one two)").statements.first) + GeneratedRubyFrontend.parse("%w(one two)").statements.first) assert_equal(AST::ArrayLiteral.new([:one, :two]), - Arpaka.parse_source("%i(one two)").statements.first) - assert_equal(AST::RegexpLiteral.new("a"), Arpaka.parse_source("%r(a)").statements.first) + GeneratedRubyFrontend.parse("%i(one two)").statements.first) + assert_equal(AST::RegexpLiteral.new("a"), GeneratedRubyFrontend.parse("%r(a)").statements.first) end test "source lexer preserves simple string interpolation" do assert_equal(AST::InterpolatedString.new([ "hello ", AST::Binary.new(:+, literal(1), literal(2)), " world" - ]), Arpaka.parse_source('"hello #{1 + 2} world"').statements.first) + ]), GeneratedRubyFrontend.parse('"hello #{1 + 2} world"').statements.first) assert_equal(AST::InterpolatedString.new([AST::BareCall.new(:name)]), - Arpaka.parse_source('"#{name}"').statements.first) + GeneratedRubyFrontend.parse('"#{name}"').statements.first) end test "source lexer preserves interpolation in percent strings" do expected = AST::InterpolatedString.new(["hello ", AST::BareCall.new(:name)]) - assert_equal(expected, Arpaka.parse_source('%Q(hello #{name})').statements.first) - assert_equal(expected, Arpaka.parse_source('%(hello #{name})').statements.first) - assert_kind_of(AST::RegexpLiteral, Arpaka.parse_source('%r(#{x})').statements.first) + assert_equal(expected, GeneratedRubyFrontend.parse('%Q(hello #{name})').statements.first) + assert_equal(expected, GeneratedRubyFrontend.parse('%(hello #{name})').statements.first) + assert_kind_of(AST::RegexpLiteral, GeneratedRubyFrontend.parse('%r(#{x})').statements.first) end test "source lexer handles heredoc indentation and line endings" do assert_equal(AST::StringLiteral.new("one\n two\n"), - Arpaka.parse_source("<<~TEXT\n one\n two\n TEXT\n").statements.first) + GeneratedRubyFrontend.parse("<<~TEXT\n one\n two\n TEXT\n").statements.first) assert_equal(AST::StringLiteral.new(" one\n"), - Arpaka.parse_source("<<-TEXT\n one\n TEXT\n").statements.first) + GeneratedRubyFrontend.parse("<<-TEXT\n one\n TEXT\n").statements.first) assert_equal(AST::StringLiteral.new("one\r\ntwo\r\n"), - Arpaka.parse_source("< { 1 }; end") - Lrama::Ruby::Languages::Ruby.parse(scope_lexer.each, lexical_context: scope_lexer.context) + scope_lexer = GeneratedRubyFrontend.const_get(:Lexer, false).new("def f; -> { 1 }; end") + GeneratedRubyFrontend.send(:parse_tokens, scope_lexer.each, lexical_context: scope_lexer.context) assert_empty(scope_lexer.context.parser_scope_stack) - pretokenized = Arpaka.const_get(:Lexer, false).new("f(1)") + pretokenized = GeneratedRubyFrontend.const_get(:Lexer, false).new("f(1)") tokens = pretokenized.each.to_a - Lrama::Ruby::Languages::Ruby.parse(tokens) + GeneratedRubyFrontend.send(:parse_tokens, tokens) assert_empty(pretokenized.context.parser_events) end test "source lexer enters fname state for definitions and aliases" do - lexer = Arpaka.const_get(:Lexer, false).new("def value=; end") + lexer = GeneratedRubyFrontend.const_get(:Lexer, false).new("def value=; end") lexer.each.to_a def_name = lexer.context.state_history.find do |entry| entry.fetch(:token) == :tFID end assert_equal(:expr_fname, def_name.fetch(:lex_state)) - lexer = Arpaka.const_get(:Lexer, false).new("alias eql? ==") + lexer = GeneratedRubyFrontend.const_get(:Lexer, false).new("alias eql? ==") lexer.each.to_a alias_name = lexer.context.state_history.find do |entry| entry.fetch(:token) == :tFID @@ -693,7 +670,7 @@ def create_table(table_name, **options) end test "source lexer distinguishes label and labeled states" do - lexer = Arpaka.const_get(:Lexer, false).new("f at: 1") + lexer = GeneratedRubyFrontend.const_get(:Lexer, false).new("f at: 1") lexer.each.to_a states = lexer.context.state_history assert_equal(:expr_label, states.find { |entry| entry.fetch(:token) == :tLABEL }.fetch(:lex_state)) @@ -701,407 +678,408 @@ def create_table(table_name, **options) end test "source lexer treats spaced empty brackets as an array literal" do - tokens = Arpaka.const_get(:Lexer, false).new("assert_equal [], value").each.to_a + tokens = GeneratedRubyFrontend.const_get(:Lexer, false).new("assert_equal [], value").each.to_a assert_equal([:tIDENTIFIER, :tLBRACK, "]", ",", :tIDENTIFIER, 0], tokens.map(&:first)) end test "source lexer treats spaced bracket arguments as array literals" do - tree = Arpaka.parse_source("foo [1], name: 2").statements.first + tree = GeneratedRubyFrontend.parse("foo [1], name: 2").statements.first assert_equal(AST::ArrayLiteral.new([literal(1)]), tree.arguments.first) assert_equal(AST::Pair.new(AST::Literal.new(:name), literal(2)), tree.arguments.last) end test "source lexer treats predicate and bang methods as identifiers" do assert_equal(AST::Def.new(:stopping?, [], [literal(1)]), - Arpaka.parse_source("def stopping?; 1; end").statements.first) + GeneratedRubyFrontend.parse("def stopping?; 1; end").statements.first) assert_equal(AST::Def.new(:halt!, [], [literal(1)]), - Arpaka.parse_source("def halt!; 1; end").statements.first) - assert_nothing_raised { Arpaka.parse_source("value.match?(pattern)") } + GeneratedRubyFrontend.parse("def halt!; 1; end").statements.first) + assert_nothing_raised { GeneratedRubyFrontend.parse("value.match?(pattern)") } end test "source lexer handles contextual punctuation in Ruby expressions" do - tokens = Arpaka.const_get(:Lexer, false).new("f(foo?: true)").each.to_a + tokens = GeneratedRubyFrontend.const_get(:Lexer, false).new("f(foo?: true)").each.to_a assert_equal([:tIDENTIFIER, "(", :tLABEL, :keyword_true, ")", 0], tokens.map(&:first)) - assert_nothing_raised { Arpaka.parse_source("f(foo?: true)") } + assert_nothing_raised { GeneratedRubyFrontend.parse("f(foo?: true)") } - tokens = Arpaka.const_get(:Lexer, false).new("list_tables[]").each.to_a + tokens = GeneratedRubyFrontend.const_get(:Lexer, false).new("list_tables[]").each.to_a assert_equal([:tIDENTIFIER, "[", "]", 0], tokens.map(&:first)) - assert_kind_of(AST::Index, Arpaka.parse_source("list_tables[]").statements.first) + assert_kind_of(AST::Index, GeneratedRubyFrontend.parse("list_tables[]").statements.first) - assert_nothing_raised { Arpaka.parse_source("x = if y; 'a'; else; 'b'; end + z") } - assert_nothing_raised { Arpaka.parse_source("helper.({a: 1})") } - assert_nothing_raised { Arpaka.parse_source("assert ?h.in?(\"hello\")") } + assert_nothing_raised { GeneratedRubyFrontend.parse("x = if y; 'a'; else; 'b'; end + z") } + assert_nothing_raised { GeneratedRubyFrontend.parse("helper.({a: 1})") } + assert_nothing_raised { GeneratedRubyFrontend.parse("assert ?h.in?(\"hello\")") } + assert_nothing_raised { GeneratedRubyFrontend.parse('value ?"" : "fallback"') } end test "source lexer uses regular brace blocks for calls and proc values" do ["foo { 1 }", "[proc { 1 }]", "foo { 1 }.first", "foo { 1 } || bar"].each do |source| - tokens = Arpaka.const_get(:Lexer, false).new(source).each.to_a + tokens = GeneratedRubyFrontend.const_get(:Lexer, false).new(source).each.to_a assert_not_include(tokens.map(&:first), :tAMPER, source) - assert_nothing_raised { Arpaka.parse_source(source) } + assert_nothing_raised { GeneratedRubyFrontend.parse(source) } end - assert_nothing_raised { Arpaka.parse_source("f(&block)") } - assert_nothing_raised { Arpaka.parse_source("map(&:to_s)") } - assert_nothing_raised { Arpaka.parse_source("a & b") } + assert_nothing_raised { GeneratedRubyFrontend.parse("f(&block)") } + assert_nothing_raised { GeneratedRubyFrontend.parse("map(&:to_s)") } + assert_nothing_raised { GeneratedRubyFrontend.parse("a & b") } end test "source lexer keeps definition and block contexts for operators" do - assert_nothing_raised { Arpaka.parse_source("def ==(x); self.x == x; end") } - assert_nothing_raised { Arpaka.parse_source("def []=(x); x; end") } - assert_nothing_raised { Arpaka.parse_source("x = -> *args do; 1; end") } - assert_nothing_raised { Arpaka.parse_source("def f; super do |x| x end; end") } - assert_nothing_raised { Arpaka.parse_source("def f; super { |x| x }; end") } + assert_nothing_raised { GeneratedRubyFrontend.parse("def ==(x); self.x == x; end") } + assert_nothing_raised { GeneratedRubyFrontend.parse("def []=(x); x; end") } + assert_nothing_raised { GeneratedRubyFrontend.parse("x = -> *args do; 1; end") } + assert_nothing_raised { GeneratedRubyFrontend.parse("def f; super do |x| x end; end") } + assert_nothing_raised { GeneratedRubyFrontend.parse("def f; super { |x| x }; end") } end test "source lexer distinguishes splat and block argument operators" do assert_equal(AST::Call.new(:f, [AST::BareCall.new(:args)]), - Arpaka.parse_source("f(*args)").statements.first) + GeneratedRubyFrontend.parse("f(*args)").statements.first) assert_equal(AST::Call.new(:map, AST::Literal.new(:to_s)), - Arpaka.parse_source("map(&:to_s)").statements.first) - assert_nothing_raised { Arpaka.parse_source("f(1, *args)") } + GeneratedRubyFrontend.parse("map(&:to_s)").statements.first) + assert_nothing_raised { GeneratedRubyFrontend.parse("f(1, *args)") } end test "symbols can be built from variable-shaped values" do - builder = Lrama::Ruby::Languages::Ruby.const_get(:Builder, false).new + builder = GeneratedRubyFrontend.const_get(:Builder, false).new assert_equal(AST::Literal.new(:value), builder.symbol(AST::Variable.new(:local, :value))) end test "source lexer accepts argumentless command blocks" do assert_equal(AST::BlockCall.new(AST::Call.new(:included, []), [literal(1)]), - Arpaka.parse_source("included do\n 1\nend").statements.first) + GeneratedRubyFrontend.parse("included do\n 1\nend").statements.first) assert_equal(AST::BlockCall.new(AST::ReceiverCall.new(AST::BareCall.new(:foo), :".", :bar, []), [literal(1)]), - Arpaka.parse_source("foo.bar do\n 1\nend").statements.first) - assert_nothing_raised { Arpaka.parse_source("foo before: :bar do |value| value end\n") } - tokens = Arpaka.const_get(:Lexer, false).new("items << lambda do").each.to_a + GeneratedRubyFrontend.parse("foo.bar do\n 1\nend").statements.first) + assert_nothing_raised { GeneratedRubyFrontend.parse("foo before: :bar do |value| value end\n") } + tokens = GeneratedRubyFrontend.const_get(:Lexer, false).new("items << lambda do").each.to_a assert_equal([:tIDENTIFIER, :tLSHFT, :tIDENTIFIER, :keyword_do], tokens[0, 4].map(&:first)) - assert_nothing_raised { Arpaka.parse_source("items << lambda do\n work\nend\n") } + assert_nothing_raised { GeneratedRubyFrontend.parse("items << lambda do\n work\nend\n") } end test "source lexer recognizes shorthand percent literals in command arguments" do - tokens = Arpaka.const_get(:Lexer, false).new("assert %(value)\n").each.to_a + tokens = GeneratedRubyFrontend.const_get(:Lexer, false).new("assert %(value)\n").each.to_a assert_equal([:tIDENTIFIER, :tSTRING_BEG, :tSTRING_CONTENT, :tSTRING_END], tokens[0, 4].map(&:first)) - assert_equal(AST::StringLiteral.new("value"), Arpaka.parse_source("assert %(value)\n").statements.first.arguments.first) + assert_equal(AST::StringLiteral.new("value"), GeneratedRubyFrontend.parse("assert %(value)\n").statements.first.arguments.first) end test "source lexer recognizes beginless ranges" do - tokens = Arpaka.const_get(:Lexer, false).new("f in: ..range_end\n").each.to_a + tokens = GeneratedRubyFrontend.const_get(:Lexer, false).new("f in: ..range_end\n").each.to_a assert_equal(:tBDOT2, tokens.map(&:first)[2]) - assert_nothing_raised { Arpaka.parse_source("f in: ..range_end\n") } + assert_nothing_raised { GeneratedRubyFrontend.parse("f in: ..range_end\n") } end test "source lexer keeps special global variables intact" do - tokens = Arpaka.const_get(:Lexer, false).new(%q{"pid #{$$}"\n}).each.to_a + tokens = GeneratedRubyFrontend.const_get(:Lexer, false).new(%q{"pid #{$$}"\n}).each.to_a assert_equal([:tSTRING_BEG, :tSTRING_CONTENT, :tSTRING_DBEG, :tGVAR, :tSTRING_DEND, :tSTRING_END], tokens[0, 6].map(&:first)) assert_equal(:"$$", tokens[3][1]) end test "source lexer keeps the load path global intact" do - tokens = Arpaka.const_get(:Lexer, false).new("$:.unshift(\"lib\")\n").each.to_a + tokens = GeneratedRubyFrontend.const_get(:Lexer, false).new("$:.unshift(\"lib\")\n").each.to_a assert_equal(:tGVAR, tokens.first.first) assert_equal(:"$:", tokens.first.last) - assert_nothing_raised { Arpaka.parse_source("$:.unshift(\"lib\")\n") } + assert_nothing_raised { GeneratedRubyFrontend.parse("$:.unshift(\"lib\")\n") } end test "source lexer treats keyword-shaped method names as fname tokens" do - tokens = Arpaka.const_get(:Lexer, false).new("def then(&block)\nend\n").each.to_a + tokens = GeneratedRubyFrontend.const_get(:Lexer, false).new("def then(&block)\nend\n").each.to_a assert_equal(:tFID, tokens[1].first) - assert_nothing_raised { Arpaka.parse_source("def then(&block)\nend\n") } + assert_nothing_raised { GeneratedRubyFrontend.parse("def then(&block)\nend\n") } end test "source lexer preserves newlines before terminators" do - assert_nothing_raised { Arpaka.parse_source("def empty\nend\n") } - assert_nothing_raised { Arpaka.parse_source("if condition\nend\n") } - assert_nothing_raised { Arpaka.parse_source("foo do\nend\n") } + assert_nothing_raised { GeneratedRubyFrontend.parse("def empty\nend\n") } + assert_nothing_raised { GeneratedRubyFrontend.parse("if condition\nend\n") } + assert_nothing_raised { GeneratedRubyFrontend.parse("foo do\nend\n") } end test "source lexer preserves block newlines inside calls" do source = "Module.new {\n define_method(:up) { yield(:up); super() }\n define_method(:down) { yield(:down); super() }\n}\n" - assert_nothing_raised { Arpaka.parse_source(source) } - assert_nothing_raised { Arpaka.parse_source("Module.new do\n define_method(:up) do\n 1\n end\n define_method(:down) do\n 2\n end\nend\n") } + assert_nothing_raised { GeneratedRubyFrontend.parse(source) } + assert_nothing_raised { GeneratedRubyFrontend.parse("Module.new do\n define_method(:up) do\n 1\n end\n define_method(:down) do\n 2\n end\nend\n") } end test "source lexer keeps block newlines at their owning delimiter" do source = "f(-> {\n if condition\n first\n else\n second\n end\n})\n" - assert_nothing_raised { Arpaka.parse_source(source) } + assert_nothing_raised { GeneratedRubyFrontend.parse(source) } nested = "f {\n g(\n first,\n second\n )\n}\n" - assert_nothing_raised { Arpaka.parse_source(nested) } + assert_nothing_raised { GeneratedRubyFrontend.parse(nested) } end test "source lexer ignores newlines after operator assignments" do - assert_nothing_raised { Arpaka.parse_source("value ||=\n if condition\n fallback\n end\n") } + assert_nothing_raised { GeneratedRubyFrontend.parse("value ||=\n if condition\n fallback\n end\n") } end test "source lexer uses ordinary do after parenthesized calls" do - tokens = Arpaka.const_get(:Lexer, false).new("each(1) do\nend").each.to_a + tokens = GeneratedRubyFrontend.const_get(:Lexer, false).new("each(1) do\nend").each.to_a assert_equal(:keyword_do, tokens.map(&:first)[-4]) assert_equal(AST::BlockCall.new(AST::Call.new(:each, [literal(1)]), []), - Arpaka.parse_source("each(1) do\nend").statements.first) + GeneratedRubyFrontend.parse("each(1) do\nend").statements.first) end test "source lexer does not carry conditional do across statements" do - tokens = Arpaka.const_get(:Lexer, false).new("while condition\n body\nend\nfoo do\nend\n").each.to_a + tokens = GeneratedRubyFrontend.const_get(:Lexer, false).new("while condition\n body\nend\nfoo do\nend\n").each.to_a assert_equal(:keyword_do, tokens.map(&:first)[-5]) end test "source lexer balances conditional context" do - lexer = Arpaka.const_get(:Lexer, false).new("while condition do; body; end") + lexer = GeneratedRubyFrontend.const_get(:Lexer, false).new("while condition do; body; end") tokens = lexer.each.to_a assert_includes(tokens.map(&:first), :keyword_do_cond) assert_empty(lexer.context.condition_stack) - lexer = Arpaka.const_get(:Lexer, false).new("until condition\nbody\nend") + lexer = GeneratedRubyFrontend.const_get(:Lexer, false).new("until condition\nbody\nend") lexer.each.to_a assert_empty(lexer.context.condition_stack) end test "grammar precedence accepts block calls as parenthesized arguments" do - tree = Arpaka.parse_source("call(lambda do\nend)\n") + tree = GeneratedRubyFrontend.parse("call(lambda do\nend)\n") assert_equal(AST::Call.new(:call, [AST::BlockCall.new(AST::Call.new(:lambda, []), [])]), tree.statements.first) assert_nothing_raised do - Arpaka.parse_source("subscribe(x, handler, lambda do\n work\nend)\n") + GeneratedRubyFrontend.parse("subscribe(x, handler, lambda do\n work\nend)\n") end end test "source lexer uses brace blocks after parenthesized calls" do - tokens = Arpaka.const_get(:Lexer, false).new("each(1) { |value| value }\n").each.to_a + tokens = GeneratedRubyFrontend.const_get(:Lexer, false).new("each(1) { |value| value }\n").each.to_a assert_equal("{", tokens.map(&:first)[4]) - assert_nothing_raised { Arpaka.parse_source("each(1) { |value| value }\n") } + assert_nothing_raised { GeneratedRubyFrontend.parse("each(1) { |value| value }\n") } end test "source lexer uses brace blocks after receiver method calls" do - tokens = Arpaka.const_get(:Lexer, false).new("mutex.synchronize { 1 }\n").each.to_a + tokens = GeneratedRubyFrontend.const_get(:Lexer, false).new("mutex.synchronize { 1 }\n").each.to_a assert_equal("{", tokens.map(&:first)[3]) - assert_nothing_raised { Arpaka.parse_source("mutex.synchronize { 1 }\n") } - assert_nothing_raised { Arpaka.parse_source("mutex.synchronize {\n first\n second\n}\n") } + assert_nothing_raised { GeneratedRubyFrontend.parse("mutex.synchronize { 1 }\n") } + assert_nothing_raised { GeneratedRubyFrontend.parse("mutex.synchronize {\n first\n second\n}\n") } end test "source lexer uses brace blocks after calls inside arguments" do - tokens = Arpaka.const_get(:Lexer, false).new("outer(1, inner { 2 })\n").each.to_a + tokens = GeneratedRubyFrontend.const_get(:Lexer, false).new("outer(1, inner { 2 })\n").each.to_a assert_equal("{", tokens.map(&:first)[5]) - assert_nothing_raised { Arpaka.parse_source("outer(1, inner { 2 })\n") } + assert_nothing_raised { GeneratedRubyFrontend.parse("outer(1, inner { 2 })\n") } end test "source lexer chains methods after assignment brace blocks" do - tokens = Arpaka.const_get(:Lexer, false).new("value = call { 1 }.dup\n").each.to_a + tokens = GeneratedRubyFrontend.const_get(:Lexer, false).new("value = call { 1 }.dup\n").each.to_a assert_equal("{", tokens.map(&:first)[3]) - assert_nothing_raised { Arpaka.parse_source("value = call { 1 }.dup\n") } + assert_nothing_raised { GeneratedRubyFrontend.parse("value = call { 1 }.dup\n") } end test "source lexer uses regular brace blocks inside call arguments" do - tokens = Arpaka.const_get(:Lexer, false).new("outer(call { 1 })\n").each.to_a + tokens = GeneratedRubyFrontend.const_get(:Lexer, false).new("outer(call { 1 })\n").each.to_a assert_equal("{", tokens.map(&:first)[3]) - assert_nothing_raised { Arpaka.parse_source("outer(call { 1 })\n") } + assert_nothing_raised { GeneratedRubyFrontend.parse("outer(call { 1 })\n") } end test "source lexer starts one-line method bodies with array literals" do - tokens = Arpaka.const_get(:Lexer, false).new("def values() [1] end\n").each.to_a + tokens = GeneratedRubyFrontend.const_get(:Lexer, false).new("def values() [1] end\n").each.to_a assert_equal(:tLBRACK, tokens.map(&:first)[4]) - assert_nothing_raised { Arpaka.parse_source("def values() [1] end\n") } + assert_nothing_raised { GeneratedRubyFrontend.parse("def values() [1] end\n") } end test "source lexer uses regular brace blocks after ternary branches" do - tokens = Arpaka.const_get(:Lexer, false).new("touch ? callback { 1 } : fallback\n").each.to_a + tokens = GeneratedRubyFrontend.const_get(:Lexer, false).new("touch ? callback { 1 } : fallback\n").each.to_a assert_equal("{", tokens.map(&:first)[3]) - assert_nothing_raised { Arpaka.parse_source("touch ? callback { 1 } : fallback\n") } + assert_nothing_raised { GeneratedRubyFrontend.parse("touch ? callback { 1 } : fallback\n") } end test "source lexer preserves newlines inside command blocks" do assert_nothing_raised do - Arpaka.parse_source("app = lambda { |env|\n req = Request.new(env)\n res = response(req)\n}\n") + GeneratedRubyFrontend.parse("app = lambda { |env|\n req = Request.new(env)\n res = response(req)\n}\n") end end test "source lexer recognizes lambda bodies after lambda arguments" do - tokens = Arpaka.const_get(:Lexer, false).new("->(value) { value }\n").each.to_a + tokens = GeneratedRubyFrontend.const_get(:Lexer, false).new("->(value) { value }\n").each.to_a assert_includes(tokens.map(&:first), :tLAMBEG) - assert_nothing_raised { Arpaka.parse_source("->(value) { value }\n") } - assert_nothing_raised { Arpaka.parse_source("-> {\n first\n second\n}\n") } - assert_nothing_raised { Arpaka.parse_source("f(&lambda { |value| value })\n") } + assert_nothing_raised { GeneratedRubyFrontend.parse("->(value) { value }\n") } + assert_nothing_raised { GeneratedRubyFrontend.parse("-> {\n first\n second\n}\n") } + assert_nothing_raised { GeneratedRubyFrontend.parse("f(&lambda { |value| value })\n") } end test "source lexer ignores newlines after logical operators" do - assert_nothing_raised { Arpaka.parse_source("left &&\nright\n") } - assert_nothing_raised { Arpaka.parse_source("left ||\nright\n") } + assert_nothing_raised { GeneratedRubyFrontend.parse("left &&\nright\n") } + assert_nothing_raised { GeneratedRubyFrontend.parse("left ||\nright\n") } end test "source lexer recognizes modifiers after argumentless control keywords" do - tokens = Arpaka.const_get(:Lexer, false).new("return unless value\n").each.to_a + tokens = GeneratedRubyFrontend.const_get(:Lexer, false).new("return unless value\n").each.to_a assert_equal(:modifier_unless, tokens[1].first) - assert_nothing_raised { Arpaka.parse_source("def stop\n return unless value\nend\n") } + assert_nothing_raised { GeneratedRubyFrontend.parse("def stop\n return unless value\nend\n") } end test "source lexer recognizes modifiers after block endings" do - tokens = Arpaka.const_get(:Lexer, false).new("foo do\nend unless value\n").each.to_a + tokens = GeneratedRubyFrontend.const_get(:Lexer, false).new("foo do\nend unless value\n").each.to_a assert_equal(:modifier_unless, tokens[4].first) - assert_nothing_raised { Arpaka.parse_source("foo do\nend unless value\n") } + assert_nothing_raised { GeneratedRubyFrontend.parse("foo do\nend unless value\n") } end test "source lexer accepts escaped newlines between expressions" do - tokens = Arpaka.const_get(:Lexer, false).new("\"left\" \\\n\"right\"\n").each.to_a + tokens = GeneratedRubyFrontend.const_get(:Lexer, false).new("\"left\" \\\n\"right\"\n").each.to_a assert_equal([:tSTRING_BEG, :tSTRING_CONTENT, :tSTRING_END, :tSTRING_BEG], tokens.map(&:first)[0, 4]) - assert_nothing_raised { Arpaka.parse_source("\"left\" \\\n\"right\"\n") } + assert_nothing_raised { GeneratedRubyFrontend.parse("\"left\" \\\n\"right\"\n") } end test "source lexer continues method chains after newlines" do - assert_nothing_raised { Arpaka.parse_source("value\n .first\n .to_s\n") } + assert_nothing_raised { GeneratedRubyFrontend.parse("value\n .first\n .to_s\n") } end test "source lexer ignores newlines before block parameters" do - assert_nothing_raised { Arpaka.parse_source("call {\n |value| value\n}\n") } + assert_nothing_raised { GeneratedRubyFrontend.parse("call {\n |value| value\n}\n") } end test "source lexer continues keyword arguments after label newlines" do assert_nothing_raised do - Arpaka.parse_source("parse(file, context:\n build_context)\n") + GeneratedRubyFrontend.parse("parse(file, context:\n build_context)\n") end end test "source lexer recognizes string hash labels" do - assert_nothing_raised { Arpaka.parse_source("{ \"key\": value }\n") } + assert_nothing_raised { GeneratedRubyFrontend.parse("{ \"key\": value }\n") } end test "source lexer recognizes unary operator symbols" do - assert_nothing_raised { Arpaka.parse_source("alias :-@ :deduplicate\n") } - assert_nothing_raised { Arpaka.parse_source("alias_method :push, :<<\nalias_method :append, :<<\n") } + assert_nothing_raised { GeneratedRubyFrontend.parse("alias :-@ :deduplicate\n") } + assert_nothing_raised { GeneratedRubyFrontend.parse("alias_method :push, :<<\nalias_method :append, :<<\n") } end test "source lexer recognizes operator method names and top-level constants" do assert_equal(AST::Def.new(:[], :x, [AST::BareCall.new(:x)]), - Arpaka.parse_source("def [](x); x; end").statements.first) - assert_nothing_raised { Arpaka.parse_source("def /(other); other; end") } - assert_nothing_raised { Arpaka.parse_source("def &(other); other; end") } - assert_equal(:Foo, Arpaka.parse_source("::Foo").statements.first) + GeneratedRubyFrontend.parse("def [](x); x; end").statements.first) + assert_nothing_raised { GeneratedRubyFrontend.parse("def /(other); other; end") } + assert_nothing_raised { GeneratedRubyFrontend.parse("def &(other); other; end") } + assert_equal(:Foo, GeneratedRubyFrontend.parse("::Foo").statements.first) end test "source lexer preserves alias boundaries and symbol statement endings" do - tokens = Arpaka.const_get(:Lexer, false).new("alias foo= bar").each.to_a + tokens = GeneratedRubyFrontend.const_get(:Lexer, false).new("alias foo= bar").each.to_a assert_equal([:keyword_alias, :tFID, :tIDENTIFIER, 0], tokens.map(&:first)) - assert_nothing_raised { Arpaka.parse_source("alias foo= bar") } + assert_nothing_raised { GeneratedRubyFrontend.parse("alias foo= bar") } source = "alias_method :eql?, :==\ndef hash; 1; end" - tokens = Arpaka.const_get(:Lexer, false).new(source).each.to_a + tokens = GeneratedRubyFrontend.const_get(:Lexer, false).new(source).each.to_a assert_include(tokens.map(&:first), "\n") - assert_nothing_raised { Arpaka.parse_source(source) } + assert_nothing_raised { GeneratedRubyFrontend.parse(source) } - assert_nothing_raised { Arpaka.parse_source("undef_method :==, :!, :!=\ndef hash; 1; end") } - assert_nothing_raised { Arpaka.parse_source("alias [] get") } - assert_nothing_raised { Arpaka.parse_source("alias []= get") } - assert_nothing_raised { Arpaka.parse_source("alias :eql? :==\n\ndef hash; 1; end") } + assert_nothing_raised { GeneratedRubyFrontend.parse("undef_method :==, :!, :!=\ndef hash; 1; end") } + assert_nothing_raised { GeneratedRubyFrontend.parse("alias [] get") } + assert_nothing_raised { GeneratedRubyFrontend.parse("alias []= get") } + assert_nothing_raised { GeneratedRubyFrontend.parse("alias :eql? :==\n\ndef hash; 1; end") } end test "source lexer allows keyword-shaped receiver setters" do - tokens = Arpaka.const_get(:Lexer, false).new("node.case = value").each.to_a + tokens = GeneratedRubyFrontend.const_get(:Lexer, false).new("node.case = value").each.to_a assert_equal([:tIDENTIFIER, ".", :tIDENTIFIER, "=", :tIDENTIFIER, 0], tokens.map(&:first)) - assert_nothing_raised { Arpaka.parse_source("node.case = value") } + assert_nothing_raised { GeneratedRubyFrontend.parse("node.case = value") } end test "source lexer accepts symbols after predicate-like identifiers" do - assert_nothing_raised { Arpaka.parse_source("alias :merge! :update\n") } - assert_nothing_raised { Arpaka.parse_source("alias :default_options= :default\n") } - assert_nothing_raised { Arpaka.parse_source("block_given? ? yield : @default_render.call\n") } + assert_nothing_raised { GeneratedRubyFrontend.parse("alias :merge! :update\n") } + assert_nothing_raised { GeneratedRubyFrontend.parse("alias :default_options= :default\n") } + assert_nothing_raised { GeneratedRubyFrontend.parse("block_given? ? yield : @default_render.call\n") } end test "source lexer accepts interpolated symbols" do - assert_nothing_raised { Arpaka.parse_source(':"#{name}_settings"') } - assert_nothing_raised { Arpaka.parse_source("super(:/, left, right)") } + assert_nothing_raised { GeneratedRubyFrontend.parse(':"#{name}_settings"') } + assert_nothing_raised { GeneratedRubyFrontend.parse("super(:/, left, right)") } end test "source lexer scans slashes inside regexp interpolation" do source = '/#{value.sub(/x/, "")}/' - tokens = Arpaka.const_get(:Lexer, false).new(source).each.to_a + tokens = GeneratedRubyFrontend.const_get(:Lexer, false).new(source).each.to_a assert_equal([:tREGEXP_BEG, :tSTRING_DBEG, :tIDENTIFIER, ".", :tIDENTIFIER, "(", :tREGEXP_BEG, :tSTRING_CONTENT, :tREGEXP_END, ",", :tSTRING_BEG, :tSTRING_END, ")", :tSTRING_DEND, :tREGEXP_END, 0], tokens.map(&:first)) - assert_kind_of(AST::RegexpLiteral, Arpaka.parse_source(source).statements.first) + assert_kind_of(AST::RegexpLiteral, GeneratedRubyFrontend.parse(source).statements.first) end test "source lexer accepts ampersand operator symbols" do - assert_nothing_raised { Arpaka.parse_source("items.reduce(:&)\n") } - assert_nothing_raised { Arpaka.parse_source("delegate :[], :[]=, to: :paths\n") } - assert_nothing_raised { Arpaka.parse_source("left &\n right\n") } - assert_nothing_raised { Arpaka.parse_source("left |\n right\n") } - assert_nothing_raised { Arpaka.parse_source("left ^\n right\n") } - assert_nothing_raised { Arpaka.parse_source("foo *args\n") } - assert_nothing_raised { Arpaka.parse_source("foo **options\n") } - assert_nothing_raised { Arpaka.parse_source("first..\n last\n") } - assert_nothing_raised { Arpaka.parse_source("left and\n right\n") } - assert_nothing_raised { Arpaka.parse_source("left or\n right\n") } - assert_nothing_raised { Arpaka.parse_source("left ==\n right\n") } - assert_nothing_raised { Arpaka.parse_source("not\n value\n") } - assert_nothing_raised { Arpaka.parse_source("fn = ->\n { 1 }\n") } + assert_nothing_raised { GeneratedRubyFrontend.parse("items.reduce(:&)\n") } + assert_nothing_raised { GeneratedRubyFrontend.parse("delegate :[], :[]=, to: :paths\n") } + assert_nothing_raised { GeneratedRubyFrontend.parse("left &\n right\n") } + assert_nothing_raised { GeneratedRubyFrontend.parse("left |\n right\n") } + assert_nothing_raised { GeneratedRubyFrontend.parse("left ^\n right\n") } + assert_nothing_raised { GeneratedRubyFrontend.parse("foo *args\n") } + assert_nothing_raised { GeneratedRubyFrontend.parse("foo **options\n") } + assert_nothing_raised { GeneratedRubyFrontend.parse("first..\n last\n") } + assert_nothing_raised { GeneratedRubyFrontend.parse("left and\n right\n") } + assert_nothing_raised { GeneratedRubyFrontend.parse("left or\n right\n") } + assert_nothing_raised { GeneratedRubyFrontend.parse("left ==\n right\n") } + assert_nothing_raised { GeneratedRubyFrontend.parse("not\n value\n") } + assert_nothing_raised { GeneratedRubyFrontend.parse("fn = ->\n { 1 }\n") } end test "source lexer recognizes bitwise assignment operators" do %w[|= &= ^=].each do |operator| - assert_nothing_raised { Arpaka.parse_source("value #{operator} other\n") } + assert_nothing_raised { GeneratedRubyFrontend.parse("value #{operator} other\n") } end end test "source lexer recognizes class variables" do - assert_nothing_raised { Arpaka.parse_source("@@templates = {}\n@@templates\n") } + assert_nothing_raised { GeneratedRubyFrontend.parse("@@templates = {}\n@@templates\n") } end test "source lexer recognizes punctuation global variables" do - assert_nothing_raised { Arpaka.parse_source("-$`\n") } - assert_nothing_raised { Arpaka.parse_source("$&\n") } + assert_nothing_raised { GeneratedRubyFrontend.parse("-$`\n") } + assert_nothing_raised { GeneratedRubyFrontend.parse("$&\n") } end test "source lexer parses parenthesized yield arguments" do - assert_nothing_raised { Arpaka.parse_source("def each\n yield(value, other)\nend\n") } + assert_nothing_raised { GeneratedRubyFrontend.parse("def each\n yield(value, other)\nend\n") } end test "source lexer distinguishes modulo from percent literals" do - assert_nothing_raised { Arpaka.parse_source("value = (left + right) % 3\n") } - assert_nothing_raised { Arpaka.parse_source("value = %q(text)\n") } - assert_nothing_raised { Arpaka.parse_source("value = %r{pattern}i\n") } + assert_nothing_raised { GeneratedRubyFrontend.parse("value = (left + right) % 3\n") } + assert_nothing_raised { GeneratedRubyFrontend.parse("value = %q(text)\n") } + assert_nothing_raised { GeneratedRubyFrontend.parse("value = %r{pattern}i\n") } end test "source lexer accepts percent literals as command arguments" do - assert_nothing_raised { Arpaka.parse_source("def f; assert_match %r{a}, value; end") } - assert_nothing_raised { Arpaka.parse_source("def f; assert_equal %w[a b], value; end") } - assert_nothing_raised { Arpaka.parse_source("test do; assert_equal %w[a b], value; end") } + assert_nothing_raised { GeneratedRubyFrontend.parse("def f; assert_match %r{a}, value; end") } + assert_nothing_raised { GeneratedRubyFrontend.parse("def f; assert_equal %w[a b], value; end") } + assert_nothing_raised { GeneratedRubyFrontend.parse("test do; assert_equal %w[a b], value; end") } end test "source lexer keeps expression-ending keywords and globals together" do - assert_nothing_raised { Arpaka.parse_source("test do; f __LINE__ + 1; end") } - assert_nothing_raised { Arpaka.parse_source("module M; def f; yield if true; end; end") } - assert_nothing_raised { Arpaka.parse_source("class C; def f; super if true; end; end") } - assert_nothing_raised { Arpaka.parse_source("$?.exitstatus") } + assert_nothing_raised { GeneratedRubyFrontend.parse("test do; f __LINE__ + 1; end") } + assert_nothing_raised { GeneratedRubyFrontend.parse("module M; def f; yield if true; end; end") } + assert_nothing_raised { GeneratedRubyFrontend.parse("class C; def f; super if true; end; end") } + assert_nothing_raised { GeneratedRubyFrontend.parse("$?.exitstatus") } end test "source lexer distinguishes command argument delimiters and blocks" do - assert_nothing_raised { Arpaka.parse_source("f ::Time, value") } - assert_nothing_raised { Arpaka.parse_source("def f; assert_equal (count * 2) - 1, total; end") } - assert_nothing_raised { Arpaka.parse_source("value = { xml: lambda { 1 } }") } - assert_nothing_raised { Arpaka.parse_source("f(:x, proc { 1 })") } - assert_nothing_raised { Arpaka.parse_source("f :x, lambda { 1 }") } - assert_nothing_raised { Arpaka.parse_source("-> arg do; arg; end") } - assert_nothing_raised { Arpaka.parse_source("test do; f only: A::B do; 1; end; end") } - assert_nothing_raised { Arpaka.parse_source("f at: 30.days.from_now do; 1; end") } - assert_nothing_raised { Arpaka.parse_source("travel_to Time.now + 3.seconds do; 1; end") } - assert_nothing_raised { Arpaka.parse_source("value = left || proc { 1 }") } + assert_nothing_raised { GeneratedRubyFrontend.parse("f ::Time, value") } + assert_nothing_raised { GeneratedRubyFrontend.parse("def f; assert_equal (count * 2) - 1, total; end") } + assert_nothing_raised { GeneratedRubyFrontend.parse("value = { xml: lambda { 1 } }") } + assert_nothing_raised { GeneratedRubyFrontend.parse("f(:x, proc { 1 })") } + assert_nothing_raised { GeneratedRubyFrontend.parse("f :x, lambda { 1 }") } + assert_nothing_raised { GeneratedRubyFrontend.parse("-> arg do; arg; end") } + assert_nothing_raised { GeneratedRubyFrontend.parse("test do; f only: A::B do; 1; end; end") } + assert_nothing_raised { GeneratedRubyFrontend.parse("f at: 30.days.from_now do; 1; end") } + assert_nothing_raised { GeneratedRubyFrontend.parse("travel_to Time.now + 3.seconds do; 1; end") } + assert_nothing_raised { GeneratedRubyFrontend.parse("value = left || proc { 1 }") } end test "source lexer accepts lambda argument defaults" do - assert_nothing_raised { Arpaka.parse_source("def [](x, y = nil); end") } - assert_nothing_raised { Arpaka.parse_source("->(x = nil) { x }") } + assert_nothing_raised { GeneratedRubyFrontend.parse("def [](x, y = nil); end") } + assert_nothing_raised { GeneratedRubyFrontend.parse("->(x = nil) { x }") } end test "source lexer ignores pending heredoc newlines in delimiters" do - assert_nothing_raised { Arpaka.parse_source("f({a: <<~A,\nx\nA\nb: 1})") } - assert_nothing_raised { Arpaka.parse_source("f unless\n predicate") } - assert_nothing_raised { Arpaka.parse_source("f(\nproc { 1 }\n)\n") } + assert_nothing_raised { GeneratedRubyFrontend.parse("f({a: <<~A,\nx\nA\nb: 1})") } + assert_nothing_raised { GeneratedRubyFrontend.parse("f unless\n predicate") } + assert_nothing_raised { GeneratedRubyFrontend.parse("f(\nproc { 1 }\n)\n") } end test "source lexer terminates aliases whose target is an operator" do - assert_nothing_raised { Arpaka.parse_source("class C\n alias eql? ==\n def hash\n 1\n end\nend\n") } + assert_nothing_raised { GeneratedRubyFrontend.parse("class C\n alias eql? ==\n def hash\n 1\n end\nend\n") } end test "single quoted heredoc keeps interpolation literal" do assert_equal(AST::StringLiteral.new("\#{x}\n"), - Arpaka.parse_source("<<'TEXT'\n\#{x}\nTEXT\n").statements.first) + GeneratedRubyFrontend.parse("<<'TEXT'\n\#{x}\nTEXT\n").statements.first) end end diff --git a/test/lrama/ruby_receiver_call_test.rb b/test/lrama/ruby_receiver_call_test.rb index 5a6cb4f..bc2c246 100644 --- a/test/lrama/ruby_receiver_call_test.rb +++ b/test/lrama/ruby_receiver_call_test.rb @@ -1,14 +1,15 @@ # frozen_string_literal: true require "test_helper" +require_relative "../generated_frontend_helper" class RubyReceiverCallTest < Test::Unit::TestCase - AST = Arpaka::AST + AST = GeneratedRubyFrontend::AST test "receiver calls retain their operator, method name and ordered arguments" do [".", "::", "&."].each do |operator| ["object#{operator}fetch(1, 2)", "object#{operator}fetch 1, 2"].each do |source| - tree = Arpaka.parse_source(source).statements.fetch(0) + tree = GeneratedRubyFrontend.parse(source).statements.fetch(0) assert_equal(AST::ReceiverCall.new(AST::BareCall.new(:object), operator.to_sym, :fetch, [AST::Literal.new(1), AST::Literal.new(2)]), tree, source) assert_predicate(tree.arguments, :frozen?) @@ -21,7 +22,7 @@ class RubyReceiverCallTest < Test::Unit::TestCase source = "handler#{operator}(message)" expected = AST::ReceiverCall.new(AST::BareCall.new(:handler), operator.to_sym, :call, [AST::BareCall.new(:message)]) - assert_equal(AST::Program.new([expected]), Arpaka.parse_source(source), source) + assert_equal(AST::Program.new([expected]), GeneratedRubyFrontend.parse(source), source) end end @@ -30,7 +31,7 @@ class RubyReceiverCallTest < Test::Unit::TestCase source = "object&.#{name}(1)" expected = AST::ReceiverCall.new(AST::BareCall.new(:object), :"&.", name.to_sym, [AST::Literal.new(1)]) - assert_equal(AST::Program.new([expected]), Arpaka.parse_source(source), source) + assert_equal(AST::Program.new([expected]), GeneratedRubyFrontend.parse(source), source) end end @@ -45,7 +46,7 @@ class RubyReceiverCallTest < Test::Unit::TestCase ] sources.each do |source| expected = normalize_rubyvm(RubyVM::AbstractSyntaxTree.parse(source).children.fetch(2)) - actual = normalize_arpaka(Arpaka.parse_source(source).statements.fetch(0)) + actual = normalize_arpaka(GeneratedRubyFrontend.parse(source).statements.fetch(0)) assert_equal(expected, actual, source) end end diff --git a/test/lrama/trick13_test.rb b/test/lrama/trick13_test.rb new file mode 100644 index 0000000..e494011 --- /dev/null +++ b/test/lrama/trick13_test.rb @@ -0,0 +1,39 @@ +# frozen_string_literal: true + +require "test_helper" +require_relative "../generated_frontend_helper" + +class Trick13Test < Test::Unit::TestCase + TRICK_ROOT = ENV["TRICK13_ROOT"] + + test "parses every trick13 Ruby file" do + omit("set TRICK13_ROOT to run the external Trick 2013 corpus") unless TRICK_ROOT && Dir.exist?(TRICK_ROOT) + + files = Dir[File.join(TRICK_ROOT, "**", "*.rb")].sort + + assert_equal(11, files.length) + files.each do |path| + assert_nothing_raised("#{path}: generated frontend parses this file") do + GeneratedRubyFrontend.parse(File.read(path), filename: path) + end + end + end + + test "recognizes a backtick as a symbol method name" do + assert_nothing_raised { GeneratedRubyFrontend.parse("value < 1 and:`#\n") } + end + + test "recognizes a method named do after a receiver" do + assert_nothing_raised { GeneratedRubyFrontend.parse("value.to.do(1)\n") } + end + + test "accepts an attribute assignment without a method-name suffix" do + assert_nothing_raised { GeneratedRubyFrontend.parse("cell.symbol=0\n") } + end + + test "accepts a Unicode heredoc delimiter" do + source = "value = <<−1.hex\nbody\n−1\n" + + assert_nothing_raised { GeneratedRubyFrontend.parse(source) } + end +end diff --git a/test/lrama/trick15_test.rb b/test/lrama/trick15_test.rb new file mode 100644 index 0000000..368a79e --- /dev/null +++ b/test/lrama/trick15_test.rb @@ -0,0 +1,44 @@ +# frozen_string_literal: true + +require "test_helper" +require_relative "../generated_frontend_helper" + +class Trick15Test < Test::Unit::TestCase + TRICK_ROOT = ENV["TRICK15_ROOT"] + + test "parses every trick15 Ruby file" do + omit("set TRICK15_ROOT to run the external Trick 2015 corpus") unless TRICK_ROOT && Dir.exist?(TRICK_ROOT) + + files = Dir[File.join(TRICK_ROOT, "**", "*.rb")].sort + + assert_equal(13, files.length) + files.each do |path| + assert_nothing_raised("#{path}: generated frontend parses this file") do + GeneratedRubyFrontend.parse(File.read(path), filename: path) + end + end + end + + test "accepts undef with an operator method name" do + assert_nothing_raised { GeneratedRubyFrontend.parse("class Object; undef[]; end\n") } + end + + test "accepts a percent method call followed by a percent literal" do + assert_nothing_raised { GeneratedRubyFrontend.parse("value.% %%x%\n") } + end + + test "accepts redo with a modifier" do + assert_nothing_raised { GeneratedRubyFrontend.parse("begin; redo if condition; end\n") } + end + + test "ignores a newline before a method name after def" do + assert_nothing_raised { GeneratedRubyFrontend.parse("def\n-(value) value end\n") } + end + + test "represents an empty percent word array" do + tree = GeneratedRubyFrontend.parse("value = %I{}\n") + + assert_kind_of(GeneratedRubyFrontend::AST::ArrayLiteral, tree.statements.first.value) + assert_empty(tree.statements.first.value.elements) + end +end diff --git a/test/lrama/trick18_test.rb b/test/lrama/trick18_test.rb new file mode 100644 index 0000000..debb3ca --- /dev/null +++ b/test/lrama/trick18_test.rb @@ -0,0 +1,78 @@ +# frozen_string_literal: true + +require "test_helper" +require_relative "../generated_frontend_helper" + +class Trick18Test < Test::Unit::TestCase + test "parses the kinaba keyword alias program" do + source = <<~'RUBY' + alias BEGIN for unless def class + super true or return defined? next + break while begin undef do end + rescue then retry else undef module + nil ensure case if yield __LINE__ + self and redo elsif not __FILE__ + alias END in end when __ENCODING__ + end until false end + RUBY + + assert_nothing_raised { GeneratedRubyFrontend.parse(source) } + end + + test "does not split equality after a receiver method" do + assert_nothing_raised { GeneratedRubyFrontend.parse("value.__id__ == __id__\n") } + end + + test "keeps unary operator method names together after def" do + assert_nothing_raised { GeneratedRubyFrontend.parse("def -@; end\ndef +@; end\n") } + end + + test "keeps unary operator method names together in aliases" do + assert_nothing_raised { GeneratedRubyFrontend.parse("alias +@ -@\nalias + -\n") } + end + + test "ignores newlines inside array literals" do + source = "value = [*self, nil, *items\n]\n" + + assert_nothing_raised { GeneratedRubyFrontend.parse(source) } + end + + test "continues comparisons across newlines" do + assert_nothing_raised { GeneratedRubyFrontend.parse("value = first >\nsecond\n") } + end + + test "recognizes the slash special global variable" do + tokens = GeneratedRubyFrontend.const_get(:Lexer, false).new("$/\n").each.to_a + + assert_equal(:tGVAR, tokens.first.first) + assert_equal(:"$/", tokens.first.last) + end + + test "uses indexing brackets for calls nested in arrays" do + assert_nothing_raised { GeneratedRubyFrontend.parse("values = [first, second [item]]\n") } + assert_nothing_raised { GeneratedRubyFrontend.parse("values = [first * index [0]]\n") } + end + + test "uses regular parentheses for receiver method definitions" do + assert_nothing_raised { GeneratedRubyFrontend.parse("def STDOUT.write (value); end\n") } + end + + test "tokenizes empty brackets after a closed expression" do + tokens = GeneratedRubyFrontend.const_get(:Lexer, false).new("value = [1][]\n").each.to_a + + assert_equal(["]", "[", "]"], tokens[4, 3].map(&:first)) + assert_nothing_raised { GeneratedRubyFrontend.parse("value = [1][]\n") } + end + + test "continues expressions after a newline following the root namespace" do + source = <<~'RUBY' + def value + result = first - :: + Second + result + end + RUBY + + assert_nothing_raised { GeneratedRubyFrontend.parse(source) } + end +end diff --git a/test/lrama/trick22_test.rb b/test/lrama/trick22_test.rb new file mode 100644 index 0000000..b5fde8d --- /dev/null +++ b/test/lrama/trick22_test.rb @@ -0,0 +1,36 @@ +# frozen_string_literal: true + +require "test_helper" +require_relative "../generated_frontend_helper" + +class Trick22Test < Test::Unit::TestCase + TRICK_ROOT = ENV["TRICK22_ROOT"] + + test "parses every trick22 Ruby file" do + omit("set TRICK22_ROOT to run the external Trick 2022 corpus") unless TRICK_ROOT && Dir.exist?(TRICK_ROOT) + + files = Dir[File.join(TRICK_ROOT, "**", "*.rb")].sort + + assert_equal(12, files.length) + files.each do |path| + assert_nothing_raised("#{path}: generated frontend parses this file") do + GeneratedRubyFrontend.parse(File.read(path), filename: path) + end + end + end + + test "does not treat an operator method call as a heredoc" do + assert_nothing_raised { GeneratedRubyFrontend.parse("receiver.< "tLBRACE_ARG" - }.freeze - - def self.parse(source, filename) - grammar = Lrama::Parser.new(source, filename).parse - unless grammar.no_stdlib - stdlib = Lrama::Command::STDLIB_FILE_PATH - grammar.prepend_parameterized_rules(Lrama::Parser.new(File.read(stdlib), stdlib).parse.parameterized_rules) - end - grammar.prepare - grammar.validate! - grammar - end - - def self.load_upstream - vendor = File.join(ROOT, "vendor/ruby") - manifest = JSON.parse(File.read(File.join(vendor, "source.json"))) - raise "Unexpected upstream revision" unless manifest.fetch("revision") == REVISION - manifest.fetch("files").each do |path, digest| - raise "Upstream checksum mismatch: #{path}" unless Digest::SHA256.file(File.join(vendor, path)).hexdigest == digest - end - source, error, result = Open3.capture3(RbConfig.ruby, - File.join(vendor, "tool/id2token.rb"), File.join(vendor, "parse.y")) - raise error unless result.success? - grammar = parse(source, "vendor/ruby/parse.y") - inventory = grammar.rules.reject(&:initial_rule?).map do |rule| - { "id" => rule.id, "lhs" => rule.lhs.id.s_value, - "rhs" => rule.rhs.map { |symbol| symbol.id.s_value }, - "line" => rule.token_code&.location&.first_line || rule.lineno, - "code" => rule.token_code&.s_value, - "midrule_position" => rule.position_in_original_rule_rhs, - "precedence" => rule.precedence_sym&.id&.s_value } - end - expected = JSON.parse(File.read(File.join(__dir__, "upstream_rules.json"))) - raise "Upstream rules changed; review action mappings" unless inventory == expected - unknown = RubyGrammarActions::ACTIONS.keys - inventory.map { |rule| rule.fetch("id") } - raise "Unknown action IDs: #{unknown.inspect}" unless unknown.empty? - [grammar, inventory] - end - - def self.symbol_names(grammar) - used = grammar.terms.map { |symbol| symbol.id.s_value } - grammar.nterms.to_h do |symbol| - original = symbol.id.s_value - candidate = if original.match?(/\A\$?@\d+\z/) - "midrule_#{original.delete('$@')}" - else - original.gsub("'\\n'", "newline").gsub(/[^a-zA-Z0-9_]/, "_") - end - candidate = "rule_#{candidate}" unless candidate.match?(/\A[a-zA-Z_]/) - candidate += "_#{symbol.number}" if used.include?(candidate) - raise "Symbol name collision: #{candidate}" if used.include?(candidate) - used << candidate - [original, candidate] - end - end - - def self.artifacts - grammar, inventory = load_upstream - names = symbol_names(grammar) - name = ->(symbol) { symbol.term ? symbol.id.s_value : names.fetch(symbol.id.s_value) } - lines = ["/* Generated from ruby/ruby #{REVISION}. See tool/ruby/actions.rb. */", - "%no-stdlib", "%expect #{grammar.expect || 0}"] - lines << "%define lr.type ielr" if grammar.ielr_defined? - grammar.terms.each do |symbol| - next if symbol.error_symbol? || symbol.undef_symbol? - declaration = "%token #{name.call(symbol)} #{symbol.token_id}" - declaration += " #{symbol.alias_name}" if symbol.alias_name - lines << declaration - end - grammar.precedences.group_by(&:precedence).sort.each do |_level, group| - lines << "%#{group.first.type} #{group.map(&:s_value).join(' ')}" - end - lines << "%left keyword_do_block" - lines << "%start #{name.call(grammar.rules.first.rhs.first)}" - lines << "%%" - metadata = inventory.to_h { |rule| [rule.fetch("id"), rule] } - rules = grammar.rules.reject(&:initial_rule?) - rules.each do |rule| - rhs = rule.rhs.empty? ? "%empty" : rule.rhs.map(&name).join(" ") - extended_precedence = EXTENDED_RULES[[rule.lhs.id.s_value, rule.rhs.map { |symbol| symbol.id.s_value }]] - precedence = if rule.precedence_sym - " %prec #{name.call(rule.precedence_sym)}" - elsif extended_precedence - " %prec #{extended_precedence}" - else - "" - end - expression = RubyGrammarActions::ACTIONS[rule.id]&.last || "@builder.unsupported(#{rule.id})" - lines << "/* upstream parse.y:#{metadata.fetch(rule.id).fetch('line')}: #{rule.as_comment} */" - lines << "#{name.call(rule.lhs)}: #{rhs}#{precedence} { $$ = #{expression} };" - end - lines << "/* arpaka extension: block call as a parenthesized argument */" - lines << "call_args: block_call { $$ = [$1].freeze };" - lines << "call_args: args ',' block_call %prec tLOWEST { $$ = ($1 + [$3]).freeze };" - source = lines.join("\n") + "\n" - verify_structure(grammar, source, names) - compact = rules.map do |rule| - { id: rule.id, rule: rule.as_comment, - line: metadata.fetch(rule.id).fetch("line"), - status: RubyGrammarActions::ACTIONS[rule.id]&.first || "unsupported" } - end - { "parse.y" => source, "rules.json" => JSON.pretty_generate(compact) + "\n" } - end - - def self.verify_structure(original, source, names) - # C-lexer inspection only: @builder is a Ruby ivar, not a yacc location. - port = parse(source.gsub("@builder.", "builder_"), "generated Ruby grammar") - signature = lambda do |grammar, rename| - grammar.rules.reject(&:initial_rule?).map do |rule| - name = ->(symbol) { rename.fetch(symbol.id.s_value, symbol.id.s_value) } - [name.call(rule.lhs), rule.rhs.map(&name), rule.precedence_sym && name.call(rule.precedence_sym)] - end.sort_by(&:inspect) - end - expected_rules = signature.call(original, names).map do |rule| - if rule[0] == names.fetch("value_expr_command") && rule[1] == [names.fetch("command")] - rule[0, 2] + ["tLBRACE_ARG"] - else - rule - end - end - expected_rules << ["call_args", ["block_call"], nil] - expected_rules << ["call_args", ["args", "','", "block_call"], "tLOWEST"] - actual_rules = signature.call(port, {}) - unless expected_rules.sort_by(&:inspect) == actual_rules.sort_by(&:inspect) - raise "Production structure changed" - end - terms = lambda do |grammar| - grammar.terms.map { |symbol| [symbol.id.s_value, symbol.token_id, symbol.precedence&.type, symbol.precedence&.precedence] }.sort_by(&:inspect) - end - expected_terms = terms.call(original).map do |term| - if term[0] == "keyword_do_block" - [term[0], term[1], :left, original.precedences.map(&:precedence).max + 1] - else - term - end - end - actual_terms = terms.call(port) - unless expected_terms.sort_by(&:inspect) == actual_terms - raise "Tokens or precedence changed: expected=#{expected_terms.sort_by(&:inspect).inspect} actual=#{actual_terms.inspect}" - end - end - - def self.run(check:) - artifacts.each do |file, contents| - path = File.join(DESTINATION, file) - if check - raise "Stale #{file}; run rake ruby:generate" unless File.file?(path) && File.read(path) == contents - else - File.write(path, contents) - end - end - puts(check ? "Ruby grammar verified (853 upstream productions + extensions)" : "Ruby grammar regenerated") - end +require_relative "../../lib/arpaka" +require_relative "../../lib/arpaka/cli" + +source = ENV.fetch("RUBY_SOURCE", File.expand_path("../../vendor/ruby", __dir__)) +if ARGV == ["--check"] + Arpaka.generate(ruby_source: source) + puts "Ruby grammar, Action mappings and standalone frontend verified" +else + exit Arpaka::CLI.run(["generate", "--ruby-source", source, *ARGV]) end - -RubyGrammar.run(check: ARGV.include?("--check")) if $PROGRAM_NAME == __FILE__