From b7b46eeb0c0e0c3fe948ae1b546c400469b1162d Mon Sep 17 00:00:00 2001 From: tompng Date: Sat, 7 Jan 2023 04:07:16 +0900 Subject: [PATCH] Add nesting parser tests, fix some existing tests --- test/irb/test_nesting_parser.rb | 299 ++++++++++++++++++++++++++++++++ test/irb/test_ruby_lex.rb | 86 ++++----- 2 files changed, 334 insertions(+), 51 deletions(-) create mode 100644 test/irb/test_nesting_parser.rb diff --git a/test/irb/test_nesting_parser.rb b/test/irb/test_nesting_parser.rb new file mode 100644 index 000000000..2edaea35f --- /dev/null +++ b/test/irb/test_nesting_parser.rb @@ -0,0 +1,299 @@ +$LOAD_PATH.unshift File.expand_path('../../lib', __FILE__) +require 'irb' +require 'rubygems' + +require_relative "helper" + +module TestIRB + class TestNestingParser < TestCase + def setup + save_encodings + end + + def teardown + restore_encodings + end + + def parse_by_line(code) + IRB::NestingParser.parse_by_line(RubyLex.ripper_lex_without_warning(code)) + end + + def test_scan + code = <<~'EOS' + class A + def f + if true + tap do + { + x: " + #{p(1, 2, 3 + EOS + opens = IRB::NestingParser.scan(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) + line_results[1...-1].each {|result| assert_equal(['class'], result[2].map(&:tok)) } + end + + def test_multiline_string + code = <<~EOS + " + aaa + bbb + " + < 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_case_in + if Gem::Version.new(RUBY_VERSION) < Gem::Version.new('2.7.0') + pend 'This test requires ruby version that supports case-in syntax' + end + 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 diff --git a/test/irb/test_ruby_lex.rb b/test/irb/test_ruby_lex.rb index e2a471c1b..745b7ecc3 100644 --- a/test/irb/test_ruby_lex.rb +++ b/test/irb/test_ruby_lex.rb @@ -95,8 +95,11 @@ def assert_code_block_open(lines, expected, local_variables: []) def check_state(lines, local_variables: []) context = build_context(local_variables) + tokens = RubyLex.ripper_lex_without_warning(lines.join("\n"), context: context) + opens = IRB::NestingParser.scan(tokens) ruby_lex = RubyLex.new(context) - _ltype, indent, _continue, code_block_open = ruby_lex.check_code_state(lines.join("\n")) + indent, _nesting_level = ruby_lex.calc_nesting_depth(opens) + code_block_open = !opens.empty? || ruby_lex.process_continue(tokens) [indent, code_block_open] end @@ -164,9 +167,9 @@ def test_multiple_braces_in_a_line Row.new(%q( ]), 4, 4), Row.new(%q( ]), 2, 2), Row.new(%q(]), 0, 0), - Row.new(%q([<