ruby2ruby-2.5.0/0000755000004100000410000000000014323354702013516 5ustar www-datawww-dataruby2ruby-2.5.0/test/0000755000004100000410000000000014323354702014475 5ustar www-datawww-dataruby2ruby-2.5.0/test/test_ruby2ruby.rb0000444000004100000410000007434014323354702020034 0ustar www-datawww-data#!/usr/local/bin/ruby -w $TESTING = true $: << "lib" require "minitest/autorun" require "ruby2ruby" require "pt_testcase" require "fileutils" require "tmpdir" require "ruby_parser" if ENV["CHECK_SEXPS"] class R2RTestCase < ParseTreeTestCase def self.previous key "ParseTree" end def self.generate_test klass, node, data, input_name, _output_name output_name = data.key?("Ruby2Ruby") ? "Ruby2Ruby" : "Ruby" klass.class_eval <<-EOM def test_#{node} pt = #{data[input_name].inspect} rb = #{data[output_name].inspect} refute_nil pt, \"ParseTree for #{node} undefined\" refute_nil rb, \"Ruby for #{node} undefined\" assert_equal rb, @processor.process(pt) end EOM end end start = __LINE__ class TestRuby2Ruby < R2RTestCase def setup super @check_sexp = ENV["CHECK_SEXPS"] @processor = Ruby2Ruby.new end def do_not_check_sexp! @check_sexp = false end def test_util_dthing_dregx inn = util_thingy(:dregx) out = '/a"b#{(1 + 1)}c"d\/e/' exp = /a"b2c"d\/e/ assert_equal exp, eval(out) assert_equal out[1..-2], @processor.util_dthing(:dregx, inn) end def test_util_dthing_dstr inn = util_thingy(:dstr) out = '"a\"b#{(1 + 1)}c\"d/e"' exp = 'a"b2c"d/e' assert_equal exp, eval(out) assert_equal out[1..-2], @processor.util_dthing(:dstr, inn) end def test_util_dthing_dregx_bug? inn = s(:dregx, '[\/\"]', s(:evstr, s(:lit, 42))) out = '/[\/\"]#{42}/' exp = /[\/\"]42/ assert_equal out[1..-2], @processor.util_dthing(:dregx, inn) assert_equal exp, eval(out) end def test_hash_parens_str inn = s(:hash, s(:lit, :k), s(:str, "banana")) out = '{ :k => "banana" }' assert_parse inn, out end def test_hash_parens_lit inn = s(:hash, s(:lit, :k), s(:lit, 0.07)) out = "{ :k => 0.07 }" assert_parse inn, out end def test_hash_parens_bool inn = s(:hash, s(:lit, :k), s(:true)) out = "{ :k => true }" assert_parse inn, out end def test_hash_parens_nil inn = s(:hash, s(:lit, :k), s(:nil)) out = "{ :k => nil }" assert_parse inn, out end def test_hash_parens_lvar inn = s(:hash, s(:lit, :k), s(:lvar, :a)) out = "{ :k => a }" assert_parse inn, out end def test_hash_parens_call inn = s(:hash, s(:lit, :k), s(:call, nil, :foo, s(:lit, :bar))) out = "{ :k => foo(:bar) }" assert_parse inn, out end def test_hash_parens_iter iter = s(:iter, s(:call, nil, :foo), 0, s(:str, "bar")) inn = s(:hash, s(:lit, :k), iter) out = '{ :k => (foo { "bar" }) }' assert_parse inn, out end def test_hash_shorthand inn = s(:hash, s(:lit, :k), nil) out = '{ k: }' assert_parse inn, out end def test_hash_shorthand_invalid_key_type do_not_check_sexp! inn = s(:hash, s(:str, 'k'), nil) out = '{ k: }' assert_raises do assert_parse inn, out end end def test_and_alias inn = s(:and, s(:true), s(:alias, s(:lit, :a), s(:lit, :b))) out = "true and (alias :a :b)" assert_parse inn, out end def test_attr_reader_diff inn = s(:defn, :same, s(:args), s(:ivar, :@diff)) out = "def same\n @diff\nend" assert_parse inn, out end def test_attr_reader_same do_not_check_sexp! inn = s(:defn, :same, s(:args), s(:ivar, :@same)) out = "attr_reader :same" assert_parse inn, out end def test_attr_reader_double inn = s(:defn, :same, s(:args), s(:ivar, :@same), s(:ivar, :@diff)) out = "def same\n @same\n @diff\nend" assert_parse inn, out end def test_attr_reader_same_name_diff_body do_not_check_sexp! inn = s(:defn, :same, s(:args), s(:not, s(:ivar, :@same))) out = "def same\n (not @same)\nend" assert_parse inn, out end def test_attr_writer_diff inn = s(:defn, :same=, s(:args, :o), s(:iasgn, :@diff, s(:lvar, :o))) out = "def same=(o)\n @diff = o\nend" assert_parse inn, out end def assert_str exp, src assert_equal s(:str, exp), RubyParser.new.process(src) end def assert_dstr exp, int, src assert_equal s(:dstr, exp, s(:evstr, int).compact), RubyParser.new.process(src) end def assert_r2r exp, sexp assert_equal exp, Ruby2Ruby.new.process(sexp) end def assert_rt src, exp = src.dup assert_equal exp, Ruby2Ruby.new.process(RubyParser.new.parse(src)) end def test_bug_033 # gentle reminder to keep some sanity # # Use %q("...") for raw input strings # Use %q(...) for raw output to avoid double-\'s # Use %(...) for output strings # # don't use '...' at all # only use "..." within sexps # "\t" assert_str %(\t), %q("\t") assert_r2r %q("\\t"), s(:str, "\t") assert_rt %q("\t") # "\\t" assert_str %(\t), %q("\\t") assert_r2r %q("\\t"), s(:str, "\t") assert_rt %q("\\t") # "\\\\t" assert_str %(\\t), %q("\\\\t") assert_r2r %q("\\\\t"), s(:str, "\\t") assert_rt %q("\\\\t") # "\t#{}" assert_dstr %(\t), nil, %q("\t#{}") assert_r2r %q("\t#{}"), s(:dstr, "\t", s(:evstr)) assert_rt %q("\t#{}") # "\\t#{}" assert_dstr %(\t), nil, %q("\\t#{}") assert_r2r %q("\\t#{}"), s(:dstr, "\t", s(:evstr)) assert_rt %q("\\t#{}") # "\\\\t#{}" assert_dstr %(\\t), nil, %q("\\\\t#{}") assert_r2r %q("\\\\t#{}"), s(:dstr, "\\t", s(:evstr)) assert_rt %q("\\\\t#{}") end def test_bug_043 inn = s(:defn, :check, s(:args), s(:rescue, s(:call, nil, :foo), s(:resbody, s(:array), s(:call, nil, :bar), s(:call, nil, :bar))), s(:call, nil, :bar), s(:if, s(:call, nil, :foo), s(:return, s(:call, nil, :bar)), s(:call, nil, :bar))) out = "def check\n begin\n foo\n rescue\n bar\n bar\n end\n bar\n if foo then\n return bar\n else\n bar\n end\nend" assert_parse inn, out end def test_bug_044 inn = s(:if, s(:call, s(:match3, s(:lit, /a/), s(:call, nil, :foo)), :or, s(:call, nil, :bar)), s(:call, nil, :puts, s(:call, nil, :bar)), nil) out = "puts(bar) if (foo =~ /a/).or(bar)" assert_parse inn, out end def test_bug_045 # return foo.baaaaaaar ? ::B.newsss(true) : ::B.newadsfasdfasdfasdfasdsssss(false) inn = s(:return, s(:if, s(:call, s(:call, nil, :foo), :baaaaaaar), s(:call, s(:colon3, :B), :newsss, s(:true)), s(:call, s(:colon3, :B), :newadsfasdfasdfasdfasdsssss, s(:false)))) out = "return (if foo.baaaaaaar then\n ::B.newsss(true)\nelse\n ::B.newadsfasdfasdfasdfasdsssss(false)\nend)" assert_parse inn, out end def assert_masgn exp, *args inn = s(:iter, s(:call, nil, :a), s(:args, *args)) out = "a { |#{exp}| }" assert_parse inn, out end def test_iter_masgn_double_bug assert_masgn("b", :b) assert_masgn("b, c", :b, :c) assert_masgn("(b, c)", s(:masgn, :b, :c)) assert_masgn("(b, c), d", s(:masgn, :b, :c), :d) assert_masgn("b, (c, d), e", :b, s(:masgn, :c, :d), :e) assert_masgn("(b, (c, d), e), f", s(:masgn, :b, s(:masgn, :c, :d), :e), :f) assert_masgn("(((b, c), d, e), f), g", s(:masgn, s(:masgn, s(:masgn, :b, :c), :d, :e), :f), :g) end def test_attr_writer_double inn = s(:defn, :same=, s(:args, :o), s(:iasgn, :@same, s(:lvar, :o)), s(:iasgn, :@diff, s(:lvar, :o))) out = "def same=(o)\n @same = o\n @diff = o\nend" assert_parse inn, out end def test_attr_writer_same_name_diff_body inn = s(:defn, :same=, s(:args, :o), s(:iasgn, :@same, s(:lit, 42))) out = "def same=(o)\n @same = 42\nend" assert_parse inn, out end def test_attr_writer_same do_not_check_sexp! inn = s(:defn, :same=, s(:args, :o), s(:iasgn, :@same, s(:lvar, :o))) out = "attr_writer :same" assert_parse inn, out end def test_dregx_slash do_not_check_sexp! inn = util_thingy(:dregx) out = '/a"b#{(1 + 1)}c"d\/e/' assert_parse inn, out, /a"b2c"d\/e/ end def test_dstr_quote inn = util_thingy(:dstr) out = '"a\"b#{(1 + 1)}c\"d/e"' assert_parse inn, out, 'a"b2c"d/e' end def test_dsym_quote inn = util_thingy(:dsym) out = ':"a\"b#{(1 + 1)}c\"d/e"' assert_parse inn, out, :'a"b2c"d/e' end def test_lit_regexp_slash do_not_check_sexp! # dunno why on this one assert_parse s(:lit, /blah\/blah/), '/blah\/blah/', /blah\/blah/ end def test_call_kwsplat inn = s(:call, nil, :test_splat, s(:hash, s(:kwsplat, s(:call, nil, :testing)))) out = "test_splat(**testing)" assert_parse inn, out end def test_call_arg_assoc_kwsplat inn = s(:call, nil, :f, s(:lit, 1), s(:hash, s(:lit, :kw), s(:lit, 2), s(:kwsplat, s(:lit, 3)))) out = "f(1, :kw => 2, **3)" assert_parse inn, out end def test_call_kwsplat_x inn = s(:call, nil, :a, s(:hash, s(:kwsplat, s(:lit, 1)))) out = "a(**1)" assert_parse inn, out end def test_defn_kwargs2 inn = s(:defn, :initialize, s(:args, :arg, s(:kwarg, :kw1, s(:nil)), s(:kwarg, :kw2, s(:nil)), :"**args"), s(:nil)) out = "def initialize(arg, kw1: nil, kw2: nil, **args)\n # do nothing\nend" assert_parse inn, out end def test_call_self_index assert_parse s(:call, s(:self), :[], s(:lit, 42)), "self[42]" end def test_call_self_index_equals assert_parse(s(:attrasgn, s(:self), :[]=, s(:lit, 42), s(:lit, 24)), "self[42] = 24") assert_parse(s(:attrasgn, s(:self), :[]=, s(:lit, 1), s(:lit, 2), s(:lit, 3)), "self[1, 2] = 3") end def test_call_arglist_hash_first_last inn = s(:call, nil, :method, s(:hash, s(:lit, :a), s(:lit, 1)), s(:lvar, :b), s(:hash, s(:lit, :c), s(:lit, 1))) out = "method({ :a => 1 }, b, :c => 1)" assert_parse inn, out end def test_call_arglist_if inn = s(:call, s(:lvar, :a), :+, s(:if, s(:lvar, :b), s(:lvar, :c), s(:lvar, :d))) out = "(a + (b ? (c) : (d)))" assert_parse inn, out end def test_defn_kwsplat inn = s(:defn, :test, s(:args, :"**testing"), s(:nil)) out = "def test(**testing)\n # do nothing\nend" assert_parse inn, out end def test_defn_rescue_return inn = s(:defn, :blah, s(:args), s(:rescue, s(:lasgn, :a, s(:lit, 1)), s(:resbody, s(:array), s(:return, s(:str, "a"))))) out = "def blah\n a = 1\nrescue\n return \"a\"\nend" assert_parse inn, out end def test_forward_args__defn inn = s(:defn, :x, s(:args, :a, s(:forward_args)), s(:nil)) out = "def x(a, ...)\n # do nothing\nend" assert_parse inn, out end def test_forward_args__call inn = s(:call, nil, :y, s(:forward_args)) out = "y(...)" assert_parse inn, out end def test_shadow_block_args inn = s(:iter, s(:call, nil, :a), s(:args, s(:shadow, :b, :c))) out = 'a { |; b, c| }' assert_parse inn, out end def test_masgn_block_arg inn = s(:iter, s(:call, s(:nil), :x), s(:args, s(:masgn, :a, :b)), s(:dstr, "", s(:evstr, s(:lvar, :a)), s(:str, "="), s(:evstr, s(:lvar, :b)))) out = 'nil.x { |(a, b)| "#{a}=#{b}" }' assert_parse inn, out end def test_single_nested_masgn_block_arg inn = s(:iter, s(:call, nil, :a), s(:args, s(:masgn, s(:masgn, s(:masgn, :b))))) out = "a { |(((b)))| }" assert_parse inn, out end def test_multiple_nested_masgn_array inn = s(:masgn, s(:array, s(:masgn, s(:array, s(:lasgn, :a), s(:lasgn, :b))), s(:lasgn, :c)), s(:to_ary, s(:call, nil, :fn))) out = "(a, b), c = fn" assert_parse inn, out end def test_masgn_wtf inn = s(:block, s(:masgn, s(:array, s(:lasgn, :k), s(:lasgn, :v)), s(:splat, s(:call, s(:call, nil, :line), :split, s(:lit, /\=/), s(:lit, 2)))), s(:attrasgn, s(:self), :[]=, s(:lvar, :k), s(:call, s(:lvar, :v), :strip))) out = "k, v = *line.split(/\\=/, 2)\nself[k] = v.strip\n" assert_parse inn, out end def test_masgn_splat_wtf inn = s(:masgn, s(:array, s(:lasgn, :k), s(:lasgn, :v)), s(:splat, s(:call, s(:call, nil, :line), :split, s(:lit, /\=/), s(:lit, 2)))) out = 'k, v = *line.split(/\\=/, 2)' assert_parse inn, out end def test_match3_asgn inn = s(:match3, s(:lit, //), s(:lasgn, :y, s(:call, nil, :x))) out = "(y = x) =~ //" # "y = x =~ //", which matches on x and assigns to y (not what sexp says). assert_parse inn, out end def test_preexe inn = s(:iter, s(:preexe), 0, s(:block, s(:lit, 1), s(:lit, 2), s(:lit, 3))) out = "BEGIN {\n 1\n 2\n 3\n}" assert_parse inn, out end def test_safe_attrasgn inn = s(:safe_attrasgn, s(:call, nil, :x), :y=, s(:lit, 1)) out = "x&.y = 1" assert_parse inn, out end def test_safe_call inn = s(:safe_call, s(:safe_call, s(:call, nil, :x), :y), :z, s(:lit, 1)) out = "x&.y&.z(1)" assert_parse inn, out end def test_safe_call_binary inn = s(:safe_call, s(:call, nil, :x), :>, s(:lit, 1)) out = "x&.>(1)" assert_parse inn, out end def test_safe_op_asgn do_not_check_sexp! # TODO: fix! inn = s(:safe_op_asgn, s(:call, nil, :x), s(:call, nil, :z, s(:lit, 1)), :y, :+) out = "x&.y += z(1)" assert_parse inn, out end def test_safe_op_asgn2 inn = s(:safe_op_asgn2, s(:call, nil, :x), :y=, :"||", s(:lit, 1)) out = "x&.y ||= 1" assert_parse inn, out end def test_splat_call inn = s(:call, nil, :x, s(:splat, s(:call, s(:call, nil, :line), :split, s(:lit, /\=/), s(:lit, 2)))) out = 'x(*line.split(/\=/, 2))' assert_parse inn, out end def test_resbody_short_with_defn_multiple inn = s(:defn, :foo, s(:args), s(:rescue, s(:lasgn, :a, s(:lit, 1)), s(:resbody, s(:array), s(:call, nil, :log), s(:call, nil, :raise)))) out = "def foo\n a = 1\nrescue\n log\n raise\nend" assert_parse inn, out end def test_regexp_options inn = s(:match3, s(:dregx, "abc", s(:evstr, s(:call, nil, :x)), s(:str, "def"), 4), s(:str, "a")) out = '"a" =~ /abc#{x}def/m' assert_parse inn, out end def test_resbody_short_with_rescue_args inn = s(:rescue, s(:call, nil, :blah), s(:resbody, s(:array, s(:const, :A), s(:const, :B)), s(:array))) out = "begin\n blah\nrescue A, B\n []\nend" assert_parse inn, out end def test_call_binary_call_with_hash_arg # args << {:key => 24} if 42 inn = s(:if, s(:lit, 42), s(:call, s(:call, nil, :args), :<<, s(:hash, s(:lit, :key), s(:lit, 24))), nil) out = "(args << { :key => 24 }) if 42" assert_parse inn, out end def test_binary_operators # (1 > 2) Ruby2Ruby::BINARY.each do |op| inn = s(:call, s(:lit, 1), op, s(:lit, 2)) out = "(1 #{op} 2)" assert_parse inn, out end end def test_binary_operators_with_multiple_arguments Ruby2Ruby::BINARY.each do |op| inn = s(:call, s(:lvar, :a), op, s(:lit, 2), s(:lit, 3)) out = "a.#{op}(2, 3)" assert_parse inn, out end end def test_call_empty_hash inn = s(:call, nil, :foo, s(:hash)) out = "foo({})" assert_parse inn, out end def test_if_empty inn = s(:if, s(:call, nil, :x), nil, nil) out = "if x then\n # do nothing\nend" assert_parse inn, out end def test_case_in_normal_01 assert_case_in "var", s(:lasgn, :var) end def test_case_in_normal_02 assert_case_in "^var", s(:lvar, :var) end def test_case_in_normal_04 assert_case_in "A if true", s(:if, s(:true), s(:const, :A), nil) end def test_case_in_normal_05 assert_case_in "A unless true", s(:if, s(:true), nil, s(:const, :A)) end def test_case_in_normal_06 assert_case_in "A::B", s(:const, s(:colon2, s(:const, :A), :B)) end def test_case_in_normal_07 assert_case_in "A | B", s(:or, s(:const, :A), s(:const, :B)), "(A | B)" # TODO: assert_case_in "(A or B)", s(:const, s(:colon2, s(:colon3, :A), :B)) end def test_case_in_normal_08 assert_case_in "::A::B", s(:const, s(:colon2, s(:colon3, :A), :B)) end def test_case_in_normal_09 assert_case_in "A", s(:const, :A) end def test_case_in__array_pat_00 assert_case_in "Object[]", s(:array_pat, s(:const, :Object)) end def test_case_in__array_pat_01 assert_case_in "[]", s(:array_pat) end def test_case_in__array_pat_02 assert_case_in "[*, ::NilClass]", s(:array_pat, nil, :*, s(:colon3, :NilClass)) end def test_case_in__array_pat_03 assert_case_in "[*, :b, :c]", s(:array_pat, nil, :*, s(:lit, :b), s(:lit, :c)) end def test_case_in__array_pat_04 assert_case_in "[[:b, ^c], [:d, ^e]]", s(:array_pat, nil, s(:array_pat, nil, s(:lit, :b), s(:lvar, :c)), s(:array_pat, nil, s(:lit, :d), s(:lvar, :e))) end def test_case_in__array_pat_06 assert_case_in "[A, *, B]", s(:array_pat, nil, s(:const, :A), :*, s(:const, :B)) end def test_case_in__array_pat_07 assert_case_in "[-> (b) { true }, ^c]", s(:array_pat, nil, s(:iter, s(:lambda), s(:args, :b), s(:true)), s(:lvar, :c)) end def test_case_in__array_pat_09 assert_case_in "[:a, ^b, ^c, [:d, *e, nil]]", s(:array_pat, nil, s(:lit, :a), s(:lvar, :b), s(:lvar, :c), s(:array_pat, nil, s(:lit, :d), :"*e", s(:nil))) end def test_case_in__array_pat_14 assert_case_in "A[*list]", s(:array_pat, s(:const, :A), :"*list") end def test_case_in__array_pat_15 assert_case_in "B[C => d]", s(:array_pat, s(:const, :B), s(:lasgn, :d, s(:const, :C))) end def test_case_in__array_pat_16 assert_case_in "B[^c]", s(:array_pat, s(:const, :B), s(:lvar, :c)) end def test_case_in__array_pat_19 assert_case_in "[^@a, ^$b, ^@@c]", s(:array_pat, nil, s(:ivar, :@a), s(:gvar, :$b), s(:cvar, :@@c)) # HACK: really not sure about this one end def test_case_in__find_pat_1 assert_case_in "[*a, :+, *b]", s(:find_pat, nil, :"*a", s(:lit, :+), :"*b") end def test_case_in__find_pat_2 assert_case_in "[*, :b, ^c, *]", s(:find_pat, nil, :*, s(:lit, :b), s(:lvar, :c), :*) end def test_case_in__find_pat_3 assert_case_in("Array(*b, n, { a: }, m, *a)", s(:find_pat, s(:const, :Array), :"*b", s(:lasgn, :n), s(:hash_pat, nil, s(:lit, :a), nil), s(:lasgn, :m), :"*a"), "Array[*b, n, { a: }, m, *a]") end def test_case_in__find_pat_4 assert_case_in("*b, n, { a: }, m, *a", s(:find_pat, nil, :"*b", s(:lasgn, :n), s(:hash_pat, nil, s(:lit, :a), nil), s(:lasgn, :m), :"*a"), "[*b, n, { a: }, m, *a]") end def test_case_in__find_pat_5 assert_case_in("Array(*lhs, ^b, *rhs)", s(:find_pat, s(:const, :Array), :"*lhs", s(:lvar, :b), :"*rhs"), "Array[*lhs, ^b, *rhs]") end def test_case_in__find_pat_6 assert_case_in("Array[*lhs, b, *rhs]", s(:find_pat, s(:const, :Array), :"*lhs", s(:lasgn, :b), :"*rhs")) end def test_case_in__find_pat_7 assert_case_in("Array[*lhs, :b, *rhs]", s(:find_pat, s(:const, :Array), :"*lhs", s(:lit, :b), :"*rhs")) end def test_case_in_10 assert_case_in("[nil, nil, nil]", s(:array_pat, nil, s(:nil), s(:nil), s(:nil))) end def test_case_in_32_2 assert_case_in "1..3", s(:dot2, s(:lit, 1), s(:lit, 3)), "(1..3)" end def test_case_in_32_3 assert_case_in "1...3", s(:dot3, s(:lit, 1), s(:lit, 3)), "(1...3)" end def test_case_in_36 pt = s(:array_pat, nil, s(:lit, :a), s(:lasgn, :b), s(:lvar, :d), :"*e", s(:nil)) assert_case_in "[:a, b, ^d, *e, nil]", pt end def test_case_in_42 rb = "case :a\nin [:b, *_] then\n nil\nend" pt = s(:case, s(:lit, :a), s(:in, s(:array_pat, nil, s(:lit, :b), :"*_", ), s(:nil)), nil) assert_parse pt, rb assert_variant pt, "case :a\nin :b, *_ then\n nil\nend" end def test_case_in_42_2 rb = "case :a\nin A[*list] then\n nil\nend" pt = s(:case, s(:lit, :a), s(:in, s(:array_pat, s(:const, :A), :"*list"), s(:nil)), nil) assert_parse pt, rb assert_variant pt, "case :a\nin A(*list) then\n nil\nend" end def test_case_in_67 rb = "case :a\nin 1.. then nil\nend" rb = "case :a\nin (1..) then\n nil\nend" pt = s(:case, s(:lit, :a), s(:in, s(:dot2, s(:lit, 1), nil), s(:nil)), nil) assert_parse pt, rb end def test_case_in_76 assert_case_in "`echo hi`", s(:xstr, "echo hi") end def test_case_in_77 assert_case_in "/regexp/", s(:lit, /regexp/) end def test_case_in_79 assert_case_in "%w[a b]", s(:array_pat, nil, s(:str, "a"), s(:str, "b")), "[\"a\", \"b\"]" end def test_case_in_80 assert_case_in "%I[a b]", s(:array_pat, nil, s(:lit, :a), s(:lit, :b)), "[:a, :b]" end def test_case_in_83 pt = s(:array_pat, nil, s(:iter, s(:lambda), s(:args, :b), s(:true)), s(:lasgn, :c)) assert_case_in "[-> (b) { true }, c]", pt end def test_case_in_85 pt = s(:array_pat, nil, s(:array_pat, nil, s(:lit, :b), s(:lasgn, :c)), s(:array_pat, nil, s(:lit, :d), s(:lvar, :e)), ) assert_case_in "[[:b, c], [:d, ^e]]", pt end def test_case_in__hash_pat_00 assert_case_in "{}", s(:hash_pat, nil) end def test_case_in__hash_pat_01 assert_case_in "**nil", s(:hash_pat, nil, s(:kwrest, :"**nil")), "{ **nil }" end def test_case_in__hash_pat_03 assert_case_in "a:", s(:hash_pat, nil, s(:lit, :a), nil), "{ a: }" end def test_case_in__hash_pat_06 assert_case_in "a:1, **r",s(:hash_pat, nil, s(:lit, :a), s(:lit, 1), s(:kwrest, :"**r")), "{ a: 1, **r }" end def test_case_in__hash_pat_08 assert_case_in "{ b: [Hash, *] }", s(:hash_pat, nil, s(:lit, :b), s(:array_pat, nil, s(:const, :Hash), :*)) end def test_case_in__hash_pat_09 assert_case_in("{ b: Integer => x, d: \"e\", f: }", s(:hash_pat, nil, s(:lit, :b), s(:lasgn, :x, s(:const, :Integer)), s(:lit, :d), s(:str, "e"), s(:lit, :f), nil)) end def test_case_in__hash_pat_10 assert_case_in "{ b: ^c, **r }", s(:hash_pat, nil, s(:lit, :b), s(:lvar, :c), s(:kwrest, :"**r")) end def test_case_in__hash_pat_11 assert_case_in "{ b: \"c\", d: \"e\" }", s(:hash_pat, nil, s(:lit, :b), s(:str, "c"), s(:lit, :d), s(:str, "e")) end def test_case_in__hash_pat_12 assert_case_in "{ b: true }", s(:hash_pat, nil, s(:lit, :b), s(:true)) end def test_case_in__hash_pat_14 assert_case_in "Object[b: 1]", s(:hash_pat, s(:const, :Object), s(:lit, :b), s(:lit, 1)) end def test_interpolation_and_escapes # log_entry = " \e[#{message_color}m#{message}\e[0m " inn = s(:lasgn, :log_entry, s(:dstr, " \e[", s(:evstr, s(:call, nil, :message_color)), s(:str, "m"), s(:evstr, s(:call, nil, :message)), s(:str, "\e[0m "))) out = "log_entry = \" \\e[#\{message_color}m#\{message}\\e[0m \"" assert_parse inn, out end def test_class_comments inn = s(:class, :Z, nil) inn.comments = "# x\n# y\n" out = "# x\n# y\nclass Z\nend" assert_parse inn, out end def test_module_comments inn = s(:module, :Z) inn.comments = "# x\n# y\n" out = "# x\n# y\nmodule Z\nend" assert_parse inn, out end def test_method_comments inn = s(:defn, :z, s(:args), s(:nil)) inn.comments = "# x\n# y\n" out = "# x\n# y\ndef z\n # do nothing\nend" assert_parse inn, out end def test_nested_ensure inn = s(:ensure, s(:lit, 1), s(:ensure, s(:lit, 2), s(:lit, 3))) out = "begin\n 1\nensure\n begin\n 2\n ensure\n 3\n end\nend" assert_parse inn, out end def test_nested_rescue inn = s(:ensure, s(:lit, 1), s(:rescue, s(:lit, 2), s(:resbody, s(:array), s(:lit, 3)))) out = "begin\n 1\nensure\n 2 rescue 3\nend" assert_parse inn, out end def test_nested_rescue_exception inn = s(:ensure, s(:lit, 1), s(:rescue, s(:lit, 2), s(:resbody, s(:array, s(:const, :Exception)), s(:lit, 3)))) out = "begin\n 1\nensure\n begin\n 2\n rescue Exception\n 3\n end\nend" assert_parse inn, out end def test_op_asgn do_not_check_sexp! # TODO: fix! inn = s(:op_asgn, s(:call, nil, :x), s(:call, nil, :z, s(:lit, 1)), :y, :+) out = "x.y += z(1)" assert_parse inn, out end def test_rescue_block inn = s(:rescue, s(:call, nil, :alpha), s(:resbody, s(:array), s(:call, nil, :beta), s(:call, nil, :gamma))) out = "begin\n alpha\nrescue\n beta\n gamma\nend" assert_parse inn, out end def test_array_adds_parens_around_rescue inn = s(:array, s(:lvar, :a), s(:rescue, s(:lvar, :b), s(:resbody, s(:array), s(:lvar, :c)))) out = "[a, (b rescue c)]" assert_parse inn, out end def test_call_arglist_rescue inn = s(:call, nil, :method, s(:rescue, s(:lvar, :a), s(:resbody, s(:array), s(:lvar, :b)))) out = "method((a rescue b))" assert_parse inn, out end def test_unless_vs_if_not do_not_check_sexp! # TODO: remove? dunno if that's possible w/ this one rb1 = "a unless b" rb2 = "a if (not b)" rb3 = "a if ! b" assert_parse ruby_parser.parse(rb1), rb1 assert_parse ruby_parser.parse(rb2), rb1 assert_parse ruby_parser.parse(rb3), rb1 end def ruby_parser parser = RubyParser.for_current_ruby %i[a b c d].each do |v| parser.env[v] = :lvar end parser end def assert_parse sexp, expected_ruby, expected_eval = nil assert_equal expected_ruby, @processor.process(sexp), "sexp -> ruby" assert_equal expected_eval, eval(expected_ruby) if expected_eval assert_variant sexp, expected_ruby if @check_sexp end def assert_variant sexp, expected_ruby assert_equal sexp, ruby_parser.process(expected_ruby), "ruby -> sexp" end def assert_case_in lit, exp_pt, normal_lit = nil rb = "case :a\nin #{normal_lit || lit} then\n # do nothing\nend" exp_pt.deep_each { |s| s.line ||= 2 } exp_pt.line ||= 2 # flunk "redundant: #{lit.inspect}" if normal_lit == lit # flunk "redundant: #{lit.inspect}" if normal_lit && normal_lit[1..-2] == lit normal_lit, lit = lit, lit[1..-2] if lit =~ /^\[.+\]$/ && !normal_lit if ENV["VERBOSE_TEST"] then puts puts rb end pt = s(:case, s(:lit, :a).line(1), s(:in, exp_pt, nil).line(2), nil).line(1) if normal_lit then assert_variant pt, "case :a\nin #{lit} then\n # do nothing\nend" end assert_parse pt, rb end def util_thingy(type) s(type, 'a"b', s(:evstr, s(:call, s(:lit, 1), :+, s(:lit, 1))), s(:str, 'c"d/e')) end end #################### # impl # old new # # t old 0 1 # e # s # t new 2 3 tr2r = File.read(__FILE__).lines[start + 1..__LINE__ - 2].join ir2r = File.read("lib/ruby2ruby.rb") require "ruby_parser" def morph_and_eval src, from, to, processor parser = RubyParser.for_current_ruby rescue RubyParser.new new_src = processor.new.process(parser.process(src.sub(from, to))) eval new_src new_src end unless ENV["SIMPLE"] then ____ = morph_and_eval tr2r, /TestRuby2Ruby/, "TestRuby2Ruby2", Ruby2Ruby ruby = morph_and_eval ir2r, /Ruby2Ruby/, "Ruby2Ruby2", Ruby2Ruby ____ = morph_and_eval ruby, /Ruby2Ruby2/, "Ruby2Ruby3", Ruby2Ruby2 class TestRuby2Ruby1 < TestRuby2Ruby def setup super @processor = Ruby2Ruby2.new end end class TestRuby2Ruby3 < TestRuby2Ruby2 def setup super @processor = Ruby2Ruby2.new end end class TestRuby2Ruby4 < TestRuby2Ruby2 def setup super @processor = Ruby2Ruby3.new end end end ruby2ruby-2.5.0/bin/0000755000004100000410000000000014323354702014266 5ustar www-datawww-dataruby2ruby-2.5.0/bin/r2r_show0000555000004100000410000000105714323354702015762 0ustar www-datawww-data#!/usr/bin/ruby -ws require 'rubygems' require 'ruby2ruby' require 'ruby_parser' $h ||= false $s ||= false if $h then puts "usage: #{File.basename $0} [options] [file...]" puts "options:" puts "-h : display usage" puts "-s : print the sexp before displaying the translated ruby" exit 1 end ARGV.push "-" if ARGV.empty? parser = RubyParser.new ruby2ruby = Ruby2Ruby.new ARGV.each do |file| ruby = file == "-" ? $stdin.read : File.read(file) sexp = parser.process(ruby, file) p sexp if $s puts ruby2ruby.process(sexp) end ruby2ruby-2.5.0/data.tar.gz.sig0000444000004100000410000000040014323354702016327 0ustar www-datawww-dataJQ7>.3Y˂PTn:*DWE› s;HDL5h1LfFPjuե bG37 /s3V>N)ɗR 3ЯLU3.j%Iսt, O pZ~ʤL*L/ޓͲh>{^~ 4.6 to sync them better === 2.3.1 / 2016-10-09 * 1 minor enhancement: * Support more op_asgn nodes. (presidentbeef) * 1 bug fix: * Fix for non-binary 'binary' calls (eg arity > 1). (presidentbeef) === 2.3.0 / 2016-02-18 * 3 minor enhancements: * Added support for safe navigation/lonely operator. (presidentbeef) * Expanded tests for 2.3 support * Support safe attrasgn. (presidentbeef). === 2.2.0 / 2015-05-27 * 1 minor enhancement: * Normalized block arg goalposts (always there unless arg slot == 0). === 2.1.4 / 2015-04-13 * 1 minor enhancement: * Wrap fewer hash values in parentheses. (jaredbeck) * 1 bug fix: * Fixed handling of kwsplat args. === 2.1.3 / 2014-09-26 * 1 bug fix: * Fixed handling of kwargs. (joenas) === 2.1.2 / 2014-08-28 * 1 bug fix: * Fixed readme to point out that deep_clone may be needed. (heathd) === 2.1.1 / 2014-06-09 * 1 bug fix: * Moved Regexp::CODES out of guard on ::ENC_NONE. (presidentbeef) === 2.1.0 / 2014-04-23 * 4 minor enhancements: * Don't indent defn body extra if it has a top-level rescue. * Don't indent defn body until fully processed. * Don't use simple rescue form if resbody is a return (statement keyword). (eyberg) * Remove superfluous begin/end for top-level defn rescue. === 2.0.8 / 2014-03-24 * 1 bug fix: * 2.0/2.1: Fixed support for **kwsplat. (troessner) === 2.0.7 / 2013-12-13 * 4 minor enhancements: * Add != to list of binary operators. (camertron) *le sigh* * Clean out cruft from process_masgn that I can't reproduce anymore. * Extend process_args to deal with masgn (eg: a.b { |(c, d)| ... }). * Extend process_masgn to deal with both sexps and var lists. * 1 bug fix: * Ensure proper parens on rescue subexpressions. (Bocete) === 2.0.6 / 2013-06-20 * 2 bug fixes: * Fixed bug with complex rescue but short enough to trigger 1-liner. (Confusion) * Fixed multiple expressions inside sclass === 2.0.5 / 2013-04-25 * 2 bug fixes: * Fixed attrasgn w/ multiple keys: a[x, y] = z. (derula) * Fixed error w/ attr_* detection when more than 1 ivar/iasgn exist in body. === 2.0.4 / 2013-03-28 * 1 bug fix: * Fixed attr_* generators in cases where the body isn't idiomatic. (robertfeldt) === 2.0.3 / 2013-02-07 * 2 minor enhancements: * 1.9: Added support for ! call to go back to (not ...). * 2nd and 3rd order testing now uses RubyPraser.for_current_ruby for maximal carnage. * 1 bug fix: * On failure (eg ruby 2.0), fall back to compound RubyParser instance for 2nd & 3rd order testing === 2.0.2 / 2013-01-16 * 1 minor enhancement: * Updated to ruby_parser 3.1 and up === 2.0.1 / 2012-11-02 * 1 bug fix: * Fixed dependency on alpha ruby_parser. *sigh* === 2.0.0 / 2012-11-02 * 1 minor enhancement: * Only do phase 1 testing if $SIMPLE=1. * 1 bug fix: * Fixed block args processing for RP 3.0 sexp changes === 2.0.0.b1 / 2012-07-27 * 4 major enhancements: * Made it work without arglist in call. * Made it work without scope/block in class/module/defn/defs. * Removed block from resbody * Removed block from when node * 4 minor enhancements: * Added debug task to help isolate an error * Empty hash is now a proper {} * Refactored and added finish method. * Switched to new Ruby18Parser to avoid deprecation warnings * 4 bug fixes: * Fixed call with empty hash arg. (neilconway) * OMG WTF... removed long decrepit ParseTree dependency * Removed isolate/rake require to reduce minimum bootstrap to hoe + rake + rake install_plugins (*2) * Skip 1.9 tests for now. === 1.3.1 / 2011-09-22 * 1 minor enhancement: * Added parenthesize to add parens in certain contexts. * 10 bug fixes: * Add newline to 'do nothing' comment in a block... seems contrived. (andreacampi) * Binary operations not work properly with some complex statements. (smorss) * Fixed if statements with no body (smorss) * Fixed logic for call with hash args in various locations (smorss) * Fixed match3 on an assignment. (smorss) * Fixed multiple nested rescue/ensure exprs (larsch) * Fixed process_alias to parenthesize (smorss) * Fixed process_and to parenthenize only when it makes sense. * Fixed rescue with 2+ statements in resbody (smorss) * Regexps with options other than /o were not showing flags. (smorss) === 1.3.0 / 2011-09-01 * 1 minor enhancement: * output comments for class and method definitions. (pythonic) === 1.2.5 / 2010-09-01 * 4 minor enhancements: * Added braces to hash args surrounded if in a binary method call. * Added rewrite_resbody to double check structure and freak if necessary. * Added stress task * rewrite_rescue now detects rescue with multiple arguments. * 2 bug fixes: * Fixed dstr/dregex/d* roundtripping problem * Fixed up call arg processing to be more correct and to work with the new sexp form === 1.2.4 / 2009-08-14 * 2 bug fixes: * Fixed all doco to use ruby_praser * Fixed bin/r2r_show to use ruby_parser. oops === 1.2.3 / 2009-06-23 * 4 minor enhancements: * Overhauled 4-generation tests to use RubyParser. Much cleaner * Removed last of ParseTree. Fully switched to RubyParser. * Switched to minitest * Updated Rakefile to new hoe capabilities === 1.2.2 / 2009-01-20 * 3 minor enhancements: * Added -s to display sexp before printing r2r * Added a bunch of backslash and masgn tests. * Refactored tests. * 4 bug fixes: * Fixed iters to deal with empty bodies. * Fixed process_call for a number of cases incl [], []=, and args processing. * Fixed process_hash to always generate braces if in arglist. * Switched process_alias to producing alias again, needed for globals. === 1.2.1 / 2008-11-04 * 1 bug fix: * Don't freak out and die if passed a c function defn. stupid rails. === 1.2.0 / 2008-10-22 * 2 minor enhancements: * Removed all PT dependent code to PT project (see parse_tree_extensions.rb). * Revamped. Got working with new unified ruby output. Much much cleaner. === 1.1.9 / 2008-06-09 * 5 minor enhancements: * Added more defensive programming in the tests to make it work with 1.9 and rubinius better. * Converted r2r_show to more plain parse style, no more discover_new_classes. * Made Proc#to_sexp and #to_ruby more resiliant. * Started to work on fallback to ruby_parser code. Should prolly do flog first. * Updated rakefile and readme format for hoe. Much cleaner! * 6 bug fixes: * Added 1.9 fixes. * Added code to tests to isolate rubyinline builds. * Fixed miniunit-deprecated assertions * Fixes for const2/3, esp in class names * Renamed ProcStoreTmp#name to #new_name. dur. * Skip proc tests in 1.9 since they require ParseTree. === 1.1.8 / 2007-08-21 * 6 minor enhancements: * Added super awesome .autotest file. YAY! * Removed nil.method_missing... too many ppl bitching about it. * Renamed RubyToRuby (the class name) to Ruby2Ruby. * Restructured self-translation tests so they were friendlier when dying. * Strings are now always one line long only. * Fully in sync with ParseTree and ruby_parser. * 2 bug fixes: * Fixed a number of issues/bugs discovered via ruby_parser. * Cleaned out some dead code and hacks we don't need anymore. === 1.1.7 / 2007-08-21 * 2 major enhancements: * Switched to ParseTree's UnifiedRuby... much much cleaner now! * Made test_ruby2ruby MUCH more rigorous with circular testing. * 5 minor enhancements: * Add r2r_show command like parse_tree_show. * Add parens for :block nodes as appropriate. May be overzealous. * Make SexpAny work with #==. * Removed calls to processor_stack / caller in favor of self.context. * Some style differences, eschew rescue. * 6 bug fixes: * Fix R2R bug with masgn/argscat. * Fixed a bug with new resbody unification. * Fixes for changes to pt_testcase. * Fixes the rest of the tests under strict sexp checking. * Fixed some circular bugs, mostly by hacking them out, wrt operator precidence. * Fixed trinary operator. === 1.1.6 / 2007-06-05 * 2 minor enhancements: * Extended tests for dstr/dsym/drgx to test against embedded slashes and quotes. * Updated for dasgn_curr changes to PT. * 2 bug fixes: * Fixed a bug with begin/rescue/ensure. * Fixed argscat and blockpass bug. blah(42, *args, &block) handled. === 1.1.5 / 2007-02-13 * 3 minor enhancements: * Can now heckle ActiveRecord::Base in full. * Cleaned up 1-liner generating code. * Made clean/simple rescues 1-liners. * 7 bug fixes: * Finally got the rest of block_pass working. * Fixed block_pass on procs in iters. UGH! * Fixed attrasgn in masgn. * Fixed splat in masgn. * Fixed unary/prefix methods. * Fixed attrasgn for []= where there were multiple args inside []. * Fixed a couple resbody bugs. === 1.1.4 / 2007-01-15 * 4 minor enhancements: * Added some extra rewriting code and tests for various bmethods. Ugh. * Added support for splatted block args. * Refactored class/module and dsym/dstr. * Short if/unless statements are now post-conditional expressions. * 4 bug fixes: * Finally fixed eric's nebulous proc code-in-goalposts bug. * Fixed dasgn_curr so block's dasgn vars decl goes away (bug 7420). * Fixed dmethod. I think the tests were bogus before. * Fixed improper end in method rescues (bug 7396). === 1.1.3 / 2006-12-20 * 1 minor enhancement * Unit tests do self-translation and retesting for 3 generations! Solid. BAM! * 1 bug fixes * iasgn inside masgn was totally borked in ruby2ruby. === 1.1.2 / 2006-12-19 * 2 minor enhancements * Improved []= and [] to be more idiomatic. * Support for nested whens (from when case has no expression). * 3 bug fixes * Fixed case output when there is no case expression. * NEARLY have RubyToRuby self-cloning and passing tests again. * Minor cleanup === 1.1.1 / 2006-11-13 * 3 bug fixes * Fixed procs * Cleaned return when no return values. * Rewrote process_if. No more elsif but no more bugs. :) === 1.1.0 / 2006-10-11 * 2 major enhancements * Released separately from ZenHacks. * Major overhaul/audit from the new ParseTree test infrastructure. Very complete now. ruby2ruby-2.5.0/metadata.gz.sig0000444000004100000410000000040014323354702016411 0ustar www-datawww-data`%~OV<}j(`]y:rK*^D(f @K1Һ5e'w? "0/c3_vƝ0 6*(]y8Ds/u]?.M?Cl ,tn3ZSU ?Ucm)E`(&E%:,ȂQb%pl&=$/a;R(#<"P1(ޅlU7c@6X0 F࠻:c(4']Y T'K ?WaT!ruby2ruby-2.5.0/checksums.yaml.gz.sig0000444000004100000410000000040014323354702017557 0ustar www-datawww-dataZbr qR\|kHPJ`!*g4τoMJUsI"P,Lپ%: MÕ#ӳlkK'ˮ [C/xG\/LQ I`&ݡ&`=ruby2ruby-2.5.0/.autotest0000444000004100000410000000116614323354702015371 0ustar www-datawww-data# -*- ruby -*- require 'autotest/restart' Autotest.add_hook :initialize do |at| at.libs << ':../../minitest/dev/lib' at.testlib = "minitest/autorun" at.extra_files << "../../sexp_processor/dev/lib/pt_testcase.rb" at.libs << ":../../sexp_processor/dev/lib:../../ruby_parser/dev/lib" (1..4).each do |n| at.extra_class_map["TestRuby2Ruby#{n}"] = "test/test_ruby2ruby.rb" end at.add_mapping(/unified|pt_testcase/) do |f, _| at.files_matching(/test.*rb$/) end end if ENV['RCOV'] then require 'autotest/rcov' Autotest::RCov.command = 'rcov_info' Autotest::RCov.pattern = 'test/test_ruby2ruby.rb' end ruby2ruby-2.5.0/ruby2ruby.gemspec0000644000004100000410000000650014323354702017031 0ustar www-datawww-data######################################################### # This file has been automatically generated by gem2tgz # ######################################################### # -*- encoding: utf-8 -*- # stub: ruby2ruby 2.5.0 ruby lib Gem::Specification.new do |s| s.name = "ruby2ruby".freeze s.version = "2.5.0" s.required_rubygems_version = Gem::Requirement.new(">= 0".freeze) if s.respond_to? :required_rubygems_version= s.metadata = { "homepage_uri" => "https://github.com/seattlerb/ruby2ruby" } if s.respond_to? :metadata= s.require_paths = ["lib".freeze] s.authors = ["Ryan Davis".freeze] s.cert_chain = ["-----BEGIN CERTIFICATE-----\nMIIDPjCCAiagAwIBAgIBBjANBgkqhkiG9w0BAQsFADBFMRMwEQYDVQQDDApyeWFu\nZC1ydWJ5MRkwFwYKCZImiZPyLGQBGRYJemVuc3BpZGVyMRMwEQYKCZImiZPyLGQB\nGRYDY29tMB4XDTIxMTIyMzIzMTkwNFoXDTIyMTIyMzIzMTkwNFowRTETMBEGA1UE\nAwwKcnlhbmQtcnVieTEZMBcGCgmSJomT8ixkARkWCXplbnNwaWRlcjETMBEGCgmS\nJomT8ixkARkWA2NvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALda\nb9DCgK+627gPJkB6XfjZ1itoOQvpqH1EXScSaba9/S2VF22VYQbXU1xQXL/WzCkx\ntaCPaLmfYIaFcHHCSY4hYDJijRQkLxPeB3xbOfzfLoBDbjvx5JxgJxUjmGa7xhcT\noOvjtt5P8+GSK9zLzxQP0gVLS/D0FmoE44XuDr3iQkVS2ujU5zZL84mMNqNB1znh\nGiadM9GHRaDiaxuX0cIUBj19T01mVE2iymf9I6bEsiayK/n6QujtyCbTWsAS9Rqt\nqhtV7HJxNKuPj/JFH0D2cswvzznE/a5FOYO68g+YCuFi5L8wZuuM8zzdwjrWHqSV\ngBEfoTEGr7Zii72cx+sCAwEAAaM5MDcwCQYDVR0TBAIwADALBgNVHQ8EBAMCBLAw\nHQYDVR0OBBYEFEfFe9md/r/tj/Wmwpy+MI8d9k/hMA0GCSqGSIb3DQEBCwUAA4IB\nAQCKB5jfsuSnKb+t/Wrh3UpdkmX7TrEsjVmERC0pPqzQ5GQJgmEXDD7oMgaKXaAq\nx2m+KSZDrqk7c8uho5OX6YMqg4KdxehfSLqqTZGoeV78qwf/jpPQZKTf+W9gUSJh\nzsWpo4K50MP+QtdSbKXZwjAafpQ8hK0MnnZ/aeCsW9ov5vdXpYbf3dpg6ADXRGE7\nlQY2y1tJ5/chqu6h7dQmnm2ABUqx9O+JcN9hbCYoA5i/EeubUEtFIh2w3SpO6YfB\nJFmxn4h9YO/pVdB962BdBNNDia0kgIjI3ENnkLq0dKpYU3+F3KhEuTksLO0L6X/V\nYsuyUzsMz6GQA4khyaMgKNSD\n-----END CERTIFICATE-----\n".freeze] s.date = "2022-10-05" s.description = "ruby2ruby provides a means of generating pure ruby code easily from\nRubyParser compatible Sexps. This makes making dynamic language\nprocessors in ruby easier than ever!".freeze s.email = ["ryand-ruby@zenspider.com".freeze] s.executables = ["r2r_show".freeze] s.extra_rdoc_files = ["History.rdoc".freeze, "Manifest.txt".freeze, "README.rdoc".freeze] s.files = [".autotest".freeze, "History.rdoc".freeze, "Manifest.txt".freeze, "README.rdoc".freeze, "Rakefile".freeze, "bin/r2r_show".freeze, "lib/ruby2ruby.rb".freeze, "test/test_ruby2ruby.rb".freeze] s.homepage = "https://github.com/seattlerb/ruby2ruby".freeze s.licenses = ["MIT".freeze] s.rdoc_options = ["--main".freeze, "README.rdoc".freeze] s.rubygems_version = "3.2.5".freeze s.summary = "ruby2ruby provides a means of generating pure ruby code easily from RubyParser compatible Sexps".freeze if s.respond_to? :specification_version then s.specification_version = 4 end if s.respond_to? :add_runtime_dependency then s.add_development_dependency(%q.freeze, ["~> 3.25"]) s.add_development_dependency(%q.freeze, [">= 4.0", "< 7"]) s.add_runtime_dependency(%q.freeze, ["~> 3.1"]) s.add_runtime_dependency(%q.freeze, ["~> 4.6"]) else s.add_dependency(%q.freeze, ["~> 3.25"]) s.add_dependency(%q.freeze, [">= 4.0", "< 7"]) s.add_dependency(%q.freeze, ["~> 3.1"]) s.add_dependency(%q.freeze, ["~> 4.6"]) end end ruby2ruby-2.5.0/Rakefile0000444000004100000410000000331414323354702015162 0ustar www-datawww-data# -*- ruby -*- require 'rubygems' require 'hoe' Hoe.add_include_dirs("lib", "../../ruby_parser/dev/lib", "../../sexp_processor/dev/lib") Hoe.plugin :seattlerb Hoe.plugin :isolate Hoe.plugin :rdoc Hoe.spec 'ruby2ruby' do developer 'Ryan Davis', 'ryand-ruby@zenspider.com' license "MIT" dependency "sexp_processor", "~> 4.6" dependency "ruby_parser", "~> 3.1" end def process ruby, file="stdin" require "ruby_parser" require "ruby2ruby" parser = RubyParser.new ruby2ruby = Ruby2Ruby.new begin sexp = parser.process(ruby, file) pp sexp if ENV["SEXP"] ruby2ruby.process(sexp) rescue Interrupt => e raise e end end task :stress do $: << "lib" $: << "../../ruby_parser/dev/lib" require "pp" files = Dir["../../*/dev/**/*.rb"].reject { |s| s =~ %r%/gems/% } warn "Stress testing against #{files.size} files" bad = {} files.each do |file| warn file begin process File.read(file), file rescue Interrupt => e raise e rescue Exception => e bad[file] = e end end pp bad end task :debug => :isolate do ENV["V"] ||= "18" file = ENV["F"] || ENV["FILE"] ruby = if file then File.read(file) else file = "env" ENV["R"] || ENV["RUBY"] end puts process(ruby, file) end task :parse => :isolate do require "ruby_parser" require "pp" parser = RubyParser.for_current_ruby file = ENV["F"] ruby = ENV["R"] if ruby then file = "env" else ruby = File.read file end pp parser.process(ruby, file) end task :bugs do sh "for f in bug*.rb ; do #{Gem.ruby} -S rake debug F=$f && rm $f ; done" end # vim: syntax=ruby ruby2ruby-2.5.0/lib/0000755000004100000410000000000014323354702014264 5ustar www-datawww-dataruby2ruby-2.5.0/lib/ruby2ruby.rb0000555000004100000410000007171514323354702016572 0ustar www-datawww-data#!/usr/bin/env ruby -w require "rubygems" require "sexp_processor" # :stopdoc: # REFACTOR: stolen from ruby_parser class Regexp unless defined? ENC_NONE then ENC_NONE = /x/n.options ENC_EUC = /x/e.options ENC_SJIS = /x/s.options ENC_UTF8 = /x/u.options end unless defined? CODES then CODES = { EXTENDED => "x", IGNORECASE => "i", MULTILINE => "m", ENC_NONE => "n", ENC_EUC => "e", ENC_SJIS => "s", ENC_UTF8 => "u", } end end # :startdoc: ## # Generate ruby code from a sexp. class Ruby2Ruby < SexpProcessor VERSION = "2.5.0" # :nodoc: # cutoff for one-liners LINE_LENGTH = 78 # binary operation messages BINARY = [:<=>, :==, :<, :>, :<=, :>=, :-, :+, :*, :/, :%, :<<, :>>, :**, :"!=", :^, :|, :&] ## # Nodes that represent assignment and probably need () around them. # # TODO: this should be replaced with full precedence support :/ ASSIGN_NODES = [ :dasgn, :flip2, :flip3, :lasgn, :masgn, :match3, :attrasgn, :op_asgn1, :op_asgn2, :op_asgn_and, :op_asgn_or, :return, :if, # HACK :rescue, ] ## # Some sexp types are OK without parens when appearing as hash values. # This list can include `:call`s because they're always printed with parens # around their arguments. For example: # # { :foo => (bar("baz")) } # The outer parens are unnecessary # { :foo => bar("baz") } # This is the normal code style HASH_VAL_NO_PAREN = [ :call, :false, :lit, :lvar, :nil, :str, :true, ] def initialize # :nodoc: super @indent = " " self.require_empty = false self.strict = true self.expected = String @calls = [] # self.debug[:defn] = /zsuper/ end ############################################################ # Processors def process_alias exp # :nodoc: _, lhs, rhs = exp parenthesize "alias #{process lhs} #{process rhs}" end def process_and exp # :nodoc: _, lhs, rhs = exp parenthesize "#{process lhs} and #{process rhs}" end def process_arglist exp # custom made node # :nodoc: _, *args = exp args.map { |arg| code = process arg arg.sexp_type == :rescue ? "(#{code})" : code }.join ", " end def process_args exp # :nodoc: _, *args = exp shadow = [] args = args.map { |arg| case arg when Symbol then arg when Sexp then case arg.sexp_type when :lasgn then process(arg) when :masgn then process(arg) when :kwarg then _, k, v = arg "#{k}: #{process v}" when :shadow then shadow << arg.sexp_body next when :forward_args then "..." else raise "unknown arg type #{arg.first.inspect}" end when nil then "" else raise "unknown arg type #{arg.inspect}" end }.compact args = args.join(", ").strip shadow = shadow.join(", ").strip shadow = "; #{shadow}" unless shadow.empty? "(%s%s)" % [args, shadow] end def process_array exp # :nodoc: "[#{process_arglist exp}]" end def process_attrasgn exp # :nodoc: _, recv, name, *args = exp rhs = args.pop args = s(:array, *args) receiver = process recv case name when :[]= then args = process args "#{receiver}#{args} = #{process rhs}" else raise "dunno what to do: #{args.inspect}" if args.size != 1 # s(:array) name = name.to_s.chomp "=" if rhs && rhs != s(:arglist) then "#{receiver}.#{name} = #{process(rhs)}" else raise "dunno what to do: #{rhs.inspect}" end end end def process_back_ref exp # :nodoc: _, n = exp "$#{n}" end # TODO: figure out how to do rescue and ensure ENTIRELY w/o begin def process_begin exp # :nodoc: _, *rest = exp code = rest.map { |sexp| src = process sexp src = indent src unless src =~ /(^|\n)(rescue|ensure)/ # ensure no level 0 rescues src } code.unshift "begin" code.push "end" code.join "\n" end def process_block exp # :nodoc: _, *body = exp result = body.map { |sexp| process sexp } result << "# do nothing\n" if result.empty? result = parenthesize result.join "\n" result += "\n" unless result.start_with? "(" result end def process_block_pass exp # :nodoc: raise "huh?: #{exp.inspect}" if exp.size > 2 _, sexp = exp "&#{process sexp}" end def process_break exp # :nodoc: _, arg = exp val = process arg if val then "break #{val}" else "break" end end def process_call(exp, safe_call = false) # :nodoc: _, recv, name, *args = exp receiver_node_type = recv && recv.sexp_type receiver = process recv receiver = "(#{receiver})" if ASSIGN_NODES.include? receiver_node_type # args = [] # this allows us to do both old and new sexp forms: # exp.push(*exp.pop[1..-1]) if exp.size == 1 && exp.first.first == :arglist @calls.push name in_context :arglist do max = args.size - 1 args = args.map.with_index { |arg, i| arg_type = arg.sexp_type is_empty_hash = arg == s(:hash) arg = process arg next if arg.empty? strip_hash = (arg_type == :hash and not BINARY.include? name and not is_empty_hash and (i == max or args[i + 1].sexp_type == :splat)) wrap_arg = Ruby2Ruby::ASSIGN_NODES.include? arg_type arg = arg[2..-3] if strip_hash arg = "(#{arg})" if wrap_arg arg }.compact end case name when *BINARY then if safe_call "#{receiver}&.#{name}(#{args.join(", ")})" elsif args.length > 1 "#{receiver}.#{name}(#{args.join(", ")})" else "(#{receiver} #{name} #{args.join(", ")})" end when :[] then receiver ||= "self" "#{receiver}[#{args.join(", ")}]" when :[]= then receiver ||= "self" rhs = args.pop "#{receiver}[#{args.join(", ")}] = #{rhs}" when :"!" then "(not #{receiver})" when :"-@" then "-#{receiver}" when :"+@" then "+#{receiver}" else args = nil if args.empty? args = "(#{args.join(", ")})" if args receiver = "#{receiver}." if receiver and not safe_call receiver = "#{receiver}&." if receiver and safe_call "#{receiver}#{name}#{args}" end ensure @calls.pop end def process_safe_call exp # :nodoc: process_call exp, :safe end def process_case exp # :nodoc: _, expr, *rest = exp result = [] expr = process expr result << if expr then "case #{expr}" else "case" end result.concat rest.map { |pt| if pt and %i[when in].include? pt.sexp_type "#{process pt}" else next unless pt code = indent process pt code = indent("# do nothing") if code =~ /^\s*$/ "else\n#{code}" end } result << "end" result.compact.join "\n" end def process_cdecl exp # :nodoc: _, lhs, rhs = exp lhs = process lhs if Sexp === lhs if rhs then rhs = process rhs "#{lhs} = #{rhs}" else lhs.to_s end end def process_class exp # :nodoc: "#{exp.comments}class #{util_module_or_class(exp, true)}" end def process_colon2 exp # :nodoc: _, lhs, rhs = exp "#{process lhs}::#{rhs}" end def process_colon3 exp # :nodoc: _, rhs = exp "::#{rhs}" end def process_const exp # :nodoc: _, name = exp name = process name if Sexp === name name.to_s end def __var name case context[1] when /_pat$/ then "^#{name}" when :in then "^#{name}" else name.to_s end end def process_cvar exp # :nodoc: _, name = exp __var name end def process_cvasgn exp # :nodoc: _, lhs, rhs = exp "#{lhs} = #{process rhs}" end def process_cvdecl exp # :nodoc: _, lhs, rhs = exp "#{lhs} = #{process rhs}" end def process_defined exp # :nodoc: _, rhs = exp "defined? #{process rhs}" end def process_defn(exp) # :nodoc: _, name, args, *body = exp comm = exp.comments args = process args args = "" if args == "()" body = s() if body == s(s(:nil)) # empty it out of a default nil expression # s(:defn, name, args, ivar|iasgn) case exp when s{ q(:defn, atom, t(:args), q(:ivar, atom)) } then # TODO: atom -> _ _, ivar = body.first ivar = ivar.to_s[1..-1] # remove leading @ reader = name.to_s return "attr_reader #{name.inspect}" if reader == ivar when s{ q(:defn, atom, t(:args), q(:iasgn, atom, q(:lvar, atom))) } then _, ivar, _val = body.first ivar = ivar.to_s[1..-1] # remove leading @ reader = name.to_s.chomp "=" return "attr_writer :#{reader}" if reader == ivar end body = body.map { |ssexp| process ssexp } simple = body.size <= 1 body << "# do nothing" if body.empty? body = body.join("\n") body = body.lines.to_a[1..-2].join("\n") if simple && body =~ /^\Abegin/ && body =~ /^end\z/ body = indent(body) unless simple && body =~ /(^|\n)rescue/ "#{comm}def #{name}#{args}\n#{body}\nend".gsub(/\n\s*\n+/, "\n") end def process_defs exp # :nodoc: _, lhs, name, args, *body = exp var = [:self, :cvar, :dvar, :ivar, :gvar, :lvar].include? lhs.sexp_type lhs = process lhs lhs = "(#{lhs})" unless var name = "#{lhs}.#{name}" process_defn s(:defn, name, args, *body) end def process_dot2(exp) # :nodoc: _, lhs, rhs = exp "(#{process lhs}..#{process rhs})" end def process_dot3(exp) # :nodoc: _, lhs, rhs = exp "(#{process lhs}...#{process rhs})" end def process_dregx exp # :nodoc: _, str, *rest = exp options = re_opt rest.pop if Integer === rest.last "/" << util_dthing(:dregx, s(:dregx, str, *rest)) << "/#{options}" end def process_dregx_once(exp) # :nodoc: process_dregx(exp) + "o" end def process_dstr(exp) # :nodoc: "\"#{util_dthing(:dstr, exp)}\"" end def process_dsym(exp) # :nodoc: ":\"#{util_dthing(:dsym, exp)}\"" end def process_dxstr(exp) # :nodoc: "`#{util_dthing(:dxstr, exp)}`" end def process_ensure(exp) # :nodoc: _, body, ens = exp body = process body ens = nil if ens == s(:nil) ens = process(ens) || "# do nothing" ens = "begin\n#{ens}\nend\n" if ens =~ /(^|\n)rescue/ body.sub!(/\n\s*end\z/, "") body = indent(body) unless body =~ /(^|\n)rescue/ "#{body}\nensure\n#{indent ens}" end def process_evstr exp # :nodoc: _, x = exp x ? process(x) : "" end def process_false exp # :nodoc: "false" end def process_flip2 exp # :nodoc: _, lhs, rhs = exp "#{process lhs}..#{process rhs}" end def process_flip3 exp # :nodoc: _, lhs, rhs = exp "#{process lhs}...#{process rhs}" end def process_for exp # :nodoc: _, recv, iter, body = exp recv = process recv iter = process iter body = process(body) || "# do nothing" result = ["for #{iter} in #{recv} do"] result << indent(body) result << "end" result.join "\n" end def process_forward_args exp "..." end def process_gasgn exp # :nodoc: process_iasgn exp end def process_gvar exp # :nodoc: _, name = exp __var name end def process_hash(exp) # :nodoc: _, *pairs = exp result = pairs.each_slice(2).map { |k, v| if k.sexp_type == :kwsplat then "%s" % process(k) elsif v.nil? # Shorthand hash syntax unless k.sexp_type == :lit and k.value.is_a? Symbol raise "Expected symbol for hash key, but got #{k.inspect}" end "#{k.value}:" else t = v.sexp_type lhs = process k rhs = process v rhs = "(#{rhs})" unless HASH_VAL_NO_PAREN.include? t "%s => %s" % [lhs, rhs] end } result.empty? ? "{}" : "{ #{result.join(", ")} }" end def process_iasgn(exp) # :nodoc: _, lhs, rhs = exp if rhs then "#{lhs} = #{process rhs}" else # part of an masgn lhs.to_s end end def process_in exp # :nodoc: _, lhs, *rhs = exp cond = process lhs body = rhs.compact.map { |sexp| indent process sexp } body << indent("# do nothing") if body.empty? body = body.join "\n" "in #{cond} then\n#{body.chomp}" end def process_find_pat exp # :nodoc: _, a, b, *c, d = exp a = process a # might be nil b = process b if Sexp === b c = c.map { |sexp| process sexp }.join(", ") d = process d if Sexp === d "%s[%s, %s, %s]" % [a, b, c, d] end def process_array_pat exp # :nodoc: _, const, *rest = exp const = process const if const rest = rest.map { |sexp| case sexp when Sexp process sexp else sexp end } "%s[%s]" % [const, rest.join(", ")] end def process_kwrest exp # :nodoc: _, name = exp name.to_s end def process_hash_pat exp # :nodoc: _, const, *rest = exp if const then const = process const kwrest = process rest.pop if rest.last && rest.last.sexp_type == :kwrest rest = rest.each_slice(2).map { |k, v| k = process(k).delete_prefix ":" v = process v "#{k}: #{v}".strip } rest << kwrest if kwrest rest.empty? ? "%s[]" % const : "%s[%s]" % [const, rest.join(", ")] else kwrest = process rest.pop if rest.last && rest.last.sexp_type == :kwrest rest = rest.each_slice(2).map { |k, v| k = process(k).delete_prefix ":" v = process v "#{k}: #{v}".strip } rest << kwrest if kwrest rest.empty? ? "{}" : "{ %s }" % [rest.join(", ")] end end def process_preexe exp # :nodoc: "BEGIN" end def process_if(exp) # :nodoc: _, c, t, f = exp expand = Ruby2Ruby::ASSIGN_NODES.include? c.sexp_type c = process c t = process t f = process f c = "(#{c.chomp})" if c =~ /\n/ if t then unless expand then if f then r = "#{c} ? (#{t}) : (#{f})" r = nil if r =~ /return/ # HACK - need contextual awareness or something else r = "#{t} if #{c}" end return r if r and (@indent + r).size < LINE_LENGTH and r !~ /\n/ end r = "if #{c} then\n#{indent(t)}\n" r << "else\n#{indent(f)}\n" if f r << "end" r elsif f unless expand then r = "#{f} unless #{c}" return r if (@indent + r).size < LINE_LENGTH and r !~ /\n/ end "unless #{c} then\n#{indent(f)}\nend" else # empty if statement, just do it in case of side effects from condition "if #{c} then\n#{indent "# do nothing"}\nend" end end def process_lambda exp # :nodoc: "->" end MUST_BE_CURLY = %w[ BEGIN END ] def process_iter(exp) # :nodoc: _, iter, args, body = exp is_lambda = iter.sexp_type == :lambda iter = process iter body = process body if body args = case when args == 0 then "" when is_lambda then " (#{process(args)[1..-2]})" else " |#{process(args)[1..-2]}|" end b, e = if MUST_BE_CURLY.include? iter then %w[ { } ] else %w[ do end ] end iter.sub!(/\(\)$/, "") # REFACTOR: ugh result = [] if is_lambda then result << iter result << args result << " {" else result << "#{iter} {" result << args end result << if body then " #{body.strip} " else " " end result << "}" result = result.join return result if result !~ /\n/ and result.size < LINE_LENGTH result = [] if is_lambda then result << iter result << args result << " #{b}" else result << "#{iter} #{b}" result << args end result << "\n" if body then result << indent(body.strip) result << "\n" end result << e result.join end def process_ivar(exp) # :nodoc: _, name = exp __var name end def process_kwsplat(exp) _, kw = exp "**#{process kw}" end def process_lasgn(exp) # :nodoc: _, name, value = exp value = process value if value then "%s %s %s" % case context[1] when /_pat$/ then [value, "=>", name] else [name, "=", value] end else "%s" % [name] end end def process_lit exp # :nodoc: _, obj = exp case obj when Range then "(#{obj.inspect})" else obj.inspect end end def process_lvar(exp) # :nodoc: _, name = exp __var name end def process_masgn(exp) # :nodoc: # s(:masgn, s(:array, s(:lasgn, :var), ...), s(:to_ary, , ...)) # s(:iter, , s(:args, s(:masgn, :a, :b)), ) parenthesize = true result = exp.sexp_body.map { |sexp| case sexp when Sexp then if sexp.sexp_type == :array then parenthesize = context.grep(:masgn).size > 1 res = process sexp res[1..-2] else process sexp end when Symbol then sexp else raise "unknown masgn: #{sexp.inspect}" end } parenthesize ? "(#{result.join ", "})" : result.join(" = ") end def process_match exp # :nodoc: _, rhs = exp "#{process rhs}" end def process_match2 exp # :nodoc: # s(:match2, s(:lit, /x/), s(:str, "blah")) _, lhs, rhs = exp lhs = process lhs rhs = process rhs "#{lhs} =~ #{rhs}" end def process_match3 exp # :nodoc: _, rhs, lhs = exp # yes, backwards left_type = lhs.sexp_type lhs = process lhs rhs = process rhs if ASSIGN_NODES.include? left_type then "(#{lhs}) =~ #{rhs}" else "#{lhs} =~ #{rhs}" end end def process_module(exp) # :nodoc: "#{exp.comments}module #{util_module_or_class(exp)}" end def process_next(exp) # :nodoc: _, rhs = exp val = rhs && process(rhs) # maybe push down into if and test rhs? if val then "next #{val}" else "next" end end def process_nil(exp) # :nodoc: "nil" end def process_not(exp) # :nodoc: _, sexp = exp "(not #{process sexp})" end def process_nth_ref(exp) # :nodoc: _, n = exp "$#{n}" end def process_op_asgn exp # :nodoc: # [[:lvar, :x], [:call, nil, :z, [:lit, 1]], :y, :"||"] case exp.length when 4 raise "NOT YET: op_asgn 4" when 5 _, lhs, rhs, index, op = exp lhs = process lhs rhs = process rhs "#{lhs}.#{index} #{op}= #{rhs}" else raise ArgumentError, "Don't know how to process this length: %p" % [exp] end end def process_op_asgn1(exp) # :nodoc: # [[:lvar, :b], [:arglist, [:lit, 1]], :"||", [:lit, 10]] _, lhs, index, msg, rhs = exp lhs = process lhs index = process index rhs = process rhs "#{lhs}[#{index}] #{msg}= #{rhs}" end def process_op_asgn2 exp # :nodoc: # [[:lvar, :c], :var=, :"||", [:lit, 20]] _, lhs, index, msg, rhs = exp lhs = process lhs index = index.to_s[0..-2] rhs = process rhs "#{lhs}.#{index} #{msg}= #{rhs}" end def process_op_asgn_and(exp) # :nodoc: # a &&= 1 # [[:lvar, :a], [:lasgn, :a, [:lit, 1]]] _, _lhs, rhs = exp process(rhs).sub(/\=/, "&&=") end def process_op_asgn_or(exp) # :nodoc: # a ||= 1 # [[:lvar, :a], [:lasgn, :a, [:lit, 1]]] _, _lhs, rhs = exp process(rhs).sub(/\=/, "||=") end def process_or(exp) # :nodoc: _, lhs, rhs = exp lhs = process lhs rhs = process rhs case context[1] when :in then "(#{lhs} | #{rhs})" else "(#{lhs} or #{rhs})" end end def process_postexe(exp) # :nodoc: "END" end def process_redo(exp) # :nodoc: "redo" end def process_resbody exp # :nodoc: # s(:resbody, s(:array), s(:return, s(:str, "a"))) _, args, *body = exp body = body.compact.map { |sexp| process sexp } body << "# do nothing" if body.empty? name = args.lasgn true name ||= args.iasgn true args = process(args)[1..-2] args = " #{args}" unless args.empty? args += " => #{name[1]}" if name "rescue#{args}\n#{indent body.join("\n")}" end def process_rescue exp # :nodoc: _, *rest = exp body = process rest.shift unless rest.first.sexp_type == :resbody els = process rest.pop unless rest.last && rest.last.sexp_type == :resbody body ||= "# do nothing" # TODO: I don't like this using method_missing, but I need to ensure tests simple = rest.size == 1 && rest.first.size <= 3 && !rest.first.block && !rest.first.return resbodies = rest.map { |resbody| _, rb_args, rb_body, *rb_rest = resbody simple &&= rb_args == s(:array) simple &&= rb_rest.empty? && rb_body && rb_body.node_type != :block process resbody } if els then "#{indent body}\n#{resbodies.join("\n")}\nelse\n#{indent els}" elsif simple then resbody = resbodies.first.sub(/\n\s*/, " ") "#{body} #{resbody}" else "#{indent body}\n#{resbodies.join("\n")}" end end def process_retry(exp) # :nodoc: "retry" end def process_return exp # :nodoc: _, rhs = exp unless rhs then "return" else rhs_type = rhs.sexp_type rhs = process rhs rhs = "(#{rhs})" if ASSIGN_NODES.include? rhs_type "return #{rhs}" end end def process_safe_attrasgn exp # :nodoc: _, receiver, name, *rest = exp receiver = process receiver rhs = rest.pop args = rest.pop # should be nil raise "dunno what to do: #{args.inspect}" if args name = name.to_s.sub(/=$/, "") if rhs && rhs != s(:arglist) then "#{receiver}&.#{name} = #{process rhs}" else raise "dunno what to do: #{rhs.inspect}" end end def process_safe_op_asgn exp # :nodoc: # [[:lvar, :x], [:call, nil, :z, [:lit, 1]], :y, :"||"] _, lhs, rhs, index, op = exp lhs = process lhs rhs = process rhs "#{lhs}&.#{index} #{op}= #{rhs}" end def process_safe_op_asgn2(exp) # :nodoc: # [[:lvar, :c], :var=, :"||", [:lit, 20]] _, lhs, index, msg, rhs = exp lhs = process lhs index = index.to_s[0..-2] rhs = process rhs "#{lhs}&.#{index} #{msg}= #{rhs}" end def process_sclass(exp) # :nodoc: _, recv, *block = exp recv = process recv block = indent process_block s(:block, *block) "class << #{recv}\n#{block}\nend" end def process_self(exp) # :nodoc: "self" end def process_splat(exp) # :nodoc: _, arg = exp if arg.nil? then "*" else "*#{process arg}" end end def process_str(exp) # :nodoc: _, s = exp s.dump end def process_super(exp) # :nodoc: _, *args = exp args = args.map { |arg| process arg } "super(#{args.join ", "})" end def process_svalue(exp) # :nodoc: _, *args = exp args.map { |arg| process arg }.join ", " end def process_to_ary exp # :nodoc: _, sexp = exp process sexp end def process_true(exp) # :nodoc: "true" end def process_undef(exp) # :nodoc: _, name = exp "undef #{process name}" end def process_until(exp) # :nodoc: cond_loop(exp, "until") end def process_valias exp # :nodoc: _, lhs, rhs = exp "alias #{lhs} #{rhs}" end def process_when exp # :nodoc: _, lhs, *rhs = exp cond = process(lhs)[1..-2] rhs = rhs.compact.map { |sexp| indent process sexp } rhs << indent("# do nothing") if rhs.empty? rhs = rhs.join "\n" "when #{cond} then\n#{rhs.chomp}" end def process_while(exp) # :nodoc: cond_loop exp, "while" end def process_xstr(exp) # :nodoc: "`#{process_str(exp)[1..-2]}`" end def process_yield(exp) # :nodoc: _, *args = exp args = args.map { |arg| process arg } unless args.empty? then "yield(#{args.join(", ")})" else "yield" end end def process_zsuper(exp) # :nodoc: "super" end ############################################################ # Rewriters: def rewrite_attrasgn exp # :nodoc: if context.first(2) == [:array, :masgn] then _, recv, msg, *args = exp exp = s(:call, recv, msg.to_s.chomp("=").to_sym, *args) end exp end def rewrite_call exp # :nodoc: _, recv, msg, *args = exp exp = s(:not, recv) if msg == :! && args.empty? exp end def rewrite_ensure exp # :nodoc: exp = s(:begin, exp) unless context.first == :begin exp end def rewrite_if exp # :nodoc: _, c, t, f = exp if c.sexp_type == :not then _, nc = c exp = s(:if, nc, f, t) end exp end def rewrite_resbody exp # :nodoc: _, args, *_rest = exp raise "no exception list in #{exp.inspect}" unless exp.size > 2 && args raise args.inspect if args.sexp_type != :array # for now, do nothing, just check and freak if we see an errant structure exp end def rewrite_rescue exp # :nodoc: complex = false complex ||= exp.size > 3 complex ||= exp.resbody.block complex ||= exp.resbody.size > 3 resbodies = exp.find_nodes(:resbody) complex ||= resbodies.any? { |n| n[1] != s(:array) } complex ||= resbodies.any? { |n| n.last.nil? } complex ||= resbodies.any? { |(_, _, body)| body and body.node_type == :block } handled = context.first == :ensure exp = s(:begin, exp) if complex unless handled exp end def rewrite_svalue(exp) # :nodoc: case exp.last.sexp_type when :array s(:svalue, *exp[1].sexp_body) when :splat exp else raise "huh: #{exp.inspect}" end end def rewrite_until exp # :nodoc: _, c, *body = exp if c.sexp_type == :not then _, nc = c exp = s(:while, nc, *body) end exp end def rewrite_while exp # :nodoc: _, c, *body = exp if c.sexp_type == :not then _, nc = c exp = s(:until, nc, *body) end exp end ############################################################ # Utility Methods: ## # Generate a post-or-pre conditional loop. def cond_loop(exp, name) _, cond, body, head_controlled = exp cond = process cond body = process body body = indent(body).chomp if body code = [] if head_controlled then code << "#{name} #{cond} do" code << body if body code << "end" else code << "begin" code << body if body code << "end #{name} #{cond}" end code.join("\n") end ## # Utility method to escape something interpolated. def dthing_escape type, lit # TODO: this needs more testing case type when :dregx then lit.gsub(/(\A|[^\\])\//, '\1\/') when :dstr, :dsym then lit.dump[1..-2] when :dxstr then lit.gsub(/`/, '\`') else raise "unsupported type #{type.inspect}" end end ## # Indent all lines of +s+ to the current indent level. def indent s s.to_s.split(/\n/).map{|line| @indent + line}.join("\n") end ## # Wrap appropriate expressions in matching parens. def parenthesize exp case context[1] when nil, :defn, :defs, :class, :sclass, :if, :iter, :resbody, :when, :while then exp else "(#{exp})" end end ## # Return the appropriate regexp flags for a given numeric code. def re_opt options bits = (0..8).map { |n| options[n] * 2**n } bits.delete 0 bits.map { |n| Regexp::CODES[n] }.join end ## # Return a splatted symbol for +sym+. def splat(sym) :"*#{sym}" end ## # Utility method to generate something interpolated. def util_dthing(type, exp) _, str, *rest = exp # first item in sexp is a string literal str = dthing_escape(type, str) rest = rest.map { |pt| case pt.sexp_type when :str then dthing_escape(type, pt.last) when :evstr then '#{%s}' % [process(pt)] else raise "unknown type: #{pt.inspect}" end } [str, rest].join end ## # Utility method to generate ether a module or class. def util_module_or_class exp, is_class = false result = [] _, name, *body = exp superk = body.shift if is_class name = process name if Sexp === name result << name if superk then superk = process superk result << " < #{superk}" if superk end result << "\n" body = body.map { |sexp| process(sexp).chomp } body = unless body.empty? then indent(body.join("\n\n")) + "\n" else "" end result << body result << "end" result.join end end ruby2ruby-2.5.0/README.rdoc0000444000004100000410000000525314323354702015327 0ustar www-datawww-data= ruby2ruby home :: https://github.com/seattlerb/ruby2ruby rdoc :: http://docs.seattlerb.org/ruby2ruby == DESCRIPTION: ruby2ruby provides a means of generating pure ruby code easily from RubyParser compatible Sexps. This makes making dynamic language processors in ruby easier than ever! == FEATURES/PROBLEMS: * Clean, simple SexpProcessor generates ruby code from RubyParser compatible sexps. == SYNOPSIS: require 'rubygems' require 'ruby2ruby' require 'ruby_parser' require 'pp' ruby = "def a\n puts 'A'\nend\n\ndef b\n a\nend" parser = RubyParser.new ruby2ruby = Ruby2Ruby.new sexp = parser.process(ruby) pp sexp p ruby2ruby.process(sexp.deep_clone) # Note: #process destroys its input, so # #deep_clone if you need to preserve it ## outputs: s(:block, s(:defn, :a, s(:args), s(:scope, s(:block, s(:call, nil, :puts, s(:arglist, s(:str, "A")))))), s(:defn, :b, s(:args), s(:scope, s(:block, s(:call, nil, :a, s(:arglist)))))) "def a\n puts(\"A\")\nend\ndef b\n a\nend\n" == REQUIREMENTS: + sexp_processor + ruby_parser == INSTALL: + sudo gem install ruby2ruby == How to Contribute: To get started all you need is a checkout, rake, and hoe. The easiest way is: % git clone seattlerb/ruby2ruby # assumes you use the `hub` wrapper. % gem i rake hoe % rake install_plugins # installs hoe-seattlerb & isolate % rake install_plugins # installs minitest (referenced from hoe-seattlerb) From here you should be good to go. We accept pull requests on github. == LICENSE: (The MIT License) Copyright (c) Ryan Davis, seattle.rb Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the 'Software'), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ruby2ruby-2.5.0/Manifest.txt0000444000004100000410000000015614323354702016025 0ustar www-datawww-data.autotest History.rdoc Manifest.txt README.rdoc Rakefile bin/r2r_show lib/ruby2ruby.rb test/test_ruby2ruby.rb