зеркало из https://github.com/github/ruby.git
339 строки
8.2 KiB
Ruby
339 строки
8.2 KiB
Ruby
# 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
|