diff options
Diffstat (limited to 'test/irb/test_nesting_parser.rb')
-rw-r--r-- | test/irb/test_nesting_parser.rb | 338 |
1 files changed, 338 insertions, 0 deletions
diff --git a/test/irb/test_nesting_parser.rb b/test/irb/test_nesting_parser.rb new file mode 100644 index 0000000000..2db3cdab57 --- /dev/null +++ b/test/irb/test_nesting_parser.rb @@ -0,0 +1,338 @@ +# frozen_string_literal: false +require 'irb' + +require_relative "helper" + +module TestIRB + class NestingParserTest < TestCase + def setup + save_encodings + end + + def teardown + restore_encodings + end + + def parse_by_line(code) + IRB::NestingParser.parse_by_line(IRB::RubyLex.ripper_lex_without_warning(code)) + end + + def test_open_tokens + code = <<~'EOS' + class A + def f + if true + tap do + { + x: " + #{p(1, 2, 3 + EOS + opens = IRB::NestingParser.open_tokens(IRB::RubyLex.ripper_lex_without_warning(code)) + assert_equal(%w[class def if do { " #{ (], opens.map(&:tok)) + end + + def test_parse_by_line + code = <<~EOS + (((((1+2 + ).to_s())).tap do ((( + EOS + _tokens, prev_opens, next_opens, min_depth = parse_by_line(code).last + assert_equal(%w[( ( ( ( (], prev_opens.map(&:tok)) + assert_equal(%w[( ( do ( ( (], next_opens.map(&:tok)) + assert_equal(2, min_depth) + end + + def test_ruby_syntax + code = <<~'EOS' + class A + 1 if 2 + 1 while 2 + 1 until 2 + 1 unless 2 + 1 rescue 2 + begin; rescue; ensure; end + tap do; rescue; ensure; end + class B; end + module C; end + def f; end + def `; end + def f() = 1 + %(); %w[]; %q(); %r{}; %i[] + "#{1}"; ''; /#{1}/; `#{1}` + :sym; :"sym"; :+; :`; :if + [1, 2, 3] + { x: 1, y: 2 } + (a, (*b, c), d), e = 1, 2, 3 + ->(a){}; ->(a) do end + -> a = -> b = :do do end do end + if 1; elsif 2; else; end + unless 1; end + while 1; end + until 1; end + for i in j; end + case 1; when 2; end + puts(1, 2, 3) + loop{|i|} + loop do |i| end + end + EOS + line_results = parse_by_line(code) + assert_equal(code.lines.size, line_results.size) + class_open, *inner_line_results, class_close = line_results + assert_equal(['class'], class_open[2].map(&:tok)) + inner_line_results.each {|result| assert_equal(['class'], result[2].map(&:tok)) } + assert_equal([], class_close[2].map(&:tok)) + end + + def test_multiline_string + code = <<~EOS + " + aaa + bbb + " + <<A + aaa + bbb + A + EOS + line_results = parse_by_line(code) + assert_equal(code.lines.size, line_results.size) + string_content_line, string_opens = line_results[1] + assert_equal("\naaa\nbbb\n", string_content_line.first.first.tok) + assert_equal("aaa\n", string_content_line.first.last) + assert_equal(['"'], string_opens.map(&:tok)) + heredoc_content_line, heredoc_opens = line_results[6] + assert_equal("aaa\nbbb\n", heredoc_content_line.first.first.tok) + assert_equal("bbb\n", heredoc_content_line.first.last) + assert_equal(['<<A'], heredoc_opens.map(&:tok)) + _line, _prev_opens, next_opens, _min_depth = line_results.last + assert_equal([], next_opens) + end + + def test_backslash_continued_nested_symbol + code = <<~'EOS' + x = <<A, :\ + heredoc #{ + here + } + A + =begin + embdoc + =end + # comment + + if # this is symbol :if + while + EOS + line_results = parse_by_line(code) + assert_equal(%w[: <<A #{], line_results[2][2].map(&:tok)) + assert_equal(%w[while], line_results.last[2].map(&:tok)) + end + + def test_oneliner_def + code = <<~EOC + if true + # normal oneliner def + def f = 1 + def f() = 1 + def f(*) = 1 + # keyword, backtick, op + def * = 1 + def ` = 1 + def if = 1 + def *() = 1 + def `() = 1 + def if() = 1 + # oneliner def with receiver + def a.* = 1 + def $a.* = 1 + def @a.` = 1 + def A.` = 1 + def ((a;b;c)).*() = 1 + def ((a;b;c)).if() = 1 + def ((a;b;c)).end() = 1 + # multiline oneliner def + def f = + 1 + def f() + = + 1 + # oneliner def with comment and embdoc + def # comment + =begin + embdoc + =end + ((a;b;c)) + . # comment + =begin + embdoc + =end + f (*) # comment + =begin + embdoc + =end + = + 1 + # nested oneliner def + def f(x = def f() = 1) = def f() = 1 + EOC + _tokens, _prev_opens, next_opens, min_depth = parse_by_line(code).last + assert_equal(['if'], next_opens.map(&:tok)) + assert_equal(1, min_depth) + end + + def test_heredoc_embexpr + code = <<~'EOS' + <<A+<<B+<<C+(<<D+(<<E) + #{ + <<~F+"#{<<~G} + #{ + here + } + F + G + " + } + A + B + C + D + E + ) + EOS + line_results = parse_by_line(code) + last_opens = line_results.last[-2] + assert_equal([], last_opens) + _tokens, _prev_opens, next_opens, _min_depth = line_results[4] + assert_equal(%w[( <<E <<D <<C <<B <<A #{ " <<~G <<~F #{], next_opens.map(&:tok)) + end + + def test_for_in + code = <<~EOS + for i in j + here + end + for i in j do + here + end + for i in + j do + here + end + for + # comment + i in j do + here + end + for (a;b;c).d in (a;b;c) do + here + end + for i in :in + :do do + here + end + for i in -> do end do + here + end + EOS + line_results = parse_by_line(code).select { |tokens,| tokens.map(&:last).include?('here') } + assert_equal(7, line_results.size) + line_results.each do |_tokens, _prev_opens, next_opens, _min_depth| + assert_equal(['for'], next_opens.map(&:tok)) + end + end + + def test_while_until + base_code = <<~'EOS' + while_or_until true + here + end + while_or_until a < c + here + end + while_or_until true do + here + end + while_or_until + # comment + (a + b) < + # comment + c do + here + end + while_or_until :\ + do do + here + end + while_or_until def do; end == :do do + here + end + while_or_until -> do end do + here + end + EOS + %w[while until].each do |keyword| + code = base_code.gsub('while_or_until', keyword) + line_results = parse_by_line(code).select { |tokens,| tokens.map(&:last).include?('here') } + assert_equal(7, line_results.size) + line_results.each do |_tokens, _prev_opens, next_opens, _min_depth| + assert_equal([keyword], next_opens.map(&:tok) ) + end + end + end + + def test_undef_alias + codes = [ + 'undef foo', + 'alias foo bar', + 'undef !', + 'alias + -', + 'alias $a $b', + 'undef do', + 'alias do do', + 'undef :do', + 'alias :do :do', + 'undef :"#{alias do do}"', + 'alias :"#{undef do}" do', + 'alias do :"#{undef do}"' + ] + code_with_comment = <<~EOS + undef # + # + do # + alias # + # + do # + # + do # + EOS + code_with_heredoc = <<~EOS + <<~A; alias + A + :"#{<<~A}" + A + do + EOS + [*codes, code_with_comment, code_with_heredoc].each do |code| + opens = IRB::NestingParser.open_tokens(IRB::RubyLex.ripper_lex_without_warning('(' + code + "\nif")) + assert_equal(%w[( if], opens.map(&:tok)) + end + end + + def test_case_in + code = <<~EOS + case 1 + in 1 + here + in + 2 + here + end + EOS + line_results = parse_by_line(code).select { |tokens,| tokens.map(&:last).include?('here') } + assert_equal(2, line_results.size) + line_results.each do |_tokens, _prev_opens, next_opens, _min_depth| + assert_equal(['in'], next_opens.map(&:tok)) + end + end + end +end |