# encoding: utf-8 # ENV["VERBOSE"] = "1" require "minitest/autorun" require "ruby_parser" $: << File.expand_path("~/Work/p4/zss/src/sexp_processor/dev/lib") require "pt_testcase" class Sexp alias oldeq2 == # TODO: push up to Sexp def == other # :nodoc: if other.class == self.class then super and (line.nil? or other.line.nil? or line == other.line) else false end end end module TestRubyParserShared def setup super # p :test => [self.class, __name__] end BLOCK_DUP_MSG = "Both block arg and actual block given." def test_bug120 skip "not ready for this yet" rb = "def f; if /(?bar)/ =~ 'bar' && p(foo); foo; end; end; f" pt = s(:if, s(:and, s(:match2, s(:lit, /(?bar)/), s(:str, "bar")), s(:call, nil, :p, s(:lvar, :foo))), s(:lvar, :foo), nil) assert_parse rb, pt end def after_process_hook klass, node, data, input_name, output_name assert_equal 1, @result.line, "should have proper line number" if node !~ /rescue|begin|ensure/ # remove_begin keeps inner line number end def test_BEGIN rb = "BEGIN { 42 }" pt = s(:iter, s(:preexe), 0, s(:lit, 42)) assert_parse rb, pt end def test_BEGIN_not_toplevel rb = "class Foo\n BEGIN {\n 42\n }\nend" assert_syntax_error rb, "BEGIN is permitted only at toplevel" end def test___ENCODING__ rb = "__ENCODING__" pt = s(:colon2, s(:const, :Encoding), :UTF_8) assert_parse rb, pt end def test_alias_gvar_backref rb = "alias $MATCH $&" pt = s(:valias, :$MATCH, :$&) assert_parse rb, pt end def test_alias_resword rb = "alias in out" pt = s(:alias, s(:lit, :in), s(:lit, :out)) assert_parse rb, pt end def test_and_multi rb = "true and\nnot false and\ntrue" pt = s(:and, s(:true).line(1), s(:and, s(:call, s(:false).line(2), :!).line(2), s(:true).line(3)).line(2)).line(1) assert_parse rb, pt end def test_aref_args_assocs rb = "[1 => 2]" pt = s(:array, s(:hash, s(:lit, 1), s(:lit, 2))) assert_parse rb, pt end def test_array_line_breaks # It seems like arrays are roughly created when a certain element is created # In ruby > 1.9 it seems like that is after the last element, so the array # itself is assigned line 3 (since the last element is on line 3) and for # ruby <= 1.9 it seems to get created after the first element, so the array # itself is assigned line 2 (since the first element is on line 2). # This seems to happen since arrays like this are created with a line in # ruby_parser.yy like `result = s(:array, val[0])`. So, the array is not # created by itself. The creation of the array itself is deferred until there # is an element to create it with. That seems to mess up line numbers # for the array. Luckily, the arary elements all seemt to get the correct # line number. rb = "[\n'a',\n'b']\n1" pt = s(:block, s(:array, s(:str, "a").line(2), s(:str, "b").line(3)).line(1), s(:lit, 1).line(4)).line 1 assert_parse rb, pt end def test_attr_asgn_colon_id rb = "A::b = 1" pt = s(:attrasgn, s(:const, :A), :b=, s(:lit, 1)) assert_parse rb, pt end def test_attrasgn_array_arg rb = "a[[1, 2]] = 3" pt = s(:attrasgn, s(:call, nil, :a), :[]=, s(:array, s(:lit, 1), s(:lit, 2)), s(:lit, 3)) assert_parse rb, pt end def test_attrasgn_array_lhs rb = '[1, 2, 3, 4][from .. to] = ["a", "b", "c"]' pt = s(:attrasgn, s(:array, s(:lit, 1), s(:lit, 2), s(:lit, 3), s(:lit, 4)), :[]=, s(:dot2, s(:call, nil, :from), s(:call, nil, :to)), s(:array, s(:str, "a"), s(:str, "b"), s(:str, "c"))) assert_parse rb, pt end def test_attrasgn_primary_dot_constant rb = "a.B = 1" pt = s(:attrasgn, s(:call, nil, :a), :"B=", s(:lit, 1)) assert_parse rb, pt end def test_backticks_interpolation_line rb = 'x `#{y}`' pt = s(:call, nil, :x, s(:dxstr, "", s(:evstr, s(:call, nil, :y).line(1)).line(1))).line(1) assert_parse rb, pt end def test_bang_eq rb = "1 != 2" pt = s(:not, s(:call, s(:lit, 1), :"==", s(:lit, 2))) assert_parse rb, pt end def test_begin_else_return_value rb = "begin; else 2; end" assert_syntax_error rb, "else without rescue is useless" end def test_begin_ensure_no_bodies rb = "begin\nensure\nend" pt = s(:ensure, s(:nil).line(2)).line(2) assert_parse rb, pt end def test_begin_rescue_ensure_no_bodies rb = "begin\nrescue\nensure\nend" pt = s(:ensure, s(:rescue, s(:resbody, s(:array).line(2), nil).line(2) ).line(2), s(:nil).line(3) ).line(2) assert_parse rb, pt end def test_begin_rescue_else_ensure_bodies rb = "begin\n 1\nrescue\n 2\nelse\n 3\nensure\n 4\nend" pt = s(:ensure, s(:rescue, s(:lit, 1).line(2), s(:resbody, s(:array).line(3), s(:lit, 2).line(4)).line(3), s(:lit, 3).line(6)).line(2), s(:lit, 4).line(8)).line(2) s(:ensure, s(:rescue, s(:resbody, s(:array), nil)), s(:nil)) assert_parse rb, pt end def test_begin_rescue_else_ensure_no_bodies rb = "begin\n\nrescue\n\nelse\n\nensure\n\nend" pt = s(:ensure, s(:rescue, s(:resbody, s(:array).line(3), # TODO: s(:nil) nil ).line(3), ).line(3), s(:nil).line(7)).line(3) s(:ensure, s(:rescue, s(:resbody, s(:array), nil)), s(:nil)) assert_parse rb, pt end def test_block_append head = s(:args).line 1 tail = s(:zsuper).line 2 expected = s(:block, s(:args).line(1), s(:zsuper).line(2)).line 1 assert_equal expected, processor.block_append(head, tail) end def test_block_append_begin_begin head = s(:begin, s(:args).line(1)).line 1 tail = s(:begin, s(:args).line(2)).line 2 expected = s(:block, s(:args).line(1), s(:begin, s(:args).line(2)).line(2)).line 1 assert_equal expected, processor.block_append(head, tail) end def test_block_append_block head = s(:block, s(:args).line(1)).line(1) tail = s(:zsuper).line(2) expected = s(:block, s(:args).line(1), s(:zsuper).line(2)).line 1 assert_equal expected, processor.block_append(head, tail) end def test_block_append_nil_head head = nil tail = s(:zsuper) expected = s(:zsuper) assert_equal expected, processor.block_append(head, tail) end def test_block_append_nil_tail head = s(:args) tail = nil expected = s(:args) assert_equal expected, processor.block_append(head, tail) end def test_block_append_tail_block head = s(:call, nil, :f1).line 1 tail = s(:block, s(:undef, s(:lit, :x)).line(2), s(:undef, s(:lit, :y)).line(3)).line 2 expected = s(:block, s(:call, nil, :f1).line(1), s(:block, s(:undef, s(:lit, :x)).line(2), s(:undef, s(:lit, :y)).line(3)).line(2)).line 1 assert_equal expected, processor.block_append(head, tail) end def test_block_decomp_splat rb = "f { |(*a)| }" pt = s(:iter, s(:call, nil, :f), s(:args, s(:masgn, :"*a"))) assert_parse rb, pt end def test_bug121 skip "not ready for this yet" rb = "if (/a/../b/)../c/; end" pt = s(:if, s(:flip2, s(:flip2, s(:match, s(:lit, /a/)), s(:match, s(:lit, /b/))), s(:match, (s(:lit, /c/)))), nil, nil) # maybe? assert_parse rb, pt end def test_bug169 rb = "m () {}" pt = s(:iter, s(:call, nil, :m, s(:nil)), 0) assert_parse rb, pt end def test_bug170 skip "not ready for this yet" # TODO: needs to fail on 2.1 and up rb = '$-' pt = s(:gvar, :"$-") assert_parse rb, pt end def test_bug179 rb = "p ()..nil" pt = s(:call, nil, :p, s(:dot2, s(:begin), s(:nil))) assert_parse rb, pt end def test_bug190 skip "not ready for this yet" rb = %{%r'\\\''} # stupid emacs assert_parse rb, :FUCK assert_syntax_error rb, "FUCK" rb = %{%r'\\''} pt = s(:lit, /'/) assert_parse rb, pt end def test_bug191 pt = s(:if, s(:call, nil, :a), s(:str, ""), s(:call, nil, :b)) rb = "a ? '': b" assert_parse rb, pt rb = "a ? \"\": b" assert_parse rb, pt end def test_bug202 rb = "$测试 = 1\n测试 = 1" pt = s(:block, s(:gasgn, :$测试, s(:lit, 1)), s(:lasgn, :测试, s(:lit, 1))) assert_parse rb, pt end def test_bug236 rb = "x{|a|}" pt = s(:iter, s(:call, nil, :x), s(:args, :a)) assert_parse rb, pt rb = "x{|a,|}" pt = s(:iter, s(:call, nil, :x), s(:args, :a, nil)) assert_parse rb, pt end def test_bug290 rb = "begin\n foo\nend" pt = s(:call, nil, :foo).line(2) assert_parse rb, pt end def test_bug_and rb = "true and []" pt = s(:and, s(:true), s(:array)) assert_parse rb, pt rb = "true and\ntrue" pt = s(:and, s(:true), s(:true)) assert_parse rb, pt end def test_bug_args_masgn rb = "f { |(a, b), c| }" pt = s(:iter, s(:call, nil, :f), s(:args, s(:masgn, :a, :b), :c)) assert_parse rb, pt.dup end def test_bug_args_masgn2 rb = "f { |((a, b), c), d| }" pt = s(:iter, s(:call, nil, :f), s(:args, s(:masgn, s(:masgn, :a, :b), :c), :d)) assert_parse rb, pt end def test_bug_begin_else rb = "begin 1; else; 2 end" assert_syntax_error rb, "else without rescue is useless" end def test_bug_call_arglist_parens rb = "g ( 1), 2" pt = s(:call, nil, :g, s(:lit, 1), s(:lit, 2)) assert_parse rb, pt rb = <<-CODE def f g ( 1), 2 end CODE pt = s(:defn, :f, s(:args), s(:call, nil, :g, s(:lit, 1), s(:lit, 2))) assert_parse rb, pt rb = <<-CODE def f() g (1), 2 end CODE assert_parse rb, pt end def test_bug_case_when_regexp rb = "case :x; when /x/ then end" pt = s(:case, s(:lit, :x), s(:when, s(:array, s(:lit, /x/)), nil), nil) assert_parse rb, pt end def test_bug_comma rb = "if test ?d, dir then end" pt = s(:if, s(:call, nil, :test, s(:str, "d"), s(:call, nil, :dir)), nil, nil) assert_parse rb, pt end def test_bug_comment_eq_begin rb = "\n\n#\n=begin\nblah\n=end\n\n" exp = rb.strip + "\n" refute_parse rb assert_equal exp, processor.lexer.comments end def test_bug_cond_pct rb = "case; when %r%blahblah%; end" pt = s(:case, nil, s(:when, s(:array, s(:lit, /blahblah/)), nil), nil) assert_parse rb, pt end def test_bug_masgn_right rb = "f { |a, (b, c)| }" pt = s(:iter, s(:call, nil, :f), s(:args, :a, s(:masgn, :b, :c))) assert_parse rb, pt end def test_bug_not_parens rb = "not(a)" pt = s(:call, s(:call, nil, :a), :"!") assert_parse rb, pt end def test_bug_op_asgn_rescue rb = "a ||= b rescue nil" pt = s(:op_asgn_or, s(:lvar, :a), s(:lasgn, :a, s(:rescue, s(:call, nil, :b), s(:resbody, s(:array), s(:nil))))) assert_parse rb, pt end def test_call_and rb = "1 & 2" pt = s(:call, s(:lit, 1), :&, s(:lit, 2)) assert_parse rb, pt end def test_call_args_command rb = "a.b c.d 1" pt = s(:call, s(:call, nil, :a), :b, s(:call, s(:call, nil, :c), :d, s(:lit, 1))) assert_parse rb, pt end def test_call_array_arg rb = "1 == [:b, :c]" pt = s(:call, s(:lit, 1), :==, s(:array, s(:lit, :b), s(:lit, :c))) assert_parse rb, pt end def test_call_bang_command_call rb = "! a.b 1" pt = s(:not, s(:call, s(:call, nil, :a), :b, s(:lit, 1))) assert_parse rb, pt end def test_call_bang_squiggle rb = "1 !~ 2" pt = s(:not, s(:call, s(:lit, 1), :=~, s(:lit, 2))) # TODO: check for 1.9+ assert_parse rb, pt end def test_call_carat rb = "1 ^ 2" pt = s(:call, s(:lit, 1), :^, s(:lit, 2)) assert_parse rb, pt end def test_call_colon2 rb = "A::b" pt = s(:call, s(:const, :A), :b) assert_parse rb, pt end def test_call_div rb = "1 / 2" pt = s(:call, s(:lit, 1), :/, s(:lit, 2)) assert_parse rb, pt end def test_call_env processor.env[:a] = :lvar rb = "a.happy" pt = s(:call, s(:lvar, :a), :happy) assert_parse rb, pt end def test_call_eq3 rb = "1 === 2" pt = s(:call, s(:lit, 1), :===, s(:lit, 2)) assert_parse rb, pt end def test_call_gt rb = "1 > 2" pt = s(:call, s(:lit, 1), :>, s(:lit, 2)) assert_parse rb, pt end def test_call_lt rb = "1 < 2" pt = s(:call, s(:lit, 1), :<, s(:lit, 2)) assert_parse rb, pt end def test_call_lte rb = "1 <= 2" pt = s(:call, s(:lit, 1), :<=, s(:lit, 2)) assert_parse rb, pt end def test_call_not rb = "not 42" pt = s(:not, s(:lit, 42)) assert_parse rb, pt end def test_call_pipe rb = "1 | 2" pt = s(:call, s(:lit, 1), :|, s(:lit, 2)) assert_parse rb, pt end def test_call_rshift rb = "1 >> 2" pt = s(:call, s(:lit, 1), :>>, s(:lit, 2)) assert_parse rb, pt end def test_call_self_brackets rb = "self[1]" pt = s(:call, s(:self), :[], s(:lit, 1)) assert_parse rb, pt end def test_call_spaceship rb = "1 <=> 2" pt = s(:call, s(:lit, 1), :<=>, s(:lit, 2)) assert_parse rb, pt end def test_call_star rb = "1 * 2" pt = s(:call, s(:lit, 1), :"*", s(:lit, 2)) assert_parse rb, pt end def test_call_star2 rb = "1 ** 2" pt = s(:call, s(:lit, 1), :"**", s(:lit, 2)) assert_parse rb, pt end def test_call_unary_bang rb = "!1" pt = s(:not, s(:lit, 1)) assert_parse rb, pt end def test_class_comments rb = "# blah 1\n# blah 2\n\nclass X\n # blah 3\n def blah\n # blah 4\n end\nend" pt = s(:class, :X, nil, s(:defn, :blah, s(:args), s(:nil))) assert_parse rb, pt assert_equal "# blah 1\n# blah 2\n\n", result.comments assert_equal "# blah 3\n", result.defn.comments end def test_cond_unary_minus rb = "if -1; end" pt = s(:if, s(:lit, -1), nil, nil) assert_parse rb, pt end def test_dasgn_icky2 rb = "a do\n v = nil\n begin\n yield\n rescue Exception => v\n break\n end\nend" pt = s(:iter, s(:call, nil, :a), 0, s(:block, s(:lasgn, :v, s(:nil)), s(:rescue, s(:yield), s(:resbody, s(:array, s(:const, :Exception), s(:lasgn, :v, s(:gvar, :$!))), s(:break))))) assert_parse rb, pt end def test_defined_eh_parens rb = "defined?(42)" pt = s(:defined, s(:lit, 42)) assert_parse rb, pt end def test_defn_comments rb = "# blah 1\n# blah 2\n\ndef blah\nend" pt = s(:defn, :blah, s(:args), s(:nil)) assert_parse rb, pt assert_equal "# blah 1\n# blah 2\n\n", result.comments end def test_defns_reserved rb = "def self.return; end" pt = s(:defs, s(:self), :return, s(:args), s(:nil)) assert_parse rb, pt end def test_defs_as_arg_with_do_block_inside rb = "p def self.b; x.y do; end; end" pt = s(:call, nil, :p, s(:defs, s(:self), :b, s(:args), s(:iter, s(:call, s(:call, nil, :x), :y), 0))) assert_parse rb, pt end def test_defs_comments rb = "# blah 1\n# blah 2\n\ndef self.blah\nend" pt = s(:defs, s(:self), :blah, s(:args), s(:nil)) assert_parse rb, pt assert_equal "# blah 1\n# blah 2\n\n", result.comments end def test_do_bug # TODO: rename rb = "a 1\na.b do |c|\n # do nothing\nend" pt = s(:block, s(:call, nil, :a, s(:lit, 1)), s(:iter, s(:call, s(:call, nil, :a), :b), s(:args, :c))) assert_parse rb, pt end def test_double_block_error_01 assert_syntax_error "a(1, &b) { }", BLOCK_DUP_MSG end def test_double_block_error_02 assert_syntax_error "a(1, &b) do end", BLOCK_DUP_MSG end def test_double_block_error_03 assert_syntax_error "a 1, &b do end", BLOCK_DUP_MSG end def test_double_block_error_04 assert_syntax_error "m.a(1, &b) { }", BLOCK_DUP_MSG end def test_double_block_error_05 assert_syntax_error "m.a(1, &b) do end", BLOCK_DUP_MSG end def test_double_block_error_06 assert_syntax_error "m.a 1, &b do end", BLOCK_DUP_MSG end def test_double_block_error_07 assert_syntax_error "m::a(1, &b) { }", BLOCK_DUP_MSG end def test_double_block_error_08 assert_syntax_error "m::a(1, &b) do end", BLOCK_DUP_MSG end def test_double_block_error_09 assert_syntax_error "m::a 1, &b do end", BLOCK_DUP_MSG end def test_dstr_evstr rb = %q("#{'a'}#{b}") pt = s(:dstr, "a", s(:evstr, s(:call, nil, :b))) assert_parse rb, pt end def test_dstr_evstr_empty_end rb = ':"#{field}"' pt = s(:dsym, "", s(:evstr, s(:call, nil, :field))) assert_parse rb, pt end def test_dstr_str rb = %q("#{'a'} b") pt = s(:str, "a b") assert_parse rb, pt end def test_dsym_to_sym pt = s(:alias, s(:lit, :<<), s(:lit, :>>)) rb = "alias :<< :>>" assert_parse rb, pt rb = 'alias :"<<" :">>"' assert_parse rb, pt end def test_empty refute_parse "" end def test_eq_begin_line_numbers rb = "1\n=begin\ncomment\ncomment\n=end\n2" pt = s(:block, s(:lit, 1).line(1), s(:lit, 2).line(6)) assert_parse rb, pt end def test_eq_begin_why_wont_people_use_their_spacebar? rb = "h[k]=begin\n 42\n end" pt = s(:attrasgn, s(:call, nil, :h), :[]=, s(:call, nil, :k), s(:lit, 42)) assert_parse rb, pt end def test_evstr_evstr rb = %q("#{a}#{b}") pt = s(:dstr, "", s(:evstr, s(:call, nil, :a)), s(:evstr, s(:call, nil, :b))) assert_parse rb, pt end def test_evstr_str rb = %q("#{a} b") pt = s(:dstr, "", s(:evstr, s(:call, nil, :a)), s(:str, " b")) assert_parse rb, pt end def test_flip2_env_lvar rb = "if a..b then end" pt = s(:if, s(:flip2, s(:call, nil, :a), s(:call, nil, :b)), nil, nil) assert_parse rb, pt top_env = processor.env.env.first assert_kind_of Hash, top_env flip = top_env.find { |k, _| k =~ /^flip/ } assert flip assert_equal :lvar, flip.last end def test_fubar_nesting err = "class definition in method body" assert_syntax_error "def a; class B; end; end", err assert_syntax_error "def a; def b; end; class B; end; end", err end def test_heredoc_bad_hex_escape rb = "s = < :lvar } assert_parse rb, pt assert_equal expected_env, processor.env.all end def test_lasgn_ivar_env rb = "@a = 42" pt = s(:iasgn, :@a, s(:lit, 42)) assert_parse rb, pt assert_empty processor.env.all end def test_list_append a = s(:lit, 1) b = s(:lit, 2) c = s(:lit, 3) result = processor.list_append(s(:array, b.dup), c.dup) assert_equal s(:array, b, c), result result = processor.list_append(b.dup, c.dup) assert_equal s(:array, b, c), result result = processor.list_append(result, a.dup) assert_equal s(:array, b, c, a), result lhs, rhs = s(:array, s(:lit, :iter)), s(:when, s(:const, :BRANCHING), nil) expected = s(:array, s(:lit, :iter), s(:when, s(:const, :BRANCHING), nil)) assert_equal expected, processor.list_append(lhs, rhs) end def test_list_prepend a = s(:lit, 1) b = s(:lit, 2) c = s(:lit, 3) result = processor.list_prepend(b.dup, s(:array, c.dup)) assert_equal s(:array, b, c), result result = processor.list_prepend(b.dup, c.dup) assert_equal s(:array, b, c), result result = processor.list_prepend(a.dup, result) assert_equal s(:array, a, b, c), result end def test_literal_concat_dstr_dstr lhs = s(:dstr, "Failed to download spec ", s(:evstr, s(:call, nil, :spec_name)), s(:str, " from "), s(:evstr, s(:call, nil, :source_uri)), s(:str, ":\n")).line 1 rhs = s(:dstr, "\t", s(:evstr, s(:call, s(:ivar, :@fetch_error), :message))).line 2 expected = s(:dstr, "Failed to download spec ", s(:evstr, s(:call, nil, :spec_name)), s(:str, " from "), s(:evstr, s(:call, nil, :source_uri)), s(:str, ":\n"), s(:str, "\t"), s(:evstr, s(:call, s(:ivar, :@fetch_error), :message))) lhs.deep_each do |s| s.line = 1 end rhs.deep_each do |s| s.line = 1 end assert_equal expected, processor.literal_concat(lhs, rhs) end def test_literal_concat_dstr_evstr lhs, rhs = s(:dstr, "a"), s(:evstr, s(:call, nil, :b)) expected = s(:dstr, "a", s(:evstr, s(:call, nil, :b))) assert_equal expected, processor.literal_concat(lhs, rhs) end def test_literal_concat_evstr_evstr lhs = s(:evstr, s(:lit, 1)).line 1 rhs = s(:evstr, s(:lit, 2)).line 2 expected = s(:dstr, "", s(:evstr, s(:lit, 1)), s(:evstr, s(:lit, 2))) assert_equal expected, processor.literal_concat(lhs, rhs) end def test_literal_concat_str_evstr lhs = s(:str, "").line 1 rhs = s(:evstr, s(:str, "blah").line(2)).line 2 assert_equal s(:str, "blah"), processor.literal_concat(lhs, rhs) end def test_logical_op_12 lhs = s(:lit, 1).line 1 rhs = s(:lit, 2).line 2 exp = s(:and, s(:lit, 1).line(1), s(:lit, 2).line(2)).line 1 assert_equal exp, processor.logical_op(:and, lhs, rhs) end def test_logical_op_1234_5 lhs = s(:and, s(:lit, 1).line(1), s(:and, s(:lit, 2).line(2), s(:and, s(:lit, 3).line(3), s(:lit, 4).line(4)).line(3)).line(2)).line 1 rhs = s(:lit, 5).line(5) exp = s(:and, s(:lit, 1).line(1), s(:and, s(:lit, 2).line(2), s(:and, s(:lit, 3).line(3), s(:and, s(:lit, 4).line(4), s(:lit, 5).line(5)).line(4)).line(3)).line(2)).line 1 assert_equal exp, processor.logical_op(:and, lhs, rhs) end def test_logical_op_123_4 lhs = s(:and, s(:lit, 1).line(1), s(:and, s(:lit, 2).line(2), s(:lit, 3).line(3)).line(2)).line 1 rhs = s(:lit, 4).line 4 exp = s(:and, s(:lit, 1).line(1), s(:and, s(:lit, 2).line(2), s(:and, s(:lit, 3).line(3), s(:lit, 4).line(4)).line(3)).line(2)).line 1 assert_equal exp, processor.logical_op(:and, lhs, rhs) end def test_logical_op_12_3 lhs = s(:and, s(:lit, 1).line(1), s(:lit, 2).line(2)).line 1 rhs = s(:lit, 3).line 3 exp = s(:and, s(:lit, 1).line(1), s(:and, s(:lit, 2).line(2), s(:lit, 3).line(3)).line(2)).line 1 assert_equal exp, processor.logical_op(:and, lhs, rhs) end def test_logical_op_nested_mix lhs = s(:or, s(:call, nil, :a).line(1), s(:call, nil, :b).line(2)).line 1 rhs = s(:and, s(:call, nil, :c).line(3), s(:call, nil, :d).line(4)).line 3 exp = s(:or, s(:or, s(:call, nil, :a).line(1), s(:call, nil, :b).line(2)).line(1), s(:and, s(:call, nil, :c).line(3), s(:call, nil, :d).line(4)).line(3)).line 1 lhs.paren = true rhs.paren = true assert_equal exp, processor.logical_op(:or, lhs, rhs) end def test_magic_encoding_comment rb = "# encoding: utf-8\nclass ExampleUTF8ClassNameVarietà; def self.è; così = :però; end\nend\n" rb.force_encoding "ASCII-8BIT" if rb.respond_to? :force_encoding # TODO: class vars # TODO: odd-ternary: a ?bb : c # TODO: globals pt = s(:class, :"ExampleUTF8ClassNameVariet\303\240", nil, s(:defs, s(:self), :"\303\250", s(:args), s(:lasgn, :"cos\303\254", s(:lit, :"per\303\262")))) err = RUBY_VERSION =~ /^1\.8/ ? "Skipping magic encoding comment\n" : "" assert_output "", err do assert_parse rb, pt end end def test_magic_encoding_comment__bad rb = "#encoding: bunk\n0" pt = s(:lit, 0) assert_parse rb, pt end def test_utf8_bom rb = "\xEF\xBB\xBF#!/usr/bin/env ruby -w\np 0\n" pt = s(:call, nil, :p, s(:lit, 0)) assert_parse rb, pt end def test_masgn_arg_colon_arg rb = "a, b::c = d" pt = s(:masgn, s(:array, s(:lasgn, :a).line(1), s(:attrasgn, s(:call, nil, :b).line(1), :c=).line(1)).line(1), s(:to_ary, s(:call, nil, :d).line(1)).line(1)).line(1) assert_parse rb, pt end def test_masgn_arg_ident rb = "a, b.C = d" pt = s(:masgn, s(:array, s(:lasgn, :a), s(:attrasgn, s(:call, nil, :b), :"C=")), s(:to_ary, s(:call, nil, :d))) assert_parse rb, pt end def test_masgn_colon2 rb = "a, b::C = 1, 2" pt = s(:masgn, s(:array, s(:lasgn, :a), s(:const, s(:colon2, s(:call, nil, :b), :C))), s(:array, s(:lit, 1), s(:lit, 2))) assert_parse rb, pt end def test_masgn_colon3 rb = "::A, ::B = 1, 2" pt = s(:masgn, s(:array, s(:const, nil, s(:colon3, :A)), s(:const, s(:colon3, :B))), s(:array, s(:lit, 1), s(:lit, 2))) assert_parse rb, pt end def test_masgn_command_call rb = "a, = b.c 1" pt = s(:masgn, s(:array, s(:lasgn, :a)), s(:to_ary, s(:call, s(:call, nil, :b), :c, s(:lit, 1)))) assert_parse rb, pt end def test_masgn_double_paren rb = "((a,b))=c" # TODO: blog pt = s(:masgn, s(:array, s(:masgn, s(:array, s(:lasgn, :a), s(:lasgn, :b)))), s(:to_ary, s(:call, nil, :c))) assert_parse rb, pt end def test_masgn_lhs_splat rb = "*a = 1, 2, 3" pt = s(:masgn, s(:array, s(:splat, s(:lasgn, :a))), s(:array, s(:lit, 1), s(:lit, 2), s(:lit, 3))) assert_parse rb, pt end def test_masgn_paren rb = "(a, b) = c.d" pt = s(:masgn, s(:array, s(:lasgn, :a), s(:lasgn, :b)), s(:to_ary, s(:call, s(:call, nil, :c), :d))) assert_parse rb, pt end def test_masgn_star rb = "* = 1" pt = s(:masgn, s(:array, s(:splat)), s(:to_ary, s(:lit, 1))) assert_parse rb, pt end def test_module_comments rb = "# blah 1\n \n # blah 2\n\nmodule X\n # blah 3\n def blah\n # blah 4\n end\nend" pt = s(:module, :X, s(:defn, :blah, s(:args), s(:nil))) assert_parse rb, pt assert_equal "# blah 1\n\n# blah 2\n\n", result.comments assert_equal "# blah 3\n", result.defn.comments end def test_non_interpolated_word_array_line_breaks rb = "%w(\na\nb\n)\n1" pt = s(:block, s(:array, s(:str, "a").line(2), s(:str, "b").line(3)).line(1), s(:lit, 1).line(5)) assert_parse rb, pt end def test_op_asgn_command_call rb = "a ||= b.c 2" pt = s(:op_asgn_or, s(:lvar, :a), s(:lasgn, :a, s(:call, s(:call, nil, :b), :c, s(:lit, 2)))) assert_parse rb, pt end def test_op_asgn_dot_ident_command_call rb = "A.B ||= c 1" pt = s(:op_asgn, s(:const, :A), s(:call, nil, :c, s(:lit, 1)), :B, :"||") assert_parse rb, pt end def test_op_asgn_index_command_call rb = "a[:b] ||= c 1, 2" pt = s(:op_asgn1, s(:call, nil, :a), s(:arglist, s(:lit, :b)), :"||", s(:call, nil, :c, s(:lit, 1), s(:lit, 2))) assert_parse rb, pt end def test_op_asgn_primary_colon_identifier1 rb = "A::b += 1" pt = s(:op_asgn, s(:const, :A), s(:lit, 1), :b, :+) # TODO: check? looks wack assert_parse rb, pt end def test_lasgn_middle_splat rb = "a = b, *c, d" pt = s(:lasgn, :a, s(:svalue, s(:array, s(:call, nil, :b), s(:splat, s(:call, nil, :c)), s(:call, nil, :d)))) assert_parse rb, pt end def test_op_asgn_primary_colon_const_command_call rb = "A::B *= c d" pt = s(:op_asgn, s(:const, :A), s(:call, nil, :c, s(:call, nil, :d)), :B, :*) assert_parse rb, pt end def test_op_asgn_primary_colon_identifier_command_call rb = "A::b *= c d" pt = s(:op_asgn, s(:const, :A), s(:call, nil, :c, s(:call, nil, :d)), :b, :*) assert_parse rb, pt end def test_op_asgn_val_dot_ident_command_call rb = "a.b ||= c 1" pt = s(:op_asgn, s(:call, nil, :a), s(:call, nil, :c, s(:lit, 1)), :b, :"||") assert_parse rb, pt end def test_parse_comments p = RubyParser.new sexp = p.parse <<-CODE # class comment class Inline def show # woot end # Returns a list of things def list # woot end end CODE assert_equal "# class comment\n", sexp.comments act = sexp.find_nodes(:defn).map(&:comments) exp = ["", "# Returns a list of things\n"] assert_equal exp, act assert_equal [], processor.comments assert_equal "", processor.lexer.comments end def test_parse_if_not_canonical rb = "if not var.nil? then 'foo' else 'bar'\nend" pt = s(:if, s(:call, s(:call, nil, :var), :nil?), s(:str, "bar"), s(:str, "foo")) assert_parse rb, pt end def test_parse_if_not_noncanonical rb = "if not var.nil? then 'foo' else 'bar'\nend" pt = s(:if, s(:not, s(:call, s(:call, nil, :var), :nil?)), s(:str, "foo"), s(:str, "bar")) processor.canonicalize_conditions = false assert_parse rb, pt end def test_parse_line_block rb = "a = 42\np a" pt = s(:block, s(:lasgn, :a, s(:lit, 42)), s(:call, nil, :p, s(:lvar, :a))) assert_parse_line rb, pt, 1 assert_equal 1, result.lasgn.line, "lasgn should have line number" assert_equal 2, result.call.line, "call should have line number" expected = "(string)" assert_equal expected, result.file assert_equal expected, result.lasgn.file assert_equal expected, result.call.file assert_same result.file, result.lasgn.file assert_same result.file, result.call.file end def test_parse_line_block_inline_comment rb = "a\nb # comment\nc" pt = s(:block, s(:call, nil, :a).line(1), s(:call, nil, :b).line(2), s(:call, nil, :c).line(3)) assert_parse rb, pt end def test_parse_line_block_inline_comment_leading_newlines rb = "\n\n\na\nb # comment\n# another comment\nc" pt = s(:block, s(:call, nil, :a).line(4), s(:call, nil, :b).line(5), s(:call, nil, :c).line(7)).line(4) assert_parse rb, pt end def test_parse_line_block_inline_multiline_comment rb = "a\nb # comment\n# another comment\nc" pt = s(:block, s(:call, nil, :a).line(1), s(:call, nil, :b).line(2), s(:call, nil, :c).line(4)).line(1) assert_parse rb, pt end def test_parse_line_call_ivar_arg_no_parens_line_break rb = "a @b\n" pt = s(:call, nil, :a, s(:ivar, :@b).line(1)).line(1) assert_parse rb, pt end def test_parse_line_call_ivar_line_break_paren rb = "a(@b\n)" pt = s(:call, nil, :a, s(:ivar, :@b).line(1)).line(1) assert_parse rb, pt end def test_parse_line_call_no_args rb = "f do |x, y|\n x + y\nend" pt = s(:iter, s(:call, nil, :f), s(:args, :x, :y), s(:call, s(:lvar, :x), :+, s(:lvar, :y))) assert_parse_line rb, pt, 1 _, a, b, c, = result assert_equal 1, a.line, "call should have line number" assert_equal 1, b.line, "masgn should have line number" assert_equal 2, c.line, "call should have line number" end def test_parse_line_defn_no_parens_args rb = "def f a\nend" pt = s(:defn, :f, s(:args, :a).line(1), s(:nil).line(2)).line(1) assert_parse_line rb, pt, 1 end def test_parse_line_defn_complex rb = "def x(y)\n p(y)\n y *= 2\n return y;\nend" # TODO: remove () & ; pt = s(:defn, :x, s(:args, :y), s(:call, nil, :p, s(:lvar, :y)), s(:lasgn, :y, s(:call, s(:lvar, :y), :*, s(:lit, 2))), s(:return, s(:lvar, :y))) assert_parse_line rb, pt, 1 body = result assert_equal 2, body.call.line, "call should have line number" assert_equal 3, body.lasgn.line, "lasgn should have line number" assert_equal 4, body.return.line, "return should have line number" end def test_parse_line_defn_no_parens pt = s(:defn, :f, s(:args).line(1), s(:nil)).line(1) rb = "def f\nend" assert_parse_line rb, pt, 1 processor.reset rb = "def f\n\nend" assert_parse_line rb, pt, 1 end def test_parse_line_dot2 rb = "0..\n4\na..\nb\nc" pt = s(:block, s(:lit, 0..4).line(1), s(:dot2, s(:call, nil, :a).line(3), s(:call, nil, :b).line(4)).line(3), s(:call, nil, :c).line(5)).line(1) assert_parse_line rb, pt, 1 end def test_parse_line_dot3 rb = "0...\n4\na...\nb\nc" pt = s(:block, s(:lit, 0...4).line(1), s(:dot3, s(:call, nil, :a).line(3), s(:call, nil, :b).line(4)).line(3), s(:call, nil, :c).line(5)).line(1) assert_parse_line rb, pt, 1 end def test_parse_line_dstr_newline rb = <<-'CODE' "a\n#{ }" true CODE pt = s(:block, s(:dstr, "a\n", s(:evstr)).line(1), s(:true).line(3)) assert_parse rb, pt end def test_parse_line_evstr_after_break rb = "\"a\"\\\n\"\#{b}\"" pt = s(:dstr, "a", s(:evstr, s(:call, nil, :b).line(2)).line(2)).line(1) assert_parse rb, pt end def test_parse_line_hash_lit rb = "{\n:s1 => 1,\n}" pt = s(:hash, s(:lit, :s1).line(2), s(:lit, 1).line(2), ).line(1) assert_parse rb, pt end def test_parse_line_heredoc rb = <<-CODE string = <<-HEREDOC very long string HEREDOC puts string CODE pt = s(:block, s(:lasgn, :string, s(:str, " very long string\n").line(1)).line(1), s(:call, nil, :puts, s(:lvar, :string).line(4)).line(4)).line(1) assert_parse rb, pt end def test_parse_line_heredoc_evstr skip "heredoc line numbers are just gonna be screwed for a while..." rb = "<<-A\na\n\#{b}\nA" pt = s(:dstr, "a\n", s(:evstr, s(:call, nil, :b).line(3)), s(:str, "\n")).line(1) assert_parse rb, pt end def test_parse_line_heredoc_regexp_chars rb = <<-CODE string = <<-"^D" very long string ^D puts string CODE pt = s(:block, s(:lasgn, :string, s(:str, " very long string\n").line(1)).line(1), s(:call, nil, :puts, s(:lvar, :string).line(4)).line(4)).line(1) assert_parse rb, pt end def test_parse_line_iter_call_no_parens rb = "f a do |x, y|\n x + y\nend" pt = s(:iter, s(:call, nil, :f, s(:call, nil, :a).line(1)).line(1), s(:args, :x, :y).line(1), s(:call, s(:lvar, :x).line(2), :+, s(:lvar, :y).line(2)).line(2)).line(1) assert_parse rb, pt end def test_parse_line_iter_call_parens rb = "f(a) do |x, y|\n x + y\nend" pt = s(:iter, s(:call, nil, :f, s(:call, nil, :a)), s(:args, :x, :y), s(:call, s(:lvar, :x), :+, s(:lvar, :y))) assert_parse_line rb, pt, 1 _, a, b, c, = result assert_equal 1, a.line, "call should have line number" assert_equal 1, b.line, "masgn should have line number" assert_equal 2, c.line, "call should have line number" end def test_parse_line_multiline_str rb = "\"a\nb\"\n1" pt = s(:block, s(:str, "a\nb").line(1), s(:lit, 1).line(3)).line(1) assert_parse rb, pt end def test_parse_line_multiline_str_literal_n rb = "\"a\\nb\"\n1" pt = s(:block, s(:str, "a\nb").line(1), s(:lit, 1).line(2)).line(1) assert_parse rb, pt end def test_parse_line_newlines rb = "true\n\n" pt = s(:true) assert_parse_line rb, pt, 1 end def test_parse_line_op_asgn rb = <<-CODE foo += bar baz CODE pt = s(:block, s(:lasgn, :foo, s(:call, s(:lvar, :foo).line(1), :+, s(:call, nil, :bar).line(2)).line(1)).line(1), s(:call, nil, :baz).line(3)).line(1) assert_parse_line rb, pt, 1 end def test_parse_line_postexe rb = "END {\nfoo\n}" pt = s(:iter, s(:postexe).line(1), 0, s(:call, nil, :foo).line(2)).line(1) assert_parse_line rb, pt, 1 end def test_parse_line_preexe rb = "BEGIN {\nfoo\n}" pt = s(:iter, s(:preexe).line(1), 0, s(:call, nil, :foo).line(2)).line(1) assert_parse_line rb, pt, 1 end def test_parse_line_rescue rb = "begin\n a\nrescue\n b\nrescue\n c\nend\n" pt = s(:rescue, s(:call, nil, :a).line(2), s(:resbody, s(:array).line(3), s(:call, nil, :b).line(4)).line(3), s(:resbody, s(:array).line(5), s(:call, nil, :c).line(6)).line(5)).line(2) assert_parse_line rb, pt, 2 end def test_parse_line_return rb = <<-RUBY def blah if true then return 42 end end RUBY pt = s(:defn, :blah, s(:args), s(:if, s(:true), s(:return, s(:lit, 42)), nil)) assert_parse_line rb, pt, 1 assert_equal 3, result.if.return.line assert_equal 3, result.if.return.lit.line end def test_parse_line_str_with_newline_escape rb = 'a("\n", true)' pt = s(:call, nil, :a, s(:str, "\n").line(1), s(:true).line(1)) assert_parse rb, pt end def test_parse_line_to_ary rb = "a,\nb = c\nd" pt = s(:block, s(:masgn, s(:array, s(:lasgn, :a).line(1), s(:lasgn, :b).line(2)).line(1), s(:to_ary, s(:call, nil, :c).line(2)).line(2)).line(1), s(:call, nil, :d).line(3)).line(1) assert_parse_line rb, pt, 1 end def test_parse_line_trailing_newlines rb = "a \nb" pt = s(:block, s(:call, nil, :a).line(1), s(:call, nil, :b).line(2)).line(1) assert_parse rb, pt end def test_parse_until_not_canonical rb = "until not var.nil?\n 'foo'\nend" pt = s(:while, s(:call, s(:call, nil, :var), :nil?), s(:str, "foo"), true) assert_parse rb, pt end def test_parse_until_not_noncanonical rb = "until not var.nil?\n 'foo'\nend" pt = s(:until, s(:not, s(:call, s(:call, nil, :var), :nil?)), s(:str, "foo"), true) processor.canonicalize_conditions = false assert_parse rb, pt end def test_parse_while_not_canonical rb = "while not var.nil?\n 'foo'\nend" pt = s(:until, s(:call, s(:call, nil, :var), :nil?), s(:str, "foo"), true) assert_parse rb, pt end def test_parse_while_not_noncanonical rb = "while not var.nil?\n 'foo'\nend" pt = s(:while, s(:not, s(:call, s(:call, nil, :var), :nil?)), s(:str, "foo"), true) processor.canonicalize_conditions = false assert_parse rb, pt end def test_pipe_space rb = "a.b do | | end" pt = s(:iter, s(:call, s(:call, nil, :a), :b), s(:args)) assert_parse rb, pt end def test_qWords_space rb = "%W( )" pt = s(:array) assert_parse rb, pt end def test_qwords_empty rb = "%w()" pt = s(:array) assert_parse rb, pt end def test_regexp regexps = { "/wtf/" => /wtf/, "/wtf/n" => /wtf/n, "/wtf/m" => /wtf/m, "/wtf/nm" => /wtf/nm, "/wtf/nmnmnmnm" => /wtf/nm, } regexps.each do |rb, lit| assert_parse rb, s(:lit, lit) end # TODO: add more including interpolation etc end def test_regexp_escape_extended assert_parse '/\“/', s(:lit, /“/) end def test_str_backslashes long_string = '\n' * 100 rb = "x '#{long_string}'" pt = s(:call, nil, :x, s(:str, long_string)) assert_parse rb, pt end def test_str_evstr rb = "\"a #\{b}\"" pt = s(:dstr, "a ", s(:evstr, s(:call, nil, :b))) assert_parse rb, pt end def test_str_evstr_escape char = [0x00bd].pack("U") rb = "\"a #\{b}\\302\\275\"" pt = s(:dstr, "a ", s(:evstr, s(:call, nil, :b)), s(:str, char)) assert_parse rb, pt end def test_str_heredoc_interp rb = "<<\"\"\n\#{x}\nblah2\n\n" pt = s(:dstr, "", s(:evstr, s(:call, nil, :x)), s(:str, "\nblah2\n")) assert_parse rb, pt end def test_str_interp_ternary_or_label env = processor.env env[:a] = :lvar rb = '"#{a.b? ? ""+a+"": ""}"' pt = s(:dstr, "", s(:evstr, s(:if, s(:call, s(:lvar, :a), :b?), s(:call, s(:call, s(:str, ""), :+, s(:lvar, :a)), :+, s(:str, "")), s(:str, "")))) assert_parse rb, pt end def test_str_newline_hash_line_number rb = "\"\\n\\n\\n\\n#\"\n1" pt = s(:block, s(:str, "\n\n\n\n#").line(1), s(:lit, 1).line(2)) assert_parse rb, pt end # def test_str_pct_nested_nested # rb = "%{ { #\{ \"#\{1}\" } } }" # assert_equal " { 1 } ", eval(rb) # pt = s(:dstr, " { ", s(:evstr, s(:lit, 1)), s(:str, " } ")) # # assert_parse rb, pt # end def test_str_pct_Q_nested rb = "%Q[before [#\{nest}] after]" pt = s(:dstr, "before [", s(:evstr, s(:call, nil, :nest)), s(:str, "] after")) assert_parse rb, pt end def test_str_pct_q rb = "%q{a b c}" pt = s(:str, "a b c") assert_parse rb, pt end def test_str_str rb = "\"a #\{'b'}\"" pt = s(:str, "a b") assert_parse rb, pt end def test_str_str_str rb = "\"a #\{'b'} c\"" pt = s(:str, "a b c") assert_parse rb, pt end def test_super_arg rb = "super 42" pt = s(:super, s(:lit, 42)) assert_parse rb, pt end def test_uminus_float rb = "-0.0" pt = s(:lit, -0.0) assert_parse rb, pt end def test_unary_minus rb = "-a" pt = s(:call, s(:call, nil, :a), :"-@") assert_parse rb, pt end def test_unary_plus rb = "+a" pt = s(:call, s(:call, nil, :a), :+@) assert_parse rb, pt end def test_unary_tilde rb = "~a" pt = s(:call, s(:call, nil, :a), :~) assert_parse rb, pt end def test_when_splat rb = "case a; when *b then; end" pt = s(:case, s(:call, nil, :a), s(:when, s(:array, s(:splat, s(:call, nil, :b))), nil), nil) assert_parse rb, pt end def test_words_interp rb = '%W(#{1}b)' pt = s(:array, s(:dstr, "", s(:evstr, s(:lit, 1)), s(:str, "b"))) assert_parse rb, pt end def test_wtf_7 rb = "a.b (1) {c}" pt = s(:iter, s(:call, s(:call, nil, :a), :b, s(:lit, 1)), 0, s(:call, nil, :c)) assert_parse rb, pt end def test_wtf_8 rb = "a::b (1) {c}" pt = s(:iter, s(:call, s(:call, nil, :a), :b, s(:lit, 1)), 0, s(:call, nil, :c)) assert_parse rb, pt end def test_yield_arg rb = "yield 42" pt = s(:yield, s(:lit, 42)) assert_parse rb, pt end def test_yield_empty_parens rb = "yield()" pt = s(:yield) assert_parse rb, pt end end module TestRubyParserShared19Plus include TestRubyParserShared def test_aref_args_lit_assocs rb = "[1, 2 => 3]" pt = s(:array, s(:lit, 1), s(:hash, s(:lit, 2), s(:lit, 3))) assert_parse rb, pt end def test_assoc_label rb = "a(b:1)" pt = s(:call, nil, :a, s(:hash, s(:lit, :b), s(:lit, 1))) assert_parse rb, pt end def test_assoc_list_19 rb = "{1, 2, 3, 4}" assert_parse_error rb, "(string):1 :: parse error on value \",\" (tCOMMA)" end def test_bang_eq rb = "1 != 2" pt = s(:call, s(:lit, 1), :"!=", s(:lit, 2)) assert_parse rb, pt end def test_block_arg_opt_arg_block rb = "a { |b, c=1, d, &e| }" pt = s(:iter, s(:call, nil, :a), s(:args, :b, s(:lasgn, :c, s(:lit, 1)), :d, :"&e")) assert_parse rb, pt end def test_block_arg_opt_splat rb = "a { |b, c = 1, *d| }" pt = s(:iter, s(:call, nil, :a), s(:args, :b, s(:lasgn, :c, s(:lit, 1)), :"*d")) assert_parse rb, pt end def test_block_arg_opt_splat_arg_block_omfg rb = "a { |b, c=1, *d, e, &f| }" pt = s(:iter, s(:call, nil, :a), s(:args, :b, s(:lasgn, :c, s(:lit, 1)), :"*d", :e, :"&f")) assert_parse rb, pt end def test_block_arg_optional rb = "a { |b = 1| }" pt = s(:iter, s(:call, nil, :a), s(:args, s(:lasgn, :b, s(:lit, 1)))) assert_parse rb, pt end def test_block_arg_scope rb = "a { |b; c| }" pt = s(:iter, s(:call, nil, :a), s(:args, :b, s(:shadow, :c))) assert_parse rb, pt end def test_block_arg_scope2 rb = "a {|b; c, d| }" pt = s(:iter, s(:call, nil, :a), s(:args, :b, s(:shadow, :c, :d))) assert_parse rb, pt end def test_block_arg_splat_arg rb = "a { |b, *c, d| }" pt = s(:iter, s(:call, nil, :a), s(:args, :b, :"*c", :d)) assert_parse rb, pt end def test_block_args_opt1 rb = "f { |a, b = 42| [a, b] }" pt = s(:iter, s(:call, nil, :f), s(:args, :a, s(:lasgn, :b, s(:lit, 42))), s(:array, s(:lvar, :a), s(:lvar, :b))) assert_parse rb, pt end def test_block_args_opt2 rb = "a { | b=1, c=2 | }" pt = s(:iter, s(:call, nil, :a), s(:args, s(:lasgn, :b, s(:lit, 1)), s(:lasgn, :c, s(:lit, 2)))) assert_parse rb, pt end def test_block_args_opt2_2 rb = "f { |a, b = 42, c = 24| [a, b, c] }" pt = s(:iter, s(:call, nil, :f), s(:args, :a, s(:lasgn, :b, s(:lit, 42)), s(:lasgn, :c, s(:lit, 24))), s(:array, s(:lvar, :a), s(:lvar, :b), s(:lvar, :c))) assert_parse rb, pt end def test_block_args_opt3 rb = "f { |a, b = 42, c = 24, &d| [a, b, c, d] }" pt = s(:iter, s(:call, nil, :f), s(:args, :a, s(:lasgn, :b, s(:lit, 42)), s(:lasgn, :c, s(:lit, 24)), :"&d"), s(:array, s(:lvar, :a), s(:lvar, :b), s(:lvar, :c), s(:lvar, :d))) assert_parse rb, pt end def test_block_break rb = "break foo arg do |bar| end" pt = s(:break, s(:iter, s(:call, nil, :foo, s(:call, nil, :arg)), s(:args, :bar))) assert_parse rb, pt end def test_block_call_operation_colon rb = "a.b c do end::d" pt = s(:call, s(:iter, s(:call, s(:call, nil, :a), :b, s(:call, nil, :c)), 0), :d) assert_parse rb, pt end def test_block_call_operation_dot rb = "a.b c do end.d" pt = s(:call, s(:iter, s(:call, s(:call, nil, :a), :b, s(:call, nil, :c)), 0), :d) assert_parse rb, pt end def test_block_command_operation_colon rb = "a :b do end::c :d" pt = s(:call, s(:iter, s(:call, nil, :a, s(:lit, :b)), 0), :c, s(:lit, :d)) assert_parse rb, pt end def test_block_command_operation_dot rb = "a :b do end.c :d" pt = s(:call, s(:iter, s(:call, nil, :a, s(:lit, :b)), 0), :c, s(:lit, :d)) assert_parse rb, pt end def test_block_decomp_anon_splat_arg rb = "f { |(*, a)| }" pt = s(:iter, s(:call, nil, :f), s(:args, s(:masgn, :*, :a))) assert_parse rb, pt end def test_block_decomp_arg_splat rb = "a { |(b, *)| }" pt = s(:iter, s(:call, nil, :a), s(:args, s(:masgn, :b, :*))) assert_parse rb, pt end def test_block_decomp_arg_splat_arg rb = "f { |(a, *b, c)| }" pt = s(:iter, s(:call, nil, :f), s(:args, s(:masgn, :a, :"*b", :c))) assert_parse rb, pt end def test_block_next rb = "next foo arg do |bar| end" pt = s(:next, s(:iter, s(:call, nil, :foo, s(:call, nil, :arg)), s(:args, :bar))) assert_parse rb, pt end def test_block_opt_arg rb = "a { |b=1, c| }" pt = s(:iter, s(:call, nil, :a), s(:args, s(:lasgn, :b, s(:lit, 1)), :c)) assert_parse rb, pt end def test_block_opt_splat rb = "a { |b = 1, *c| }" pt = s(:iter, s(:call, nil, :a), s(:args, s(:lasgn, :b, s(:lit, 1)), :"*c")) assert_parse rb, pt end def test_block_opt_splat_arg_block_omfg rb = "a { |b=1, *c, d, &e| }" pt = s(:iter, s(:call, nil, :a), s(:args, s(:lasgn, :b, s(:lit, 1)), :"*c", :d, :"&e")) assert_parse rb, pt end def test_block_optarg rb = "a { |b = :c| }" pt = s(:iter, s(:call, nil, :a), s(:args, s(:lasgn, :b, s(:lit, :c)))) assert_parse rb, pt end def test_block_paren_splat # TODO: rename # TODO: should work on 1.8 rb = "a { |(b, *c)| }" pt = s(:iter, s(:call, nil, :a), s(:args, s(:masgn, :b, :"*c"))) assert_parse rb, pt end def test_block_reg_optarg rb = "a { |b, c = :d| }" pt = s(:iter, s(:call, nil, :a), s(:args, :b, s(:lasgn, :c, s(:lit, :d)))) assert_parse rb, pt end def test_block_return rb = "return foo arg do |bar| end" pt = s(:return, s(:iter, s(:call, nil, :foo, s(:call, nil, :arg)), s(:args, :bar))) assert_parse rb, pt end def test_block_scope rb = "a { |;b| }" pt = s(:iter, s(:call, nil, :a), s(:args, s(:shadow, :b))) assert_parse rb, pt end def test_block_splat_reg rb = "a { |*b, c| }" pt = s(:iter, s(:call, nil, :a), s(:args, :"*b", :c)) assert_parse rb, pt end def test_block_yield rb = "yield foo arg do |bar| end" pt = s(:yield, s(:iter, s(:call, nil, :foo, s(:call, nil, :arg)), s(:args, :bar))) assert_parse rb, pt end def test_bug_187 rb = "private def f\na.b do end\nend" pt = s(:call, nil, :private, s(:defn, :f, s(:args), s(:iter, s(:call, s(:call, nil, :a), :b), 0))) assert_parse rb, pt end def test_bug_args__19 rb = "f { |(a, b)| d }" pt = s(:iter, s(:call, nil, :f), s(:args, s(:masgn, :a, :b)), s(:call, nil, :d)) assert_parse rb, pt end def test_bug_args_masgn_outer_parens__19 rb = "f { |((k, v), i)| }" pt = s(:iter, # NOTE: same sexp as test_bug_args_masgn s(:call, nil, :f), s(:args, s(:masgn, s(:masgn, :k, :v), :i))) assert_parse rb, pt.dup end def test_bug_hash_args rb = "foo(:bar, baz: nil)" pt = s(:call, nil, :foo, s(:lit, :bar), s(:hash, s(:lit, :baz), s(:nil))) assert_parse rb, pt end def test_bug_hash_args_trailing_comma rb = "foo(:bar, baz: nil,)" pt = s(:call, nil, :foo, # NOTE: same sexp as test_bug_hash_args s(:lit, :bar), s(:hash, s(:lit, :baz), s(:nil))) assert_parse rb, pt end def test_call_arg_assoc rb = "f(1, 2=>3)" pt = s(:call, nil, :f, s(:lit, 1), s(:hash, s(:lit, 2), s(:lit, 3))) assert_parse rb, pt end def test_call_args_assoc_trailing_comma rb = "f(1, 2=>3,)" pt = s(:call, nil, :f, s(:lit, 1), s(:hash, s(:lit, 2), s(:lit, 3))) assert_parse rb, pt end def test_call_array_lit_inline_hash rb = "a([:b, :c => 1])" pt = s(:call, nil, :a, s(:array, s(:lit, :b), s(:hash, s(:lit, :c), s(:lit, 1)))) assert_parse rb, pt end def test_call_assoc rb = "f(2=>3)" pt = s(:call, nil, :f, s(:hash, s(:lit, 2), s(:lit, 3))) assert_parse rb, pt end def test_call_assoc_new rb = "f(a:3)" pt = s(:call, nil, :f, s(:hash, s(:lit, :a), s(:lit, 3))) assert_parse rb, pt end def test_call_assoc_new_if_multiline rb = "a(b: if :c\n1\nelse\n2\nend)" pt = s(:call, nil, :a, s(:hash, s(:lit, :b), s(:if, s(:lit, :c), s(:lit, 1), s(:lit, 2)))) assert_parse rb, pt end def test_call_assoc_trailing_comma rb = "f(1=>2,)" pt = s(:call, nil, :f, s(:hash, s(:lit, 1), s(:lit, 2))) assert_parse rb, pt end def test_call_bang_command_call rb = "! a.b 1" pt = s(:call, s(:call, s(:call, nil, :a), :b, s(:lit, 1)), :"!") assert_parse rb, pt end def test_call_colon_parens rb = "1::()" pt = s(:call, s(:lit, 1), :call) assert_parse rb, pt end def test_call_dot_parens rb = "1.()" pt = s(:call, s(:lit, 1), :call) assert_parse rb, pt end def test_call_not rb = "not 42" pt = s(:call, s(:lit, 42), :"!") assert_parse rb, pt end def test_call_stabby_do_end_with_block rb = "a -> do 1 end do 2 end" pt = s(:iter, s(:call, nil, :a, s(:iter, s(:lambda), 0, s(:lit, 1))), 0, s(:lit, 2)) assert_parse rb, pt end def test_call_stabby_with_braces_block rb = "a -> { 1 } do 2 end" pt = s(:iter, s(:call, nil, :a, s(:iter, s(:lambda), 0, s(:lit, 1))), 0, s(:lit, 2)) assert_parse rb, pt end def test_call_trailing_comma rb = "f(1,)" pt = s(:call, nil, :f, s(:lit, 1)) assert_parse rb, pt end def test_call_unary_bang rb = "!1" pt = s(:call, s(:lit, 1), :"!") assert_parse rb, pt end def test_case_then_colon_19 rb = <<-EOM case x when Fixnum : # need the space to not hit new hash arg syntax 42 end EOM assert_parse_error rb, "(string):2 :: parse error on value \":\" (tCOLON)" end def test_defn_arg_asplat_arg rb = "def call(interp, *, args) end" pt = s(:defn, :call, s(:args, :interp, :*, :args), s(:nil)) assert_parse rb, pt end def test_defn_opt_last_arg rb = "def m arg = false\nend" pt = s(:defn, :m, s(:args, s(:lasgn, :arg, s(:false).line(1)).line(1)).line(1), s(:nil).line(2)).line(1) assert_parse rb, pt end def test_defn_opt_reg rb = "def f(a=nil, b) end" pt = s(:defn, :f, s(:args, s(:lasgn, :a, s(:nil)), :b), s(:nil)) assert_parse rb, pt end def test_defn_opt_splat_arg rb = "def f (a = 1, *b, c) end" pt = s(:defn, :f, s(:args, s(:lasgn, :a, s(:lit, 1)), :"*b", :c), s(:nil)) assert_parse rb, pt end def test_defn_reg_opt_reg rb = "def f(a, b = :c, d) end" pt = s(:defn, :f, s(:args, :a, s(:lasgn, :b, s(:lit, :c)), :d), s(:nil)) assert_parse rb, pt end def test_defn_splat_arg rb = "def f(*, a) end" pt = s(:defn, :f, s(:args, :*, :a), s(:nil)) assert_parse rb, pt end def test_do_colon_19 rb = "while false : 42 end" assert_parse_error rb, "(string):1 :: parse error on value \":\" (tCOLON)" end def test_do_lambda rb = "->() do end" pt = s(:iter, s(:lambda), s(:args)) assert_parse rb, pt end def test_expr_not_bang rb = "! a b" pt = s(:call, s(:call, nil, :a, s(:call, nil, :b)), :"!") assert_parse rb, pt end def test_i_have_no_freakin_clue rb = "1 ? b('') : 2\na d: 3" pt = s(:block, s(:if, s(:lit, 1), s(:call, nil, :b, s(:str, "")), s(:lit, 2)), s(:call, nil, :a, s(:hash, s(:lit, :d), s(:lit, 3)))) assert_parse rb, pt end def test_index_0 rb = "a[] = b" pt = s(:attrasgn, s(:call, nil, :a), :[]=, s(:call, nil, :b)) assert_parse rb, pt end def test_iter_args_10_1 rb = "f { |a, b = 42, *c| }" pt = s(:iter, s(:call, nil, :f), s(:args, :a, s(:lasgn, :b, s(:lit, 42)), :"*c")) assert_parse rb, pt end def test_iter_args_10_2 rb = "f { |a, b = 42, *c, &d| }" pt = s(:iter, s(:call, nil, :f), s(:args, :a, s(:lasgn, :b, s(:lit, 42)), :"*c", :"&d")) assert_parse rb, pt end def test_iter_args_11_1 rb = "f { |a, b = 42, *c, d| }" pt = s(:iter, s(:call, nil, :f), s(:args, :a, s(:lasgn, :b, s(:lit, 42)), :"*c", :d)) assert_parse rb, pt end def test_iter_args_11_2 rb = "f { |a, b = 42, *c, d, &e| }" pt = s(:iter, s(:call, nil, :f), s(:args, :a, s(:lasgn, :b, s(:lit, 42)), :"*c", :d, :"&e")) assert_parse rb, pt end def test_iter_args_2__19 rb = "f { |(a, b)| }" pt = s(:iter, s(:call, nil, :f), s(:args, s(:masgn, :a, :b))) assert_parse rb, pt end def test_iter_args_4 rb = "f { |a, *b, c| }" pt = s(:iter, s(:call, nil, :f), s(:args, :a, :"*b", :c)) assert_parse rb, pt end def test_iter_args_5 rb = "f { |a, &b| }" pt = s(:iter, s(:call, nil, :f), s(:args, :a, :"&b")) assert_parse rb, pt end def test_iter_args_6 rb = "f { |a, b=42, c| }" pt = s(:iter, s(:call, nil, :f), s(:args, :a, s(:lasgn, :b, s(:lit, 42)), :c)) assert_parse rb, pt end def test_iter_args_7_1 rb = "f { |a = 42, *b| }" pt = s(:iter, s(:call, nil, :f), s(:args, s(:lasgn, :a, s(:lit, 42)), :"*b")) assert_parse rb, pt end def test_iter_args_7_2 rb = "f { |a = 42, *b, &c| }" pt = s(:iter, s(:call, nil, :f), s(:args, s(:lasgn, :a, s(:lit, 42)), :"*b", :"&c")) assert_parse rb, pt end def test_iter_args_8_1 rb = "f { |a = 42, *b, c| }" pt = s(:iter, s(:call, nil, :f), s(:args, s(:lasgn, :a, s(:lit, 42)), :"*b", :c)) assert_parse rb, pt end def test_iter_args_8_2 rb = "f { |a = 42, *b, c, &d| }" pt = s(:iter, s(:call, nil, :f), s(:args, s(:lasgn, :a, s(:lit, 42)), :"*b", :c, :"&d")) assert_parse rb, pt end def test_iter_args_9_1 rb = "f { |a = 42, b| }" pt = s(:iter, s(:call, nil, :f), s(:args, s(:lasgn, :a, s(:lit, 42)), :b)) assert_parse rb, pt end def test_iter_args_9_2 rb = "f { |a = 42, b, &c| }" pt = s(:iter, s(:call, nil, :f), s(:args, s(:lasgn, :a, s(:lit, 42)), :b, :"&c")) assert_parse rb, pt end def test_kill_me rb = "f { |a, (b, *c)| }" pt = s(:iter, s(:call, nil, :f), s(:args, :a, s(:masgn, :b, :"*c"))) assert_parse rb, pt end def test_kill_me2 rb = "f { |*a, b| }" pt = s(:iter, s(:call, nil, :f), s(:args, :"*a", :b)) assert_parse rb, pt end def test_kill_me3 rb = "f { |*a, b, &c| }" pt = s(:iter, s(:call, nil, :f), s(:args, :"*a", :b, :"&c")) assert_parse rb, pt end def test_kill_me4 rb = "a=b ? true: false" pt = s(:lasgn, :a, s(:if, s(:call, nil, :b), s(:true), s(:false))) assert_parse rb, pt end def test_kill_me5 rb = "f ->() { g do end }" pt = s(:call, nil, :f, s(:iter, s(:lambda), s(:args), s(:iter, s(:call, nil, :g), 0))) assert_parse rb, pt end def test_kill_me_10 # | tSTAR f_norm_arg tCOMMA f_marg_list rb = "f { |a, (*b, c)| }" pt = s(:iter, s(:call, nil, :f), s(:args, :a, s(:masgn, :"*b", :c))) assert_parse rb, pt end def test_kill_me_11 # | tSTAR rb = "f { |a, (*)| }" pt = s(:iter, s(:call, nil, :f), s(:args, :a, s(:masgn, :*))) assert_parse rb, pt end def test_kill_me_12 # | tSTAR tCOMMA f_marg_list rb = "f { |a, (*, b)| }" pt = s(:iter, s(:call, nil, :f), s(:args, :a, s(:masgn, :*, :b))) assert_parse rb, pt end def test_kill_me_6 # | f_marg_list tCOMMA tSTAR f_norm_arg tCOMMA f_marg_list rb = "f { |a, (b, *c, d)| }" pt = s(:iter, s(:call, nil, :f), s(:args, :a, s(:masgn, :b, :"*c", :d))) assert_parse rb, pt end def test_kill_me_7 # | f_marg_list tCOMMA tSTAR rb = "f { |a, (b, *)| }" pt = s(:iter, s(:call, nil, :f), s(:args, :a, s(:masgn, :b, :*))) assert_parse rb, pt end def test_kill_me_8 # | f_marg_list tCOMMA tSTAR tCOMMA f_marg_list rb = "f { |a, (b, *, c)| }" pt = s(:iter, s(:call, nil, :f), s(:args, :a, s(:masgn, :b, :*, :c))) assert_parse rb, pt end def test_kill_me_9 # | tSTAR f_norm_arg rb = "f { |a, (*b)| }" pt = s(:iter, s(:call, nil, :f), s(:args, :a, s(:masgn, :"*b"))) assert_parse rb, pt end def test_lambda_do_vs_brace pt = s(:call, nil, :f, s(:iter, s(:lambda), s(:args))) rb = "f ->() {}" assert_parse rb, pt rb = "f ->() do end" assert_parse rb, pt pt = s(:call, nil, :f, s(:iter, s(:lambda), 0)) rb = "f -> {}" assert_parse rb, pt rb = "f -> do end" assert_parse rb, pt end def test_lasgn_lasgn_command_call rb = "a = b = c 1" pt = s(:lasgn, :a, s(:lasgn, :b, s(:call, nil, :c, s(:lit, 1)))) assert_parse rb, pt end def test_masgn_anon_splat_arg rb = "*, a = b" pt = s(:masgn, s(:array, s(:splat), s(:lasgn, :a)), s(:to_ary, s(:call, nil, :b))) assert_parse rb, pt end def test_masgn_arg_splat_arg rb = "a, *b, c = d" pt = s(:masgn, s(:array, s(:lasgn, :a), s(:splat, s(:lasgn, :b)), s(:lasgn, :c)), s(:to_ary, s(:call, nil, :d))) assert_parse rb, pt end def test_masgn_splat_arg rb = "*a, b = c" pt = s(:masgn, s(:array, s(:splat, s(:lasgn, :a)), s(:lasgn, :b)), s(:to_ary, s(:call, nil, :c))) assert_parse rb, pt end def test_masgn_splat_arg_arg rb = "*a, b, c = d" pt = s(:masgn, s(:array, s(:splat, s(:lasgn, :a)), s(:lasgn, :b), s(:lasgn, :c)), s(:to_ary, s(:call, nil, :d))) assert_parse rb, pt end def test_masgn_var_star_var rb = "a, *, b = c" # TODO: blog pt = s(:masgn, s(:array, s(:lasgn, :a), s(:splat), s(:lasgn, :b)), s(:to_ary, s(:call, nil, :c))) assert_parse rb, pt end def test_method_call_assoc_trailing_comma rb = "a.f(1=>2,)" pt = s(:call, s(:call, nil, :a), :f, s(:hash, s(:lit, 1), s(:lit, 2))) assert_parse rb, pt end def test_method_call_trailing_comma rb = "a.f(1,)" pt = s(:call, s(:call, nil, :a), :f, s(:lit, 1)) assert_parse rb, pt end def test_mlhs_back_anonsplat rb = "a, b, c, * = f" pt = s(:masgn, s(:array, s(:lasgn, :a), s(:lasgn, :b), s(:lasgn, :c), s(:splat)), s(:to_ary, s(:call, nil, :f))) assert_parse rb, pt end def test_mlhs_back_splat rb = "a, b, c, *s = f" pt = s(:masgn, s(:array, s(:lasgn, :a), s(:lasgn, :b), s(:lasgn, :c), s(:splat, s(:lasgn, :s))), s(:to_ary, s(:call, nil, :f))) assert_parse rb, pt end def test_mlhs_front_anonsplat rb = "*, x, y, z = f" pt = s(:masgn, s(:array, s(:splat), s(:lasgn, :x), s(:lasgn, :y), s(:lasgn, :z)), s(:to_ary, s(:call, nil, :f))) assert_parse rb, pt end def test_mlhs_front_splat rb = "*s, x, y, z = f" pt = s(:masgn, s(:array, s(:splat, s(:lasgn, :s)), s(:lasgn, :x), s(:lasgn, :y), s(:lasgn, :z)), s(:to_ary, s(:call, nil, :f))) assert_parse rb, pt end def test_mlhs_keyword rb = "a.!=(true, true)" pt = s(:call, s(:call, nil, :a), :"!=", s(:true), s(:true)) assert_parse rb, pt end def test_mlhs_mid_anonsplat rb = "a, b, c, *, x, y, z = f" pt = s(:masgn, s(:array, s(:lasgn, :a), s(:lasgn, :b), s(:lasgn, :c), s(:splat), s(:lasgn, :x), s(:lasgn, :y), s(:lasgn, :z)), s(:to_ary, s(:call, nil, :f))) assert_parse rb, pt end def test_mlhs_mid_splat rb = "a, b, c, *s, x, y, z = f" pt = s(:masgn, s(:array, s(:lasgn, :a), s(:lasgn, :b), s(:lasgn, :c), s(:splat, s(:lasgn, :s)), s(:lasgn, :x), s(:lasgn, :y), s(:lasgn, :z)), s(:to_ary, s(:call, nil, :f))) assert_parse rb, pt end def test_motherfuckin_leading_dots rb = "a\n.b" pt = s(:call, s(:call, nil, :a), :b) assert_parse rb, pt end def test_motherfuckin_leading_dots2 rb = "a\n..b" assert_parse_error rb, '(string):2 :: parse error on value ".." (tDOT2)' end def test_multiline_hash_declaration pt = s(:call, nil, :f, s(:hash, s(:lit, :state), s(:hash))) assert_parse "f(state: {})", pt assert_parse "f(state: {\n})", pt assert_parse "f(state:\n {\n})", pt end def test_parse_def_special_name rb = "def next; end" pt = s(:defn, :next, s(:args), s(:nil)) assert_parse rb, pt end def test_parse_def_xxx1 rb = "def f(a, *b, c = nil) end" assert_parse_error rb, '(string):1 :: parse error on value "=" (tEQL)' end def test_parse_def_xxx2 rb = "def f(a = nil, *b, c = nil) end" assert_parse_error rb, '(string):1 :: parse error on value "=" (tEQL)' end def test_parse_if_not_canonical rb = "if not var.nil? then 'foo' else 'bar'\nend" pt = s(:if, s(:call, s(:call, s(:call, nil, :var), :nil?), :"!"), s(:str, "foo"), s(:str, "bar")) assert_parse rb, pt end def test_parse_if_not_noncanonical rb = "if not var.nil? then 'foo' else 'bar'\nend" pt = s(:if, s(:call, s(:call, s(:call, nil, :var), :nil?), :"!"), s(:str, "foo"), s(:str, "bar")) processor.canonicalize_conditions = false assert_parse rb, pt end def test_parse_opt_call_args_assocs_comma rb = "1[2=>3,]" pt = s(:call, s(:lit, 1), :[], s(:hash, s(:lit, 2), s(:lit, 3))) assert_parse rb, pt end def test_parse_opt_call_args_lit_comma rb = "1[2,]" pt = s(:call, s(:lit, 1), :[], s(:lit, 2)) assert_parse rb, pt end def test_parse_until_not_canonical rb = "until not var.nil?\n 'foo'\nend" pt = s(:until, s(:call, s(:call, s(:call, nil, :var), :nil?), :"!"), s(:str, "foo"), true) assert_parse rb, pt end def test_parse_until_not_noncanonical rb = "until not var.nil?\n 'foo'\nend" pt = s(:until, s(:call, s(:call, s(:call, nil, :var), :nil?), :"!"), s(:str, "foo"), true) processor.canonicalize_conditions = false assert_parse rb, pt end def test_parse_while_not_canonical rb = "while not var.nil?\n 'foo'\nend" pt = s(:while, s(:call, s(:call, s(:call, nil, :var), :nil?), :"!"), s(:str, "foo"), true) assert_parse rb, pt end def test_parse_while_not_noncanonical rb = "while not var.nil?\n 'foo'\nend" pt = s(:while, s(:call, s(:call, s(:call, nil, :var), :nil?), :"!"), s(:str, "foo"), true) processor.canonicalize_conditions = false assert_parse rb, pt end def test_pipe_semicolon rb = "a.b do | ; c | end" pt = s(:iter, s(:call, s(:call, nil, :a), :b), s(:args, s(:shadow, :c))) assert_parse rb, pt end def test_return_call_assocs rb = "return y(z:1)" pt = s(:return, s(:call, nil, :y, s(:hash, s(:lit, :z), s(:lit, 1)))) assert_parse rb, pt rb = "return y z:1" pt = s(:return, s(:call, nil, :y, s(:hash, s(:lit, :z), s(:lit, 1)))) assert_parse rb, pt rb = "return y(z=>1)" pt = s(:return, s(:call, nil, :y, s(:hash, s(:call, nil, :z), s(:lit, 1)))) assert_parse rb, pt rb = "return y :z=>1" pt = s(:return, s(:call, nil, :y, s(:hash, s(:lit, :z), s(:lit, 1)))) assert_parse rb, pt rb = "return 1, :z => 1" pt = s(:return, s(:array, s(:lit, 1), s(:hash, s(:lit, :z), s(:lit, 1)))) assert_parse rb, pt rb = "return 1, :z => 1, :w => 2" pt = s(:return, s(:array, s(:lit, 1), s(:hash, s(:lit, :z), s(:lit, 1), s(:lit, :w), s(:lit, 2)))) assert_parse rb, pt end def test_stabby_arg_no_paren rb = "->a{}" pt = s(:iter, s(:lambda), s(:args, :a)) assert_parse rb, pt end def test_stabby_arg_opt_splat_arg_block_omfg rb = "->(b, c=1, *d, e, &f){}" pt = s(:iter, s(:lambda), s(:args, :b, s(:lasgn, :c, s(:lit, 1)), :"*d", :e, :"&f")) assert_parse rb, pt end def test_stabby_proc_scope rb = "->(a; b) {}" pt = s(:iter, s(:lambda), s(:args, :a, s(:shadow, :b))) assert_parse rb, pt end def test_symbol_empty rb = ":''" pt = s(:lit, "".to_sym) assert_parse rb, pt end def test_thingy pt = s(:call, s(:call, nil, :f), :call, s(:lit, 42)) rb = "f.(42)" assert_parse rb, pt rb = "f::(42)" assert_parse rb, pt end def test_unary_plus_on_literal rb = "+:a" pt = s(:call, s(:lit, :a), :+@) assert_parse rb, pt end def test_wtf # lambda -> f_larglist lambda_body # f_larglist -> f_args opt_bv_decl # opt_bv_decl # bv_decls # bvar rb = "->(a, b=nil) { p [a, b] }" pt = s(:iter, s(:lambda), s(:args, :a, s(:lasgn, :b, s(:nil))), s(:call, nil, :p, s(:array, s(:lvar, :a), s(:lvar, :b)))) assert_parse rb, pt # rb = "->(a; b) { p [a, b] }" # # assert_parse rb, pt end def test_yield_call_assocs rb = "yield y(z:1)" pt = s(:yield, s(:call, nil, :y, s(:hash, s(:lit, :z), s(:lit, 1)))) assert_parse rb, pt rb = "yield y z:1" pt = s(:yield, s(:call, nil, :y, s(:hash, s(:lit, :z), s(:lit, 1)))) assert_parse rb, pt rb = "yield y(z=>1)" pt = s(:yield, s(:call, nil, :y, s(:hash, s(:call, nil, :z), s(:lit, 1)))) assert_parse rb, pt rb = "yield y :z=>1" pt = s(:yield, s(:call, nil, :y, s(:hash, s(:lit, :z), s(:lit, 1)))) assert_parse rb, pt rb = "yield 1, :z => 1" pt = s(:yield, s(:lit, 1), s(:hash, s(:lit, :z), s(:lit, 1))) assert_parse rb, pt rb = "yield 1, :z => 1, :w => 2" pt = s(:yield, s(:lit, 1), s(:hash, s(:lit, :z), s(:lit, 1), s(:lit, :w), s(:lit, 2))) assert_parse rb, pt end def test_zomg_sometimes_i_hate_this_project rb = <<-RUBY { a: lambda { b ? c() : d }, e: nil, } RUBY pt = s(:hash, s(:lit, :a), s(:iter, s(:call, nil, :lambda), 0, s(:if, s(:call, nil, :b), s(:call, nil, :c), s(:call, nil, :d))), s(:lit, :e), s(:nil)) assert_parse rb, pt end end module TestRubyParserShared20Plus include TestRubyParserShared19Plus def test_args_kw_block rb = "def f(a: 1, &b); end" pt = s(:defn, :f, s(:args, s(:kwarg, :a, s(:lit, 1)), :"&b"), s(:nil)) assert_parse rb, pt end def test_block_arg_kwsplat rb = "a { |**b| }" pt = s(:iter, s(:call, nil, :a), s(:args, :"**b")) assert_parse rb, pt end def test_block_call_dot_op2_brace_block rb = "a.b c() do d end.e do |f| g end" pt = s(:iter, s(:call, s(:iter, s(:call, s(:call, nil, :a), :b, s(:call, nil, :c)), 0, s(:call, nil, :d)), :e), s(:args, :f), s(:call, nil, :g)) assert_parse rb, pt end def test_call_array_block_call rb = "a [ nil, b do end ]" pt = s(:call, nil, :a, s(:array, s(:nil), s(:iter, s(:call, nil, :b), 0))) assert_parse rb, pt end def test_block_call_paren_call_block_call rb = "a (b)\nc.d do end" pt = s(:block, s(:call, nil, :a, s(:call, nil, :b)), s(:iter, s(:call, s(:call, nil, :c), :d), 0)) assert_parse rb, pt end def test_block_call_defn_call_block_call rb = "a def b(c)\n d\n end\n e.f do end" pt = s(:block, s(:call, nil, :a, s(:defn, :b, s(:args, :c), s(:call, nil, :d))), s(:iter, s(:call, s(:call, nil, :e), :f), 0)) assert_parse rb, pt end def test_call_array_lambda_block_call rb = "a [->() {}] do\nend" pt = s(:iter, s(:call, nil, :a, s(:array, s(:iter, s(:lambda), s(:args)))), 0) assert_parse rb, pt end def test_call_begin_call_block_call rb = "a begin\nb.c do end\nend" pt = s(:call, nil, :a, s(:iter, s(:call, s(:call, nil, :b), :c), 0)) assert_parse rb, pt end def test_messy_op_asgn_lineno rb = "a (B::C *= d e)" pt = s(:call, nil, :a, s(:op_asgn, s(:const, :B), s(:call, nil, :d, s(:call, nil, :e)), :C, :*)).line(1) assert_parse rb, pt end def test_str_lit_concat_bad_encodings rb = '"\xE3\xD3\x8B\xE3\x83\xBC\x83\xE3\x83\xE3\x82\xB3\xA3\x82\x99" \ "\xE3\x83\xB3\xE3\x83\x8F\xE3\x82\x9A\xC3\xBD;foo@bar.com"'.b pt = s(:str, "\xE3\xD3\x8B\xE3\x83\xBC\x83\xE3\x83\xE3\x82\xB3\xA3\x82\x99\xE3\x83\xB3\xE3\x83\x8F\xE3\x82\x9A\xC3\xBD;foo@bar.com".b) assert_parse rb, pt sexp = processor.parse rb assert_equal Encoding::ASCII_8BIT, sexp.last.encoding end def test_block_call_dot_op2_cmd_args_do_block rb = "a.b c() do d end.e f do |g| h end" pt = s(:iter, s(:call, s(:iter, s(:call, s(:call, nil, :a), :b, s(:call, nil, :c)), 0, s(:call, nil, :d)), :e, s(:call, nil, :f)), s(:args, :g), s(:call, nil, :h)) assert_parse rb, pt end def test_block_kwarg_lvar rb = "bl { |kw: :val| kw }" pt = s(:iter, s(:call, nil, :bl), s(:args, s(:kwarg, :kw, s(:lit, :val))), s(:lvar, :kw)) assert_parse rb, pt end def test_block_kwarg_lvar_multiple rb = "bl { |kw: :val, kw2: :val2 | kw }" pt = s(:iter, s(:call, nil, :bl), s(:args, s(:kwarg, :kw, s(:lit, :val)), s(:kwarg, :kw2, s(:lit, :val2))), s(:lvar, :kw)) assert_parse rb, pt end def test_bug_249 rb = "mount (Class.new do\ndef initialize\nend\n end).new, :at => 'endpoint'" pt = s(:call, nil, :mount, s(:call, s(:iter, s(:call, s(:const, :Class), :new), 0, s(:defn, :initialize, s(:args), s(:nil))), :new), s(:hash, s(:lit, :at), s(:str, "endpoint"))) assert_parse rb, pt end def test_call_arg_assoc_kwsplat rb = "f(1, kw: 2, **3)" pt = s(:call, nil, :f, s(:lit, 1), s(:hash, s(:lit, :kw), s(:lit, 2), s(:kwsplat, s(:lit, 3)))) assert_parse rb, pt end def test_call_arg_kwsplat rb = "a(b, **1)" pt = s(:call, nil, :a, s(:call, nil, :b), s(:hash, s(:kwsplat, s(:lit, 1)))) assert_parse rb, pt end def test_call_kwsplat rb = "a(**1)" pt = s(:call, nil, :a, s(:hash, s(:kwsplat, s(:lit, 1)))) assert_parse rb, pt end def test_defn_kwarg_env rb = "def test(**testing) test_splat(**testing) end" pt = s(:defn, :test, s(:args, :"**testing"), s(:call, nil, :test_splat, s(:hash, s(:kwsplat, s(:lvar, :testing))))) assert_parse rb, pt end def test_defn_kwarg_kwarg rb = "def f(a, b: 1, c: 2) end" pt = s(:defn, :f, s(:args, :a, s(:kwarg, :b, s(:lit, 1)), s(:kwarg, :c, s(:lit, 2))), s(:nil)) assert_parse rb, pt end def test_defn_kwarg_kwsplat rb = "def a(b: 1, **c) end" pt = s(:defn, :a, s(:args, s(:kwarg, :b, s(:lit, 1)), :"**c"), s(:nil)) assert_parse rb, pt end def test_defn_kwarg_kwsplat_anon rb = "def a(b: 1, **) end" pt = s(:defn, :a, s(:args, s(:kwarg, :b, s(:lit, 1)), :"**"), s(:nil)) assert_parse rb, pt end def test_defn_kwarg_lvar rb = "def fun(kw: :val); kw; end" pt = s(:defn, :fun, s(:args, s(:kwarg, :kw, s(:lit, :val))), s(:lvar, :kw)) assert_parse rb, pt end def test_defn_kwarg_no_parens rb = "def f a: 1\nend" pt = s(:defn, :f, s(:args, s(:kwarg, :a, s(:lit, 1))), s(:nil)) assert_parse rb, pt end def test_defn_kwarg_val rb = "def f(a, b:1) end" pt = s(:defn, :f, s(:args, :a, s(:kwarg, :b, s(:lit, 1))), s(:nil)) assert_parse rb, pt end def test_defn_powarg rb = "def f(**opts) end" pt = s(:defn, :f, s(:args, :"**opts"), s(:nil)) assert_parse rb, pt end def test_defs_kwarg rb = "def self.a b: 1\nend" pt = s(:defs, s(:self), :a, s(:args, s(:kwarg, :b, s(:lit, 1))), s(:nil)) assert_parse rb, pt end def test_dstr_lex_state rb = '"#{p:a}"' pt = s(:dstr, "", s(:evstr, s(:call, nil, :p, s(:lit, :a)))) assert_parse rb, pt end def test_interpolated_symbol_array_line_breaks rb = "%I(\na\nb\n)\n1" pt = s(:block, s(:array, s(:lit, :a).line(2), s(:lit, :b).line(3)).line(1), s(:lit, 1).line(5)) assert_parse rb, pt end def test_iter_array_curly skip if processor.class.version >= 25 rb = "f :a, [:b] { |c, d| }" # yes, this is bad code... that's their problem pt = s(:iter, s(:call, nil, :f, s(:lit, :a), s(:array, s(:lit, :b))), s(:args, :c, :d)) assert_parse rb, pt end def test_iter_kwarg rb = "a { |b: 1| }" pt = s(:iter, s(:call, nil, :a), s(:args, s(:kwarg, :b, s(:lit, 1)))) assert_parse rb, pt end def test_iter_kwarg_kwsplat rb = "a { |b: 1, **c| }" pt = s(:iter, s(:call, nil, :a), s(:args, s(:kwarg, :b, s(:lit, 1)), :"**c")) assert_parse rb, pt end def test_non_interpolated_symbol_array_line_breaks rb = "%i(\na\nb\n)\n1" pt = s(:block, s(:array, s(:lit, :a).line(2), s(:lit, :b).line(3)).line(1), s(:lit, 1).line(5)) assert_parse rb, pt end def test_qsymbols rb = "%I(a b c)" pt = s(:array, s(:lit, :a), s(:lit, :b), s(:lit, :c)) assert_parse rb, pt end def test_qsymbols_empty rb = "%I()" pt = s(:array) assert_parse rb, pt end def test_qsymbols_empty_space rb = "%I( )" pt = s(:array) assert_parse rb, pt end def test_qsymbols_interp rb = '%I(a b#{1+1} c)' pt = s(:array, s(:lit, :a), s(:dsym, "b", s(:evstr, s(:call, s(:lit, 1), :+, s(:lit, 1)))), s(:lit, :c)) assert_parse rb, pt end def test_stabby_block_iter_call rb = "x -> () do\na.b do\nend\nend" pt = s(:call, nil, :x, s(:iter, s(:lambda), s(:args), s(:iter, s(:call, s(:call, nil, :a), :b), 0))) assert_parse rb, pt end def test_stabby_block_iter_call_no_target_with_arg rb = "x -> () do\na(1) do\nend\nend" pt = s(:call, nil, :x, s(:iter, s(:lambda), s(:args), s(:iter, s(:call, nil, :a, s(:lit, 1)), 0))) assert_parse rb, pt end def test_symbols rb = "%i(a b c)" pt = s(:array, s(:lit, :a), s(:lit, :b), s(:lit, :c)) assert_parse rb, pt end def test_symbols_empty rb = "%i()" pt = s(:array) assert_parse rb, pt end def test_symbols_empty_space rb = "%i( )" pt = s(:array) assert_parse rb, pt end def test_symbols_interp rb = '%i(a b#{1+1} c)' pt = s(:array, s(:lit, :a), s(:lit, :'b#{1+1}'), s(:lit, :c)) assert_parse rb, pt end end module TestRubyParserShared21Plus include TestRubyParserShared20Plus def test_block_kw rb = "blah { |k:42| }" pt = s(:iter, s(:call, nil, :blah), s(:args, s(:kwarg, :k, s(:lit, 42)))) assert_parse rb, pt rb = "blah { |k:42| }" assert_parse rb, pt end def test_block_kw__required rb = "blah do |k:| end" pt = s(:iter, s(:call, nil, :blah), s(:args, s(:kwarg, :k))) assert_parse rb, pt rb = "blah do |k:| end" assert_parse rb, pt end def test_bug162__21plus rb = %q(<, s(:lit, 1)).line(1) assert_parse rb, pt end def test_safe_call_rhs_newline rb = "c = a&.b\n" pt = s(:lasgn, :c, s(:safe_call, s(:call, nil, :a), :b)) assert_parse rb, pt end def test_safe_calls rb = "a&.b&.c(1)" pt = s(:safe_call, s(:safe_call, s(:call, nil, :a), :b), :c, s(:lit, 1)) assert_parse rb, pt end def test_safe_op_asgn rb = "a&.b += x 1\n" pt = s(:safe_op_asgn, s(:call, nil, :a), s(:call, nil, :x, s(:lit, 1)), :b, :+).line(1) assert_parse rb, pt end def test_safe_op_asgn2 rb = "a&.b ||=\nx;" pt = s(:safe_op_asgn2, s(:call, nil, :a), :b=, :"||", s(:call, nil, :x)).line(1) assert_parse rb, pt end def test_slashy_newlines_within_string rb = %(puts "hello\\ my\\ dear\\ friend" a + b ) pt = s(:block, s(:call, nil, :puts, s(:str, "hello my dear friend").line(1)).line(1), s(:call, s(:call, nil, :a).line(6), :+, s(:call, nil, :b).line(6)).line(6) ).line(1) assert_parse rb, pt end end module TestRubyParserShared24Plus include TestRubyParserShared23Plus def test_lasgn_call_nobracket_rescue_arg rb = "a = b 1 rescue 2" pt = s(:lasgn, :a, s(:rescue, s(:call, nil, :b, s(:lit, 1)), s(:resbody, s(:array), s(:lit, 2)))) assert_parse rb, pt end end module TestRubyParserShared25Plus include TestRubyParserShared24Plus # ...version specific tests to go here... end module TestRubyParserShared26Plus include TestRubyParserShared25Plus def test_dot2_nil__26 rb = "a.." pt = s(:dot2, s(:call, nil, :a), nil) assert_parse rb, pt end def test_dot3_nil__26 rb = "a..." pt = s(:dot3, s(:call, nil, :a), nil) assert_parse rb, pt end def test_symbol_list rb = '%I[#{a} #{b}]' pt = s(:array, s(:dsym, "", s(:evstr, s(:call, nil, :a)).line(1)).line(1), s(:dsym, "", s(:evstr, s(:call, nil, :b)).line(1)).line(1)).line 1 assert_parse rb, pt end end module TestRubyParserShared27Plus include TestRubyParserShared26Plus end class TestRubyParser < Minitest::Test def test_cls_version assert_equal 23, RubyParser::V23.version assert_equal 24, RubyParser::V24.version assert_equal 24, Ruby24Parser.version refute RubyParser::Parser.version end def test_parse processor = RubyParser.new rb = "a.()" pt = s(:call, s(:call, nil, :a), :call) assert_equal pt, processor.parse(rb) # bad syntax e = assert_raises Racc::ParseError do capture_io do processor.parse "a.(" end end assert_includes e.message, 'parse error on value false ($end)' end def test_parse_error_from_first processor = RubyParser.new e = assert_raises Racc::ParseError do capture_io do processor.parse "a -> () {" end end # This is a 2.x error, will fail on 1.8/1.9. assert_includes e.message, 'parse error on value false ($end)' end end class RubyParserTestCase < ParseTreeTestCase attr_accessor :result, :processor make_my_diffs_pretty! def self.previous key "Ruby" end def self.generate_test klass, node, data, input_name, output_name return if node.to_s =~ /bmethod|dmethod/ return if Array === data["Ruby"] output_name = "ParseTree" super end def assert_parse rb, pt self.result = processor.parse rb assert_equal pt, result end def assert_parse_error rb, emsg e = nil assert_silent do e = assert_raises Racc::ParseError do processor.parse rb end end if Regexp === emsg then assert_match emsg, e.message else assert_equal emsg, e.message end end def assert_parse_line rb, pt, line old_env = ENV["VERBOSE"] ENV["VERBOSE"] = "1" assert_parse rb, pt assert_equal line, result.line, "call should have line number" ensure ENV["VERBOSE"] = old_env end def assert_syntax_error rb, emsg e = nil assert_silent do e = assert_raises RubyParser::SyntaxError do processor.parse rb end end assert_equal emsg, e.message end def refute_parse rb self.result = processor.parse rb assert_nil result end end class TestRubyParserV20 < RubyParserTestCase include TestRubyParserShared20Plus def setup super self.processor = RubyParser::V20.new end def test_bug162__20 skip "not ready for this yet" # Ignore everything after \r in heredoc marker in <= 2.0 #162 rb = %q(< { }" => s(:iter, s(:lambda), 0), "lambda { }" => s(:iter, s(:call, nil, :lambda), 0), "proc { }" => s(:iter, s(:call, nil, :proc), 0), "Proc.new { }" => s(:iter, s(:call, s(:const, :Proc), :new), 0), "-> () { }" => s(:iter, s(:lambda), s(:args)), "lambda { || }" => s(:iter, s(:call, nil, :lambda), s(:args)), "proc { || }" => s(:iter, s(:call, nil, :proc), s(:args)), "Proc.new { || }" => s(:iter, s(:call, s(:const, :Proc), :new), s(:args)), }.each do |input, expected| next if v == 18 and input =~ /->/ next if v == 19 and input =~ /-> \(\)/ it "parses '#{input}'" do assert_equal expected, parser.parse(input) end input = input.sub(/\{/, "do").sub(/\}/, "end") it "parses '#{input}'" do assert_equal expected, parser.parse(input) end end end end