diff options
author | Stan Lo <[email protected]> | 2023-08-11 19:44:48 +0100 |
---|---|---|
committer | git <[email protected]> | 2023-08-11 18:44:52 +0000 |
commit | 0781e55206d94079c15ab315fc082f49bf8bf780 (patch) | |
tree | 16ee7c71b17b87ad166fd23b4afc31d40b88da94 /lib/irb/ruby-lex.rb | |
parent | c173c637ab7e971a5b6c56deabe9e1a7c95667fb (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.rb | 44 |
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.` |