[ruby/irb] Encapsulate input details in Statement objects
(https://github.com/ruby/irb/pull/682) * Introduce Statement class * Split Statement class for better clarity https://github.com/ruby/irb/commit/65e8e68690
This commit is contained in:
parent
0982c5fa00
commit
5a40f7db54
42
lib/irb.rb
42
lib/irb.rb
@ -570,26 +570,19 @@ module IRB
|
|||||||
|
|
||||||
configure_io
|
configure_io
|
||||||
|
|
||||||
@scanner.each_top_level_statement do |line, line_no, is_assignment|
|
@scanner.each_top_level_statement do |statement, line_no|
|
||||||
signal_status(:IN_EVAL) do
|
signal_status(:IN_EVAL) do
|
||||||
begin
|
begin
|
||||||
# If the integration with debugger is activated, we need to handle certain input differently
|
# If the integration with debugger is activated, we need to handle certain input differently
|
||||||
if @context.with_debugger
|
if @context.with_debugger && statement.should_be_handled_by_debugger?
|
||||||
command_class = load_command_class(line)
|
return statement.code
|
||||||
# First, let's pass debugging command's input to debugger
|
|
||||||
# Secondly, we need to let debugger evaluate non-command input
|
|
||||||
# Otherwise, the expression will be evaluated in the debugger's main session thread
|
|
||||||
# This is the only way to run the user's program in the expected thread
|
|
||||||
if !command_class || ExtendCommand::DebugCommand > command_class
|
|
||||||
return line
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
evaluate_line(line, line_no)
|
@context.evaluate(statement.evaluable_code, line_no)
|
||||||
|
|
||||||
# Don't echo if the line ends with a semicolon
|
# Don't echo if the line ends with a semicolon
|
||||||
if @context.echo? && !line.match?(/;\s*\z/)
|
if @context.echo? && !statement.suppresses_echo?
|
||||||
if is_assignment
|
if statement.is_assignment?
|
||||||
if @context.echo_on_assignment?
|
if @context.echo_on_assignment?
|
||||||
output_value(@context.echo_on_assignment? == :truncate)
|
output_value(@context.echo_on_assignment? == :truncate)
|
||||||
end
|
end
|
||||||
@ -659,29 +652,6 @@ module IRB
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def evaluate_line(line, line_no)
|
|
||||||
# Transform a non-identifier alias (@, $) or keywords (next, break)
|
|
||||||
command, args = line.split(/\s/, 2)
|
|
||||||
if original = @context.command_aliases[command.to_sym]
|
|
||||||
line = line.gsub(/\A#{Regexp.escape(command)}/, original.to_s)
|
|
||||||
command = original
|
|
||||||
end
|
|
||||||
|
|
||||||
# Hook command-specific transformation
|
|
||||||
command_class = ExtendCommandBundle.load_command(command)
|
|
||||||
if command_class&.respond_to?(:transform_args)
|
|
||||||
line = "#{command} #{command_class.transform_args(args)}"
|
|
||||||
end
|
|
||||||
|
|
||||||
@context.evaluate(line, line_no)
|
|
||||||
end
|
|
||||||
|
|
||||||
def load_command_class(line)
|
|
||||||
command, _ = line.split(/\s/, 2)
|
|
||||||
command_name = @context.command_aliases[command.to_sym]
|
|
||||||
ExtendCommandBundle.load_command(command_name || command)
|
|
||||||
end
|
|
||||||
|
|
||||||
def convert_invalid_byte_sequence(str, enc)
|
def convert_invalid_byte_sequence(str, enc)
|
||||||
str.force_encoding(enc)
|
str.force_encoding(enc)
|
||||||
str.scrub { |c|
|
str.scrub { |c|
|
||||||
|
@ -7,6 +7,7 @@
|
|||||||
require "ripper"
|
require "ripper"
|
||||||
require "jruby" if RUBY_ENGINE == "jruby"
|
require "jruby" if RUBY_ENGINE == "jruby"
|
||||||
require_relative "nesting_parser"
|
require_relative "nesting_parser"
|
||||||
|
require_relative "statement"
|
||||||
|
|
||||||
# :stopdoc:
|
# :stopdoc:
|
||||||
class RubyLex
|
class RubyLex
|
||||||
@ -221,16 +222,30 @@ class RubyLex
|
|||||||
break unless code
|
break unless code
|
||||||
|
|
||||||
if code != "\n"
|
if code != "\n"
|
||||||
code.force_encoding(@context.io.encoding)
|
yield build_statement(code), @line_no
|
||||||
yield code, @line_no, assignment_expression?(code)
|
|
||||||
end
|
end
|
||||||
increase_line_no(code.count("\n"))
|
increase_line_no(code.count("\n"))
|
||||||
rescue TerminateLineInput
|
rescue TerminateLineInput
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def assignment_expression?(line)
|
def build_statement(code)
|
||||||
# Try to parse the line and check if the last of possibly multiple
|
code.force_encoding(@context.io.encoding)
|
||||||
|
command_or_alias, arg = code.split(/\s/, 2)
|
||||||
|
# Transform a non-identifier alias (@, $) or keywords (next, break)
|
||||||
|
command_name = @context.command_aliases[command_or_alias.to_sym]
|
||||||
|
command = command_name || command_or_alias
|
||||||
|
command_class = IRB::ExtendCommandBundle.load_command(command)
|
||||||
|
|
||||||
|
if command_class
|
||||||
|
IRB::Statement::Command.new(code, command, arg, command_class)
|
||||||
|
else
|
||||||
|
IRB::Statement::Expression.new(code, assignment_expression?(code))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def assignment_expression?(code)
|
||||||
|
# Try to parse the code and check if the last of possibly multiple
|
||||||
# expressions is an assignment type.
|
# expressions is an assignment type.
|
||||||
|
|
||||||
# If the expression is invalid, Ripper.sexp should return nil which will
|
# If the expression is invalid, Ripper.sexp should return nil which will
|
||||||
@ -239,7 +254,7 @@ class RubyLex
|
|||||||
# array of parsed expressions. The first element of each expression is the
|
# array of parsed expressions. The first element of each expression is the
|
||||||
# expression's type.
|
# expression's type.
|
||||||
verbose, $VERBOSE = $VERBOSE, nil
|
verbose, $VERBOSE = $VERBOSE, nil
|
||||||
code = "#{RubyLex.generate_local_variables_assign_code(@context.local_variables) || 'nil;'}\n#{line}"
|
code = "#{RubyLex.generate_local_variables_assign_code(@context.local_variables) || 'nil;'}\n#{code}"
|
||||||
# Get the last node_type of the line. drop(1) is to ignore the local_variables_assign_code part.
|
# Get the last node_type of the line. drop(1) is to ignore the local_variables_assign_code part.
|
||||||
node_type = Ripper.sexp(code)&.dig(1)&.drop(1)&.dig(-1, 0)
|
node_type = Ripper.sexp(code)&.dig(1)&.drop(1)&.dig(-1, 0)
|
||||||
ASSIGNMENT_NODE_TYPES.include?(node_type)
|
ASSIGNMENT_NODE_TYPES.include?(node_type)
|
||||||
|
78
lib/irb/statement.rb
Normal file
78
lib/irb/statement.rb
Normal file
@ -0,0 +1,78 @@
|
|||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
module IRB
|
||||||
|
class Statement
|
||||||
|
attr_reader :code
|
||||||
|
|
||||||
|
def is_assignment?
|
||||||
|
raise NotImplementedError
|
||||||
|
end
|
||||||
|
|
||||||
|
def suppresses_echo?
|
||||||
|
raise NotImplementedError
|
||||||
|
end
|
||||||
|
|
||||||
|
def should_be_handled_by_debugger?
|
||||||
|
raise NotImplementedError
|
||||||
|
end
|
||||||
|
|
||||||
|
def evaluable_code
|
||||||
|
raise NotImplementedError
|
||||||
|
end
|
||||||
|
|
||||||
|
class Expression < Statement
|
||||||
|
def initialize(code, is_assignment)
|
||||||
|
@code = code
|
||||||
|
@is_assignment = is_assignment
|
||||||
|
end
|
||||||
|
|
||||||
|
def suppresses_echo?
|
||||||
|
@code.match?(/;\s*\z/)
|
||||||
|
end
|
||||||
|
|
||||||
|
def should_be_handled_by_debugger?
|
||||||
|
true
|
||||||
|
end
|
||||||
|
|
||||||
|
def is_assignment?
|
||||||
|
@is_assignment
|
||||||
|
end
|
||||||
|
|
||||||
|
def evaluable_code
|
||||||
|
@code
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
class Command < Statement
|
||||||
|
def initialize(code, command, arg, command_class)
|
||||||
|
@code = code
|
||||||
|
@command = command
|
||||||
|
@arg = arg
|
||||||
|
@command_class = command_class
|
||||||
|
end
|
||||||
|
|
||||||
|
def is_assignment?
|
||||||
|
false
|
||||||
|
end
|
||||||
|
|
||||||
|
def suppresses_echo?
|
||||||
|
false
|
||||||
|
end
|
||||||
|
|
||||||
|
def should_be_handled_by_debugger?
|
||||||
|
IRB::ExtendCommand::DebugCommand > @command_class
|
||||||
|
end
|
||||||
|
|
||||||
|
def evaluable_code
|
||||||
|
# Hook command-specific transformation to return valid Ruby code
|
||||||
|
if @command_class.respond_to?(:transform_args)
|
||||||
|
arg = @command_class.transform_args(@arg)
|
||||||
|
else
|
||||||
|
arg = @arg
|
||||||
|
end
|
||||||
|
|
||||||
|
[@command, arg].compact.join(' ')
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
Loading…
x
Reference in New Issue
Block a user