Skip to content

Commit

Permalink
Move each_top_level_statement and readmultiline to Irb
Browse files Browse the repository at this point in the history
This will save RubyLex from knowning information about commands and aliases.
  • Loading branch information
st0012 committed Aug 17, 2023
1 parent 1ac1ccf commit 9d34cde
Show file tree
Hide file tree
Showing 2 changed files with 85 additions and 82 deletions.
103 changes: 83 additions & 20 deletions lib/irb.rb
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
require_relative "irb/extend-command"

require_relative "irb/ruby-lex"
require_relative "irb/statement"
require_relative "irb/input-method"
require_relative "irb/locale"
require_relative "irb/color"
Expand Down Expand Up @@ -550,27 +551,9 @@ def eval_input
@context.io.prompt
end

@scanner.set_input do
signal_status(:IN_INPUT) do
if l = @context.io.gets
print l if @context.verbose?
else
if @context.ignore_eof? and @context.io.readable_after_eof?
l = "\n"
if @context.verbose?
printf "Use \"exit\" to leave %s\n", @context.ap_name
end
else
print "\n" if @context.prompting?
end
end
l
end
end

configure_io

@scanner.each_top_level_statement do |statement, line_no|
each_top_level_statement do |statement, line_no|
signal_status(:IN_EVAL) do
begin
# If the integration with debugger is activated, we need to handle certain input differently
Expand Down Expand Up @@ -600,6 +583,86 @@ def eval_input
end
end

def read_input
signal_status(:IN_INPUT) do
if l = @context.io.gets
print l if @context.verbose?
else
if @context.ignore_eof? and @context.io.readable_after_eof?
l = "\n"
if @context.verbose?
printf "Use \"exit\" to leave %s\n", @context.ap_name
end
else
print "\n" if @context.prompting?
end
end
l
end
end

def readmultiline
@scanner.save_prompt_to_context_io([], false, 0)

# multiline
return read_input if @context.io.respond_to?(:check_termination)

# nomultiline
code = ''
line_offset = 0
loop do
line = read_input
unless line
return code.empty? ? nil : code
end

code << line

# Accept any single-line input for symbol aliases or commands that transform args
return code if single_line_command?(code)

tokens, opens, terminated = @scanner.check_code_state(code)
return code if terminated

line_offset += 1
continue = @scanner.should_continue?(tokens)
@scanner.save_prompt_to_context_io(opens, continue, line_offset)
end
end

def each_top_level_statement
loop do
code = readmultiline
break unless code

if code != "\n"
yield build_statement(code), @scanner.line_no
end
@scanner.increase_line_no(code.count("\n"))
rescue RubyLex::TerminateLineInput
end
end

def build_statement(code)
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 = ExtendCommandBundle.load_command(command)

if command_class
Statement::Command.new(code, command, arg, command_class)
else
Statement::Expression.new(code, @scanner.assignment_expression?(code))
end
end

def single_line_command?(code)
command = code.split(/\s/, 2).first
@context.symbol_alias?(command) || @context.transform_args?(command)
end

def configure_io
if @context.io.respond_to?(:check_termination)
@context.io.check_termination do |code|
Expand All @@ -616,7 +679,7 @@ def configure_io
end
else
# Accept any single-line input for symbol aliases or commands that transform args
next true if @scanner.single_line_command?(code)
next true if single_line_command?(code)

_tokens, _opens, terminated = @scanner.check_code_state(code)
terminated
Expand Down
64 changes: 2 additions & 62 deletions lib/irb/ruby-lex.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
require "ripper"
require "jruby" if RUBY_ENGINE == "jruby"
require_relative "nesting_parser"
require_relative "statement"

# :stopdoc:
class RubyLex
Expand Down Expand Up @@ -42,6 +41,8 @@ def initialize
end
end

attr_reader :line_no

def initialize(context)
@context = context
@line_no = 1
Expand All @@ -65,11 +66,6 @@ def self.compile_with_errors_suppressed(code, line_no: 1)
result
end

def single_line_command?(code)
command = code.split(/\s/, 2).first
@context.symbol_alias?(command) || @context.transform_args?(command)
end

# io functions
def set_input(&block)
@input = block
Expand Down Expand Up @@ -188,62 +184,6 @@ def increase_line_no(addition)
@line_no += addition
end

def readmultiline
save_prompt_to_context_io([], false, 0)

# multiline
return @input.call if @context.io.respond_to?(:check_termination)

# nomultiline
code = ''
line_offset = 0
loop do
line = @input.call
unless line
return code.empty? ? nil : code
end

code << line
# Accept any single-line input for symbol aliases or commands that transform args
return code if single_line_command?(code)

tokens, opens, terminated = check_code_state(code)
return code if terminated

line_offset += 1
continue = should_continue?(tokens)
save_prompt_to_context_io(opens, continue, line_offset)
end
end

def each_top_level_statement
loop do
code = readmultiline
break unless code

if code != "\n"
yield build_statement(code), @line_no
end
increase_line_no(code.count("\n"))
rescue TerminateLineInput
end
end

def build_statement(code)
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.
Expand Down

0 comments on commit 9d34cde

Please sign in to comment.