summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authornagachika <nagachika@ruby-lang.org>2021-02-20 18:46:40 +0900
committernagachika <nagachika@ruby-lang.org>2021-02-20 18:46:40 +0900
commit931815bfd86df603337194f3fcefb46bfe3e7940 (patch)
tree88cada33acfc5f71947c0b54ef6f8b4da5a358fd
parent0cfd491732162eab61227ac4b49617c37ddbb316 (diff)
merge revision(s) eeacdcb9a073c7d8ad703e0dc9faf229a5ebbe3c: [Backport #17558]
Fixed premature return After setting ruby2_keywords for bmethod, the rest of arguments had been ignored. [Bug #17558] --- test/ruby/test_keyword.rb | 9 +++++++++ vm_method.c | 2 +- 2 files changed, 10 insertions(+), 1 deletion(-)
-rwxr-xr-xlib/exe/bundle47
-rwxr-xr-xlib/exe/bundler4
-rwxr-xr-xlib/exe/irb11
-rwxr-xr-xlib/exe/racc306
-rwxr-xr-xlib/exe/racc2y195
-rwxr-xr-xlib/exe/rdoc44
-rwxr-xr-xlib/exe/ri12
-rwxr-xr-xlib/exe/y2racc339
-rw-r--r--test/ruby/test_keyword.rb9
-rw-r--r--version.h2
-rw-r--r--vm_method.c2
11 files changed, 969 insertions, 2 deletions
diff --git a/lib/exe/bundle b/lib/exe/bundle
new file mode 100755
index 0000000000..b3b1b691d8
--- /dev/null
+++ b/lib/exe/bundle
@@ -0,0 +1,47 @@
+#!/usr/bin/env ruby
+# frozen_string_literal: true
+
+# Exit cleanly from an early interrupt
+Signal.trap("INT") do
+ Bundler.ui.debug("\n#{caller.join("\n")}") if defined?(Bundler)
+ exit 1
+end
+
+base_path = File.expand_path("../lib", __dir__)
+
+if File.exist?(base_path)
+ require_relative "../lib/bundler"
+else
+ require "bundler"
+end
+
+# Check if an older version of bundler is installed
+$LOAD_PATH.each do |path|
+ next unless path =~ %r{/bundler-0\.(\d+)} && $1.to_i < 9
+ err = String.new
+ err << "Looks like you have a version of bundler that's older than 0.9.\n"
+ err << "Please remove your old versions.\n"
+ err << "An easy way to do this is by running `gem cleanup bundler`."
+ abort(err)
+end
+
+if File.exist?(base_path)
+ require_relative "../lib/bundler/friendly_errors"
+else
+ require "bundler/friendly_errors"
+end
+
+Bundler.with_friendly_errors do
+ if File.exist?(base_path)
+ require_relative "../lib/bundler/cli"
+ else
+ require "bundler/cli"
+ end
+
+ # Allow any command to use --help flag to show help for that command
+ help_flags = %w[--help -h]
+ help_flag_used = ARGV.any? {|a| help_flags.include? a }
+ args = help_flag_used ? Bundler::CLI.reformatted_help_args(ARGV) : ARGV
+
+ Bundler::CLI.start(args, :debug => true)
+end
diff --git a/lib/exe/bundler b/lib/exe/bundler
new file mode 100755
index 0000000000..d9131fe834
--- /dev/null
+++ b/lib/exe/bundler
@@ -0,0 +1,4 @@
+#!/usr/bin/env ruby
+# frozen_string_literal: true
+
+load File.expand_path("../bundle", __FILE__)
diff --git a/lib/exe/irb b/lib/exe/irb
new file mode 100755
index 0000000000..c64ee85fbd
--- /dev/null
+++ b/lib/exe/irb
@@ -0,0 +1,11 @@
+#!/usr/bin/env ruby
+#
+# irb.rb - interactive ruby
+# $Release Version: 0.9.6 $
+# $Revision$
+# by Keiju ISHITSUKA(keiju@ruby-lang.org)
+#
+
+require "irb"
+
+IRB.start(__FILE__)
diff --git a/lib/exe/racc b/lib/exe/racc
new file mode 100755
index 0000000000..5656b25e42
--- /dev/null
+++ b/lib/exe/racc
@@ -0,0 +1,306 @@
+#!/usr/bin/env ruby
+#
+# $Id$
+#
+# Copyright (c) 1999-2006 Minero Aoki
+#
+# This program is free software.
+# You can distribute/modify this program under the same terms of ruby.
+# see the file "COPYING".
+
+require 'racc/static'
+require 'optparse'
+
+def main
+ output = nil
+ debug_parser = false
+ make_logfile = false
+ logfilename = nil
+ make_executable = false
+ rubypath = nil
+ embed_runtime = false
+ debug_flags = Racc::DebugFlags.new
+ line_convert = true
+ line_convert_all = false
+ omit_action_call = true
+ superclass = nil
+ check_only = false
+ verbose = false
+ profiler = RaccProfiler.new(false)
+
+ parser = OptionParser.new
+ parser.banner = "Usage: #{File.basename($0)} [options] <input>"
+ parser.on('-o', '--output-file=PATH',
+ 'output file name [<input>.tab.rb]') {|name|
+ output = name
+ }
+ parser.on('-t', '--debug', 'Outputs debugging parser.') {|fl|
+ debug_parser = fl
+ }
+ parser.on('-g', 'Equivalent to -t (obsolete).') {|fl|
+ $stderr.puts "racc -g is obsolete. Use racc -t instead." if $VERBOSE
+ debug_parser = fl
+ }
+ parser.on('-v', '--verbose',
+ 'Creates <filename>.output log file.') {|fl|
+ make_logfile = fl
+ }
+ parser.on('-O', '--log-file=PATH',
+ 'Log file name [<input>.output]') {|path|
+ make_logfile = true
+ logfilename = path
+ }
+ parser.on('-e', '--executable [RUBYPATH]', 'Makes executable parser.') {|path|
+ executable = true
+ rubypath = (path == 'ruby' ? nil : path)
+ }
+ parser.on('-E', '--embedded', "Embeds Racc runtime in output.") {
+ embed_runtime = true
+ }
+ parser.on('--line-convert-all', 'Converts line numbers of user codes.') {
+ line_convert_all = true
+ }
+ parser.on('-l', '--no-line-convert', 'Never convert line numbers.') {
+ line_convert = false
+ line_convert_all = false
+ }
+ parser.on('-a', '--no-omit-actions', 'Never omit actions.') {
+ omit_action_call = false
+ }
+ parser.on('--superclass=CLASSNAME',
+ 'Uses CLASSNAME instead of Racc::Parser.') {|name|
+ superclass = name
+ }
+ parser.on('--runtime=FEATURE',
+ "Uses FEATURE instead of 'racc/parser'") {|feat|
+ runtime = feature
+ }
+ parser.on('-C', '--check-only', 'Checks syntax and quit immediately.') {|fl|
+ check_only = fl
+ }
+ parser.on('-S', '--output-status', 'Outputs internal status time to time.') {
+ verbose = true
+ }
+ parser.on('-P', 'Enables generator profile') {
+ profiler = RaccProfiler.new(true)
+ }
+ parser.on('-D flags', "Flags for Racc debugging (do not use).") {|flags|
+ debug_flags = Racc::DebugFlags.parse_option_string(flags)
+ }
+ #parser.on('--no-extensions', 'Run Racc without any Ruby extension.') {
+ # Racc.const_set :Racc_No_Extentions, true
+ #}
+ parser.on('--version', 'Prints version and quit.') {
+ puts "racc version #{Racc::Version}"
+ exit 0
+ }
+ parser.on('--runtime-version', 'Prints runtime version and quit.') {
+ printf "racc runtime version %s (rev. %s); %s\n",
+ Racc::Parser::Racc_Runtime_Version,
+ Racc::Parser::Racc_Runtime_Revision,
+ if Racc::Parser.racc_runtime_type == 'ruby'
+ sprintf('ruby core version %s (rev. %s)',
+ Racc::Parser::Racc_Runtime_Core_Version_R,
+ Racc::Parser::Racc_Runtime_Core_Revision_R)
+ else
+ sprintf('c core version %s (rev. %s)',
+ Racc::Parser::Racc_Runtime_Core_Version_C,
+ Racc::Parser::Racc_Runtime_Core_Revision_C)
+ end
+ exit 0
+ }
+ parser.on('--copyright', 'Prints copyright and quit.') {
+ puts Racc::Copyright
+ exit 0
+ }
+ parser.on('--help', 'Prints this message and quit.') {
+ puts parser.help
+ exit 1
+ }
+ begin
+ parser.parse!
+ rescue OptionParser::ParseError => err
+ $stderr.puts err.message
+ $stderr.puts parser.help
+ exit 1
+ end
+ if ARGV.empty?
+ $stderr.puts 'no input'
+ exit 1
+ end
+ if ARGV.size > 1
+ $stderr.puts 'too many input'
+ exit 1
+ end
+ input = ARGV[0]
+
+ begin
+ $stderr.puts 'Parsing grammar file...' if verbose
+ result = profiler.section('parse') {
+ parser = Racc::GrammarFileParser.new(debug_flags)
+ parser.parse(File.read(input), File.basename(input))
+ }
+ if check_only
+ $stderr.puts 'syntax ok'
+ exit 0
+ end
+
+ $stderr.puts 'Generating LALR states...' if verbose
+ states = profiler.section('nfa') {
+ Racc::States.new(result.grammar).nfa
+ }
+
+ $stderr.puts "Resolving #{states.size} states..." if verbose
+ profiler.section('dfa') {
+ states.dfa
+ }
+
+ $stderr.puts 'Creating parser file...' if verbose
+ params = result.params.dup
+ # Overwrites parameters given by a grammar file with command line options.
+ params.superclass = superclass if superclass
+ params.omit_action_call = true if omit_action_call
+ # From command line option
+ if make_executable
+ params.make_executable = true
+ params.interpreter = rubypath
+ end
+ params.debug_parser = debug_parser
+ params.convert_line = line_convert
+ params.convert_line_all = line_convert_all
+ params.embed_runtime = embed_runtime
+ profiler.section('generation') {
+ generator = Racc::ParserFileGenerator.new(states, params)
+ generator.generate_parser_file(output || make_filename(input, '.tab.rb'))
+ }
+
+ if make_logfile
+ profiler.section('logging') {
+ $stderr.puts 'Creating log file...' if verbose
+ logfilename ||= make_filename(output || File.basename(input), '.output')
+ File.open(logfilename, 'w') {|f|
+ Racc::LogFileGenerator.new(states, debug_flags).output f
+ }
+ }
+ end
+ if debug_flags.status_logging
+ log_useless states.grammar
+ log_conflict states
+ else
+ report_useless states.grammar
+ report_conflict states
+ end
+
+ profiler.report
+ rescue Racc::Error, Errno::ENOENT, Errno::EPERM => err
+ raise if $DEBUG or debug_flags.any?
+ lineno = err.message.slice(/\A\d+:/).to_s
+ $stderr.puts "#{File.basename $0}: #{input}:#{lineno} #{err.message.strip}"
+ exit 1
+ end
+end
+
+def make_filename(path, suffix)
+ path.sub(/(?:\..*?)?\z/, suffix)
+end
+
+def report_conflict(states)
+ if states.should_report_srconflict?
+ $stderr.puts "#{states.n_srconflicts} shift/reduce conflicts"
+ end
+ if states.rrconflict_exist?
+ $stderr.puts "#{states.n_rrconflicts} reduce/reduce conflicts"
+ end
+end
+
+def log_conflict(states)
+ logging('w') {|f|
+ f.puts "ex#{states.grammar.n_expected_srconflicts}"
+ if states.should_report_srconflict?
+ f.puts "sr#{states.n_srconflicts}"
+ end
+ if states.rrconflict_exist?
+ f.puts "rr#{states.n_rrconflicts}"
+ end
+ }
+end
+
+def report_useless(grammar)
+ if grammar.useless_nonterminal_exist?
+ $stderr.puts "#{grammar.n_useless_nonterminals} useless nonterminals"
+ end
+ if grammar.useless_rule_exist?
+ $stderr.puts "#{grammar.n_useless_rules} useless rules"
+ end
+ if grammar.start.useless?
+ $stderr.puts 'fatal: start symbol does not derive any sentence'
+ end
+end
+
+def log_useless(grammar)
+ logging('a') {|f|
+ if grammar.useless_nonterminal_exist?
+ f.puts "un#{grammar.n_useless_nonterminals}"
+ end
+ if grammar.useless_rule_exist?
+ f.puts "ur#{grammar.n_useless_rules}"
+ end
+ }
+end
+
+def logging(mode, &block)
+ File.open("log/#{File.basename(ARGV[0])}", mode, &block)
+end
+
+class RaccProfiler
+ def initialize(really)
+ @really = really
+ @log = []
+ unless ::Process.respond_to?(:times)
+ # Ruby 1.6
+ @class = ::Time
+ else
+ @class = ::Process
+ end
+ end
+
+ def section(name)
+ if @really
+ t1 = @class.times.utime
+ result = yield
+ t2 = @class.times.utime
+ @log.push [name, t2 - t1]
+ result
+ else
+ yield
+ end
+ end
+
+ def report
+ return unless @really
+ f = $stderr
+ total = cumulative_time()
+ f.puts '--task-----------+--sec------+---%-'
+ @log.each do |name, time|
+ f.printf "%-19s %s %3d%%\n", name, pjust(time,4,4), (time/total*100).to_i
+ end
+ f.puts '-----------------+-----------+-----'
+ f.printf "%-20s%s\n", 'total', pjust(total,4,4)
+ end
+
+ private
+
+ def cumulative_time
+ t = @log.inject(0) {|sum, (name, time)| sum + time }
+ t == 0 ? 0.01 : t
+ end
+
+ def pjust(num, i, j)
+ m = /(\d+)(\.\d+)?/.match(num.to_s)
+ str = m[1].rjust(i)
+ str.concat m[2].ljust(j+1)[0,j+1] if m[2]
+ str
+ end
+end
+
+main
diff --git a/lib/exe/racc2y b/lib/exe/racc2y
new file mode 100755
index 0000000000..f88d73ed2c
--- /dev/null
+++ b/lib/exe/racc2y
@@ -0,0 +1,195 @@
+#!/usr/local/bin/ruby
+#
+# $Id$
+#
+# Copyright (c) 1999-2006 Minero Aoki
+#
+# This program is feee software.
+# You can distribute/modify this program under the terms of
+# the GNU LGPL, Lesser General Public License version 2.1.
+# For details of the LGPL, see the file "COPYING".
+#
+
+require 'racc/grammarfileparser'
+require 'racc/info'
+require 'optparse'
+
+def main
+ @with_action = true
+ with_header = false
+ with_inner = false
+ with_footer = false
+ output = nil
+ parser = OptionParser.new
+ parser.banner = "Usage: #{File.basename($0)} [-AHIF] [-oFILENAME] GRAMMARFILE"
+ parser.on('-o', '--output=FILENAME', 'output file name [<input>.yacc]') {|name|
+ output = name
+ }
+ parser.on('-A', '--without-action', 'Does not include actions.') {
+ @with_action = false
+ }
+ parser.on('-H', '--with-header', 'Includes header part.') {
+ with_header = true
+ }
+ parser.on('-I', '--with-inner', 'Includes inner part.') {
+ with_inner = true
+ }
+ parser.on('-F', '--with-footer', 'Includes footer part.') {
+ with_footer = true
+ }
+ parser.on('--version', 'Prints version and quit.') {
+ puts "racc2y version #{Racc::Version}"
+ exit 0
+ }
+ parser.on('--copyright', 'Prints copyright and quit.') {
+ puts Racc::Copyright
+ exit 0
+ }
+ parser.on('--help', 'Prints this message and quit.') {
+ puts parser.help
+ exit 1
+ }
+ begin
+ parser.parse!
+ rescue OptionParser::ParseError => err
+ $stderr.puts err.message
+ $stderr.puts parser.help
+ exit 1
+ end
+ if ARGV.empty?
+ $stderr.puts "no input file"
+ exit 1
+ end
+ unless ARGV.size == 1
+ $stderr.puts "too many inputs"
+ exit 1
+ end
+ input = ARGV[0]
+
+ begin
+ result = Racc::GrammarFileParser.parse_file(input)
+ result.grammar.init
+ File.open(output || "#{input}.yacc", 'w') {|f|
+ f.puts "/* generated from #{input} */"
+ if with_header
+ f.puts
+ f.puts '%{'
+ print_user_codes f, result.params.header
+ f.puts '%}'
+ end
+ f.puts
+ print_terminals f, result.grammar
+ f.puts
+ print_precedence_table f, precedence_table(result.grammar)
+ f.puts
+ f.puts '%%'
+ print_grammar f, result.grammar
+ f.puts '%%'
+ if with_inner
+ f.puts '/*---- inner ----*/'
+ print_user_codes f, result.params.inner
+ end
+ if with_footer
+ f.puts '/*---- footer ----*/'
+ print_user_codes f, result.params.footer
+ end
+ }
+ rescue SystemCallError => err
+ $stderr.puts err.message
+ exit 1
+ end
+end
+
+def print_terminals(f, grammar)
+ init_indent = '%token'.size
+ f.print '%token'
+ columns = init_indent
+ grammar.symboltable.each_terminal do |t|
+ next unless t.terminal?
+ next if t.dummy?
+ next if t == grammar.symboltable.anchor
+ next if t == grammar.symboltable.error
+ unless t.value.kind_of?(String)
+ if columns > 60
+ f.puts
+ f.print ' ' * init_indent
+ columns = init_indent
+ end
+ columns += f.write(" #{yacc_symbol(t)}")
+ end
+ end
+ f.puts
+end
+
+def precedence_table(grammar)
+ table = []
+ grammar.symboltable.select {|sym| sym.precedence }.each do |sym|
+ (table[sym.prec] ||= [sym.assoc]).push sym
+ end
+ table.compact
+end
+
+def print_precedence_table(f, table)
+ return if table.empty?
+ f.puts '/* precedance table */'
+ table.each do |syms|
+ assoc = syms.shift
+ f.printf '%%%-8s ', assoc.to_s.downcase
+ f.puts syms.map {|s| yacc_symbol(s) }.join(' ')
+ end
+ f.puts
+end
+
+def print_grammar(f, grammar)
+ prev_target = nil
+ indent = 10
+ embactions = []
+ grammar.each do |rule|
+ if rule.target.dummy?
+ embactions.push rule.action unless rule.action.empty?
+ next
+ end
+ if rule.target == prev_target
+ f.print ' ' * indent, '|'
+ else
+ prev_target = rule.target
+ f.printf "\n%-10s:", yacc_symbol(prev_target)
+ end
+ rule.symbols.each do |s|
+ if s.dummy? # target of dummy rule for embedded action
+ f.puts
+ print_action f, embactions.shift, indent
+ f.print ' ' * (indent + 1)
+ else
+ f.print ' ', yacc_symbol(s)
+ end
+ end
+ if rule.specified_prec
+ f.print ' %prec ', yacc_symbol(rule.specified_prec)
+ end
+ f.puts
+ unless rule.action.empty?
+ print_action f, rule.action, indent
+ end
+ end
+end
+
+def print_action(f, action, indent)
+ return unless @with_action
+ f.print ' ' * (indent + 4), "{\n"
+ f.print ' ' * (indent + 6), action.source.text.strip, "\n"
+ f.print ' ' * (indent + 4) , "}\n"
+end
+
+def print_user_codes(f, srcs)
+ return if srcs.empty?
+ srcs.each do |src|
+ f.puts src.text
+ end
+end
+
+def yacc_symbol(s)
+ s.to_s.gsub('"', "'")
+end
+
+main
diff --git a/lib/exe/rdoc b/lib/exe/rdoc
new file mode 100755
index 0000000000..aaa23292df
--- /dev/null
+++ b/lib/exe/rdoc
@@ -0,0 +1,44 @@
+#!/usr/bin/env ruby
+#
+# RDoc: Documentation tool for source code
+# (see lib/rdoc/rdoc.rb for more information)
+#
+# Copyright (c) 2003 Dave Thomas
+# Released under the same terms as Ruby
+
+begin
+ gem 'rdoc'
+rescue NameError => e # --disable-gems
+ raise unless e.name == :gem
+rescue Gem::LoadError
+end
+
+require 'rdoc/rdoc'
+
+begin
+ r = RDoc::RDoc.new
+ r.document ARGV
+rescue Errno::ENOSPC
+ $stderr.puts 'Ran out of space creating documentation'
+ $stderr.puts
+ $stderr.puts 'Please free up some space and try again'
+rescue SystemExit
+ raise
+rescue Exception => e
+ if $DEBUG_RDOC then
+ $stderr.puts e.message
+ $stderr.puts "#{e.backtrace.join "\n\t"}"
+ $stderr.puts
+ elsif Interrupt === e then
+ $stderr.puts
+ $stderr.puts 'Interrupted'
+ else
+ $stderr.puts "uh-oh! RDoc had a problem:"
+ $stderr.puts e.message
+ $stderr.puts
+ $stderr.puts "run with --debug for full backtrace"
+ end
+
+ exit 1
+end
+
diff --git a/lib/exe/ri b/lib/exe/ri
new file mode 100755
index 0000000000..7fbed0c099
--- /dev/null
+++ b/lib/exe/ri
@@ -0,0 +1,12 @@
+#!/usr/bin/env ruby
+
+begin
+ gem 'rdoc'
+rescue NameError => e # --disable-gems
+ raise unless e.name == :gem
+rescue Gem::LoadError
+end
+
+require 'rdoc/ri/driver'
+
+RDoc::RI::Driver.run ARGV
diff --git a/lib/exe/y2racc b/lib/exe/y2racc
new file mode 100755
index 0000000000..7933f94153
--- /dev/null
+++ b/lib/exe/y2racc
@@ -0,0 +1,339 @@
+#!/usr/local/bin/ruby
+#
+# $Id$
+#
+# Copyright (c) 1999-2006 Minero Aoki
+#
+# This program is free software.
+# You can distribute/modify this program under the terms of
+# the GNU LGPL, Lesser General Public License version 2.1.
+# For details of the GNU LGPL, see the file "COPYING".
+#
+
+require 'racc/info'
+require 'strscan'
+require 'forwardable'
+require 'optparse'
+
+def main
+ @with_action = true
+ @with_header = false
+ @with_usercode = false
+ cname = 'MyParser'
+ input = nil
+ output = nil
+ parser = OptionParser.new
+ parser.banner = "Usage: #{File.basename($0)} [-Ahu] [-c <classname>] [-o <filename>] <input>"
+ parser.on('-o', '--output=FILENAME', 'output file name [<input>.racc]') {|name|
+ output = name
+ }
+ parser.on('-c', '--classname=NAME', "Name of the parser class. [#{cname}]") {|name|
+ cname = name
+ }
+ parser.on('-A', '--without-action', 'Does not include actions.') {
+ @with_action = false
+ }
+ parser.on('-h', '--with-header', 'Includes header (%{...%}).') {
+ @with_header = true
+ }
+ parser.on('-u', '--with-user-code', 'Includes user code.') {
+ @with_usercode = true
+ }
+ parser.on('--version', 'Prints version and quit.') {
+ puts "y2racc version #{Racc::Version}"
+ exit 0
+ }
+ parser.on('--copyright', 'Prints copyright and quit.') {
+ puts Racc::Copyright
+ exit 0
+ }
+ parser.on('--help', 'Prints this message and quit.') {
+ puts parser.help
+ exit 1
+ }
+ begin
+ parser.parse!
+ rescue OptionParser::ParseError => err
+ $stderr.puts err.message
+ $stderr.puts parser.help
+ exit 1
+ end
+ if ARGV.empty?
+ $stderr.puts 'no input'
+ exit 1
+ end
+ if ARGV.size > 1
+ $stderr.puts 'too many input'
+ exit 1
+ end
+ input = ARGV[0]
+
+ begin
+ result = YaccFileParser.parse_file(input)
+ File.open(output || "#{input}.racc", 'w') {|f|
+ convert cname, result, f
+ }
+ rescue SystemCallError => err
+ $stderr.puts err.message
+ exit 1
+ end
+end
+
+def convert(classname, result, f)
+ init_indent = 'token'.size
+ f.puts %<# Converted from "#{result.filename}" by y2racc version #{Racc::Version}>
+ f.puts
+ f.puts "class #{classname}"
+ unless result.terminals.empty?
+ f.puts
+ f.print 'token'
+ columns = init_indent
+ result.terminals.each do |t|
+ if columns > 60
+ f.puts
+ f.print ' ' * init_indent
+ columns = init_indent
+ end
+ columns += f.write(" #{t}")
+ end
+ f.puts
+ end
+ unless result.precedence_table.empty?
+ f.puts
+ f.puts 'preclow'
+ result.precedence_table.each do |assoc, toks|
+ f.printf " %-8s %s\n", assoc, toks.join(' ') unless toks.empty?
+ end
+ f.puts 'prechigh'
+ end
+ if result.start
+ f.puts
+ f.puts "start #{@start}"
+ end
+
+ f.puts
+ f.puts 'rule'
+ texts = @with_action ? result.grammar : result.grammar_without_actions
+ texts.each do |text|
+ f.print text
+ end
+
+ if @with_header and result.header
+ f.puts
+ f.puts '---- header'
+ f.puts result.header
+ end
+ if @with_usercode and result.usercode
+ f.puts
+ f.puts '---- footer'
+ f.puts result.usercode
+ end
+end
+
+class ParseError < StandardError; end
+
+class StringScanner_withlineno
+ def initialize(src)
+ @s = StringScanner.new(src)
+ @lineno = 1
+ end
+
+ extend Forwardable
+ def_delegator "@s", :eos?
+ def_delegator "@s", :rest
+
+ attr_reader :lineno
+
+ def scan(re)
+ advance_lineno(@s.scan(re))
+ end
+
+ def scan_until(re)
+ advance_lineno(@s.scan_until(re))
+ end
+
+ def skip(re)
+ str = advance_lineno(@s.scan(re))
+ str ? str.size : nil
+ end
+
+ def getch
+ advance_lineno(@s.getch)
+ end
+
+ private
+
+ def advance_lineno(str)
+ @lineno += str.count("\n") if str
+ str
+ end
+end
+
+class YaccFileParser
+
+ Result = Struct.new(:terminals, :precedence_table, :start,
+ :header, :grammar, :usercode, :filename)
+ class Result # reopen
+ def initialize
+ super
+ self.terminals = []
+ self.precedence_table = []
+ self.start = nil
+ self.grammar = []
+ self.header = nil
+ self.usercode = nil
+ self.filename = nil
+ end
+
+ def grammar_without_actions
+ grammar().map {|text| text[0,1] == '{' ? '{}' : text }
+ end
+ end
+
+ def YaccFileParser.parse_file(filename)
+ new().parse(File.read(filename), filename)
+ end
+
+ def parse(src, filename = '-')
+ @result = Result.new
+ @filename = filename
+ @result.filename = filename
+ s = StringScanner_withlineno.new(src)
+ parse_header s
+ parse_grammar s
+ @result
+ end
+
+ private
+
+ COMMENT = %r</\*[^*]*\*+(?:[^/*][^*]*\*+)*/>
+ CHAR = /'((?:[^'\\]+|\\.)*)'/
+ STRING = /"((?:[^"\\]+|\\.)*)"/
+
+ def parse_header(s)
+ skip_until_percent s
+ until s.eos?
+ case
+ when t = s.scan(/left/)
+ @result.precedence_table.push ['left', scan_symbols(s)]
+ when t = s.scan(/right/)
+ @result.precedence_table.push ['right', scan_symbols(s)]
+ when t = s.scan(/nonassoc/)
+ @result.precedence_table.push ['nonassoc', scan_symbols(s)]
+ when t = s.scan(/token/)
+ list = scan_symbols(s)
+ list.shift if /\A<(.*)>\z/ =~ list[0]
+ @result.terminals.concat list
+ when t = s.scan(/start/)
+ @result.start = scan_symbols(s)[0]
+ when s.skip(%r<(?:
+ type | union | expect | thong | binary |
+ semantic_parser | pure_parser | no_lines |
+ raw | token_table
+ )\b>x)
+ skip_until_percent s
+ when s.skip(/\{/) # header (%{...%})
+ str = s.scan_until(/\%\}/)
+ str.chop!
+ str.chop!
+ @result.header = str
+ skip_until_percent s
+ when s.skip(/\%/) # grammar (%%...)
+ return
+ else
+ raise ParseError, "#{@filename}:#{s.lineno}: scan error"
+ end
+ end
+ end
+
+ def skip_until_percent(s)
+ until s.eos?
+ s.skip /[^\%\/]+/
+ next if s.skip(COMMENT)
+ return if s.getch == '%'
+ end
+ end
+
+ def scan_symbols(s)
+ list = []
+ until s.eos?
+ s.skip /\s+/
+ if s.skip(COMMENT)
+ ;
+ elsif t = s.scan(CHAR)
+ list.push t
+ elsif t = s.scan(STRING)
+ list.push t
+ elsif s.skip(/\%/)
+ break
+ elsif t = s.scan(/\S+/)
+ list.push t
+ else
+ raise ParseError, "#{@filename}:#{@lineno}: scan error"
+ end
+ end
+ list
+ end
+
+ def parse_grammar(s)
+ buf = []
+ until s.eos?
+ if t = s.scan(/[^%'"{\/]+/)
+ buf.push t
+ break if s.eos?
+ end
+ if s.skip(/\{/)
+ buf.push scan_action(s)
+ elsif t = s.scan(/'(?:[^'\\]+|\\.)*'/) then buf.push t
+ elsif t = s.scan(/"(?:[^"\\]+|\\.)*"/) then buf.push t
+ elsif t = s.scan(COMMENT) then buf.push t
+ elsif s.skip(/%prec\b/) then buf.push '='
+ elsif s.skip(/%%/)
+ @result.usercode = s.rest
+ break
+ else
+ buf.push s.getch
+ end
+ end
+ @result.grammar = buf
+ end
+
+ def scan_action(s)
+ buf = '{'
+ nest = 1
+ until s.eos?
+ if t = s.scan(%r<[^/{}'"]+>)
+ buf << t
+ break if s.eos?
+ elsif t = s.scan(COMMENT)
+ buf << t
+ elsif t = s.scan(CHAR)
+ buf << t
+ elsif t = s.scan(STRING)
+ buf << t
+ else
+ c = s.getch
+ buf << c
+ case c
+ when '{'
+ nest += 1
+ when '}'
+ nest -= 1
+ return buf if nest == 0
+ end
+ end
+ end
+ $stderr.puts "warning: unterminated action in #{@filename}"
+ buf
+ end
+
+end
+
+unless Object.method_defined?(:funcall)
+ class Object
+ alias funcall __send__
+ end
+end
+
+
+main
diff --git a/test/ruby/test_keyword.rb b/test/ruby/test_keyword.rb
index e9be530754..ab3c11e149 100644
--- a/test/ruby/test_keyword.rb
+++ b/test/ruby/test_keyword.rb
@@ -2741,6 +2741,11 @@ class TestKeywordArguments < Test::Unit::TestCase
baz(*args)
end
+ define_method(:block_splat) {|*args| }
+ ruby2_keywords :block_splat, def foo_bar_after_bmethod(*args)
+ bar(*args)
+ end
+
ruby2_keywords def foo_baz2(*args)
baz(*args)
baz(*args)
@@ -2876,6 +2881,7 @@ class TestKeywordArguments < Test::Unit::TestCase
assert_equal([1, h1], o.foo(:foo_baz, 1, :a=>1))
assert_equal([[1], h1], o.foo_foo_bar(1, :a=>1))
assert_equal([1, h1], o.foo_foo_baz(1, :a=>1))
+ assert_equal([[1], h1], o.foo_bar_after_bmethod(1, :a=>1))
assert_equal([[1], h1], o.foo(:bar, 1, **h1))
assert_equal([1, h1], o.foo(:baz, 1, **h1))
@@ -2891,6 +2897,7 @@ class TestKeywordArguments < Test::Unit::TestCase
assert_equal([1, h1], o.foo(:foo_baz, 1, **h1))
assert_equal([[1], h1], o.foo_foo_bar(1, **h1))
assert_equal([1, h1], o.foo_foo_baz(1, **h1))
+ assert_equal([[1], h1], o.foo_bar_after_bmethod(1, **h1))
assert_equal([[h1], {}], o.foo(:bar, h1, **{}))
assert_equal([h1], o.foo(:baz, h1, **{}))
@@ -2906,6 +2913,7 @@ class TestKeywordArguments < Test::Unit::TestCase
assert_equal([h1], o.foo(:foo_baz, h1, **{}))
assert_equal([[h1], {}], o.foo_foo_bar(h1, **{}))
assert_equal([h1], o.foo_foo_baz(h1, **{}))
+ assert_equal([[h1], {}], o.foo_bar_after_bmethod(h1, **{}))
assert_warn(/Using the last argument as keyword parameters is deprecated.*The called method `bar'/m) do
assert_equal([[1], h1], o.foo(:bar, 1, h1))
@@ -2923,6 +2931,7 @@ class TestKeywordArguments < Test::Unit::TestCase
assert_equal([[1], h1], o.foo_bar(1, h1))
end
assert_equal([1, h1], o.foo_baz(1, h1))
+ assert_equal([[1], h1], o.foo_bar_after_bmethod(1, h1))
assert_equal([[1, h1, 1], {}], o.foo_mod(:bar, 1, :a=>1))
assert_equal([1, h1, 1], o.foo_mod(:baz, 1, :a=>1))
diff --git a/version.h b/version.h
index b0d01d94ab..50a3682093 100644
--- a/version.h
+++ b/version.h
@@ -2,7 +2,7 @@
# define RUBY_VERSION_MINOR RUBY_API_VERSION_MINOR
#define RUBY_VERSION_TEENY 3
#define RUBY_RELEASE_DATE RUBY_RELEASE_YEAR_STR"-"RUBY_RELEASE_MONTH_STR"-"RUBY_RELEASE_DAY_STR
-#define RUBY_PATCHLEVEL 159
+#define RUBY_PATCHLEVEL 160
#define RUBY_RELEASE_YEAR 2021
#define RUBY_RELEASE_MONTH 2
diff --git a/vm_method.c b/vm_method.c
index 779ef460a4..efe71601d1 100644
--- a/vm_method.c
+++ b/vm_method.c
@@ -1864,7 +1864,7 @@ rb_mod_ruby2_keywords(int argc, VALUE *argv, VALUE module)
else {
rb_warn("Skipping set of ruby2_keywords flag for %s (method accepts keywords or method does not accept argument splat)", rb_id2name(name));
}
- return Qnil;
+ break;
}
}
/* fallthrough */