summaryrefslogtreecommitdiff
path: root/lib/irb/ruby-lex.rb
diff options
context:
space:
mode:
authorStan Lo <stan001212@gmail.com>2023-08-11 19:44:48 +0100
committergit <svn-admin@ruby-lang.org>2023-08-11 18:44:52 +0000
commit0781e55206d94079c15ab315fc082f49bf8bf780 (patch)
tree16ee7c71b17b87ad166fd23b4afc31d40b88da94 /lib/irb/ruby-lex.rb
parentc173c637ab7e971a5b6c56deabe9e1a7c95667fb (diff)
[ruby/irb] Move assignment check to RubyLex
(https://github.com/ruby/irb/pull/670) Since assignment check relies on tokenization with `Ripper`, it feels like the responsibility of `RubyLex`. `Irb#eval_input` should simply get the result when calling `each_top_level_statement` on `RubyLex`. https://github.com/ruby/irb/commit/89d1adb3fd
Diffstat (limited to 'lib/irb/ruby-lex.rb')
-rw-r--r--lib/irb/ruby-lex.rb44
1 files changed, 43 insertions, 1 deletions
diff --git a/lib/irb/ruby-lex.rb b/lib/irb/ruby-lex.rb
index b9f498614f..d436f98244 100644
--- a/lib/irb/ruby-lex.rb
+++ b/lib/irb/ruby-lex.rb
@@ -10,6 +10,30 @@ require_relative "nesting_parser"
# :stopdoc:
class RubyLex
+ ASSIGNMENT_NODE_TYPES = [
+ # Local, instance, global, class, constant, instance, and index assignment:
+ # "foo = bar",
+ # "@foo = bar",
+ # "$foo = bar",
+ # "@@foo = bar",
+ # "::Foo = bar",
+ # "a::Foo = bar",
+ # "Foo = bar"
+ # "foo.bar = 1"
+ # "foo[1] = bar"
+ :assign,
+
+ # Operation assignment:
+ # "foo += bar"
+ # "foo -= bar"
+ # "foo ||= bar"
+ # "foo &&= bar"
+ :opassign,
+
+ # Multiple assignment:
+ # "foo, bar = 1, 2
+ :massign,
+ ]
class TerminateLineInput < StandardError
def initialize
@@ -248,13 +272,31 @@ class RubyLex
if code != "\n"
code.force_encoding(@io.encoding)
- yield code, @line_no
+ yield code, @line_no, assignment_expression?(code)
end
@line_no += code.count("\n")
rescue TerminateLineInput
end
end
+ def assignment_expression?(line)
+ # Try to parse the line and check if the last of possibly multiple
+ # expressions is an assignment type.
+
+ # If the expression is invalid, Ripper.sexp should return nil which will
+ # result in false being returned. Any valid expression should return an
+ # s-expression where the second element of the top level array is an
+ # array of parsed expressions. The first element of each expression is the
+ # expression's type.
+ verbose, $VERBOSE = $VERBOSE, nil
+ code = "#{RubyLex.generate_local_variables_assign_code(@context.local_variables) || 'nil;'}\n#{line}"
+ # 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)
+ ASSIGNMENT_NODE_TYPES.include?(node_type)
+ ensure
+ $VERBOSE = verbose
+ end
+
def should_continue?(tokens)
# Look at the last token and check if IRB need to continue reading next line.
# Example code that should continue: `a\` `a +` `a.`