sexp_processor-4.17.0/0000755000004100000410000000000014443772242014722 5ustar www-datawww-datasexp_processor-4.17.0/test/0000755000004100000410000000000014443772242015701 5ustar www-datawww-datasexp_processor-4.17.0/test/test_environment.rb0000555000004100000410000000300714443772242021632 0ustar www-datawww-data$TESTING = true require "minitest/autorun" require "sexp_processor" class TestEnvironment < Minitest::Test def setup @env = SexpProcessor::Environment.new end def test_all @env.scope do @env[:x] = 42 @env.scope do @env[:y] = 3 @env[:x] = Math::PI expected = { :x => Math::PI, :y => 3 } assert_equal expected, @env.all end expected = { :x => Math::PI } assert_equal expected, @env.all end end def test_depth assert_equal 1, @env.depth @env.scope do assert_equal 2, @env.depth end assert_equal 1, @env.depth end def test_index test_index_equals end def test_index_unknown assert_nil @env[:unknown] end def test_index_out_of_scope @env.scope do @env[:var] = 42 assert_equal 42, @env[:var] end assert_nil @env[:var] end def test_index_equals @env[:var] = 42 assert_equal 42, @env[:var] end def test_lookup_scope @env[:var] = 42 assert_equal 42, @env[:var] @env.scope do assert_equal 42, @env[:var] end end def test_scope @env[:var] = 42 assert_equal 42, @env[:var] @env.scope do @env[:var] = Math::PI assert_in_epsilon Math::PI, @env[:var] end assert_in_epsilon Math::PI, @env[:var] end def test_current_shadow @env[:var] = 42 assert_equal 42, @env[:var] @env.scope do @env.current[:var] = 23 assert_equal 23, @env[:var] end assert_equal 42, @env[:var] end end sexp_processor-4.17.0/test/test_sexp.rb0000555000004100000410000012071514443772242020253 0ustar www-datawww-data$TESTING = true if ENV["COV"] require "simplecov" SimpleCov.start do add_filter "lib/sexp_processor" add_filter "test" end warn "Running simplecov" end require "minitest/autorun" require "minitest/hell" # beat these up require "minitest/benchmark" if ENV["BENCH"] require "sexp_processor" # for deep_clone (TODO: why is that on SP and not S?) require "sexp" def pyramid_sexp max # s(:array, # s(:array, s(:s, 1)), # s(:array, s(:s, 1), s(:s, 2)), # ... # s(:array, s(:s, 1), s(:s, 2), ... s(:s, max-1))) s(:array, *(1...max).map { |n| s(:array, *(1..n).map { |m| s(:s, m) })}) end class SexpTestCase < Minitest::Test M = Sexp::Matcher MC = Sexp::MatchCollection MR = Sexp # ::MatchResult CLASS_SEXP = s(:class, :cake, nil, s(:defn, :foo, s(:args), s(:add, :a, :b)), s(:defn, :bar, s(:args), s(:sub, :a, :b))) def skip_if_strict n = 1 strict = ENV["STRICT_SEXP"].to_i skip "Can't pass on STRICT_SEXP mode" if strict >= n end # KEY for regex tests # :a == no change # :b == will change (but sometimes ONLY once) # :c == change to def assert_equal3 x, y skip_if_strict assert_operator x, :===, y end def refute_equal3 x, y refute_operator x, :===, y end def assert_pretty_print expect, input assert_equal expect, input.pretty_inspect.chomp end def assert_inspect expect, input assert_equal expect, input.inspect end def assert_search count, sexp, pattern assert_equal count, sexp.search_each(pattern).count end def assert_satisfy pattern, sexp assert_operator pattern, :satisfy?, sexp end def refute_satisfy pattern, sexp refute_operator pattern, :satisfy?, sexp end end # class SexpTestCase class MatcherTestCase < SexpTestCase def self.abstract_test_case! klass = self # REFACTOR: push this up to minitest extend Module.new { define_method :run do |*args| super(*args) unless self == klass end } end abstract_test_case! def matcher raise "Subclass responsibility" end def inspect_str raise "Subclass responsibility" end def pretty_str inspect_str end def sexp s(:a) end def bad_sexp s(:b) end def test_satisfy_eh assert_equal3 matcher, sexp end def test_satisfy_eh_fail skip "not applicable" unless bad_sexp refute_equal3 matcher, bad_sexp end def test_greedy refute_operator matcher, :greedy? end def test_inspect assert_inspect inspect_str, matcher end def test_pretty_print assert_pretty_print pretty_str, matcher end end # class MatcherTestCase class TestSexp < SexpTestCase # ZenTest FULL def setup super @sexp_class = Object.const_get(self.class.name[4..-1]) @sexp = @sexp_class.new(1, 2, 3) @basic_sexp = s(:lasgn, :var, s(:lit, 42).line(1)).line(1) @basic_sexp.each_sexp do |s| s.file = "file.rb" end @complex_sexp = s(:block, s(:lasgn, :foo, s(:str, "foo").line(1)).line(1), s(:if, s(:and, s(:true).line(2), s(:lit, 1).line(2)).line(2), s(:if, s(:lvar, :foo).line(3), s(:str, "bar").line(3), nil).line(3), s(:true).line(5)).line(2)).line(1) @re = s(:lit, 42) @bad1 = s(:lit, 24) @bad1 = s(:blah, 42) end def assert_from_array exp, input assert_equal exp, Sexp.from_array(input) end def test_class_from_array assert_from_array s(), [] assert_from_array s(:s), [:s] assert_from_array s(:s, s(:m)), [:s, [:m]] assert_from_array s(:s, s(:m)), [:s, s(:m)] assert_from_array s(:s, s(:m, [:not_converted])), [:s, s(:m, [:not_converted])] end def test_compact input = s(:a, nil, :b) actual = input.compact assert_equal s(:a, :b), actual assert_same input, actual # returns mutated result end def test_array_type_eh capture_io do # HACK assert_equal false, s(:lit, 42).array_type? assert_equal true, s(:array, 42).array_type? end end def test_each_of_type # TODO: huh... this tests fails if top level sexp :b is removed @sexp = s(:b, s(:a, s(:b, s(:a), :a, s(:b, :a), s(:b, s(:a))))) count = 0 @sexp.each_of_type :a do count += 1 end assert_equal(3, count, "must find 3 a's in #{@sexp.inspect}") end def test_each_of_type_no_block @sexp = s(:a, s(:b), s(:c), :d) assert_equal [s(:b)], @sexp.each_of_type(:b).to_a end def test_equals2_array refute_equal @sexp, [1, 2, 3] # Sexp == Array assert_raises Minitest::Assertion do # Array == Sexp. refute_equal [1, 2, 3], @sexp # This is a bug in ruby: end # TODO: test if it is calling to_ary first? seems not to end def test_equals2_not_body sexp2 = s(1, 2, 5) refute_equal(@sexp, sexp2) end def test_equals2_sexp sexp2 = s(1, 2, 3) if @sexp.class == Sexp then skip "Not applicable to this target." else refute_equal(@sexp, sexp2) end end def test_equal3_full_match assert_equal3 s(), s() # 0 assert_equal3 s(:blah), s(:blah) # 1 assert_equal3 s(:a, :b), s(:a, :b) # 2 assert_equal3 @basic_sexp, @basic_sexp.dup # deeper structure end def test_equal3_mismatch refute_equal3 s(), s(:a) refute_equal3 s(:a), s() refute_equal3 s(:blah1), s(:blah2) refute_equal3 s(:a), s(:a, :b) refute_equal3 s(:a, :b), s(:a) refute_equal3 s(:a1, :b), s(:a2, :b) refute_equal3 s(:a, :b1), s(:a, :b2) end def test_equal3_subset_match assert_match s{q(:a)}, s(s(:a), s(:b)) # left - =~ assert_equal3 s{q(:a)}, s(s(:a), s(:b)) # left - === assert_equal3 s{q(:a)}, s(:blah, s(:a ), s(:b)) # mid 1 assert_equal3 s{q(:a, 1)}, s(:blah, s(:a, 1), s(:b)) # mid 2 assert_equal3 s{q(:a)}, s(:blah, s(:blah, s(:a))) # left deeper end def test_equalstilde_fancy assert_match s{ q(:b) }, s(:a, s(:b), :c) assert_match s(:a, s(:b), :c), s{ q(:b) } e = assert_raises ArgumentError do s(:b) =~ s(:a, s(:b), :c) end assert_equal "Not a pattern: s(:a, s(:b), :c)", e.message e = assert_raises ArgumentError do s(:a, s(:b), :c) =~ s(:b) end assert_equal "Not a pattern: s(:b)", e.message end def test_equalstilde_plain s{ q(:re) } =~ s(:data) # pattern on LHS s(:data) =~ s{ q(:re) } # pattern on RHS e = assert_raises ArgumentError do s(:re) =~ s(:data) # no pattern end assert_equal "Not a pattern: s(:data)", e.message end def test_find_and_replace_all skip_if_strict 2 @sexp = s(:a, s(:a, :b, s(:a, :b), s(:a), :b, s(:a, s(:a)))) expected = s(:a, s(:a, :a, s(:a, :a), s(:a), :a, s(:a, s(:a)))) @sexp.find_and_replace_all(:b, :a) assert_equal(expected, @sexp) end def assert_gsub exp, sexp, from, to assert_equal exp, sexp.gsub(from, to) end def test_gsub assert_gsub s(:c), s(:b), s(:b), s(:c) assert_gsub s(:a), s(:a), s(:b), s(:c) assert_gsub s(:a, s(:c)), s(:a, s(:b)), s(:b), s(:c) end def test_gsub_empty assert_gsub s(:c), s(), s(), s(:c) end def test_gsub_multiple assert_gsub s(:a, s(:c), s(:c)), s(:a, s(:b), s(:b)), s(:b), s(:c) assert_gsub s(:a, s(:c), s(:a, s(:c))), s(:a, s(:b), s(:a, s(:b))), s(:b), s(:c) end def test_gsub_matcher assert_gsub s(:a, :b, :c), s(:a, s(:b, 42), :c), s{ q(:b, _) }, :b assert_gsub s(:a, s(:b), :c), s(:a, s(:b), :c), s{ q(:b, _) }, :b assert_gsub s(:a, s(:c, :b), :d), s(:a, s(:c, s(:b, 42)), :d), s{ q(:b, _) }, :b assert_gsub s(:a, s(:q), :c), s(:a, s(:q), :c), s{ q(:b, _) }, :b end def with_env key old_val, ENV[key] = ENV[key], "1" yield ensure ENV[key] = old_val end def with_verbose &block with_env "VERBOSE", &block end def with_debug &block with_env "DEBUG", &block end def test_inspect k = @sexp_class n = k.name[0].chr.downcase assert_equal("#{n}()", k.new().inspect) assert_equal("#{n}(:a)", k.new(:a).inspect) assert_equal("#{n}(:a, :b)", k.new(:a, :b).inspect) assert_equal("#{n}(:a, #{n}(:b))", k.new(:a, k.new(:b)).inspect) with_verbose do assert_equal("#{n}().line(42)", k.new().line(42).inspect) assert_equal("#{n}(:a).line(42)", k.new(:a).line(42).inspect) assert_equal("#{n}(:a, :b).line(42)", k.new(:a, :b).line(42).inspect) assert_equal("#{n}(:a, #{n}(:b).line(43)).line(42)", k.new(:a, k.new(:b).line(43)).line(42).inspect) end end def test_line assert_nil @sexp.line assert_equal 1, @basic_sexp.line assert_equal 1, @complex_sexp.line end def test_line_max assert_nil @sexp.line_max assert_equal 1, @basic_sexp.line_max assert_equal 5, @complex_sexp.line_max end def test_new file = "file.rb" old = s(:lasgn, :var, s(:lit, 42).line(2)).line(1) old.file = file old.each_sexp do |x| x.file = file end old.comments = "do the thing" assert_same file, old.file assert_equal 1, old.line assert_same file, old.last.file assert_equal 2, old.last.line new = old.new(1, 2, 3) assert_equal s(1, 2, 3), new assert_same file, new.file assert_equal 1, new.line assert_same old.comments, new.comments end def test_map file = "file.rb" old = s(:lasgn, :var, s(:lit, 42).line(2)).line(1) old.file = file old.each_sexp do |x| x.file = file end old.comments = "do the thing" assert_same file, old.file assert_equal 1, old.line assert_same file, old.last.file assert_equal 2, old.last.line new = old.map { |x| x } assert_same file, new.file assert_equal 1, new.line assert_same file, new.last.file assert_equal 2, new.last.line assert_same old.comments, new.comments end def test_mass assert_equal 1, s(:a).mass assert_equal 3, s(:a, s(:b), s(:c)).mass s = s(:iter, s(:call, nil, :a, s(:arglist, s(:lit, 1))), s(:lasgn, :c), s(:call, nil, :d, s(:arglist))) assert_equal 7, s.mass end def test_mass_auto_shift assert_equal 1, s(:a).mass assert_equal 3, s(s(:b), s(:c)).mass s = s(s(:call, nil, :a, s(:arglist, s(:lit, 1))), s(:lasgn, :c), s(:call, nil, :d, s(:arglist))) assert_equal 7, s.mass end def test_mass_huge max = 100 sexp = pyramid_sexp max exp = (max*max + max)/2 # pyramid number 1+2+3+...+m assert_equal exp, sexp.mass end def test_method_missing skip_if_strict 3 capture_io do assert_nil @sexp.not_there assert_equal s(:lit, 42), @basic_sexp.lit end end def test_method_missing_missing skip_if_strict 3 skip "debugging for now" if ENV["DEBUG"] assert_silent do assert_nil @basic_sexp.missing end end def test_method_missing_missing_debug skip_if_strict 3 exp = /#{Regexp.escape @basic_sexp.to_s}.method_missing\(:missing\) => nil from/ with_debug do assert_output "", exp do assert_nil @basic_sexp.missing end end end def test_method_missing_hit_debug_verbose skip_if_strict 3 with_debug do with_verbose do exp = /#{Regexp.escape @basic_sexp.to_s}.method_missing\(:lit\) from/ assert_output "", exp do assert_equal s(:lit, 42), @basic_sexp.lit end end end end def test_method_missing_ambigious skip_if_strict 3 assert_raises NoMethodError do pirate = s(:says, s(:arrr!), s(:arrr!), s(:arrr!)) pirate.arrr! end end def test_method_missing_deep skip_if_strict 3 capture_io do sexp = s(:blah, s(:a, s(:b, s(:c, :yay!)))) assert_equal(s(:c, :yay!), sexp.a.b.c) end end def test_method_missing_delete skip_if_strict 3 sexp = s(:blah, s(:a, s(:b, s(:c, :yay!)))) capture_io do assert_equal(s(:c, :yay!), sexp.a.b.c(true)) assert_equal(s(:blah, s(:a, s(:b))), sexp) end end def test_pretty_print assert_pretty_print("s()", s()) assert_pretty_print("s(:a)", s(:a)) assert_pretty_print("s(:a, :b)", s(:a, :b)) assert_pretty_print("s(:a, s(:b))", s(:a, s(:b))) end def test_sexp_body assert_equal s(2, 3), @sexp.sexp_body assert_equal s(), s(:x).sexp_body assert_equal s(), s().sexp_body assert_instance_of Sexp, s().sexp_body end def test_shift skip_if_strict 5 assert_equal(1, @sexp.shift) assert_equal(2, @sexp.shift) assert_equal(3, @sexp.shift) assert_raises(RuntimeError) do @sexp.shift end end def test_deep_clone @sexp = s(:a, 1, 2, s(:b, 3, 4), 5, 6) backup = @sexp.deep_clone refute_same @sexp, backup, "deep clone is broken again?" assert_equal @sexp, backup, "deep clone is broken again?" end def test_structure @sexp = s(:a, 1, 2, s(:b, 3, 4), 5, 6) backup = @sexp.deep_clone refute_same @sexp, backup, "deep clone is broken again?" assert_equal @sexp, backup, "deep clone is broken again?" expected = s(:a, s(:b)) assert_equal(expected, @sexp.structure) assert_equal(backup, @sexp) end def test_structure_deprecated exp_err = "NOTE: form s(s(:subsexp)).structure is deprecated. Removing in 5.0\n" assert_output "", exp_err do sexp = s(s(:subsexp)) act = sexp.structure assert_equal s(:bogus, s(:subsexp)), act end end def test_sub assert_equal s(:c), s(:b). sub(s(:b), s(:c)) assert_equal s(:a, s(:c), s(:b)), s(:a, s(:b), s(:b)). sub(s(:b), s(:c)) assert_equal s(:a, s(:c), s(:a)), s(:a, s(:b), s(:a)). sub(s(:b), s(:c)) end def test_sub_miss assert_equal s(:a), s(:a). sub(s(:b), s(:c)) assert_equal s(:a, s(:c)), s(:a, s(:c)). sub(s(:b), s(:c)) end def test_sub_empty assert_equal s(:c), s(). sub(s(), s(:c)) end def assert_sub exp, sexp, from, to assert_equal exp, sexp.sub(from, to) end def test_sub_matcher assert_sub s(:c), s(:b), s{ q(:b) }, s(:c) assert_sub s(:a, s(:c), s(:b)), s(:a, s(:b), s(:b)), s{ q(:b) }, s(:c) assert_sub s(:a, s(:c), s(:a)), s(:a, s(:b), s(:a)), s{ q(:b) }, s(:c) assert_sub s(:a, :b, :c), s(:a, s(:b, 42), :c), s{ q(:b, _) }, :b assert_sub s(:a, s(:b), :c), s(:a, s(:b), :c), s{ q(:b, _) }, :b assert_sub s(:a, s(:c, :b), :d), s(:a, s(:c, s(:b, 42)), :d), s{ q(:b, _) }, :b assert_sub s(:a, s(:q), :c), s(:a, s(:q), :c), s{ q(:b, _) }, :b end def test_sub_structure assert_sub s(:a, s(:c, s(:b))), s(:a, s(:b)), s(:b), s(:c, s(:b)) end def test_sexp_type_eq sexp = s(:bad_type, 42) sexp.sexp_type = :good_type assert_equal s(:good_type, 42), sexp end def test_sexp_body_eq sexp = s(:type, 42) sexp.sexp_body = [1, 2, 3] assert_equal s(:type, 1, 2, 3), sexp end def test_to_a assert_equal([1, 2, 3], @sexp.to_a) end def test_to_s test_inspect end def test_each_sexp result = [] @basic_sexp.each_sexp { |_, val| result << val } assert_equal [42], result end def test_each_sexp_without_block assert_kind_of Enumerator, @basic_sexp.each_sexp assert_equal [42], @basic_sexp.each_sexp.map { |_, n| n } end def test_depth assert_equal 1, s(:a).depth assert_equal 2, s(:a, s(:b)).depth assert_equal 3, s(:a, s(:b1, s(:c)), s(:b2)).depth assert_equal 5, s(:a, s(:b, s(:c, s(:d, s(:e))))).depth end DEEP_EXP = [:lasgn, :str, :if, :and, :true, :lit, :if, :lvar, :str, :true] def test_deep_each act = [] @complex_sexp.deep_each { |s| act << s } assert_equal DEEP_EXP, act.map { |k, _| k } end def test_deep_each_sexp_recursive sexp = s(:array, s(:lit, 1), nil, 42, s(:array, s(:lit, 2))) result = [] sexp.deep_each { |x| result << x.last if x.sexp_type == :lit } assert_equal [1, 2], result result = [] sexp.each_of_type(:lit) { |x| result << x.last } assert_equal [1, 2], result end def test_deep_each_skip exp = DEEP_EXP.first(3) + DEEP_EXP.last(4) act = [] @complex_sexp.deep_each { |s| next :skip if s.sexp_type == :and; act << s } assert_equal exp, act.map { |k, _| k } end def test_deep_each_without_block assert_kind_of Enumerator, @complex_sexp.deep_each assert_equal DEEP_EXP, @complex_sexp.deep_each.map(&:sexp_type) end def test_unary_not skip "TODO?" assert_equal M::Not.new(M.q(:a)), s{ !s(:a) } end def test_unary_not_outside skip "TODO?" assert_equal M::Not.new(s(:a)), !s(:a) end end # TestSexp class TestSexpMatcher < SexpTestCase def test_cls_s assert_equal M.q(:x), s{ q(:x) } end def test_cls_underscore assert_equal M::Wild.new, s{ _ } end def test_cls_underscore3 assert_equal M::Remaining.new, s{ ___ } end def test_cls_include assert_equal M::Include.new(:a), s{ include(:a) } end def test_cls_atom assert_equal M::Atom.new, s{ atom } end def test_cls_any assert_equal M::Any.new(M.q(:a), M.q(:b)), s{ any(q(:a), q(:b)) } end def test_cls_all assert_equal M::All.new(M.q(:a), M.q(:b)), s{ all(q(:a), q(:b)) } end def test_cls_not_eh assert_equal M::Not.new(M.q(:a)), s{ not?(q(:a)) } end def test_cls_child assert_equal M::Child.new(M.q(:a)), s{ child(q(:a)) } end def test_cls_t assert_equal M::Type.new(:a), s{ t(:a) } end def test_cls_m re = Regexp.union [/\w/, /\d/] assert_equal M::Pattern.new(/a/), s{ m(/a/) } assert_equal M::Pattern.new(/\Aa\Z/), s{ m(:a) } assert_equal M::Pattern.new(/test_\w/), s{ m(/test_\w/) } assert_equal M::Pattern.new(re), s{ m(/\w/,/\d/) } end def test_cls_k assert_equal M::Klass.new(Float), s{ k(Float) } assert_operator M::Klass.new(Float), :===, 6.28 end def test_amp m = s{ q(:a) & q(:b) } e = M::All.new(M.q(:a), M.q(:b)) assert_equal e, m end def test_pipe m = s{ q(:a) | q(:b) } e = M::Any.new(M.q(:a), M.q(:b)) assert_equal e, m end def test_unary_minus assert_equal M::Not.new(M.q(:a)), s{ -q(:a) } end def test_rchevron assert_equal M::Sibling.new(M.q(:a), M.q(:b)), s{ q(:a) >> q(:b) } end def test_greedy_eh refute_operator s{ q(:a) }, :greedy? end def test_inspect assert_inspect "q(:a)", s{ q(:a) } end def test_pretty_print assert_pretty_print "q(:a)", s{ q(:a) } end end # class TestSexpMatcher class TestWild < MatcherTestCase def matcher s{ _ } end def bad_sexp nil end def inspect_str "_" end def test_wild_satisfy_eh # TODO: possibly remove w = Sexp::Wild.new assert_satisfy w, :a assert_satisfy w, 1 assert_satisfy w, nil assert_satisfy w, [] assert_satisfy w, s() end def test_wild_search # TODO: possibly remove sexp = CLASS_SEXP.dup assert_search 1, s(:add, :a, :b), s{ q(:add, _, :b) } assert_search 1, sexp, s{ q(:defn, :bar, _, _) } assert_search 2, sexp, s{ q(:defn, _, _, q(_, :a, :b) ) } assert_search 1, s(:a, s()), s{ q(:a, _) } assert_search 1, s(:a, :b, :c), s{ q(_, _, _) } assert_search 7, sexp, s{ _ } end end class TestRemaining < MatcherTestCase def matcher s{ ___ } end def bad_sexp nil end def inspect_str "___" end def test_remaining_satisfy_eh # TODO: possibly remove assert_satisfy s{ ___ }, s(:a) assert_satisfy s{ ___ }, s(:a, :b, :c) assert_satisfy s{ q(:x, ___ ) }, s(:x, :y) refute_satisfy s{ q(:y, ___ ) }, s(:x, :y) end def test_greedy assert_operator matcher, :greedy? end end class TestAny < MatcherTestCase def matcher s{ q(:a) | q(:c) } end def inspect_str "q(:a) | q(:c)" end def pretty_str "any(q(:a), q(:c))" end def test_any_search # TODO: possibly remove assert_search 2, s(:foo, s(:a), s(:b)), s{ q(any(:a, :b)) } assert_search 1, s(:foo, s(:a), s(:b)), s{ any( q(:a), q(:c)) } end def test_or_satisfy_eh # TODO: possibly remove assert_satisfy s{ q(:a) | q(:b) }, s(:a) refute_satisfy s{ q(:a) | q(:b) }, s(:c) end def test_or_search # TODO: possibly remove sexp = CLASS_SEXP.dup assert_search 2, s(:a, s(:b, :c), s(:b, :d)), s{ q(:b, :c) | q(:b, :d) } assert_search 2, sexp, s{ q(:add, :a, :b) | q(:defn, :bar, _, _) } end end class TestAll < MatcherTestCase def matcher s{ q(:a) & q(:a) } end def inspect_str "q(:a) & q(:a)" end def pretty_str "all(q(:a), q(:a))" end def test_and_satisfy_eh # TODO: possibly remove refute_satisfy s{ q(:a) & q(:b) }, s(:a) assert_satisfy s{ q(:a) & q(atom) }, s(:a) end end class TestNot < MatcherTestCase def matcher s{ not? q(:b) } # TODO: test unary minus end def inspect_str "not?(q(:b))" # TODO: change? end def pretty_str "not?(q(:b))" # TODO: change? end def test_not_satisfy_eh # TODO: possibly remove refute_satisfy s{ -_ }, s(:a) assert_satisfy s{ -q(:b) }, s(:a) assert_satisfy s{ not?(q(:b)) }, s(:a) refute_satisfy s{ -q(atom) }, s(:a) assert_satisfy s{ q(not?(:b)) }, s(:a) end end class TestChild < MatcherTestCase def matcher s{ child(q(:a)) } end def sexp s(:x, s(:b), s(:a)) end def bad_sexp s(:x, s(:b)) end def inspect_str "child(q(:a))" end def test_child_search # TODO: possibly remove sexp = CLASS_SEXP.dup assert_search 1, sexp, s{ q(:class, :cake, _, _, child( q(:sub, :a, :b) ) ) } assert_search 1, sexp, s{ q(:class, :cake, _, _, child(include(:a))) } end def test_satisfy_eh_by_child assert_satisfy matcher, s(:a) assert_satisfy matcher, sexp refute_satisfy matcher, bad_sexp end end class TestAtom < MatcherTestCase def matcher s{ atom } end def sexp 42 end def bad_sexp s(:a) end def inspect_str "atom" end def test_atom_satisfy_eh # TODO: possibly remove a = s{ atom } assert_satisfy a, :a assert_satisfy a, 1 assert_satisfy a, nil refute_satisfy a, s() end def test_atom_search # TODO: possibly remove sexp = CLASS_SEXP.dup assert_search 1, s(:add, :a, :b), s{ q(:add, atom, :b) } assert_search 2, sexp, s{ q(:defn, atom, _, q(atom, :a, :b) ) } assert_search 0, s(:a, s()), s{ q(:a, atom) } end end class TestPattern < MatcherTestCase def matcher s{ q(:a, m(/a/)) } end def sexp s(:a, :blah) end def inspect_str "q(:a, m(/a/))" end def test_pattern_satisfy_eh # TODO: possibly remove assert_satisfy s{ m(/a/) }, :a assert_satisfy s{ m(/^test/) }, :test_case assert_satisfy s{ m("test") }, :test refute_satisfy s{ m("test") }, :test_case refute_satisfy s{ m(/a/) }, s(:a) end def test_pattern_search # TODO: possibly remove sexp = CLASS_SEXP.dup assert_search 2, sexp, s{ q(m(/\w{3}/), :a, :b) } assert_search 0, s(:a), s{ m(/\w/) } assert_search 1, s(:a), s{ q(m(/\w/)) } assert_search 0, s(:a), s{ m(/\w/,/\d/) } assert_search 1, s(:a), s{ q(m(/\w/,/\d/)) } assert_search 0, s(:tests, s(s(:test_a), s(:test_b))), s{ m(/test_\w/) } assert_search 2, s(:tests, s(s(:test_a), s(:test_b))), s{ q(m(/test_\w/)) } end end class TestType < MatcherTestCase def matcher s{ t(:a) } end def test_type_satisfy_eh # TODO: possibly remove assert_satisfy s{ t(:a) }, s(:a) assert_satisfy s{ t(:a) }, s(:a, :b, s(:oh_hai), :d) end def test_type_search assert_search 2, CLASS_SEXP.dup, s{ t(:defn) } end def inspect_str "t(:a)" end end class TestInclude < MatcherTestCase def sexp s(:x, s(:a)) end def matcher s{ include(q(:a)) } end def inspect_str "include(q(:a))" end def test_include_search # TODO: possibly remove sexp = CLASS_SEXP.dup assert_search 1, s(:add, :a, :b), s{ include(:a) } assert_search 1, sexp, s{ include(:bar) } assert_search 2, sexp, s{ q(:defn, atom, _, include(:a)) } assert_search 2, sexp, s{ include(:a) } assert_search 0, s(:a, s(:b, s(:c))), s{ q(:a, include(:c)) } end end class TestSibling < MatcherTestCase def sexp s(:x, s(:a), s(:x), s(:b)) end def matcher s{ q(:a) >> q(:b) } end def inspect_str "q(:a) >> q(:b)" end def test_pretty_print_distance m = s{ M::Sibling.new(q(:a), q(:b), 3) } # maybe q(:a) << q(:b) << 3 ? assert_pretty_print "sibling(q(:a), q(:b), 3)", m end def test_sibling_satisfy_eh # TODO: possibly remove a_a = s{ q(:a) >> q(:a) } a_b = s{ q(:a) >> q(:b) } a_c = s{ q(:a) >> q(:c) } c_a = s{ q(:c) >> q(:a) } assert_satisfy a_b, s(s(:a), s(:b)) assert_satisfy a_b, s(s(:a), s(:b), s(:c)) assert_satisfy a_c, s(s(:a), s(:b), s(:c)) refute_satisfy c_a, s(s(:a), s(:b), s(:c)) refute_satisfy a_a, s(s(:a)) assert_satisfy a_a, s(s(:a), s(:b), s(:a)) end def test_sibling_search # TODO: possibly remove sexp = CLASS_SEXP.dup assert_search 1, sexp, s{ t(:defn) >> t(:defn) } end end class TestMatchCollection < SexpTestCase attr_accessor :sexp, :pat, :act def setup self.sexp = s(:a, :b, :c) self.pat = s{ _ } self.act = sexp / pat end def test_slash self.sexp = s(:class, :cake, nil, s(:defn, :foo, s(:args), s(:add, :a, :b)), s(:defn, :bar, s(:args), s(:sub, :a, :b))) res = sexp / s{ q(:class, atom, _, ___) } # sexp / pat => MC act = res / s{ q(:defn, atom, ___) } # MC / pat => MC _, _, _, defn1, defn2 = sexp exp = MC.new exp << defn1.deep_clone exp << defn2.deep_clone assert_equal exp, act end def test_sanity act = sexp / pat exp = MC.new << sexp assert_equal exp, act end STR = "MatchCollection.new(s(:a, :b, :c))" def test_to_s assert_equal STR, act.to_s end def test_inspect assert_inspect STR, act end def test_pretty_print assert_pretty_print STR, act end end class TestSexpSearch < SexpTestCase attr_accessor :sexp make_my_diffs_pretty! def setup self.sexp = CLASS_SEXP.dup end def coll *args exp = MC.new args.each_slice 2 do |sexp, hash| exp << res(sexp, hash) end exp end def res sexp, hash MR.new sexp.deep_clone, hash end def test_sexp_hash s1 = s(:a) s2 = s(nil) refute_equal s1.hash, s2.hash end def test_matcher_inspect pat1 = M.parse "(a [m /b/] (c))" assert_equal "q(:a, m(/b/), q(:c))", pat1.inspect end def test_matcher_hash a = M::Pattern.new(/x/) # M.parse "[m /x/]" b = M::Atom.new # M.parse "_" refute_operator a, :eql?, b refute_equal a.hash, b.hash h = {} h[a] = true refute_operator h, :key?, b # original problem: a = M.parse "(call nil assert_equal (true) (call _ [m /include./] _))" b = M.parse "(call nil assert_equal (true) (call _ _ _))" refute_operator a, :eql?, b refute_equal a.hash, b.hash h = {} h[a] = true refute_operator h, :key?, b end def test_slash_simple act = sexp / s{ q(:class, atom, _, ___) } exp = MC.new exp << sexp.deep_clone assert_equal exp, act end def test_slash_subsexp act = sexp / s{ q(:defn, atom, ___) } exp = MC.new exp << s(:defn, :foo, s(:args), s(:add, :a, :b)) exp << s(:defn, :bar, s(:args), s(:sub, :a, :b)) assert_equal exp, act end def test_slash_data pat = s{ q(:defn, m(/^test_.+/), ___ ) } _, _, (_klass, _, _, _setup, t1, t2, t3) = TestUseCase.sexp.deep_clone exp = [t1, t2, t3] assert_equal exp, TestUseCase.sexp.deep_clone / pat end def test_search_each_no_block assert_kind_of Enumerator, sexp.search_each(s{_}) assert_equal 7, sexp.search_each(s{_}).count assert_equal 2, sexp.search_each(s{t(:defn)}).count assert_search 7, sexp, s{_} assert_search 2, sexp, s{t(:defn)} _, _, _, defn1, defn2 = sexp mc = [] mc << defn1.deep_clone mc << defn2.deep_clone assert_equal mc, sexp.search_each(s{t(:defn)}).map { |x| x } end def test_searching_simple_examples # TODO: possibly remove assert_raises ArgumentError do assert_search 0, sexp, :class # non-pattern should raise end assert_search 0, sexp, s{ q(:class) } assert_search 1, sexp, s{ q(:add, :a, :b) } assert_search 1, s(:a, s(:b, s(:c))), s{ q(:b, q(:c)) } assert_search 0, s(:a, s(:b, s(:c))), s{ q(:a, q(:c)) } assert_search 1, sexp, s{ q(:defn, :bar, _, q(:sub, :a, :b)) } end def test_satisfy_eh_any_capture # TODO: remove sexp = s(:add, :a, :b) assert_satisfy s{ any(q(:add, :a, :b), q(:sub, :a, :b)) }, sexp assert_satisfy s{ any(q(atom, :a, :b), q(:sub, :a, :b)) }, sexp end def test_satisfy_eh_all_capture # TODO: remove sexp = s(:add, :a, :b) assert_satisfy s{ all(q(_, :a, :b), q(atom, :a, :b)) }, sexp assert_satisfy s{ all(q(_, :a, :b), q(atom, :a, :b)) }, sexp assert_search 1, sexp, s{ all(q(_, :a, :b), q(atom, :a, :b)) } end end class TestSexpPath < Minitest::Test def test_global_s_block sexp = s(:a, :b, :c) # s called outside block assert_instance_of Sexp, s{ sexp.deep_clone } assert_instance_of Sexp::Matcher, s{ q(:a, :b, :c) } assert_instance_of Sexp::Matcher, s{ q(:a, atom, :c) } end end class TestSexpReplaceSexp < SexpTestCase def test_replace_sexp sexp = s(:a, s(:b), :c) actual = sexp.replace_sexp(s{ q(:b) }) { :b } assert_equal s(:a, :b, :c), actual end def test_replace_sexp_root sexp = s(:a, s(:b), :c) actual = sexp.replace_sexp(s{ t(:a) }) { s(:new) } assert_equal s(:new), actual end def test_replace_sexp_yields_match_result sexp = s(:a, s(:b), :c) exp = sexp.deep_clone sexp.replace_sexp(s{ t(:a) }) { |x| assert_equal exp, x } end def test_replace_sexp_non_matcher e = assert_raises ArgumentError do s(:a, s(:b), :c).replace_sexp(42) { :b } end assert_equal "Needs a pattern", e.message end def test_search_each_yields_match_result sexp = s(:a, s(:b), :c) exp = sexp.deep_clone sexp.search_each(s{ t(:a) }) { |x| assert_equal exp, x } end def test_search_each_no_pattern e = assert_raises ArgumentError do s(:a, s(:b), :c).search_each(42) { :b } end assert_equal "Needs a pattern", e.message end end # Here's a crazy idea, these tests actually use sexp_path on some "real" # code to see if it can satisfy my requirements. # # These tests are two fold: # 1. Make sure it works # 2. Make sure it's not painful to use class TestUseCase < SexpTestCase @@sexp = eval File.read(__FILE__).split(/^__END__/).last def self.sexp @@sexp end def setup @sexp = @@sexp.deep_clone end def test_finding_methods methods = @sexp / s{ t(:defn) } assert_equal 5, methods.length end def test_finding_classes_and_methods res = @sexp / s{ q(:class, atom, ___ ) } _klass, name, * = res.first assert_equal 1, res.length assert_equal :ExampleTest, name methods = res / s{ t(:defn) } assert_equal 5, methods.length end def test_finding_empty_test_methods empty_test = s{ q(:defn, m(/^test_.+/), q(:args), q(:nil)) } res = @sexp / empty_test _, _, (_klass, _, _, _setup, _t1, t2, _t3) = TestUseCase.sexp.deep_clone assert_equal [t2], res end def test_search_each_finding_duplicate_test_names pat = s{ q(:defn, m(/^test_.+/), ___ ) } counts = Hash.new { |h, k| h[k] = 0 } @sexp.search_each pat do |x| _, name, * = x counts[name] += 1 end assert_equal 1, counts[:test_b], "Should have seen test_b once" assert_equal 2, counts[:test_a], "Should have caught test_a being repeated" end def test_finding_duplicate_test_names_via_res pat = s{ q(:defn, m(/^test_.+/), ___ ) } res = @sexp / pat counts = Hash.new { |h, k| h[k] = 0 } _, _, (_klass, _, _, _setup, t1, t2, t3) = TestUseCase.sexp.deep_clone exp = [t1, t2, t3] assert_equal exp, res res.each do |m| _, name, * = m counts[name] += 1 end assert_equal 1, counts[:test_b], "Should have seen test_b once" assert_equal 2, counts[:test_a], "Should have caught test_a being repeated" end def test_rewriting_colon2s colon2 = s{ q(:colon2, q(:const, atom), atom) } expected = s{ q(:const, "Minitest::Test") } new_sexp = @sexp.replace_sexp(colon2) { |r| (_, (_, a), b) = r s(:const, "%s::%s" % [a, b]) } assert_search 1, new_sexp, expected assert_search 0, @sexp, expected end end ## # NOTE: this entire class is now redundant, but it illustrates usage # and edge cases well. class TestSexpMatchers < SexpTestCase CLASS_LIT = s(:class, :X, nil, s(:lasgn, :x, s(:lit, 42)), s(:cdecl, :Y, s(:hash, s(:lit, :a), s(:lit, 1), s(:lit, :b), s(:lit, 2)))) SEXP = s(:class, :X, nil, s(:defn, :x, s(:args))) def test_match_subset assert_match s{ child(q(:a)) }, s(:blah, s(:blah, s(:a))) assert_match s{ child(q(:a)) }, s(:a) end def test_match_simple assert_match s{ q(:lit, _) }, s(:lit, 42) end def test_match_mismatch_type refute_match s{ q(:xxx, 42) }, s(:lit, 42) end def test_match_mismatch_data refute_match s{ q(:lit, 24) }, s(:lit, 42) end def test_match_mismatch_length_shorter refute_match s{ q(:a, :b) }, s(:a, :b, :c) end def test_match_mismatch_length_longer refute_match s{ q(:a, :b, :c) }, s(:a, :b) end def test_match_wild assert_match s{ q(:class, _, _, _) }, SEXP end def test_match_rest_same_length assert_match s{ q(:class, _, _, ___) }, SEXP end def test_match_rest_diff_length skip_if_strict assert_match s{ q(:class, ___) }, SEXP end def test_match_reversed assert_match SEXP, s{ q(:class, _, _, ___) } end def assert_match_case pat, data case data when pat then assert true else flunk "Expected %p to match %p" % [pat, data] end end def test_match_case assert_match_case s{ q(:class, _, _, ___) }, SEXP end # NOTE: eqt is =~ (equal-tilde) # cmt = create_match_test def self.cmt e1, e2, e3, e4, lit, pat Class.new SexpTestCase do attr_accessor :lit, :pat define_method :setup do self.lit = lit self.pat = pat end define_method :test_match_lit_eqt_pat do skip_if_strict if e1 then assert_match lit, pat else refute_match lit, pat end end define_method :test_match_pat_eqt_lit do skip_if_strict if e2 then assert_match pat, lit else refute_match pat, lit end end define_method :test_match_lit_eq3_pat do if e3 then assert_equal3 lit, pat else refute_equal3 lit, pat end end define_method :test_match_pat_eq3_lit do if e4 then assert_equal3 pat, lit else refute_equal3 pat, lit end end end end l_a = s(:a) l_abc = s(:a, s(:b, s(:c))) l_cls = s(:class, :X, nil, s(:something_in_between), s(:cdecl, :Y, s(:hash, s(:lit, :a), s(:lit, 1)))) p_cls1 = s{ q(:class, ___) & include(q(:cdecl, _, q(:hash, ___))) } p_cls2 = s{ q(:class, _, _, q(:cdecl, _, q(:hash, ___))) } x, o = true, false TestMatcherDirectMatch = cmt x, x, o, x, l_a, s{ q(:a) } TestMatcherSubtree = cmt x, x, o, x, l_abc, s{ q(:c) } TestMatcherSubtreeType = cmt x, x, o, x, l_abc, s{ t(:c) } TestMatcherDisparateSubtree = cmt x, x, o, x, l_cls, p_cls1 TestMatcherDisparateSubtree2 = cmt o, o, o, o, l_cls, p_cls2 # TODO: make pass end class TestSexpMatcherParser < Minitest::Test def assert_parse exp, str act = Sexp::Matcher.parse str if exp.nil? then assert_nil act else assert_equal exp, act end end def self.test_parse name, exp_lambda, str define_method "test_parse_#{name}" do exp = exp_lambda && exp_lambda.call assert_parse exp, str end end def self.test_bad_parse name, str define_method "test_parse_bad_#{name}" do assert_raises SyntaxError do assert_parse :whatever, str end end end def self.delay &b lambda { s(&b) } end re = /(?-mix:\Aa\Z)|(?-mix:\Ab\Z)|(?-mix:\Ac\Z)/ # [m a b c] test_parse "nothing", nil, "" test_parse "nil", delay{ nil }, "nil" test_parse "empty", delay{ q() }, "()" test_parse "simple", delay{ q(:a) }, "(a)" test_parse "number", delay{ q(:a, 42) }, "(a 42)" test_parse "string", delay{ q(:a, "s") }, "(a \"s\")" test_parse "compound", delay{ q(:b) }, "(a) (b)" test_parse "complex", delay{ q(:a, _, q(:b, :cde), ___) }, "(a _ (b cde) ___)" test_parse "type", delay{ q(:a, t(:b)) }, "(a [t b])" test_parse "match", delay{ q(:a, m(/b/)) }, "(a [m /b/])" test_parse "not_atom", delay{ q(:atom) }, "(atom)" test_parse "atom", delay{ atom }, "[atom]" test_parse "match_n", delay{ m(re) }, "[m a b c]" test_parse "ne", delay{ q(:call, _, :!=, _) }, "(call _ != _)" test_parse "eq", delay{ q(:call, _, :==, _) }, "(call _ == _)" test_parse "not_call", delay{ q(:call, _, :"!") }, "(call _ !)" test_parse "eh", delay{ q(:call, _, :include?, _) }, "(call _ include? _)" test_parse "under", delay{ q(:call, nil, :_, _) }, "(call nil :_ _)" test_parse "sym_nil", delay{ q(:call, nil, :nil, _) }, "(call nil :nil _)" test_parse "not?", delay{ not?(m(/^_$/)) }, "[not? [m /^_$/]]" test_parse "not2", delay{ -_ }, "[- _]" test_parse "any", delay{ q(:a) | q(:b) }, "[any (a) (b)]" test_parse "child", delay{ child(q(:str, m(/woot/))) }, "[child (str [m /woot/])]" test_parse "klass", delay{ q(:lit, k(Float)) }, "(lit [k Float])" test_parse "const", delay{ q(:const, :Float) }, "(const :Float)" test_bad_parse "open_sexp", "(a" test_bad_parse "closed_sexp", "a)" test_bad_parse "open_cmd", "[a" test_bad_parse "closed_cmd", "a]" end # class TestSexpMatcherParser class BenchSexp < Minitest::Benchmark def run GC.disable super ensure GC.enable end def self.bench_range bench_linear 100, 500, 50 end @@data = Hash[bench_range.map { |n| [n, pyramid_sexp(n)] }] def bench_pyramid assert_performance_power do |max| pyramid_sexp max end end def bench_mass assert_performance_power do |max| @@data[max].mass end end end if ENV["BENCH"] # class ExampleTest < Minitest::Test # def setup # 1 + 2 # end # # def test_a # assert_equal 1+2, 4 # end # # def test_b # # assert 1+1 # end # # def test_a # assert_equal 1+2, 3 # end # # private # # def helper_method apples, oranges, cakes = nil # [apples, oranges, cakes].compact.map { |food| food.to_s.upcase } # end # end __END__ s(:block, s(:call, nil, :require, s(:str, "minitest/autorun")), s(:class, :ExampleTest, s(:colon2, s(:const, :Minitest), :Test), s(:defn, :setup, s(:args), s(:call, s(:lit, 1), :+, s(:lit, 2))), s(:defn, :test_a, s(:args), s(:call, nil, :assert_equal, s(:call, s(:lit, 1), :+, s(:lit, 2)), s(:lit, 4))), s(:defn, :test_b, s(:args), s(:nil)), s(:defn, :test_a, s(:args), s(:call, nil, :assert_equal, s(:call, s(:lit, 1), :+, s(:lit, 2)), s(:lit, 3))), s(:call, nil, :private), s(:defn, :helper_method, s(:args, :apples, :oranges, s(:lasgn, :cakes, s(:nil))), s(:iter, s(:call, s(:call, s(:array, s(:lvar, :apples), s(:lvar, :oranges), s(:lvar, :cakes)), :compact), :map), s(:args, :food), s(:call, s(:call, s(:lvar, :food), :to_s), :upcase))))) sexp_processor-4.17.0/test/test_composite_sexp_processor.rb0000555000004100000410000000246314443772242024433 0ustar www-datawww-data$TESTING = true require "composite_sexp_processor" require "minitest/autorun" class FakeProcessor1 < SexpProcessor # ZenTest SKIP def initialize super self.warn_on_default = false self.default_method = :default_processor self.require_empty = false self.expected = Array end def default_processor exp t, *rest = exp s(t, *rest.map { |s| "#{s} woot" }) end end class TestCompositeSexpProcessor < Minitest::Test def setup @p = CompositeSexpProcessor.new end def test_process_default data = s(1, 2, 3) result = @p.process(data.dup) assert_equal(data.dup, result) end def test_process_fake1 data = s(:x, 1, 2, 3) @p << FakeProcessor1.new result = @p.process(data.dup) assert_equal [:x, "1 woot", "2 woot", "3 woot"], result end def test_process_fake1_twice data = s(:x, 1, 2, 3) @p << FakeProcessor1.new @p << FakeProcessor1.new result = @p.process(data.dup) assert_equal [:x, "1 woot woot", "2 woot woot", "3 woot woot"], result end def test_processors # everything is tested by test_append skip end def test_append assert_equal([], @p.processors) assert_raises(ArgumentError) do @p << 42 end fp1 = FakeProcessor1.new @p << fp1 assert_equal([fp1], @p.processors) end end sexp_processor-4.17.0/test/test_sexp_processor.rb0000555000004100000410000002170014443772242022344 0ustar www-datawww-data$TESTING = true if ENV["COV"] require "simplecov" SimpleCov.start do add_filter "lib/sexp.rb" add_filter "test" end warn "Running simplecov" end require "sexp_processor" require "stringio" require "minitest/autorun" require "pp" # Fake test classes: class TestProcessor < SexpProcessor # ZenTest SKIP attr_accessor :auto_shift_type def initialize super self.require_empty = false self.auto_shift_type = false end def process_acc1 exp out = self.expected.new(:acc2, exp.thing_three, exp.thing_two, exp.thing_one) exp.clear out end def process_acc2 exp out = s() out << exp.thing_one end def process_specific exp _, *data = exp s(:blah, *data.map { |x| process x }) end def process_strip exp exp.deep_clone end def process_nonempty exp s(*exp) end def process_broken exp result = [*exp] exp.clear result end def process_expected exp exp.clear {} end def process_string exp exp.sexp_type end def rewrite_rewritable exp # (a b c) => (a c b) a, b, c = exp s(a, c, b) end def process_rewritable exp _, *data = exp @n ||= 0 result = s(:rewritten, *data.map { |x| process x }, @n) @n += 1 result end def rewrite_major_rewrite exp exp.sexp_type = :rewritable exp end end class TestProcessorDefault < SexpProcessor # ZenTest SKIP def initialize super self.default_method = :def_method end def def_method exp exp.clear self.expected.new(42) end end # Real test classes: class TestSexpProcessor < Minitest::Test def setup @processor = TestProcessor.new end def test_process_specific a = s(:specific, s(:x, 1), s(:y, 2), s(:z, 3)) expected = s(:blah, s(:x, 1), s(:y, 2), s(:z, 3)) assert_equal(expected, @processor.process(a)) end def test_process_generic a = s(:blah, 1, 2, 3) expected = a.deep_clone assert_equal(expected, @processor.process(a)) end def test_process_default @processor = TestProcessorDefault.new @processor.warn_on_default = false a = s(:blah, 1, 2, 3) assert_equal(@processor.expected.new(42), @processor.process(a)) end def test_process_not_sexp @processor = TestProcessor.new @processor.warn_on_default = false assert_raises SexpTypeError do @processor.process(s(:broken, 1, 2, 3)) end end def test_process_unsupported_wrong @processor = TestProcessor.new @processor.unsupported << :strip assert_raises UnsupportedNodeError do @processor.process(s(:whatever)) end end def test_unsupported_equal @processor.strict = true @processor.unsupported = [ :unsupported ] assert_raises UnsupportedNodeError do @processor.process(s(:unsupported, 42)) end end def test_strict @processor.strict = true assert_raises UnknownNodeError do @processor.process(s(:blah, 1, 2, 3)) end end def test_strict=; skip; end # handled def test_require_empty_false @processor.require_empty = false assert_equal s(:nonempty, 1, 2, 3), @processor.process(s(:nonempty, 1, 2, 3)) end def test_require_empty_true assert_raises NotEmptyError do @processor.require_empty = true @processor.process(s(:nonempty, 1, 2, 3)) end end def test_require_empty=; skip; end # handled def test_process_strip @processor.auto_shift_type = true assert_equal([1, 2, 3], @processor.process(s(:strip, 1, 2, 3))) end def test_rewrite assert_equal(s(:rewritable, :b, :a), @processor.rewrite(s(:rewritable, :a, :b))) end def test_rewrite_different_type assert_equal(s(:rewritable, :b, :a), @processor.rewrite(s(:major_rewrite, :a, :b))) end def test_rewrite_deep assert_equal(s(:specific, s(:rewritable, :b, :a)), @processor.rewrite(s(:specific, s(:rewritable, :a, :b)))) end def test_rewrite_not_empty insert = s(:rewritable, 1, 2, 2) expect = s(:rewritable, 2, 1) result = @processor.rewrite(insert) assert_equal(expect, result) end def test_process_rewrite assert_equal(s(:rewritten, s(:y, 2), s(:x, 1), 0), @processor.process(s(:rewritable, s(:x, 1), s(:y, 2)))) end def test_process_rewrite_deep assert_equal(s(:blah, s(:rewritten, s(:b), s(:a), 0)), @processor.process(s(:specific, s(:rewritable, s(:a), s(:b))))) end def test_rewrite_depth_first inn = s(:specific, s(:rewritable, s(:a), s(:rewritable, s(:rewritable, s(:b), s(:c)), s(:d)))) out = s(:specific, s(:rewritable, s(:rewritable, s(:d), s(:rewritable, s(:c), s(:b))), s(:a))) assert_equal(out, @processor.rewrite(inn)) end def test_process_rewrite_depth_first inn = s(:specific, s(:rewritable, s(:a), s(:rewritable, s(:rewritable, s(:b), s(:c)), s(:d)))) out = s(:blah, s(:rewritten, s(:rewritten, s(:d), s(:rewritten, s(:c), s(:b), 0), 1), s(:a), 2)) assert_equal(out, @processor.process(inn)) end def test_assert_type_hit assert_nil @processor.assert_type(s(:blah, 1, 2, 3), :blah) end def test_assert_type_miss assert_raises SexpTypeError do @processor.assert_type(s(:thingy, 1, 2, 3), :blah) end end def test_generate skip "nothing to test at this time... soon." end def test_auto_shift_type @processor.auto_shift_type = false assert_equal(false, @processor.auto_shift_type) @processor.auto_shift_type = true assert_equal(true, @processor.auto_shift_type) end def test_auto_shift_type_equal; skip; end # handled def test_default_method # default functionality tested in process_default assert_nil @processor.default_method @processor.default_method = :something assert_equal :something, @processor.default_method end def test_default_method=; skip; end # handled def test_expected assert_equal Sexp, @processor.expected assert_raises SexpTypeError do @processor.process(s(:expected)) # should raise end @processor.process(s(:str, "string")) # shouldn't raise @processor.expected = Hash assert_equal Hash, @processor.expected assert !(Hash === s()), "Hash === s() should not be true" assert_raises SexpTypeError do @processor.process(s(:string, "string")) # should raise end @processor.process(s(:expected)) # shouldn't raise end def test_expected=; skip; end # handled # Not Testing: def test_debug; skip; end def test_debug=; skip; end def test_warn_on_default; skip; end def test_warn_on_default=; skip; end end class TestMethodBasedSexpProcessor < Minitest::Test attr_accessor :processor def setup self.processor = MethodBasedSexpProcessor.new end def test_in_klass assert_empty processor.class_stack processor.in_method "method", "file", 42 do processor.in_klass "xxx::yyy" do assert_equal ["xxx::yyy"], processor.class_stack assert_empty processor.method_stack end end assert_empty processor.class_stack end def test_in_method assert_empty processor.method_stack processor.in_method "xxx", "file.rb", 42 do assert_equal ["xxx"], processor.method_stack end assert_empty processor.method_stack expected = { "main#xxx" => "file.rb:42" } assert_equal expected, processor.method_locations end def test_in_method_line_max assert_empty processor.method_stack processor.in_method "xxx", "file.rb", 42, 44 do assert_equal ["xxx"], processor.method_stack end assert_empty processor.method_stack expected = { "main#xxx" => "file.rb:42-44" } assert_equal expected, processor.method_locations end def test_klass_name assert_equal :main, processor.klass_name processor.class_stack << "whatevs" << "flog" assert_equal "flog::whatevs", processor.klass_name end def test_klass_name_sexp processor.in_klass s(:colon2, s(:const, :X), :Y) do assert_equal "X::Y", processor.klass_name end processor.in_klass s(:colon3, :Y) do assert_equal "Y", processor.klass_name end end def test_method_name assert_equal "#none", processor.method_name processor.method_stack << "whatevs" assert_equal "#whatevs", processor.method_name end def test_method_name_cls assert_equal "#none", processor.method_name processor.method_stack << "::whatevs" assert_equal "::whatevs", processor.method_name end def test_signature assert_equal "main#none", processor.signature processor.class_stack << "X" assert_equal "X#none", processor.signature processor.method_stack << "y" assert_equal "X#y", processor.signature processor.class_stack.shift assert_equal "main#y", processor.signature end end sexp_processor-4.17.0/sexp_processor.gemspec0000644000004100000410000000661414443772242021354 0ustar www-datawww-data######################################################### # This file has been automatically generated by gem2tgz # ######################################################### # -*- encoding: utf-8 -*- # stub: sexp_processor 4.17.0 ruby lib Gem::Specification.new do |s| s.name = "sexp_processor".freeze s.version = "4.17.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/sexp_processor" } if s.respond_to? :metadata= s.require_paths = ["lib".freeze] s.authors = ["Ryan Davis".freeze] s.cert_chain = ["-----BEGIN CERTIFICATE-----\nMIIDPjCCAiagAwIBAgIBBzANBgkqhkiG9w0BAQsFADBFMRMwEQYDVQQDDApyeWFu\nZC1ydWJ5MRkwFwYKCZImiZPyLGQBGRYJemVuc3BpZGVyMRMwEQYKCZImiZPyLGQB\nGRYDY29tMB4XDTIzMDEwMTA3NTExN1oXDTI0MDEwMTA3NTExN1owRTETMBEGA1UE\nAwwKcnlhbmQtcnVieTEZMBcGCgmSJomT8ixkARkWCXplbnNwaWRlcjETMBEGCgmS\nJomT8ixkARkWA2NvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALda\nb9DCgK+627gPJkB6XfjZ1itoOQvpqH1EXScSaba9/S2VF22VYQbXU1xQXL/WzCkx\ntaCPaLmfYIaFcHHCSY4hYDJijRQkLxPeB3xbOfzfLoBDbjvx5JxgJxUjmGa7xhcT\noOvjtt5P8+GSK9zLzxQP0gVLS/D0FmoE44XuDr3iQkVS2ujU5zZL84mMNqNB1znh\nGiadM9GHRaDiaxuX0cIUBj19T01mVE2iymf9I6bEsiayK/n6QujtyCbTWsAS9Rqt\nqhtV7HJxNKuPj/JFH0D2cswvzznE/a5FOYO68g+YCuFi5L8wZuuM8zzdwjrWHqSV\ngBEfoTEGr7Zii72cx+sCAwEAAaM5MDcwCQYDVR0TBAIwADALBgNVHQ8EBAMCBLAw\nHQYDVR0OBBYEFEfFe9md/r/tj/Wmwpy+MI8d9k/hMA0GCSqGSIb3DQEBCwUAA4IB\nAQAkg3y+PBnBAPWdxxITm5sPHqdWQgSyCpRA20o4LTuWr8BWhSXBkfQNa7cY6fOn\nxyM34VPzBFbExv6XOGDfOMFBVaYTHuN9peC/5/umL7kLl+nflXzL2QA7K6LYj5Bg\nsM574Onr0dZDM6Vn69bzQ7rBIFDfK/OhlPzqKZad4nsdcsVH8ODCiT+ATMIZyz5K\nWCnNtqlyiWXI8tdTpahDgcUwfcN/oN7v4K8iU5IbLJX6HQ5DKgmKjfb6XyMth16k\nROfWo9Uyp8ba/j9eVG14KkYRaLydAY1MNQk2yd3R5CGfeOpD1kttxjoypoUJ2dOG\nnsNBRuQJ1UfiCG97a6DNm+Fr\n-----END CERTIFICATE-----\n".freeze] s.date = "2023-05-03" s.description = "sexp_processor branches from ParseTree bringing all the generic sexp\nprocessing tools with it. Sexp, SexpProcessor, Environment, etc... all\nfor your language processing pleasure.".freeze s.email = ["ryand-ruby@zenspider.com".freeze] s.extra_rdoc_files = ["History.rdoc".freeze, "Manifest.txt".freeze, "README.rdoc".freeze] s.files = ["History.rdoc".freeze, "Manifest.txt".freeze, "README.rdoc".freeze, "Rakefile".freeze, "lib/composite_sexp_processor.rb".freeze, "lib/pt_testcase.rb".freeze, "lib/sexp.rb".freeze, "lib/sexp_matcher.rb".freeze, "lib/sexp_processor.rb".freeze, "lib/strict_sexp.rb".freeze, "lib/unique.rb".freeze, "test/test_composite_sexp_processor.rb".freeze, "test/test_environment.rb".freeze, "test/test_sexp.rb".freeze, "test/test_sexp_processor.rb".freeze] s.homepage = "https://github.com/seattlerb/sexp_processor".freeze s.licenses = ["MIT".freeze] s.rdoc_options = ["--main".freeze, "README.rdoc".freeze] s.required_ruby_version = Gem::Requirement.new([">= 2.6".freeze, "< 4".freeze]) s.rubygems_version = "3.2.5".freeze s.summary = "sexp_processor branches from ParseTree bringing all the generic sexp processing tools with it".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, ["~> 4.0"]) s.add_development_dependency(%q.freeze, [">= 4.0", "< 7"]) else s.add_dependency(%q.freeze, ["~> 4.0"]) s.add_dependency(%q.freeze, [">= 4.0", "< 7"]) end end sexp_processor-4.17.0/data.tar.gz.sig0000444000004100000410000000040014443772242017533 0ustar www-datawww-data)]pl&-rۉas7)/pBB}aǡp=e\|= 4 * Added deprecation message to Sexp#structure for [s(...)] forms. * Added strict_sexp.rb to help you clamp down for future changes. STRICT_SEXP=1+ * Auto-require strict_sexp if $STRICT_SEXP is > 0. * Converted a lot of indexed access to sexp_type/sexp_body, etc. * Finally enforced SexpProcessor#process to only process sexps, not bare arrays. * Made Sexp#/ double-dispatch to Matcher#/. * Made Sexp#gsub work with new patterns. * Made Sexp#sub work with new patterns. * Made SexpProcessor STRICT_SEXP=4 compliant. * Retired SexpMatchSpecial & SexpAny. Never used by anything AFAICT. * Sexp#=== goes back to default. * Sexp#=~(pat) calls pat =~ self. * Sexp#sexp_body now takes optional offset. Use instead of sexp[n..-1]. * 9 bug fixes: * Extended Sexp::Matcher::Parser.parse to lex more forms of regexp. * Finished off all missing doco. * Fixed == methods on all Matcher classes to include ivars. * Fixed Child#satisfy? to properly return false if failed. * Fixed Sexp#sexp_body to return a sexp using Sexp#new. * Fixed map to use Sexp#new. * Only try to set c_type if it responds to it. Make STRICT_SEXP safe. * R2C has a hack in SexpProcessor to call sexp_type=. Renamed to c_type= in R2C. * Removed very obsolete attrset test from pt_testcase.rb === 4.10.0b1 / 2017-06-13 Beta of the above. === 4.9.0 / 2017-04-13 * 9 minor enhancements: * Added Sexp.depth * Added Sexp.sexp_type= * Cache Sexp.line_max. Massively speeds up large flay runs. * Cleaned up SexpProcessor.process handling of result node type. * Extend pt_testcase for ruby 2.4 tests. * Extended Sexp.method_missing to only print on every invocation if $VERBOSE=1 * Extended Sexp.method_missing to warn if the expected sub-sexp is not found. * Rewrote Sexp.mass to be MUCH faster. Helps tremendously with flay on large files. * Warn that Sexp#method_missing was tripped if $DEBUG. === 4.8.0 / 2017-02-01 * 2 minor enhancements: * Added Sexp#line_max * Extended MethodBasedSexpProcessor#in_method to take line_max and record span. === 4.7.0 / 2016-02-18 * 2 minor enhancements: * Expand to support 2.3 in tests. (presidentbeef) * Return enumerable for deep_each, each_sexp, and each_of_type. (ridiculous) === 4.6.1 / 2016-01-21 * 1 bug fix: * defs should have a nil node if body is empty. === 4.6.0 / 2015-05-28 * 2 minor enhancements: * Extended generate_test to deal with 19 and up. * Extended pt_testcase.rb so add_19tests means 19 and up. * 1 bug fix: * Added and normalized tests to deal with canonicalized block args from ruby_parser. === 4.5.1 / 2015-04-27 * 1 minor enhancement: * Cache processors and rewriters. Significant speedup. (presidentbeef) === 4.5.0 / 2015-03-09 * 1 minor enhancement: * Added SexpProcessor::expand_dirs_to_files as a utility to cmdline tools. === 4.4.5 / 2015-01-16 * 1 bug fix: * Removed shebangs in tests because of bugs (aka 'features') in RPM packaging tools. === 4.4.4 / 2014-08-14 * 1 bug fix: * MethodBasedSexpProcessor#in_klass clears out the method_stack for the duration of the block. === 4.4.3 / 2014-03-24 * 1 bug fix: * Fixed a bunch of pt_testcase entries for 1.9/2.0 wrt ruby2ruby. === 4.4.2 / 2014-03-14 * 2 minor enhancements: * Changed skipped versioned tests to return, not skip. Reduces noise so you can focus on real skips. * Extended versioned tests to include 2.0 and 2.1. === 4.4.1 / 2013-12-13 * 1 bug fix: * Added parenthesis to fix the structure_remove_begin_1 testcase. (bocete) === 4.4.0 / 2013-10-18 * 1 minor enhancement: * Added MethodBasedSexpProcessor, extracted from Flog. === 4.3.0 / 2013-08-19 * 1 minor enhancement: * Switched ParseTreeTestCase to minitest 5. === 4.2.1 / 2013-04-09 * 1 bug fix: * Removed structure_unused_literal_wwtt because I just don't care anymore === 4.2.0 / 2013-03-18 * 2 minor enhancements: * Added SexpInterpreter since it is such a common pattern * Added aliases Sexp#head & Sexp#rest for readability === 4.1.5 / 2013-02-14 * 2 bug fixes: * Clarified role of s method. Fixes #12. * maglev: Workaround for bug in Array#shift === 4.1.4 / 2013-01-22 * 1 minor enhancement: * Gave Sexp#structure a ~10% boost * 2 bug fixes: * Fixed Sexp#mass lying when using auto_shifted sexps. * Stupid fix for ruby 2.0 method_missing 'bug'. === 4.1.3 / 2012-12-06 * 2 bug fixes: * Code cleanup... what was I thinking??? * Explicitly setting pt_testcase.rb to US-ASCII to avoid encoding hell === 4.1.2 / 2012-11-03 * 1 bug fix: * Sexp#structure should grep for Sexps, not Arrays === 4.1.1 / 2012-11-02 * 1 minor enhancement: * iter args revamp for ruby_parser 3 changes. === 4.1.0 / 2012-10-22 * 1 minor enhancement: * Added Environment#current so you can explicitly shadow variables. (meh) === 4.0.1 / 2012-07-03 * 4 minor enhancements: * 1.9: Had to version split all my 'not' tests since 1.9 is insane. * 1.9: f (args) is not allowed in 1.9. * 1.9: f(hash, *splat) is not allowed in 1.9. * Added module2 test to verify module w/ 2+ entities * 2 bug fixes: * 1.9: Changed !@ to ! since that is what you implement * True to my wtf comment, there was a bug in my case_splat test === 4.0.0 / 2012-06-07 * 5 major enhancements: * Removed :arglist from everything except :op_asgn1. * Removed block from resbody * Removed block from when node * Removed block nodes inside of scope nodes (defn/defs/class/sclass). * Removed scope nodes in defn/defs/class/sclass nodes. * 1 minor enhancement: * Added Sexp#deep_each and Sexp#each_sexp. Refactored from Flay === 3.2.0 / 2012-04-15 * 5 minor enhancements: * Added a ton of block arg tests. * Added add19_edgecases to help refactor a bunch of tests that all have the same output. * Added better debugging output for rewrites. * Cleaned and added a bunch of stabby proc tests. * Moved RawParseTree test data to ParseTree project. * 2 bug fixes: * Fixed a bunch of entries for r2r changes against edgecase parse/lex tests * Fixes for R2R === 3.1.0 / 2012-02-29 * 4 minor enhancements: * Added test_call_arglist_trailing_comma__19 (presidentbeef) * Added test_fcall_inside_parens. (presidentbeef) * Added test_if_args_no_space_symbol__18. (presidentbeef) * Added tests for new hash syntax and ternaries in 1.9 (lastobelus) === 3.0.10 / 2012-01-04 * 1 minor enhancement: * Added test for bare hash at end of array in 1.9. (presidentbeef) * 1 bug fix: * Fixed 1.9.3 warnings === 3.0.9 / 2011-12-07 * 1 minor enhancement: * Add missing test for a ternary with nil without a space before the colon (brynary) === 3.0.8 / 2011-11-16 * 4 minor enhancements: * Add 6 missing ruby 1.9 tests (brynary) * Added new 1.9 hash tests to pt_testcase.rb * Version specific tests are now skipped at runtime, not test creation time * Added new block arg tests. === 3.0.7 / 2011-09-21 * 2 bug fixes: * Fixed and test to not have mandatory parens * Fixed r2r's handling of dregexp options === 3.0.6 / 2011-08-16 * 5 minor enhancements: * Added ParseTreeTestCase#add_19tests * Added a bunch of FAILING 1.9 tests stolen from presidentbeef's repo! yay! * Added add_19tests and add_18tests to segregate version specific parsing. * Moved pt_testcase.rb to sexp_processor * Segregated a bunch of 1.8 specific tests using add_18tests. === 3.0.5 / 2010-09-01 * 2 minor enhancements: * Added in_context to clean up code. * optimize inspect to avoid needlessly caching @line === 3.0.4 / 2010-03-27 * 1 minor enhancement: * Added line number to pretty_print output if $VERBOSE === 3.0.3 / 2009-08-14 * 1 minor enhancement: * Pulled #mass up from flog/flay === 3.0.2 / 2009-06-23 * 2 minor enhancements: * Pulled unique.rb from ParseTree to sexp_processor. * Switched to minitest. === 3.0.1 / 2009-01-20 * 3 minor enhancements: * Filled out README * Promoted file/line/comments from ruby_parser. * Added sexp_type to compliment sexp_body. === 3.0.0 / 2008-10-22 * 2 major enhancements: * Released as its own project, splitting from ParseTree * Added Environment to SexpProcessor and built it in. YAY! * 6 minor enhancements: * Allowed CompositeSexpProcessor to be more ducktypey. * Refactored Sexp#method_missing into find_node and find_nodes. * Removed Sexp#for and other PT specific code. * SexpProcessor#process now runs rewriters before everything else. * SexpProcessor#rewrite context only for subs, EMPTY for top level rewrites. * SexpProcessor#rewrite will stop iterating if the result isn't another Sexp. sexp_processor-4.17.0/metadata.gz.sig0000444000004100000410000000040014443772242017615 0ustar www-datawww-dataKB kmRKxYJD>\~q`>3k%'ąB~mt>!G0>! 'mcxM~. \U3N7NljDc|)'N9<dnPS_:໗J\Q*|Hkd}Ur"Q W(;,M6#F~U}F{C~c[vv!/ij'*R jsexp_processor-4.17.0/checksums.yaml.gz.sig0000444000004100000410000000040014443772242020763 0ustar www-datawww-datadd1n۪@WO!8@ !zMWD= 2.6", "< 4"] license "MIT" end # vim: syntax=ruby sexp_processor-4.17.0/lib/0000755000004100000410000000000014443772242015470 5ustar www-datawww-datasexp_processor-4.17.0/lib/sexp.rb0000444000004100000410000002076214443772242017001 0ustar www-datawww-data$TESTING ||= false # unless defined $TESTING ## # Sexps are the basic storage mechanism of SexpProcessor. Sexps have # a +type+ (to be renamed +node_type+) which is the first element of # the Sexp. The type is used by SexpProcessor to determine whom to # dispatch the Sexp to for processing. class Sexp < Array # ZenTest FULL ## # A setter for the line this sexp was found on. Usually set by ruby_parser. attr_writer :line ## # Set the maximum line number for this sexp. Often set by ruby_parser. attr_writer :line_max ## # Accessors for the file. Usually set by ruby_parser. attr_accessor :file ## # Optional comments above/aside this sexp. Usually set by ruby_parser. attr_accessor :comments @@array_types = [ :array, :args ] # TODO: remove ## # Create a new Sexp containing +args+. def initialize *args super(args) end alias _concat concat ## # Creates a new Sexp from Array +a+. def self.from_array a ary = Array === a ? a : [a] self.new._concat(ary.map { |x| case x when Sexp x when Array self.from_array(x) else x end }) end ## # Creates a new sexp with the new contents of +body+, but with the # same +file+, +line+, and +comment+ as self. def new(*body) r = self.class.new._concat(body) # ensures a sexp from map r.file = self.file if self.file r.line = self.line if self.line r.line_max = self.line_max if defined?(@line_max) r.comments = self.comments if self.comments r end def map &blk # :nodoc: self.new._concat(super(&blk)) # ensures a sexp from map end def == obj # :nodoc: obj.class == self.class and super # only because of a bug in ruby end def eql? o self.class == o.class && super end def hash @hash ||= [self.class, *self].hash end ## # Returns true if the node_type is +array+ or +args+. # # REFACTOR: to TypedSexp - we only care when we have units. def array_type? warn "DEPRECATED: please file an issue if you actually use this. from #{caller.first}" type = self.sexp_type @@array_types.include? type end def compact # :nodoc: self.delete_if(&:nil?) end ## # Recursively enumerates the sexp yielding to +block+ for every sub-Sexp. # # Returning :skip will stop traversing that subtree: # # sexp.deep_each do |s| # next :skip if s.sexp_type == :if # # ... # end def deep_each &block return enum_for(:deep_each) unless block_given? self.each_sexp do |sexp| next if block[sexp] == :skip sexp.deep_each(&block) end end ## # Return the maximum depth of the sexp. One-based. def depth 1 + (each_sexp.map(&:depth).max || 0) end ## # Enumeratates the sexp yielding to +b+ when the node_type == +t+. def each_of_type t, &b return enum_for(:each_of_type, t) unless block_given? each_sexp do | sexp | sexp.each_of_type(t, &b) yield sexp if sexp.sexp_type == t end end ## # Enumerates all sub-sexps skipping non-Sexp elements. def each_sexp return enum_for(:each_sexp) unless block_given? self.each do |sexp| next unless Sexp === sexp yield sexp end end ## # Replaces all elements whose node_type is +from+ with +to+. Used # only for the most trivial of rewrites. def find_and_replace_all from, to each_with_index do | elem, index | if Sexp === elem then elem.find_and_replace_all(from, to) elsif elem == from self[index] = to end end end ## # Replaces all Sexps matching +pattern+ with Sexp +repl+. def gsub pattern, repl return repl if pattern == self new = self.map { |subset| case subset when Sexp then if Matcher === pattern && pattern.satisfy?(subset) then # TODO: make === be satisfy? maybe? repl.dup rescue repl else subset.gsub pattern, repl end else subset end } Sexp.from_array new end def inspect # :nodoc: sexp_str = self.map(&:inspect).join ", " if ENV["VERBOSE"] && line then "s(#{sexp_str}).line(#{line})" else "s(#{sexp_str})" end end def find_node name, delete = false # :nodoc: matches = find_nodes name case matches.size when 0 then nil when 1 then match = matches.first delete match if delete match else raise NoMethodError, "multiple nodes for #{name} were found in #{inspect}" end end ## # Find every node with type +name+. def find_nodes name each_sexp.find_all { |sexp| sexp.sexp_type == name } end UNASSIGNED = Object.new ## # If passed a line number, sets the line and returns self. Otherwise # returns the line number. This allows you to do message cascades # and still get the sexp back. def line n = UNASSIGNED if n != UNASSIGNED then raise ArgumentError, "setting %p.line %p" % [self, n] unless Integer === n @line = n self else @line ||= nil end end ## # Returns the maximum line number of the children of self. def line_max @line_max ||= self.deep_each.map(&:line).compact.max end ## # Returns the size of the sexp, flattened. def mass @mass ||= inject(1) { |t, s| Sexp === s ? t + s.mass : t } end ## # Returns the node named +node+, deleting it if +delete+ is true. def method_missing meth, delete = false r = find_node meth, delete if ENV["DEBUG"] then if r.nil? then warn "%p.method_missing(%p) => nil from %s" % [self, meth, caller.first] elsif ENV["VERBOSE"] warn "%p.method_missing(%p) from %s" % [self, meth, caller.first] end end r end def respond_to? msg, private = false # :nodoc: # why do I need this? Because ruby 2.0 is broken. That's why. super end def pretty_print q # :nodoc: nnd = ")" nnd << ".line(#{line})" if line && ENV["VERBOSE"] q.group(1, "s(", nnd) do q.seplist(self) {|v| q.pp v } end end ## # Returns the node type of the Sexp. def sexp_type first end ## # Sets the node type of the Sexp. def sexp_type= v self[0] = v end ## # Returns the Sexp body (starting at +from+, defaulting to 1), ie # the values without the node type. def sexp_body from = 1 self.new._concat(self[from..-1] || []) end ## # Sets the Sexp body to new content. def sexp_body= v self[1..-1] = v end alias :head :sexp_type alias :rest :sexp_body ## # If run with debug, Sexp will raise if you shift on an empty # Sexp. Helps with debugging. def shift raise "I'm empty" if self.empty? super end if ($DEBUG or $TESTING) ## # Returns the bare bones structure of the sexp. # s(:a, :b, s(:c, :d), :e) => s(:a, s(:c)) def structure if Array === self.sexp_type then warn "NOTE: form s(s(:subsexp)).structure is deprecated. Removing in 5.0" s(:bogus, *self).structure # TODO: remove >= 4.2.0 else s(self.sexp_type, *each_sexp.map(&:structure)) end end ## # Replaces the Sexp matching +pattern+ with +repl+. def sub pattern, repl return repl.dup if pattern == self return repl.dup if Matcher === pattern && pattern.satisfy?(self) done = false new = self.map do |subset| if done then subset else case subset when Sexp then if pattern == subset then done = true repl.dup rescue repl elsif Matcher === pattern && pattern.satisfy?(subset) then done = true repl.dup rescue repl else subset.sub pattern, repl end else subset end end end Sexp.from_array new end def to_a # :nodoc: self.map { |o| Sexp === o ? o.to_a : o } end alias to_s inspect # :nodoc: ## # Return the value (last item) of a single element sexp (eg `s(:lit, 42)`). def value raise "multi item sexp" if size > 2 last end end ## # This is a very important shortcut to make using Sexps much more awesome. # # In its normal form +s(...)+, creates a Sexp instance. If passed a # block, it creates a Sexp::Matcher using the factory methods on Sexp. # # See Matcher and other factory class methods on Sexp. def s *args, &blk return Sexp.class_eval(&blk) if blk Sexp.new(*args) end require "sexp_matcher" unless defined? Sexp::Matcher require "strict_sexp" if ENV["SP_DEBUG"] || ENV["STRICT_SEXP"].to_i > 0 sexp_processor-4.17.0/lib/pt_testcase.rb0000444000004100000410000040563014443772242020341 0ustar www-datawww-data# encoding: US-ASCII $TESTING = true # :stopdoc: require "minitest/test" require "sexp_processor" # for deep_clone # key: # wwtt = what were they thinking? class Examples attr_reader :reader attr_writer :writer def a_method x; x+1; end alias an_alias a_method define_method(:bmethod_noargs) do x + 1 end define_method(:unsplatted) do |x| x + 1 end define_method :splatted do |*args| y = args.first y + 42 end define_method :dmethod_added, instance_method(:a_method) if RUBY_VERSION < "1.9" end class ParseTreeTestCase < Minitest::Test all_versions = %w[18 19 20 21 22 23 24 25 26 27 30 31 32] most_versions = all_versions.drop(1) TEST_SUFFIX = "_#{most_versions.join "_"}" VER_RE = /(#{Regexp.union(*all_versions)})/ attr_accessor :processor # to be defined by subclass def setup super @processor = nil end def after_process_hook klass, node, data, input_name, output_name end def before_process_hook klass, node, data, input_name, output_name end def self.add_test name, data, klass = self.name[4..-1] name = name.to_s klass = klass.to_s if testcases.has_key? name then if testcases[name].has_key? klass then warn "testcase #{klass}##{name} already has data" else testcases[name][klass] = data end else warn "testcase #{name} does not exist" end end def self.add_tests name, hash name = name.to_s hash.each do |klass, data| warn "testcase #{klass}##{name} already has data" if testcases[name].has_key? klass testcases[name][klass] = data end end def self.add_18tests name, hash add_tests "#{name}__18", hash end def self.add_19tests name, hash add_tests "#{name}_#{TEST_SUFFIX}", hash # HACK? end def self.add_19edgecases ruby, sexp, cases cases.each do |name, code| add_19tests name, "Ruby" => code, "ParseTree" => sexp, "Ruby2Ruby" => ruby end end def self.clone_same @@testcases.each do |node, data| data.each do |key, val| if val == :same then prev_key = self.previous(key) data[key] = data[prev_key].deep_clone end end end end def self.copy_test_case nonverbose, klass verbose = nonverbose + "_mri_verbose_flag" testcases[verbose][klass] = testcases[nonverbose][klass] end def self.generate_test klass, node, data, input_name, output_name klass.send :define_method, "test_#{node}" do flunk "Processor is nil" if processor.nil? tversions = node[/(?:_#{VER_RE})+$/] if tversions then cversion = self.class.name[/#{VER_RE}/] assert true # shut up prove_it! # can't push this up because it may be generating into an # abstract test class and the actual subclass is versioned. return "version specific test" unless tversions.include? cversion if cversion end assert data.has_key?(input_name), "Unknown input data" assert data.has_key?(output_name), "Missing test data" $missing[node] << output_name unless data.has_key? output_name input = data[input_name].deep_clone expected = data[output_name].deep_clone case expected when :unsupported then assert_raises(UnsupportedNodeError) do processor.process(input) end else extra_expected = [] extra_input = [] _, expected, extra_expected = *expected if Array === expected and expected.sexp_type == :defx _, input, extra_input = *input if Array === input and input.sexp_type == :defx # OMG... I can't believe I have to do this this way. these # hooks are here instead of refactoring this define_method # body into an assertion. It'll allow subclasses to hook in # and add behavior before or after the processor does it's # thing. If you go the body refactor route, some of the # RawParseTree test cases fail for completely bogus reasons. before_process_hook klass, node, data, input_name, output_name refute_nil data[input_name], "testcase does not exist?" timeout = (ENV["RP_TIMEOUT"] || 10).to_i @result = processor.process input, "(string)", timeout assert_equal(expected, @result, "failed on input: #{data[input_name].inspect}") after_process_hook klass, node, data, input_name, output_name extra_input.each do |extra| processor.process(extra) end extra = if processor.respond_to?(:extra_methods) then processor.extra_methods else [] end assert_equal extra_expected, extra end end end def self.generate_tests klass install_missing_reporter clone_same output_name = klass.name.to_s.sub(/^Test/, "") input_name = self.previous(output_name) @@testcases.each do |node, data| next if [:skip, :unsupported].include? data[input_name] next if data[output_name] == :skip generate_test klass, node, data, input_name, output_name end end def self.inherited klass super generate_tests klass unless klass.name =~ /TestCase/ end def self.install_missing_reporter unless defined? $missing then $missing = Hash.new { |h,k| h[k] = [] } at_exit { at_exit { warn "" $missing.sort.each do |name, klasses| warn "add_tests(#{name.inspect}," klasses.map! { |klass| " #{klass.inspect} => :same" } warn klasses.join("\n") + ")" end warn "" } } end end def self.previous key, extra=0 # FIX: remove R2C code idx = @@testcase_order.index(key) raise "Unknown class #{key} in @@testcase_order" if idx.nil? case key when "RubyToRubyC" then idx -= 1 end @@testcase_order[idx - 1 - extra] end def self.testcase_order; @@testcase_order; end def self.testcases; @@testcases; end def self.unsupported_tests *tests tests.flatten.each do |name| add_test name, :unsupported end end ############################################################ # Shared TestCases: @@testcase_order = %w(Ruby ParseTree) @@testcases = Hash.new { |h,k| h[k] = {} } ### # 1.8 specific tests add_18tests("call_arglist_norm_hash_splat", "Ruby" => "o.m(42, :a => 1, :b => 2, *c)", "ParseTree" => s(:call, s(:call, nil, :o), :m, s(:lit, 42), s(:hash, s(:lit, :a), s(:lit, 1), s(:lit, :b), s(:lit, 2)), s(:splat, s(:call, nil, :c)))) add_18tests("call_arglist_space", "Ruby" => "a (1,2,3)", "ParseTree" => s(:call, nil, :a, s(:lit, 1), s(:lit, 2), s(:lit, 3)), "Ruby2Ruby" => "a(1, 2, 3)") add_18tests("fcall_arglist_norm_hash_splat", "Ruby" => "m(42, :a => 1, :b => 2, *c)", "ParseTree" => s(:call, nil, :m, s(:lit, 42), s(:hash, s(:lit, :a), s(:lit, 1), s(:lit, :b), s(:lit, 2)), s(:splat, s(:call, nil, :c)))) add_18tests("if_args_no_space_symbol", "Ruby" => "x if y:z", "ParseTree" => s(:if, s(:call, nil, :y, s(:lit, :z)), s(:call, nil, :x), nil), "Ruby2Ruby" => "x if y(:z)") add_18tests("if_post_not", "Ruby" => "a if not b", "ParseTree" => s(:if, s(:call, nil, :b), nil, s(:call, nil, :a)), "Ruby2Ruby" => "a unless b") add_18tests("if_pre_not", "Ruby" => "if not b then a end", "ParseTree" => s(:if, s(:call, nil, :b), nil, s(:call, nil, :a)), "Ruby2Ruby" => "a unless b") add_18tests("iter_args_ivar", "Ruby" => "a { |@a| 42 }", "ParseTree" => s(:iter, s(:call, nil, :a), s(:args, :@a), s(:lit, 42))) add_18tests("iter_masgn_args_ivar", "Ruby" => "a { |a, @b| 42 }", "ParseTree" => s(:iter, s(:call, nil, :a), s(:args, :a, :@b), s(:lit, 42))) add_18tests("not", "Ruby" => "(not true)", "ParseTree" => s(:not, s(:true))) add_18tests("str_question_control", "Ruby" => '?\M-\C-a', "ParseTree" => s(:lit, 129), "Ruby2Ruby" => "129") add_18tests("str_question_escape", "Ruby" => '?\n', "ParseTree" => s(:lit, 10), "Ruby2Ruby" => "10") add_18tests("str_question_literal", "Ruby" => "?a", "ParseTree" => s(:lit, 97), "Ruby2Ruby" => "97") add_18tests("unless_post_not", "Ruby" => "a unless not b", "ParseTree" => s(:if, s(:call, nil, :b), s(:call, nil, :a), nil), "Ruby2Ruby" => "a if b") add_18tests("unless_pre_not", "Ruby" => "unless not b then a end", "ParseTree" => s(:if, s(:call, nil, :b), s(:call, nil, :a), nil), "Ruby2Ruby" => "a if b") add_18tests("until_post_not", "Ruby" => "begin\n (1 + 1)\nend until not true", "ParseTree" => s(:while, s(:true), s(:call, s(:lit, 1), :+, s(:lit, 1)), false), "Ruby2Ruby" => "begin\n (1 + 1)\nend while true") add_18tests("until_pre_not", "Ruby" => "until not true do\n (1 + 1)\nend", "ParseTree" => s(:while, s(:true), s(:call, s(:lit, 1), :+, s(:lit, 1)), true), "Ruby2Ruby" => "while true do\n (1 + 1)\nend") add_18tests("until_pre_not_mod", "Ruby" => "(1 + 1) until not true", "ParseTree" => s(:while, s(:true), s(:call, s(:lit, 1), :+, s(:lit, 1)), true), "Ruby2Ruby" => "while true do\n (1 + 1)\nend") add_18tests("while_post_not", "Ruby" => "begin\n (1 + 1)\nend while not true", "ParseTree" => s(:until, s(:true), s(:call, s(:lit, 1), :+, s(:lit, 1)), false), "Ruby2Ruby" => "begin\n (1 + 1)\nend until true") add_18tests("while_pre_not", "Ruby" => "while not true do\n (1 + 1)\nend", "ParseTree" => s(:until, s(:true), s(:call, s(:lit, 1), :+, s(:lit, 1)), true), "Ruby2Ruby" => "until true do\n (1 + 1)\nend") add_18tests("while_pre_not_mod", "Ruby" => "(1 + 1) while not true", "ParseTree" => s(:until, s(:true), s(:call, s(:lit, 1), :+, s(:lit, 1)), true), "Ruby2Ruby" => "until true do\n (1 + 1)\nend") # FIX ### # 1.9 specific tests add_19edgecases("-> () { (x + 1) }", s(:iter, s(:lambda), s(:args), s(:call, s(:call, nil, :x), :+, s(:lit, 1))), "stabby_args" => "->() { (x + 1) }", "stabby_args_doend" => "->() do (x + 1) end") add_19edgecases("-> { (x + 1) }", s(:iter, s(:lambda), 0, s(:call, s(:call, nil, :x), :+, s(:lit, 1))), "stabby_args_0_no_parens" => "-> { (x + 1) }", "stabby_args_0_no_parens_doend" => "-> do (x + 1) end", "stabby_args_0_spacebar_broken" => "->{x+1}") # I hate you add_19edgecases("-> (x, y) { (x + y) }", s(:iter, s(:lambda), s(:args, :x, :y), s(:call, s(:lvar, :x), :+, s(:lvar, :y))), "stabby_args_2" => "->(x, y) { (x + y) }", "stabby_args_2_doend" => "->(x, y) do (x + y) end", "stabby_args_2_no_parens" => "-> x, y { (x + y) }", "stabby_args_2_no_parens_doend" => "-> x, y do (x + y) end") add_19edgecases("-> (x) { (x + 1) }", s(:iter, s(:lambda), s(:args, :x), s(:call, s(:lvar, :x), :+, s(:lit, 1))), "stabby_args_1" => "->(x) { (x + 1) }", "stabby_args_1_doend" => "->(x) do (x + 1) end", "stabby_args_1_no_parens" => "-> x { (x + 1) }", "stabby_args_1_no_parens_doend" => "-> x do (x + 1) end") add_19tests("array_bare_hash", "Ruby" => "[:a, :b => :c]", "ParseTree" => s(:array, s(:lit, :a), s(:hash, s(:lit, :b), s(:lit, :c))), "Ruby2Ruby" => "[:a, { :b => :c }]") add_19tests("array_bare_hash_labels", "Ruby" => "[:a, b: :c]", "ParseTree" => s(:array, s(:lit, :a), s(:hash, s(:lit, :b), s(:lit, :c))), "Ruby2Ruby" => "[:a, { :b => :c }]") add_19tests("call_arglist_norm_hash_colons", "Ruby" => "o.m(42, a: 1, b: 2)", "ParseTree" => s(:call, s(:call, nil, :o), :m, s(:lit, 42), s(:hash, s(:lit, :a), s(:lit, 1), s(:lit, :b), s(:lit, 2))), "Ruby2Ruby" => "o.m(42, :a => 1, :b => 2)") add_19tests("call_arglist_trailing_comma", "Ruby" => "a(1,2,3,)", "ParseTree" => s(:call, nil, :a, s(:lit, 1), s(:lit, 2), s(:lit, 3)), "Ruby2Ruby" => "a(1, 2, 3)") add_19tests("call_bang", "Ruby" => "!a", "ParseTree" => s(:call, s(:call, nil, :a), :"!"), "Ruby2Ruby" => "(not a)") add_19tests("call_bang_empty", "Ruby" => "! ()", "ParseTree" => s(:call, s(:nil), :"!"), "Ruby2Ruby" => "(not nil)") add_19tests("call_fonz", "Ruby" => "a.()", "ParseTree" => s(:call, s(:call, nil, :a), :call), "Ruby2Ruby" => "a.call") add_19tests("call_fonz_cm", "Ruby" => "a::()", "ParseTree" => s(:call, s(:call, nil, :a), :call), "Ruby2Ruby" => "a.call") add_19tests("call_not", "Ruby" => "not (42)", "ParseTree" => s(:call, s(:lit, 42), :"!"), "Ruby2Ruby" => "(not 42)") # add_19tests("call_not_empty", # "Ruby" => "not ()", # "ParseTree" => s(:call, s(:lit, 42), :"!")) add_19tests("call_not_equal", "Ruby" => "a != b", "ParseTree" => s(:call, s(:call, nil, :a), :"!=", s(:call, nil, :b)), "Ruby2Ruby" => "(a != b)") add_19tests("call_splat_mid", "Ruby" => "def f(a = nil, *b, c)\n # do nothing\nend", "ParseTree" => s(:defn, :f, s(:args, s(:lasgn, :a, s(:nil)), :"*b", :c), s(:nil))) add_19tests("defn_args_mand_opt_mand", "Ruby" => "def f(mand1, opt = 42, mand2)\n # do nothing\nend", "ParseTree" => s(:defn, :f, s(:args, :mand1, s(:lasgn, :opt, s(:lit, 42)), :mand2), s(:nil))) add_19tests("defn_args_mand_opt_splat_mand", "Ruby" => "def f(mand1, opt = 42, *rest, mand2)\n # do nothing\nend", "ParseTree" => s(:defn, :f, s(:args, :mand1, s(:lasgn, :opt, s(:lit, 42)), :"*rest", :mand2), s(:nil))) add_19tests("defn_args_opt_mand", "Ruby" => "def f(opt = 42, mand)\n # do nothing\nend", "ParseTree" => s(:defn, :f, s(:args, s(:lasgn, :opt, s(:lit, 42)), :mand), s(:nil))) add_19tests("defn_args_opt_splat_mand", "Ruby" => "def f(opt = 42, *rest, mand)\n # do nothing\nend", "ParseTree" => s(:defn, :f, s(:args, s(:lasgn, :opt, s(:lit, 42)), :"*rest", :mand), s(:nil))) add_19tests("defn_args_splat_mand", "Ruby" => "def f(*rest, mand)\n # do nothing\nend", "ParseTree" => s(:defn, :f, s(:args, :"*rest", :mand), s(:nil))) add_19tests("defn_args_splat_middle", "Ruby" => "def f(first, *middle, last)\n # do nothing\nend", "ParseTree" => s(:defn, :f, s(:args, :first, :"*middle", :last), s(:nil))) add_19tests("fcall_arglist_hash_colons", "Ruby" => "m(a: 1, b: 2)", "ParseTree" => s(:call, nil, :m, s(:hash, s(:lit, :a), s(:lit, 1), s(:lit, :b), s(:lit, 2))), "Ruby2Ruby" => "m(:a => 1, :b => 2)") add_19tests("hash_new", "Ruby" => "{ a: 1, b: 2 }", "ParseTree" => s(:hash, s(:lit, :a), s(:lit, 1), s(:lit, :b), s(:lit, 2)), "Ruby2Ruby" => "{ :a => 1, :b => 2 }") add_19tests("hash_new_no_space", "Ruby" => "{a:1,b:2}", "ParseTree" => s(:hash, s(:lit, :a), s(:lit, 1), s(:lit, :b), s(:lit, 2)), "Ruby2Ruby" => "{ :a => 1, :b => 2 }") add_19tests("hash_new_with_keyword", "Ruby" => "{ true: 1, b: 2 }", "ParseTree" => s(:hash, s(:lit, :true), s(:lit, 1), s(:lit, :b), s(:lit, 2)), "Ruby2Ruby" => "{ :true => 1, :b => 2 }") add_19tests("if_post_not", "Ruby" => "a if not b", "ParseTree" => s(:if, s(:call, s(:call, nil, :b), :"!"), s(:call, nil, :a), nil), "Ruby2Ruby" => "a unless b") add_19tests("if_pre_not", "Ruby" => "if not b then a end", "ParseTree" => s(:if, s(:call, s(:call, nil, :b), :"!"), s(:call, nil, :a), nil), "Ruby2Ruby" => "a unless b") add_19tests("label_in_bare_hash_in_array_in_ternary", "Ruby" => "1 ? [:a, b: 2] : 1", "ParseTree" => s(:if, s(:lit, 1), s(:array, s(:lit, :a), s(:hash, s(:lit, :b), s(:lit, 2))), s(:lit, 1)), "Ruby2Ruby" => "1 ? ([:a, { :b => 2 }]) : (1)") add_19tests("label_in_callargs_in_ternary", "Ruby" => "1 ? m(a: 2) : 1", "ParseTree" => s(:if, s(:lit, 1), s(:call, nil, :m, s(:hash, s(:lit, :a), s(:lit, 2))), s(:lit, 1)), "Ruby2Ruby" => "1 ? (m(:a => 2)) : (1)") add_19tests("not", "Ruby" => "(not true)", "ParseTree" => s(:call, s(:true), :"!")) add_19tests("splat_fcall_middle", "Ruby" => "meth(1, *[2], 3)", "ParseTree" => s(:call, nil, :meth, s(:lit, 1), s(:splat, s(:array, s(:lit, 2))), s(:lit, 3))) add_19tests("str_question_control", "Ruby" => '?\M-\C-a', "ParseTree" => s(:str, "\x81"), "Ruby2Ruby" => "\"\\x81\"") add_19tests("str_question_escape", "Ruby" => '?\n', "ParseTree" => s(:str, "\n"), "Ruby2Ruby" => "\"\\n\"") add_19tests("str_question_literal", "Ruby" => "?a", "ParseTree" => s(:str, "a"), "Ruby2Ruby" => '"a"') add_19tests("unless_post_not", "Ruby" => "a unless not b", "ParseTree" => s(:if, s(:call, s(:call, nil, :b), :"!"), nil, s(:call, nil, :a)), "Ruby2Ruby" => "a if b") add_19tests("unless_pre_not", "Ruby" => "unless not b then a end", "ParseTree" => s(:if, s(:call, s(:call, nil, :b), :"!"), nil, s(:call, nil, :a)), "Ruby2Ruby" => "a if b") add_19tests("until_post_not", "Ruby" => "begin\n (1 + 1)\nend until not true", "ParseTree" => s(:until, s(:call, s(:true), :"!"), s(:call, s(:lit, 1), :+, s(:lit, 1)), false), "Ruby2Ruby" => "begin\n (1 + 1)\nend while true") add_19tests("until_pre_not", "Ruby" => "until not true do\n (1 + 1)\nend", "ParseTree" => s(:until, s(:call, s(:true), :"!"), s(:call, s(:lit, 1), :+, s(:lit, 1)), true), "Ruby2Ruby" => "while true do\n (1 + 1)\nend") add_19tests("until_pre_not_mod", "Ruby" => "(1 + 1) until not true", "ParseTree" => s(:until, s(:call, s(:true), :"!"), s(:call, s(:lit, 1), :+, s(:lit, 1)), true), "Ruby2Ruby" => "while true do\n (1 + 1)\nend") add_19tests("while_post_not", "Ruby" => "begin\n (1 + 1)\nend while not true", "ParseTree" => s(:while, s(:call, s(:true), :"!"), s(:call, s(:lit, 1), :+, s(:lit, 1)), false), "Ruby2Ruby" => "begin\n (1 + 1)\nend until true") add_19tests("while_pre_not", "Ruby" => "while not true do\n (1 + 1)\nend", "ParseTree" => s(:while, s(:call, s(:true), :"!"), s(:call, s(:lit, 1), :+, s(:lit, 1)), true), "Ruby2Ruby" => "until true do\n (1 + 1)\nend") add_19tests("while_pre_not_mod", "Ruby" => "(1 + 1) while not true", "ParseTree" => s(:while, s(:call, s(:true), :"!"), s(:call, s(:lit, 1), :+, s(:lit, 1)), true), "Ruby2Ruby" => "until true do\n (1 + 1)\nend") # FIX ### # Shared tests: add_tests("alias", "Ruby" => "class X\n alias :y :x\nend", "ParseTree" => s(:class, :X, nil, s(:alias, s(:lit, :y), s(:lit, :x)))) add_tests("alias_ugh", "Ruby" => "class X\n alias y x\nend", "ParseTree" => s(:class, :X, nil, s(:alias, s(:lit, :y), s(:lit, :x))), "Ruby2Ruby" => "class X\n alias :y :x\nend") add_tests("and", "Ruby" => "a and b", "ParseTree" => s(:and, s(:call, nil, :a), s(:call, nil, :b))) add_tests("argscat_inside", "Ruby" => "a = [b, *c]", "ParseTree" => s(:lasgn, :a, s(:array, s(:call, nil, :b), s(:splat, s(:call, nil, :c))))) add_tests("argscat_svalue", "Ruby" => "a = b, c, *d", "ParseTree" => s(:lasgn, :a, s(:svalue, s(:array, s(:call, nil, :b), s(:call, nil, :c), s(:splat, s(:call, nil, :d)))))) add_tests("argspush", "Ruby" => "a[*b] = c", "ParseTree" => s(:attrasgn, s(:call, nil, :a), :[]=, s(:splat, s(:call, nil, :b)), s(:call, nil, :c))) add_tests("array", "Ruby" => "[1, :b, \"c\"]", "ParseTree" => s(:array, s(:lit, 1), s(:lit, :b), s(:str, "c"))) add_tests("array_pct_W", "Ruby" => "%W[a b c]", "ParseTree" => s(:array, s(:str, "a"), s(:str, "b"), s(:str, "c")), "Ruby2Ruby" => "[\"a\", \"b\", \"c\"]") add_tests("array_pct_W_dstr", "Ruby" => "%W[a #\{@b} c]", "ParseTree" => s(:array, s(:str, "a"), s(:dstr, "", s(:evstr, s(:ivar, :@b))), s(:str, "c")), "Ruby2Ruby" => "[\"a\", \"#\{@b}\", \"c\"]") add_tests("array_pct_w", "Ruby" => "%w[a b c]", "ParseTree" => s(:array, s(:str, "a"), s(:str, "b"), s(:str, "c")), "Ruby2Ruby" => "[\"a\", \"b\", \"c\"]") add_tests("array_pct_w_dstr", "Ruby" => "%w[a #\{@b} c]", "ParseTree" => s(:array, s(:str, "a"), s(:str, "#\{@b}"), s(:str, "c")), "Ruby2Ruby" => "[\"a\", \"\\\#{@b}\", \"c\"]") # TODO: huh? add_tests("attrasgn", "Ruby" => "y = 0\n42.method = y\n", "ParseTree" => s(:block, s(:lasgn, :y, s(:lit, 0)), s(:attrasgn, s(:lit, 42), :method=, s(:lvar, :y)))) add_tests("attrasgn_index_equals", "Ruby" => "a[42] = 24", "ParseTree" => s(:attrasgn, s(:call, nil, :a), :[]=, s(:lit, 42), s(:lit, 24))) add_tests("attrasgn_index_equals_space", "Ruby" => "a = []; a [42] = 24", "ParseTree" => s(:block, s(:lasgn, :a, s(:array)), s(:attrasgn, s(:lvar, :a), :[]=, s(:lit, 42), s(:lit, 24))), "Ruby2Ruby" => "a = []\na[42] = 24\n") add_tests("back_ref", "Ruby" => "[$&, $`, $', $+]", "ParseTree" => s(:array, s(:back_ref, :&), s(:back_ref, :"`"), s(:back_ref, :"'"), s(:back_ref, :+))) add_tests("begin", "Ruby" => "begin\n (1 + 1)\nend", "ParseTree" => s(:call, s(:lit, 1), :+, s(:lit, 1)), "Ruby2Ruby" => "(1 + 1)") add_tests("begin_def", "Ruby" => "def m\n begin\n\n end\nend", "ParseTree" => s(:defn, :m, s(:args), s(:nil)), "Ruby2Ruby" => "def m\n # do nothing\nend") add_tests("begin_rescue_ensure", "Ruby" => "begin\n a\nrescue\n # do nothing\nensure\n # do nothing\nend", "ParseTree" => s(:ensure, s(:rescue, s(:call, nil, :a), s(:resbody, s(:array), nil)), s(:nil))) add_tests("begin_rescue_ensure_all_empty", "Ruby" => "begin\n # do nothing\nrescue\n # do nothing\nensure\n # do nothing\nend", "ParseTree" => s(:ensure, s(:rescue, s(:resbody, s(:array), nil)), s(:nil))) add_tests("begin_rescue_twice", "Ruby" => "begin\n a\nrescue => mes\n # do nothing\nend\nbegin\n b\nrescue => mes\n # do nothing\nend\n", "ParseTree" => s(:block, s(:rescue, s(:call, nil, :a), s(:resbody, s(:array, s(:lasgn, :mes, s(:gvar, :$!))), nil)), s(:rescue, s(:call, nil, :b), s(:resbody, s(:array, s(:lasgn, :mes, s(:gvar, :$!))), nil)))) copy_test_case "begin_rescue_twice", "Ruby" copy_test_case "begin_rescue_twice", "ParseTree" add_tests("block_attrasgn", "Ruby" => "def self.setup(ctx)\n bind = allocate\n bind.context = ctx\n return bind\nend", "ParseTree" => s(:defs, s(:self), :setup, s(:args, :ctx), s(:lasgn, :bind, s(:call, nil, :allocate)), s(:attrasgn, s(:lvar, :bind), :context=, s(:lvar, :ctx)), s(:return, s(:lvar, :bind)))) add_tests("block_lasgn", "Ruby" => "x = (y = 1\n(y + 2))", "ParseTree" => s(:lasgn, :x, s(:block, s(:lasgn, :y, s(:lit, 1)), s(:call, s(:lvar, :y), :+, s(:lit, 2))))) add_tests("block_mystery_block", "Ruby" => "a(b) do\n if b then\n true\n else\n c = false\n d { |x| c = true }\n c\n end\nend", "ParseTree" => s(:iter, s(:call, nil, :a, s(:call, nil, :b)), 0, s(:if, s(:call, nil, :b), s(:true), s(:block, s(:lasgn, :c, s(:false)), s(:iter, s(:call, nil, :d), s(:args, :x), s(:lasgn, :c, s(:true))), s(:lvar, :c))))) add_tests("block_pass_args_and_splat", "Ruby" => "def blah(*args, &block)\n other(42, *args, &block)\nend", "ParseTree" => s(:defn, :blah, s(:args, :"*args", :"&block"), s(:call, nil, :other, s(:lit, 42), s(:splat, s(:lvar, :args)), s(:block_pass, s(:lvar, :block))))) add_tests("block_pass_call_0", "Ruby" => "a.b(&c)", "ParseTree" => s(:call, s(:call, nil, :a), :b, s(:block_pass, s(:call, nil, :c)))) add_tests("block_pass_call_1", "Ruby" => "a.b(4, &c)", "ParseTree" => s(:call, s(:call, nil, :a), :b, s(:lit, 4), s(:block_pass, s(:call, nil, :c)))) add_tests("block_pass_call_n", "Ruby" => "a.b(1, 2, 3, &c)", "ParseTree" => s(:call, s(:call, nil, :a), :b, s(:lit, 1), s(:lit, 2), s(:lit, 3), s(:block_pass, s(:call, nil, :c)))) add_tests("block_pass_fcall_0", "Ruby" => "a(&b)", "ParseTree" => s(:call, nil, :a, s(:block_pass, s(:call, nil, :b)))) add_tests("block_pass_fcall_1", "Ruby" => "a(4, &b)", "ParseTree" => s(:call, nil, :a, s(:lit, 4), s(:block_pass, s(:call, nil, :b)))) add_tests("block_pass_fcall_n", "Ruby" => "a(1, 2, 3, &b)", "ParseTree" => s(:call, nil, :a, s(:lit, 1), s(:lit, 2), s(:lit, 3), s(:block_pass, s(:call, nil, :b)))) add_tests("block_pass_omgwtf", "Ruby" => "define_attr_method(:x, :sequence_name, &Proc.new { |*args| nil })", "ParseTree" => s(:call, nil, :define_attr_method, s(:lit, :x), s(:lit, :sequence_name), s(:block_pass, s(:iter, s(:call, s(:const, :Proc), :new), s(:args, :"*args"), s(:nil))))) add_tests("block_pass_splat", "Ruby" => "def blah(*args, &block)\n other(*args, &block)\nend", "ParseTree" => s(:defn, :blah, s(:args, :"*args", :"&block"), s(:call, nil, :other, s(:splat, s(:lvar, :args)), s(:block_pass, s(:lvar, :block))))) add_tests("block_pass_thingy", "Ruby" => "r.read_body(dest, &block)", "ParseTree" => s(:call, s(:call, nil, :r), :read_body, s(:call, nil, :dest), s(:block_pass, s(:call, nil, :block)))) add_tests("block_stmt_after", "Ruby" => "def f\n begin\n b\n rescue\n c\n end\n\n d\nend", "ParseTree" => s(:defn, :f, s(:args), s(:rescue, s(:call, nil, :b), s(:resbody, s(:array), s(:call, nil, :c))), s(:call, nil, :d)), "Ruby2Ruby" => "def f\n b rescue c\n d\nend") copy_test_case "block_stmt_after", "Ruby" copy_test_case "block_stmt_after", "ParseTree" copy_test_case "block_stmt_after", "Ruby2Ruby" add_tests("block_stmt_before", "Ruby" => "def f\n a\n begin\n b\n rescue\n c\n end\nend", "ParseTree" => s(:defn, :f, s(:args), s(:call, nil, :a), s(:rescue, s(:call, nil, :b), s(:resbody, s(:array), s(:call, nil, :c)))), "Ruby2Ruby" => "def f\n a\n b rescue c\nend") # oddly... this one doesn't HAVE any differences when verbose... new? copy_test_case "block_stmt_before", "Ruby" copy_test_case "block_stmt_before", "ParseTree" copy_test_case "block_stmt_before", "Ruby2Ruby" add_tests("block_stmt_both", "Ruby" => "def f\n a\n begin\n b\n rescue\n c\n end\n d\nend", "ParseTree" => s(:defn, :f, s(:args), s(:call, nil, :a), s(:rescue, s(:call, nil, :b), s(:resbody, s(:array), s(:call, nil, :c))), s(:call, nil, :d)), "Ruby2Ruby" => "def f\n a\n b rescue c\n d\nend") copy_test_case "block_stmt_both", "Ruby" copy_test_case "block_stmt_both", "ParseTree" copy_test_case "block_stmt_both", "Ruby2Ruby" add_tests("bmethod", "Ruby" => [Examples, :unsplatted], "ParseTree" => s(:defn, :unsplatted, s(:args, :x), s(:call, s(:lvar, :x), :+, s(:lit, 1))), "Ruby2Ruby" => "def unsplatted(x)\n (x + 1)\nend") add_tests("bmethod_noargs", "Ruby" => [Examples, :bmethod_noargs], "ParseTree" => s(:defn, :bmethod_noargs, s(:args), s(:call, s(:call, nil, :x), :"+", s(:lit, 1))), "Ruby2Ruby" => "def bmethod_noargs\n (x + 1)\nend") add_tests("bmethod_splat", "Ruby" => [Examples, :splatted], "ParseTree" => s(:defn, :splatted, s(:args, :"*args"), s(:lasgn, :y, s(:call, s(:lvar, :args), :first)), s(:call, s(:lvar, :y), :+, s(:lit, 42))), "Ruby2Ruby" => "def splatted(*args)\n y = args.first\n (y + 42)\nend") add_tests("break", "Ruby" => "loop { break if true }", "ParseTree" => s(:iter, s(:call, nil, :loop), 0, s(:if, s(:true), s(:break), nil))) add_tests("break_arg", "Ruby" => "loop { break 42 if true }", "ParseTree" => s(:iter, s(:call, nil, :loop), 0, s(:if, s(:true), s(:break, s(:lit, 42)), nil))) add_tests("call", "Ruby" => "self.method", "ParseTree" => s(:call, s(:self), :method)) add_tests("call_arglist", "Ruby" => "o.puts(42)", "ParseTree" => s(:call, s(:call, nil, :o), :puts, s(:lit, 42))) add_tests("call_arglist_hash", "Ruby" => "o.m(:a => 1, :b => 2)", "ParseTree" => s(:call, s(:call, nil, :o), :m, s(:hash, s(:lit, :a), s(:lit, 1), s(:lit, :b), s(:lit, 2)))) add_tests("call_arglist_norm_hash", "Ruby" => "o.m(42, :a => 1, :b => 2)", "ParseTree" => s(:call, s(:call, nil, :o), :m, s(:lit, 42), s(:hash, s(:lit, :a), s(:lit, 1), s(:lit, :b), s(:lit, 2)))) add_tests("call_command", "Ruby" => "1.b(c)", "ParseTree" => s(:call, s(:lit, 1), :b, s(:call, nil, :c))) add_tests("call_expr", "Ruby" => "(v = (1 + 1)).zero?", "ParseTree" => s(:call, s(:lasgn, :v, s(:call, s(:lit, 1), :+, s(:lit, 1))), :zero?)) add_tests("call_index", "Ruby" => "a = []\na[42]\n", "ParseTree" => s(:block, s(:lasgn, :a, s(:array)), s(:call, s(:lvar, :a), :[], s(:lit, 42)))) add_tests("call_index_no_args", "Ruby" => "a[]", "ParseTree" => s(:call, s(:call, nil, :a), :[])) add_tests("call_index_space", "Ruby" => "a = []\na [42]\n", "ParseTree" => s(:block, s(:lasgn, :a, s(:array)), s(:call, s(:lvar, :a), :[], s(:lit, 42))), "Ruby2Ruby" => "a = []\na[42]\n") add_tests("call_no_space_symbol", "Ruby" => "foo:bar", "ParseTree" => s(:call, nil, :foo, s(:lit, :bar)), "Ruby2Ruby" => "foo(:bar)") add_tests("call_unary_neg", "Ruby" => "-2**31", "ParseTree" => s(:call, s(:call, s(:lit, 2), :**, s(:lit, 31)), :-@), "Ruby2Ruby" => "-(2 ** 31)") add_tests("case", "Ruby" => "var = 2\nresult = \"\"\ncase var\nwhen 1 then\n puts(\"something\")\n result = \"red\"\nwhen 2, 3 then\n result = \"yellow\"\nwhen 4 then\n # do nothing\nelse\n result = \"green\"\nend\ncase result\nwhen \"red\" then\n var = 1\nwhen \"yellow\" then\n var = 2\nwhen \"green\" then\n var = 3\nend\n", "ParseTree" => s(:block, s(:lasgn, :var, s(:lit, 2)), s(:lasgn, :result, s(:str, "")), s(:case, s(:lvar, :var), s(:when, s(:array, s(:lit, 1)), s(:call, nil, :puts, s(:str, "something")), s(:lasgn, :result, s(:str, "red"))), s(:when, s(:array, s(:lit, 2), s(:lit, 3)), s(:lasgn, :result, s(:str, "yellow"))), s(:when, s(:array, s(:lit, 4)), nil), s(:lasgn, :result, s(:str, "green"))), s(:case, s(:lvar, :result), s(:when, s(:array, s(:str, "red")), s(:lasgn, :var, s(:lit, 1))), s(:when, s(:array, s(:str, "yellow")), s(:lasgn, :var, s(:lit, 2))), s(:when, s(:array, s(:str, "green")), s(:lasgn, :var, s(:lit, 3))), nil))) add_tests("case_nested", "Ruby" => "var1 = 1\nvar2 = 2\nresult = nil\ncase var1\nwhen 1 then\n case var2\n when 1 then\n result = 1\n when 2 then\n result = 2\n else\n result = 3\n end\nwhen 2 then\n case var2\n when 1 then\n result = 4\n when 2 then\n result = 5\n else\n result = 6\n end\nelse\n result = 7\nend\n", "ParseTree" => s(:block, s(:lasgn, :var1, s(:lit, 1)), s(:lasgn, :var2, s(:lit, 2)), s(:lasgn, :result, s(:nil)), s(:case, s(:lvar, :var1), s(:when, s(:array, s(:lit, 1)), s(:case, s(:lvar, :var2), s(:when, s(:array, s(:lit, 1)), s(:lasgn, :result, s(:lit, 1))), s(:when, s(:array, s(:lit, 2)), s(:lasgn, :result, s(:lit, 2))), s(:lasgn, :result, s(:lit, 3)))), s(:when, s(:array, s(:lit, 2)), s(:case, s(:lvar, :var2), s(:when, s(:array, s(:lit, 1)), s(:lasgn, :result, s(:lit, 4))), s(:when, s(:array, s(:lit, 2)), s(:lasgn, :result, s(:lit, 5))), s(:lasgn, :result, s(:lit, 6)))), s(:lasgn, :result, s(:lit, 7))))) add_tests("case_nested_inner_no_expr", "Ruby" => "case a\nwhen b then\n case\n when (d and e) then\n f\n end\nend", "ParseTree" => s(:case, s(:call, nil, :a), s(:when, s(:array, s(:call, nil, :b)), s(:case, nil, s(:when, s(:array, s(:and, s(:call, nil, :d), s(:call, nil, :e))), s(:call, nil, :f)), nil)), nil)) add_tests("case_no_expr", "Ruby" => "case\nwhen (a == 1) then\n :a\nwhen (a == 2) then\n :b\nelse\n :c\nend", "ParseTree" => s(:case, nil, s(:when, s(:array, s(:call, s(:call, nil, :a), :==, s(:lit, 1))), s(:lit, :a)), s(:when, s(:array, s(:call, s(:call, nil, :a), :==, s(:lit, 2))), s(:lit, :b)), s(:lit, :c))) add_tests("case_splat", "Ruby" => "case a\nwhen :b, *c then\n d\nelse\n e\nend", "ParseTree" => s(:case, s(:call, nil, :a), s(:when, s(:array, s(:lit, :b), s(:splat, s(:call, nil, :c))), s(:call, nil, :d)), s(:call, nil, :e))) add_tests("cdecl", "Ruby" => "X = 42", "ParseTree" => s(:cdecl, :X, s(:lit, 42))) add_tests("class_plain", "Ruby" => "class X\n puts((1 + 1))\n \n def blah\n puts(\"hello\")\n end\nend", "ParseTree" => s(:class, :X, nil, s(:call, nil, :puts, s(:call, s(:lit, 1), :+, s(:lit, 1))), s(:defn, :blah, s(:args), s(:call, nil, :puts, s(:str, "hello"))))) add_tests("class_scoped", "Ruby" => "class X::Y\n c\nend", "ParseTree" => s(:class, s(:colon2, s(:const, :X), :Y), nil, s(:call, nil, :c))) add_tests("class_scoped3", "Ruby" => "class ::Y\n c\nend", "ParseTree" => s(:class, s(:colon3, :Y), nil, s(:call, nil, :c))) add_tests("class_super_array", "Ruby" => "class X < Array\nend", "ParseTree" => s(:class, :X, s(:const, :Array))) add_tests("class_super_expr", "Ruby" => "class X < expr\nend", "ParseTree" => s(:class, :X, s(:call, nil, :expr))) add_tests("class_super_object", "Ruby" => "class X < Object\nend", "ParseTree" => s(:class, :X, s(:const, :Object))) add_tests("colon2", "Ruby" => "X::Y", "ParseTree" => s(:colon2, s(:const, :X), :Y)) add_tests("colon3", "Ruby" => "::X", "ParseTree" => s(:colon3, :X)) add_tests("const", "Ruby" => "X", "ParseTree" => s(:const, :X)) add_tests("constX", "Ruby" => "X = 1", "ParseTree" => s(:cdecl, :X, s(:lit, 1))) add_tests("constY", "Ruby" => "::X = 1", "ParseTree" => s(:cdecl, s(:colon3, :X), s(:lit, 1))) add_tests("constZ", "Ruby" => "X::Y = 1", "ParseTree" => s(:cdecl, s(:colon2, s(:const, :X), :Y), s(:lit, 1))) add_tests("cvar", "Ruby" => "@@x", "ParseTree" => s(:cvar, :@@x)) add_tests("cvasgn", "Ruby" => "def x\n @@blah = 1\nend", "ParseTree" => s(:defn, :x, s(:args), s(:cvasgn, :@@blah, s(:lit, 1)))) add_tests("cvasgn_cls_method", "Ruby" => "def self.quiet_mode=(boolean)\n @@quiet_mode = boolean\nend", "ParseTree" => s(:defs, s(:self), :quiet_mode=, s(:args, :boolean), s(:cvasgn, :@@quiet_mode, s(:lvar, :boolean)))) add_tests("cvdecl", "Ruby" => "class X\n @@blah = 1\nend", "ParseTree" => s(:class, :X, nil, s(:cvdecl, :@@blah, s(:lit, 1)))) add_tests("dasgn_0", "Ruby" => "a.each { |x| b.each { |y| x = (x + 1) } if true }", "ParseTree" => s(:iter, s(:call, s(:call, nil, :a), :each), s(:args, :x), s(:if, s(:true), s(:iter, s(:call, s(:call, nil, :b), :each), s(:args, :y), s(:lasgn, :x, s(:call, s(:lvar, :x), :+, s(:lit, 1)))), nil))) add_tests("dasgn_1", "Ruby" => "a.each { |x| b.each { |y| c = (c + 1) } if true }", "ParseTree" => s(:iter, s(:call, s(:call, nil, :a), :each), s(:args, :x), s(:if, s(:true), s(:iter, s(:call, s(:call, nil, :b), :each), s(:args, :y), s(:lasgn, :c, s(:call, s(:lvar, :c), :+, s(:lit, 1)))), nil))) add_tests("dasgn_2", "Ruby" => "a.each do |x|\n if true then\n c = 0\n b.each { |y| c = (c + 1) }\n end\nend", "ParseTree" => s(:iter, s(:call, s(:call, nil, :a), :each), s(:args, :x), s(:if, s(:true), s(:block, s(:lasgn, :c, s(:lit, 0)), s(:iter, s(:call, s(:call, nil, :b), :each), s(:args, :y), s(:lasgn, :c, s(:call, s(:lvar, :c), :+, s(:lit, 1))))), nil))) add_tests("dasgn_curr", "Ruby" => "data.each do |x, y|\n a = 1\n b = a\n b = a = x\nend", "ParseTree" => s(:iter, s(:call, s(:call, nil, :data), :each), s(:args, :x, :y), s(:block, s(:lasgn, :a, s(:lit, 1)), s(:lasgn, :b, s(:lvar, :a)), s(:lasgn, :b, s(:lasgn, :a, s(:lvar, :x)))))) add_tests("dasgn_icky", "Ruby" => "a do\n v = nil\n assert_block(full_message) do\n begin\n yield\n rescue Exception => v\n break\n end\n end\nend", "ParseTree" => s(:iter, s(:call, nil, :a), 0, s(:block, s(:lasgn, :v, s(:nil)), s(:iter, s(:call, nil, :assert_block, s(:call, nil, :full_message)), 0, s(:rescue, s(:yield), s(:resbody, s(:array, s(:const, :Exception), s(:lasgn, :v, s(:gvar, :$!))), s(:break))))))) add_tests("dasgn_mixed", "Ruby" => "t = 0\nns.each { |n| t += n }\n", "ParseTree" => s(:block, s(:lasgn, :t, s(:lit, 0)), s(:iter, s(:call, s(:call, nil, :ns), :each), s(:args, :n), s(:lasgn, :t, s(:call, s(:lvar, :t), :+, s(:lvar, :n))))), "Ruby2Ruby" => "t = 0\nns.each { |n| t = (t + n) }\n") add_tests("defined", "Ruby" => "defined? $x", "ParseTree" => s(:defined, s(:gvar, :$x))) add_tests("defn_args_block", # TODO: make all the defn_args* p their arglist "Ruby" => "def f(&block)\n # do nothing\nend", "ParseTree" => s(:defn, :f, s(:args, :"&block"), s(:nil))) add_tests("defn_args_mand", "Ruby" => "def f(mand)\n # do nothing\nend", "ParseTree" => s(:defn, :f, s(:args, :mand), s(:nil))) add_tests("defn_args_mand_block", "Ruby" => "def f(mand, &block)\n # do nothing\nend", "ParseTree" => s(:defn, :f, s(:args, :mand, :"&block"), s(:nil))) add_tests("defn_args_mand_opt", "Ruby" => "def f(mand, opt = 42)\n # do nothing\nend", "ParseTree" => s(:defn, :f, s(:args, :mand, s(:lasgn, :opt, s(:lit, 42))), s(:nil))) add_tests("defn_args_mand_opt_block", "Ruby" => "def f(mand, opt = 42, &block)\n # do nothing\nend", "ParseTree" => s(:defn, :f, s(:args, :mand, s(:lasgn, :opt, s(:lit, 42)), :"&block"), s(:nil))) add_tests("defn_args_mand_opt_splat", "Ruby" => "def f(mand, opt = 42, *rest)\n # do nothing\nend", "ParseTree" => s(:defn, :f, s(:args, :mand, s(:lasgn, :opt, s(:lit, 42)), :"*rest"), s(:nil))) add_tests("defn_args_mand_opt_splat_block", "Ruby" => "def f(mand, opt = 42, *rest, &block)\n # do nothing\nend", "ParseTree" => s(:defn, :f, s(:args, :mand, s(:lasgn, :opt, s(:lit, 42)), :"*rest", :"&block"), s(:nil))) add_tests("defn_args_mand_opt_splat_no_name", "Ruby" => "def x(a, b = 42, *)\n # do nothing\nend", "ParseTree" => s(:defn, :x, s(:args, :a, s(:lasgn, :b, s(:lit, 42)), :"*"), s(:nil))) add_tests("defn_args_mand_splat", "Ruby" => "def f(mand, *rest)\n # do nothing\nend", "ParseTree" => s(:defn, :f, s(:args, :mand, :"*rest"), s(:nil))) add_tests("defn_args_mand_splat_block", "Ruby" => "def f(mand, *rest, &block)\n # do nothing\nend", "ParseTree" => s(:defn, :f, s(:args, :mand, :"*rest", :"&block"), s(:nil))) add_tests("defn_args_mand_splat_no_name", "Ruby" => "def x(a, *args)\n p(a, args)\nend", "ParseTree" => s(:defn, :x, s(:args, :a, :"*args"), s(:call, nil, :p, s(:lvar, :a), s(:lvar, :args)))) add_tests("defn_args_none", "Ruby" => "def empty\n # do nothing\nend", "ParseTree" => s(:defn, :empty, s(:args), s(:nil))) add_tests("defn_args_opt", "Ruby" => "def f(opt = 42)\n # do nothing\nend", "ParseTree" => s(:defn, :f, s(:args, s(:lasgn, :opt, s(:lit, 42))), s(:nil))) add_tests("defn_args_opt_block", "Ruby" => "def f(opt = 42, &block)\n # do nothing\nend", "ParseTree" => s(:defn, :f, s(:args, s(:lasgn, :opt, s(:lit, 42)), :"&block"), s(:nil))) add_tests("defn_args_opt_splat", "Ruby" => "def f(opt = 42, *rest)\n # do nothing\nend", "ParseTree" => s(:defn, :f, s(:args, s(:lasgn, :opt, s(:lit, 42)), :"*rest"), s(:nil))) add_tests("defn_args_opt_splat_block", "Ruby" => "def f(opt = 42, *rest, &block)\n # do nothing\nend", "ParseTree" => s(:defn, :f, s(:args, s(:lasgn, :opt, s(:lit, 42)), :"*rest", :"&block"), s(:nil))) add_tests("defn_args_opt_splat_no_name", "Ruby" => "def x(b = 42, *)\n # do nothing\nend", "ParseTree" => s(:defn, :x, s(:args, s(:lasgn, :b, s(:lit, 42)), :"*"), s(:nil))) add_tests("defn_args_splat", "Ruby" => "def f(*rest)\n # do nothing\nend", "ParseTree" => s(:defn, :f, s(:args, :"*rest"), s(:nil))) add_tests("defn_args_splat_no_name", "Ruby" => "def x(*)\n # do nothing\nend", "ParseTree" => s(:defn, :x, s(:args, :"*"), s(:nil))) add_tests("defn_or", "Ruby" => "def |(o)\n # do nothing\nend", "ParseTree" => s(:defn, :|, s(:args, :o), s(:nil))) add_tests("defn_rescue", "Ruby" => "def eql?(resource)\n (self.uuid == resource.uuid)\nrescue\n false\nend", "ParseTree" => s(:defn, :eql?, s(:args, :resource), s(:rescue, s(:call, s(:call, s(:self), :uuid), :==, s(:call, s(:lvar, :resource), :uuid)), s(:resbody, s(:array), s(:false)))), "Ruby2Ruby" => "def eql?(resource)\n (self.uuid == resource.uuid) rescue false\nend") add_tests("defn_rescue_mri_verbose_flag", "Ruby" => "def eql?(resource)\n (self.uuid == resource.uuid)\nrescue\n false\nend", "ParseTree" => s(:defn, :eql?, s(:args, :resource), s(:rescue, s(:call, s(:call, s(:self), :uuid), :==, s(:call, s(:lvar, :resource), :uuid)), s(:resbody, s(:array), s(:false)))), "Ruby2Ruby" => "def eql?(resource)\n (self.uuid == resource.uuid) rescue false\nend") add_tests("defn_something_eh", "Ruby" => "def something?\n # do nothing\nend", "ParseTree" => s(:defn, :something?, s(:args), s(:nil))) add_tests("defn_splat_no_name", "Ruby" => "def x(a, *)\n p(a)\nend", "ParseTree" => s(:defn, :x, s(:args, :a, :"*"), s(:call, nil, :p, s(:lvar, :a)))) add_tests("defn_zarray", "Ruby" => "def zarray\n a = []\n return a\nend", "ParseTree" => s(:defn, :zarray, s(:args), s(:lasgn, :a, s(:array)), s(:return, s(:lvar, :a)))) add_tests("defs", "Ruby" => "def self.x(y)\n (y + 1)\nend", "ParseTree" => s(:defs, s(:self), :x, s(:args, :y), s(:call, s(:lvar, :y), :+, s(:lit, 1)))) add_tests("defs_empty", "Ruby" => "def self.empty\n # do nothing\nend", "ParseTree" => s(:defs, s(:self), :empty, s(:args), s(:nil))) add_tests("defs_empty_args", "Ruby" => "def self.empty(*)\n # do nothing\nend", "ParseTree" => s(:defs, s(:self), :empty, s(:args, :*), s(:nil))) add_tests("defs_expr_wtf", "Ruby" => "def (a.b).empty(*)\n # do nothing\nend", "ParseTree" => s(:defs, s(:call, s(:call, nil, :a), :b), :empty, s(:args, :*), s(:nil))) add_tests("dmethod", "Ruby" => [Examples, :dmethod_added], "ParseTree" => s(:defn, :dmethod_added, s(:args, :x), s(:call, s(:lvar, :x), :+, s(:lit, 1))), "Ruby2Ruby" => "def dmethod_added(x)\n (x + 1)\nend") add_tests("dot2", "Ruby" => "(a..b)", "ParseTree" => s(:dot2, s(:call, nil, :a), s(:call, nil, :b))) add_tests("dot3", "Ruby" => "(a...b)", "ParseTree" => s(:dot3, s(:call, nil, :a), s(:call, nil, :b))) add_tests("dregx", "Ruby" => "/x#\{(1 + 1)}y/", "ParseTree" => s(:dregx, "x", s(:evstr, s(:call, s(:lit, 1), :+, s(:lit, 1))), s(:str, "y"))) add_tests("dregx_interp", "Ruby" => "/#\{@rakefile}/", "ParseTree" => s(:dregx, "", s(:evstr, s(:ivar, :@rakefile)))) add_tests("dregx_interp_empty", "Ruby" => "/a#\{}b/", "ParseTree" => s(:dregx, "a", s(:evstr), s(:str, "b"))) add_tests("dregx_n", "Ruby" => '/#{1}/n', "ParseTree" => s(:dregx, "", s(:evstr, s(:lit, 1)), /x/n.options)) add_tests("dregx_once", "Ruby" => "/x#\{(1 + 1)}y/o", "ParseTree" => s(:dregx_once, "x", s(:evstr, s(:call, s(:lit, 1), :+, s(:lit, 1))), s(:str, "y"))) add_tests("dregx_once_n_interp", "Ruby" => "/#\{IAC}#\{SB}/no", "ParseTree" => s(:dregx_once, "", s(:evstr, s(:const, :IAC)), s(:evstr, s(:const, :SB)), /x/n.options)) add_tests("dstr", "Ruby" => "argl = 1\n\"x#\{argl}y\"\n", "ParseTree" => s(:block, s(:lasgn, :argl, s(:lit, 1)), s(:dstr, "x", s(:evstr, s(:lvar, :argl)), s(:str, "y")))) add_tests("dstr_2", "Ruby" => "argl = 1\n\"x#\{(\"%.2f\" % 3.14159)}y\"\n", "ParseTree" => s(:block, s(:lasgn, :argl, s(:lit, 1)), s(:dstr, "x", s(:evstr, s(:call, s(:str, "%.2f"), :%, s(:lit, 3.14159))), s(:str, "y")))) add_tests("dstr_3", "Ruby" => "max = 2\nargl = 1\n\"x#\{(\"%.#\{max}f\" % 3.14159)}y\"\n", "ParseTree" => s(:block, s(:lasgn, :max, s(:lit, 2)), s(:lasgn, :argl, s(:lit, 1)), s(:dstr, "x", s(:evstr, s(:call, s(:dstr, "%.", s(:evstr, s(:lvar, :max)), s(:str, "f")), :%, s(:lit, 3.14159))), s(:str, "y")))) add_tests("dstr_concat", "Ruby" => '"#{22}aa" "cd#{44}" "55" "#{66}"', "ParseTree" => s(:dstr, "", s(:evstr, s(:lit, 22)), s(:str, "aa"), s(:str, "cd"), s(:evstr, s(:lit, 44)), s(:str, "55"), s(:evstr, s(:lit, 66))), "Ruby2Ruby" => '"#{22}aacd#{44}55#{66}"') add_tests("dstr_gross", "Ruby" => '"a #$global b #@ivar c #@@cvar d"', "ParseTree" => s(:dstr, "a ", s(:evstr, s(:gvar, :$global)), s(:str, " b "), s(:evstr, s(:ivar, :@ivar)), s(:str, " c "), s(:evstr, s(:cvar, :@@cvar)), s(:str, " d")), "Ruby2Ruby" => '"a #{$global} b #{@ivar} c #{@@cvar} d"') add_tests("dstr_heredoc_expand", "Ruby" => "< s(:dstr, " blah\n", s(:evstr, s(:call, s(:lit, 1), :+, s(:lit, 1))), s(:str, "blah\n")), "Ruby2Ruby" => "\" blah\\n#\{(1 + 1)}blah\\n\"") add_tests("dstr_heredoc_windoze_sucks", "Ruby" => "<<-EOF\r\ndef test_#\{action}_valid_feed\r\n EOF\r\n", "ParseTree" => s(:dstr, "def test_", s(:evstr, s(:call, nil, :action)), s(:str, "_valid_feed\n")), "Ruby2Ruby" => "\"def test_#\{action}_valid_feed\\n\"") add_tests("dstr_heredoc_yet_again", "Ruby" => "<<-EOF\ns1 '#\{RUBY_PLATFORM}' s2\n#\{__FILE__}\n EOF\n", "ParseTree" => s(:dstr, "s1 '", s(:evstr, s(:const, :RUBY_PLATFORM)), s(:str, "' s2\n"), s(:str, "(string)"), s(:str, "\n")), "Ruby2Ruby" => "\"s1 '#\{RUBY_PLATFORM}' s2\\n(string)\\n\"") add_tests("dstr_nest", "Ruby" => "%Q[before [#\{nest}] after]", "ParseTree" => s(:dstr, "before [", s(:evstr, s(:call, nil, :nest)), s(:str, "] after")), "Ruby2Ruby" => "\"before [#\{nest}] after\"") add_tests("dstr_str_lit_start", "Ruby" => '"#{"blah"}#{__FILE__}:#{__LINE__}: warning: #{$!.message} (#{$!.class})"', "ParseTree" => s(:dstr, "blah(string):", s(:evstr, s(:lit, 1)), s(:str, ": warning: "), s(:evstr, s(:call, s(:gvar, :$!), :message)), s(:str, " ("), s(:evstr, s(:call, s(:gvar, :$!), :class)), s(:str, ")")), "Ruby2Ruby" => '"blah(string):#{1}: warning: #{$!.message} (#{$!.class})"') add_tests("dstr_the_revenge", "Ruby" => '"before #{from} middle #{to} (#{__FILE__}:#{__LINE__})"', "ParseTree" => s(:dstr, "before ", s(:evstr, s(:call, nil, :from)), s(:str, " middle "), s(:evstr, s(:call, nil, :to)), s(:str, " ("), s(:str, "(string)"), s(:str, ":"), s(:evstr, s(:lit, 1)), s(:str, ")")), "Ruby2Ruby" => '"before #{from} middle #{to} ((string):#{1})"') add_tests("dsym", "Ruby" => ":\"x#\{(1 + 1)}y\"", "ParseTree" => s(:dsym, "x", s(:evstr, s(:call, s(:lit, 1), :+, s(:lit, 1))), s(:str, "y"))) add_tests("dxstr", "Ruby" => "t = 5\n`touch #\{t}`\n", "ParseTree" => s(:block, s(:lasgn, :t, s(:lit, 5)), s(:dxstr, "touch ", s(:evstr, s(:lvar, :t))))) add_tests("ensure", "Ruby" => "begin\n (1 + 1)\nrescue SyntaxError => e1\n 2\nrescue Exception => e2\n 3\nelse\n 4\nensure\n 5\nend", "ParseTree" => s(:ensure, s(:rescue, s(:call, s(:lit, 1), :+, s(:lit, 1)), s(:resbody, s(:array, s(:const, :SyntaxError), s(:lasgn, :e1, s(:gvar, :$!))), s(:lit, 2)), s(:resbody, s(:array, s(:const, :Exception), s(:lasgn, :e2, s(:gvar, :$!))), s(:lit, 3)), s(:lit, 4)), s(:lit, 5))) add_tests("false", "Ruby" => "false", "ParseTree" => s(:false)) add_tests("fbody", "Ruby" => [Examples, :an_alias], "ParseTree" => s(:defn, :an_alias, s(:args, :x), s(:call, s(:lvar, :x), :+, s(:lit, 1))), "Ruby2Ruby" => "def an_alias(x)\n (x + 1)\nend") add_tests("fcall_arglist", "Ruby" => "m(42)", "ParseTree" => s(:call, nil, :m, s(:lit, 42))) add_tests("fcall_arglist_hash", "Ruby" => "m(:a => 1, :b => 2)", "ParseTree" => s(:call, nil, :m, s(:hash, s(:lit, :a), s(:lit, 1), s(:lit, :b), s(:lit, 2)))) add_tests("fcall_arglist_norm_hash", "Ruby" => "m(42, :a => 1, :b => 2)", "ParseTree" => s(:call, nil, :m, s(:lit, 42), s(:hash, s(:lit, :a), s(:lit, 1), s(:lit, :b), s(:lit, 2)))) add_tests("fcall_block", "Ruby" => "a(:b) { :c }", "ParseTree" => s(:iter, s(:call, nil, :a, s(:lit, :b)), 0, s(:lit, :c))) add_tests("fcall_index_space", "Ruby" => "a [42]", "ParseTree" => s(:call, nil, :a, s(:array, s(:lit, 42))), "Ruby2Ruby" => "a([42])") add_tests("fcall_inside_parens", "Ruby" => "( a (b), c)", "ParseTree" => s(:call, nil, :a, s(:call, nil, :b), s(:call, nil, :c)), "Ruby2Ruby" => "a(b, c)") add_tests("fcall_keyword", "Ruby" => "42 if block_given?", "ParseTree" => s(:if, s(:call, nil, :block_given?), s(:lit, 42), nil)) add_tests("flip2", "Ruby" => "x = if ((i % 4) == 0)..((i % 3) == 0) then\n i\nelse\n nil\nend", "ParseTree" => s(:lasgn, :x, s(:if, s(:flip2, s(:call, s(:call, s(:call, nil, :i), :%, s(:lit, 4)), :==, s(:lit, 0)), s(:call, s(:call, s(:call, nil, :i), :%, s(:lit, 3)), :==, s(:lit, 0))), s(:call, nil, :i), s(:nil)))) add_tests("flip2_method", "Ruby" => "if 1..2.a?(b) then\n nil\nend", "ParseTree" => s(:if, s(:flip2, s(:lit, 1), s(:call, s(:lit, 2), :a?, s(:call, nil, :b))), s(:nil), nil)) add_tests("flip3", "Ruby" => "x = if ((i % 4) == 0)...((i % 3) == 0) then\n i\nelse\n nil\nend", "ParseTree" => s(:lasgn, :x, s(:if, s(:flip3, s(:call, s(:call, s(:call, nil, :i), :%, s(:lit, 4)), :==, s(:lit, 0)), s(:call, s(:call, s(:call, nil, :i), :%, s(:lit, 3)), :==, s(:lit, 0))), s(:call, nil, :i), s(:nil)))) add_tests("for", "Ruby" => "for o in ary do\n puts(o)\nend", "ParseTree" => s(:for, s(:call, nil, :ary), s(:lasgn, :o), s(:call, nil, :puts, s(:lvar, :o)))) add_tests("for_no_body", "Ruby" => "for i in (0..max) do\n # do nothing\nend", "ParseTree" => s(:for, s(:dot2, s(:lit, 0), s(:call, nil, :max)), s(:lasgn, :i))) add_tests("gasgn", "Ruby" => "$x = 42", "ParseTree" => s(:gasgn, :$x, s(:lit, 42))) add_tests("global", "Ruby" => "$stderr", "ParseTree" => s(:gvar, :$stderr)) add_tests("gvar", "Ruby" => "$x", "ParseTree" => s(:gvar, :$x)) add_tests("gvar_underscore", "Ruby" => "$_", "ParseTree" => s(:gvar, :$_)) add_tests("gvar_underscore_blah", "Ruby" => "$__blah", "ParseTree" => s(:gvar, :$__blah)) add_tests("hash", "Ruby" => "{ 1 => 2, 3 => 4 }", "ParseTree" => s(:hash, s(:lit, 1), s(:lit, 2), s(:lit, 3), s(:lit, 4))) add_tests("hash_rescue", "Ruby" => "{ 1 => (2 rescue 3) }", "ParseTree" => s(:hash, s(:lit, 1), s(:rescue, s(:lit, 2), s(:resbody, s(:array), s(:lit, 3))))) add_tests("iasgn", "Ruby" => "@a = 4", "ParseTree" => s(:iasgn, :@a, s(:lit, 4))) add_tests("if_block_condition", "Ruby" => "if (x = 5\n(x + 1)) then\n nil\nend", "ParseTree" => s(:if, s(:block, s(:lasgn, :x, s(:lit, 5)), s(:call, s(:lvar, :x), :+, s(:lit, 1))), s(:nil), nil)) add_tests("if_lasgn_short", "Ruby" => "if x = obj.x then\n x.do_it\nend", "ParseTree" => s(:if, s(:lasgn, :x, s(:call, s(:call, nil, :obj), :x)), s(:call, s(:lvar, :x), :do_it), nil)) add_tests("if_nested", "Ruby" => "return if false unless true", "ParseTree" => s(:if, s(:true), nil, s(:if, s(:false), s(:return), nil))) add_tests("if_post", "Ruby" => "a if b", "ParseTree" => s(:if, s(:call, nil, :b), s(:call, nil, :a), nil)) add_tests("if_pre", "Ruby" => "if b then a end", "ParseTree" => s(:if, s(:call, nil, :b), s(:call, nil, :a), nil), "Ruby2Ruby" => "a if b") add_tests("iter_call_arglist_space", "Ruby" => "a (1) {|c|d}", "ParseTree" => s(:iter, s(:call, nil, :a, s(:lit, 1)), s(:args, :c), s(:call, nil, :d)), "Ruby2Ruby" => "a(1) { |c| d }") add_tests("iter_dasgn_curr_dasgn_madness", "Ruby" => "as.each { |a|\n b += a.b(false) }", "ParseTree" => s(:iter, s(:call, s(:call, nil, :as), :each), s(:args, :a), s(:lasgn, :b, s(:call, s(:lvar, :b), :+, s(:call, s(:lvar, :a), :b, s(:false))))), "Ruby2Ruby" => "as.each { |a| b = (b + a.b(false)) }") add_tests("iter_downto", "Ruby" => "3.downto(1) { |n| puts(n.to_s) }", "ParseTree" => s(:iter, s(:call, s(:lit, 3), :downto, s(:lit, 1)), s(:args, :n), s(:call, nil, :puts, s(:call, s(:lvar, :n), :to_s)))) add_tests("iter_each_lvar", "Ruby" => "array = [1, 2, 3]\narray.each { |x| puts(x.to_s) }\n", "ParseTree" => s(:block, s(:lasgn, :array, s(:array, s(:lit, 1), s(:lit, 2), s(:lit, 3))), s(:iter, s(:call, s(:lvar, :array), :each), s(:args, :x), s(:call, nil, :puts, s(:call, s(:lvar, :x), :to_s))))) add_tests("iter_each_nested", "Ruby" => "array1 = [1, 2, 3]\narray2 = [4, 5, 6, 7]\narray1.each do |x|\n array2.each do |y|\n puts(x.to_s)\n puts(y.to_s)\n end\nend\n", "ParseTree" => s(:block, s(:lasgn, :array1, s(:array, s(:lit, 1), s(:lit, 2), s(:lit, 3))), s(:lasgn, :array2, s(:array, s(:lit, 4), s(:lit, 5), s(:lit, 6), s(:lit, 7))), s(:iter, s(:call, s(:lvar, :array1), :each), s(:args, :x), s(:iter, s(:call, s(:lvar, :array2), :each), s(:args, :y), s(:block, s(:call, nil, :puts, s(:call, s(:lvar, :x), :to_s)), s(:call, nil, :puts, s(:call, s(:lvar, :y), :to_s))))))) add_tests("iter_loop_empty", "Ruby" => "loop { }", "ParseTree" => s(:iter, s(:call, nil, :loop), 0)) add_tests("iter_masgn_2", "Ruby" => "a { |b, c| p(c) }", "ParseTree" => s(:iter, s(:call, nil, :a), s(:args, :b, :c), s(:call, nil, :p, s(:lvar, :c)))) add_tests("iter_masgn_args_splat", "Ruby" => "a { |b, c, *d| p(c) }", "ParseTree" => s(:iter, s(:call, nil, :a), s(:args, :b, :c, :"*d"), s(:call, nil, :p, s(:lvar, :c)))) add_tests("iter_masgn_args_splat_no_name", "Ruby" => "a { |b, c, *| p(c) }", "ParseTree" => s(:iter, s(:call, nil, :a), s(:args, :b, :c, :*), s(:call, nil, :p, s(:lvar, :c)))) add_tests("iter_masgn_splat", "Ruby" => "a { |*c| p(c) }", "ParseTree" => s(:iter, s(:call, nil, :a), s(:args, :"*c"), s(:call, nil, :p, s(:lvar, :c)))) add_tests("iter_masgn_splat_no_name", "Ruby" => "a { |*| p(c) }", "ParseTree" => s(:iter, s(:call, nil, :a), s(:args, :*), s(:call, nil, :p, s(:call, nil, :c)))) add_tests("iter_shadowed_var", "Ruby" => "a do |x|\n b do |x|\n puts x\n end\nend", "ParseTree" => s(:iter, s(:call, nil, :a), s(:args, :x), s(:iter, s(:call, nil, :b), s(:args, :x), s(:call, nil, :puts, s(:lvar, :x)))), "Ruby2Ruby" => "a { |x| b { |x| puts(x) } }") add_tests("iter_upto", "Ruby" => "1.upto(3) { |n| puts(n.to_s) }", "ParseTree" => s(:iter, s(:call, s(:lit, 1), :upto, s(:lit, 3)), s(:args, :n), s(:call, nil, :puts, s(:call, s(:lvar, :n), :to_s)))) add_tests("iter_while", "Ruby" => "argl = 10\nwhile (argl >= 1) do\n puts(\"hello\")\n argl = (argl - 1)\nend\n", "ParseTree" => s(:block, s(:lasgn, :argl, s(:lit, 10)), s(:while, s(:call, s(:lvar, :argl), :">=", s(:lit, 1)), s(:block, s(:call, nil, :puts, s(:str, "hello")), s(:lasgn, :argl, s(:call, s(:lvar, :argl), :"-", s(:lit, 1)))), true))) add_tests("ivar", "Ruby" => [Examples, :reader], "ParseTree" => s(:defn, :reader, s(:args), s(:ivar, :@reader)), "Ruby2Ruby" => "attr_reader :reader") add_tests("lambda_args_anon_star", "Ruby" => "lambda { |*| nil }", "ParseTree" => s(:iter, s(:call, nil, :lambda), s(:args, :*), s(:nil))) add_tests("lambda_args_anon_star_block", "Ruby" => "lambda { |*, &block| block }", "ParseTree" => s(:iter, s(:call, nil, :lambda), s(:args, :*, :"&block"), s(:lvar, :block))) add_tests("lambda_args_block", "Ruby" => "lambda { |&block| block }", "ParseTree" => s(:iter, s(:call, nil, :lambda), s(:args, :"&block"), s(:lvar, :block))) add_tests("lambda_args_norm_anon_star", "Ruby" => "lambda { |a, *| a }", "ParseTree" => s(:iter, s(:call, nil, :lambda), s(:args, :a, :*), s(:lvar, :a))) add_tests("lambda_args_norm_anon_star_block", "Ruby" => "lambda { |a, *, &block| block }", "ParseTree" => s(:iter, s(:call, nil, :lambda), s(:args, :a, :*, :"&block"), s(:lvar, :block))) add_tests("lambda_args_norm_block", "Ruby" => "lambda { |a, &block| block }", "ParseTree" => s(:iter, s(:call, nil, :lambda), s(:args, :a, :"&block"), s(:lvar, :block))) add_tests("lambda_args_norm_comma", "Ruby" => "lambda { |a,| a }", "ParseTree" => s(:iter, s(:call, nil, :lambda), s(:args, :a, nil), s(:lvar, :a))) add_tests("lambda_args_norm_comma2", "Ruby" => "lambda { |a, b,| a }", "ParseTree" => s(:iter, s(:call, nil, :lambda), s(:args, :a, :b, nil), s(:lvar, :a))) add_tests("lambda_args_norm_star", "Ruby" => "lambda { |a, *star| star }", "ParseTree" => s(:iter, s(:call, nil, :lambda), s(:args, :a, :"*star"), s(:lvar, :star))) add_tests("lambda_args_norm_star_block", "Ruby" => "lambda { |a, *star, &block| block }", "ParseTree" => s(:iter, s(:call, nil, :lambda), s(:args, :a, :"*star", :"&block"), s(:lvar, :block))) add_tests("lambda_args_star", "Ruby" => "lambda { |*star| star }", "ParseTree" => s(:iter, s(:call, nil, :lambda), s(:args, :"*star"), s(:lvar, :star))) add_tests("lambda_args_star_block", "Ruby" => "lambda { |*star, &block| block }", "ParseTree" => s(:iter, s(:call, nil, :lambda), s(:args, :"*star", :"&block"), s(:lvar, :block))) add_tests("lasgn_array", "Ruby" => "var = [\"foo\", \"bar\"]", "ParseTree" => s(:lasgn, :var, s(:array, s(:str, "foo"), s(:str, "bar")))) add_tests("lasgn_call", "Ruby" => "c = (2 + 3)", "ParseTree" => s(:lasgn, :c, s(:call, s(:lit, 2), :+, s(:lit, 3)))) add_tests("lit_bool_false", "Ruby" => "false", "ParseTree" => s(:false)) add_tests("lit_bool_true", "Ruby" => "true", "ParseTree" => s(:true)) add_tests("lit_float", "Ruby" => "1.1", "ParseTree" => s(:lit, 1.1)) add_tests("lit_long", "Ruby" => "1", "ParseTree" => s(:lit, 1)) add_tests("lit_long_negative", "Ruby" => "-1", "ParseTree" => s(:lit, -1)) add_tests("lit_range2", "Ruby" => "(1..10)", "ParseTree" => s(:lit, 1..10)) add_tests("lit_range3", "Ruby" => "(1...10)", "ParseTree" => s(:lit, 1...10)) add_tests("lit_regexp", "Ruby" => "/x/", "ParseTree" => s(:lit, /x/)) add_tests("lit_regexp_i_wwtt", "Ruby" => "str.split(//i)", "ParseTree" => s(:call, s(:call, nil, :str), :split, s(:lit, //i))) add_tests("lit_regexp_n", "Ruby" => "/x/n", # HACK differs on 1.9 - this is easiest "ParseTree" => s(:lit, /x/n), "Ruby2Ruby" => /x/n.inspect) add_tests("lit_regexp_once", "Ruby" => "/x/o", "ParseTree" => s(:lit, /x/), "Ruby2Ruby" => "/x/") add_tests("lit_sym", "Ruby" => ":x", "ParseTree" => s(:lit, :x)) add_tests("lit_sym_splat", "Ruby" => ":\"*args\"", "ParseTree" => s(:lit, :"*args")) add_tests("lvar_def_boundary", "Ruby" => "b = 42\ndef a\n c do\n begin\n do_stuff\n rescue RuntimeError => b\n puts(b)\n end\n end\nend\n", "ParseTree" => s(:block, s(:lasgn, :b, s(:lit, 42)), s(:defn, :a, s(:args), s(:iter, s(:call, nil, :c), 0, s(:rescue, s(:call, nil, :do_stuff), s(:resbody, s(:array, s(:const, :RuntimeError), s(:lasgn, :b, s(:gvar, :$!))), s(:call, nil, :puts, s(:lvar, :b)))))))) add_tests("masgn", "Ruby" => "a, b = c, d", "ParseTree" => s(:masgn, s(:array, s(:lasgn, :a), s(:lasgn, :b)), s(:array, s(:call, nil, :c), s(:call, nil, :d)))) add_tests("masgn_argscat", "Ruby" => "a, b, *c = 1, 2, *[3, 4]", "ParseTree" => s(:masgn, s(:array, s(:lasgn, :a), s(:lasgn, :b), s(:splat, s(:lasgn, :c))), s(:array, s(:lit, 1), s(:lit, 2), s(:splat, s(:array, s(:lit, 3), s(:lit, 4)))))) add_tests("masgn_attrasgn", "Ruby" => "a, b.c = d, e", "ParseTree" => s(:masgn, s(:array, s(:lasgn, :a), s(:attrasgn, s(:call, nil, :b), :c=)), s(:array, s(:call, nil, :d), s(:call, nil, :e)))) add_tests("masgn_attrasgn_array_rhs", "Ruby" => "a.b, a.c, _ = q", "ParseTree" => s(:masgn, s(:array, s(:attrasgn, s(:call, nil, :a), :b=), s(:attrasgn, s(:call, nil, :a), :c=), s(:lasgn, :_)), s(:to_ary, s(:call, nil, :q)))) add_tests("masgn_attrasgn_idx", "Ruby" => "a, i, j = [], 1, 2\na[i], a[j] = a[j], a[i]\n", "ParseTree" => s(:block, s(:masgn, s(:array, s(:lasgn, :a), s(:lasgn, :i), s(:lasgn, :j)), s(:array, s(:array), s(:lit, 1), s(:lit, 2))), s(:masgn, s(:array, s(:attrasgn, s(:lvar, :a), :[]=, s(:lvar, :i)), s(:attrasgn, s(:lvar, :a), :[]=, s(:lvar, :j))), s(:array, s(:call, s(:lvar, :a), :[], s(:lvar, :j)), s(:call, s(:lvar, :a), :[], s(:lvar, :i)))))) add_tests("masgn_cdecl", "Ruby" => "A, B, C = 1, 2, 3", "ParseTree" => s(:masgn, s(:array, s(:cdecl, :A), s(:cdecl, :B), s(:cdecl, :C)), s(:array, s(:lit, 1), s(:lit, 2), s(:lit, 3)))) add_tests("masgn_iasgn", "Ruby" => "a, @b = c, d", "ParseTree" => s(:masgn, s(:array, s(:lasgn, :a), s(:iasgn, :"@b")), s(:array, s(:call, nil, :c), s(:call, nil, :d)))) add_tests("masgn_masgn", "Ruby" => "a, (b, c) = [1, [2, 3]]", "ParseTree" => s(:masgn, s(:array, s(:lasgn, :a), s(:masgn, s(:array, s(:lasgn, :b), s(:lasgn, :c)))), s(:to_ary, s(:array, s(:lit, 1), s(:array, s(:lit, 2), s(:lit, 3)))))) add_tests("masgn_splat_lhs", "Ruby" => "a, b, *c = d, e, f, g", "ParseTree" => s(:masgn, s(:array, s(:lasgn, :a), s(:lasgn, :b), s(:splat, s(:lasgn, :c))), s(:array, s(:call, nil, :d), s(:call, nil, :e), s(:call, nil, :f), s(:call, nil, :g)))) add_tests("masgn_splat_no_name_to_ary", "Ruby" => "a, b, * = c", "ParseTree" => s(:masgn, s(:array, s(:lasgn, :a), s(:lasgn, :b), s(:splat)), s(:to_ary, s(:call, nil, :c)))) add_tests("masgn_splat_no_name_trailing", "Ruby" => "a, b, = c", "ParseTree" => s(:masgn, s(:array, s(:lasgn, :a), s(:lasgn, :b)), s(:to_ary, s(:call, nil, :c))), "Ruby2Ruby" => "a, b = c") # TODO: check this is right add_tests("masgn_splat_rhs_1", "Ruby" => "a, b = *c", "ParseTree" => s(:masgn, s(:array, s(:lasgn, :a), s(:lasgn, :b)), s(:splat, s(:call, nil, :c)))) add_tests("masgn_splat_rhs_n", "Ruby" => "a, b = c, d, *e", "ParseTree" => s(:masgn, s(:array, s(:lasgn, :a), s(:lasgn, :b)), s(:array, s(:call, nil, :c), s(:call, nil, :d), s(:splat, s(:call, nil, :e))))) add_tests("masgn_splat_to_ary", "Ruby" => "a, b, *c = d", "ParseTree" => s(:masgn, s(:array, s(:lasgn, :a), s(:lasgn, :b), s(:splat, s(:lasgn, :c))), s(:to_ary, s(:call, nil, :d)))) add_tests("masgn_splat_to_ary2", "Ruby" => "a, b, *c = d.e(\"f\")", "ParseTree" => s(:masgn, s(:array, s(:lasgn, :a), s(:lasgn, :b), s(:splat, s(:lasgn, :c))), s(:to_ary, s(:call, s(:call, nil, :d), :e, s(:str, "f"))))) add_tests("match", "Ruby" => "1 if /x/", "ParseTree" => s(:if, s(:match, s(:lit, /x/)), s(:lit, 1), nil)) add_tests("match2", "Ruby" => "/x/ =~ \"blah\"", "ParseTree" => s(:match2, s(:lit, /x/), s(:str, "blah"))) add_tests("match3", "Ruby" => "\"blah\" =~ /x/", "ParseTree" => s(:match3, s(:lit, /x/), s(:str, "blah"))) add_tests("module", "Ruby" => "module X\n def y\n # do nothing\n end\nend", "ParseTree" => s(:module, :X, s(:defn, :y, s(:args), s(:nil)))) add_tests("module2", "Ruby" => "module X\n def y\n # do nothing\n end\n \n def z\n # do nothing\n end\nend", "ParseTree" => s(:module, :X, s(:defn, :y, s(:args), s(:nil)), s(:defn, :z, s(:args), s(:nil)))) add_tests("module_scoped", "Ruby" => "module X::Y\n c\nend", "ParseTree" => s(:module, s(:colon2, s(:const, :X), :Y), s(:call, nil, :c))) add_tests("module_scoped3", "Ruby" => "module ::Y\n c\nend", "ParseTree" => s(:module, s(:colon3, :Y), s(:call, nil, :c))) add_tests("next", "Ruby" => "loop { next if false }", "ParseTree" => s(:iter, s(:call, nil, :loop), 0, s(:if, s(:false), s(:next), nil))) add_tests("next_arg", "Ruby" => "loop { next 42 if false }", "ParseTree" => s(:iter, s(:call, nil, :loop), 0, s(:if, s(:false), s(:next, s(:lit, 42)), nil))) add_tests("nth_ref", "Ruby" => "$1", "ParseTree" => s(:nth_ref, 1)) add_tests("op_asgn1", "Ruby" => "b = []\nb[1] ||= 10\nb[2] &&= 11\nb[3] += 12\n", "ParseTree" => s(:block, s(:lasgn, :b, s(:array)), s(:op_asgn1, s(:lvar, :b), s(:arglist, s(:lit, 1)), :"||", s(:lit, 10)), s(:op_asgn1, s(:lvar, :b), s(:arglist, s(:lit, 2)), :"&&", s(:lit, 11)), s(:op_asgn1, s(:lvar, :b), s(:arglist, s(:lit, 3)), :+, s(:lit, 12)))) add_tests("op_asgn1_ivar", "Ruby" => "@b = []\n@b[1] ||= 10\n@b[2] &&= 11\n@b[3] += 12\n", "ParseTree" => s(:block, s(:iasgn, :@b, s(:array)), s(:op_asgn1, s(:ivar, :@b), s(:arglist, s(:lit, 1)), :"||", s(:lit, 10)), s(:op_asgn1, s(:ivar, :@b), s(:arglist, s(:lit, 2)), :"&&", s(:lit, 11)), s(:op_asgn1, s(:ivar, :@b), s(:arglist, s(:lit, 3)), :+, s(:lit, 12)))) add_tests("op_asgn2", "Ruby" => "s = Struct.new(:var)\nc = s.new(nil)\nc.var ||= 20\nc.var &&= 21\nc.var += 22\nc.d.e.f ||= 42\n", "ParseTree" => s(:block, s(:lasgn, :s, s(:call, s(:const, :Struct), :new, s(:lit, :var))), s(:lasgn, :c, s(:call, s(:lvar, :s), :new, s(:nil))), s(:op_asgn2, s(:lvar, :c), :var=, :"||", s(:lit, 20)), s(:op_asgn2, s(:lvar, :c), :var=, :"&&", s(:lit, 21)), s(:op_asgn2, s(:lvar, :c), :var=, :+, s(:lit, 22)), s(:op_asgn2, s(:call, s(:call, s(:lvar, :c), :d), :e), :f=, :"||", s(:lit, 42)))) add_tests("op_asgn2_self", "Ruby" => "self.Bag ||= Bag.new", "ParseTree" => s(:op_asgn2, s(:self), :"Bag=", :"||", s(:call, s(:const, :Bag), :new))) add_tests("op_asgn_and", "Ruby" => "a = 0\na &&= 2\n", "ParseTree" => s(:block, s(:lasgn, :a, s(:lit, 0)), s(:op_asgn_and, s(:lvar, :a), s(:lasgn, :a, s(:lit, 2))))) add_tests("op_asgn_and_ivar2", "Ruby" => "@fetcher &&= new(Gem.configuration[:http_proxy])", "ParseTree" => s(:op_asgn_and, s(:ivar, :@fetcher), s(:iasgn, :@fetcher, s(:call, nil, :new, s(:call, s(:call, s(:const, :Gem), :configuration), :[], s(:lit, :http_proxy)))))) add_tests("op_asgn_or", "Ruby" => "a = 0\na ||= 1\n", "ParseTree" => s(:block, s(:lasgn, :a, s(:lit, 0)), s(:op_asgn_or, s(:lvar, :a), s(:lasgn, :a, s(:lit, 1))))) add_tests("op_asgn_or_block", "Ruby" => "a ||= begin\n b\n rescue\n c\n end", "ParseTree" => s(:op_asgn_or, s(:lvar, :a), s(:lasgn, :a, s(:rescue, s(:call, nil, :b), s(:resbody, s(:array), s(:call, nil, :c))))), "Ruby2Ruby" => "a ||= b rescue c") add_tests("op_asgn_or_ivar", "Ruby" => "@v ||= {}", "ParseTree" => s(:op_asgn_or, s(:ivar, :@v), s(:iasgn, :@v, s(:hash)))) add_tests("op_asgn_or_ivar2", "Ruby" => "@fetcher ||= new(Gem.configuration[:http_proxy])", "ParseTree" => s(:op_asgn_or, s(:ivar, :@fetcher), s(:iasgn, :@fetcher, s(:call, nil, :new, s(:call, s(:call, s(:const, :Gem), :configuration), :[], s(:lit, :http_proxy)))))) add_tests("or", "Ruby" => "(a or b)", "ParseTree" => s(:or, s(:call, nil, :a), s(:call, nil, :b))) add_tests("or_big", "Ruby" => "((a or b) or (c and d))", "ParseTree" => s(:or, s(:or, s(:call, nil, :a), s(:call, nil, :b)), s(:and, s(:call, nil, :c), s(:call, nil, :d)))) add_tests("or_big2", "Ruby" => "((a || b) || (c && d))", "ParseTree" => s(:or, s(:or, s(:call, nil, :a), s(:call, nil, :b)), s(:and, s(:call, nil, :c), s(:call, nil, :d))), "Ruby2Ruby" => "((a or b) or (c and d))") add_tests("parse_floats_as_args", "Ruby" => "def x(a=0.0,b=0.0)\n a+b\nend", "ParseTree" => s(:defn, :x, s(:args, s(:lasgn, :a, s(:lit, 0.0)), s(:lasgn, :b, s(:lit, 0.0))), s(:call, s(:lvar, :a), :+, s(:lvar, :b))), "Ruby2Ruby" => "def x(a = 0.0, b = 0.0)\n (a + b)\nend") add_tests("postexe", "Ruby" => "END { 1 }", "ParseTree" => s(:iter, s(:postexe), 0, s(:lit, 1))) add_tests("proc_args_0", "Ruby" => "proc { || (x + 1) }", "ParseTree" => s(:iter, s(:call, nil, :proc), s(:args), s(:call, s(:call, nil, :x), :+, s(:lit, 1)))) add_tests("proc_args_1", "Ruby" => "proc { |x| (x + 1) }", "ParseTree" => s(:iter, s(:call, nil, :proc), s(:args, :x), s(:call, s(:lvar, :x), :+, s(:lit, 1)))) add_tests("proc_args_2", "Ruby" => "proc { |x, y| (x + y) }", "ParseTree" => s(:iter, s(:call, nil, :proc), s(:args, :x, :y), s(:call, s(:lvar, :x), :+, s(:lvar, :y)))) add_tests("proc_args_no", "Ruby" => "proc { (x + 1) }", "ParseTree" => s(:iter, s(:call, nil, :proc), 0, s(:call, s(:call, nil, :x), :+, s(:lit, 1)))) add_tests("redo", "Ruby" => "loop { redo if false }", "ParseTree" => s(:iter, s(:call, nil, :loop), 0, s(:if, s(:false), s(:redo), nil))) add_tests("rescue", # TODO: need a resbody w/ multiple classes and a splat "Ruby" => "blah rescue nil", "ParseTree" => s(:rescue, s(:call, nil, :blah), s(:resbody, s(:array), s(:nil)))) add_tests("rescue_block_body", "Ruby" => "begin\n a\nrescue => e\n c\n d\nend", "ParseTree" => s(:rescue, s(:call, nil, :a), s(:resbody, s(:array, s(:lasgn, :e, s(:gvar, :$!))), s(:call, nil, :c), s(:call, nil, :d)))) add_tests("rescue_block_body_3", "Ruby" => "begin\n a\nrescue A\n b\nrescue B\n c\nrescue C\n d\nend", "ParseTree" => s(:rescue, s(:call, nil, :a), s(:resbody, s(:array, s(:const, :A)), s(:call, nil, :b)), s(:resbody, s(:array, s(:const, :B)), s(:call, nil, :c)), s(:resbody, s(:array, s(:const, :C)), s(:call, nil, :d)))) add_tests("rescue_block_body_ivar", "Ruby" => "begin\n a\nrescue => @e\n c\n d\nend", "ParseTree" => s(:rescue, s(:call, nil, :a), s(:resbody, s(:array, s(:iasgn, :@e, s(:gvar, :$!))), s(:call, nil, :c), s(:call, nil, :d)))) add_tests("rescue_block_nada", "Ruby" => "begin\n blah\nrescue\n # do nothing\nend", "ParseTree" => s(:rescue, s(:call, nil, :blah), s(:resbody, s(:array), nil))) add_tests("rescue_exceptions", "Ruby" => "begin\n blah\nrescue RuntimeError => r\n # do nothing\nend", "ParseTree" => s(:rescue, s(:call, nil, :blah), s(:resbody, s(:array, s(:const, :RuntimeError), s(:lasgn, :r, s(:gvar, :$!))), nil))) add_tests("rescue_iasgn_var_empty", "Ruby" => "begin\n 1\nrescue => @e\n # do nothing\nend", "ParseTree" => s(:rescue, s(:lit, 1), s(:resbody, s(:array, s(:iasgn, :@e, s(:gvar, :$!))), nil))) add_tests("rescue_lasgn", "Ruby" => "begin\n 1\nrescue\n var = 2\nend", "ParseTree" => s(:rescue, s(:lit, 1), s(:resbody, s(:array), s(:lasgn, :var, s(:lit, 2)))), "Ruby2Ruby" => "1 rescue var = 2") add_tests("rescue_lasgn_var", "Ruby" => "begin\n 1\nrescue => e\n var = 2\nend", "ParseTree" => s(:rescue, s(:lit, 1), s(:resbody, s(:array, s(:lasgn, :e, s(:gvar, :$!))), s(:lasgn, :var, s(:lit, 2))))) add_tests("rescue_lasgn_var_empty", "Ruby" => "begin\n 1\nrescue => e\n # do nothing\nend", "ParseTree" => s(:rescue, s(:lit, 1), s(:resbody, s(:array, s(:lasgn, :e, s(:gvar, :$!))), nil))) add_tests("retry", "Ruby" => "retry", "ParseTree" => s(:retry)) add_tests("return_0", "Ruby" => "return", "ParseTree" => s(:return)) add_tests("return_1", "Ruby" => "return 1", "ParseTree" => s(:return, s(:lit, 1))) add_tests("return_1_splatted", "Ruby" => "return *1", "ParseTree" => s(:return, s(:svalue, s(:splat, s(:lit, 1))))) add_tests("return_n", "Ruby" => "return 1, 2, 3", "ParseTree" => s(:return, s(:array, s(:lit, 1), s(:lit, 2), s(:lit, 3))), "Ruby2Ruby" => "return [1, 2, 3]") add_tests("sclass", "Ruby" => "class << self\n 42\nend", "ParseTree" => s(:sclass, s(:self), s(:lit, 42))) add_tests("sclass_multiple", "Ruby" => "class << self\n x\n y\nend", "ParseTree" => s(:sclass, s(:self), s(:call, nil, :x), s(:call, nil, :y))) add_tests("sclass_trailing_class", "Ruby" => "class A\n class << self\n a\n end\n \n class B\n end\nend", "ParseTree" => s(:class, :A, nil, s(:sclass, s(:self), s(:call, nil, :a)), s(:class, :B, nil))) add_tests("splat", "Ruby" => "def x(*b)\n a(*b)\nend", "ParseTree" => s(:defn, :x, s(:args, :"*b"), s(:call, nil, :a, s(:splat, s(:lvar, :b))))) add_tests("splat_array", "Ruby" => "[*[1]]", "ParseTree" => s(:array, s(:splat, s(:array, s(:lit, 1))))) add_tests("splat_break", "Ruby" => "break *[1]", "ParseTree" => s(:break, s(:svalue, s(:splat, s(:array, s(:lit, 1)))))) add_tests("splat_break_array", "Ruby" => "break [*[1]]", "ParseTree" => s(:break, s(:array, s(:splat, s(:array, s(:lit, 1)))))) add_tests("splat_fcall", "Ruby" => "meth(*[1])", "ParseTree" => s(:call, nil, :meth, s(:splat, s(:array, s(:lit, 1))))) add_tests("splat_fcall_array", "Ruby" => "meth([*[1]])", "ParseTree" => s(:call, nil, :meth, s(:array, s(:splat, s(:array, s(:lit, 1)))))) add_tests("splat_lasgn", "Ruby" => "x = *[1]", "ParseTree" => s(:lasgn, :x, s(:svalue, s(:splat, s(:array, s(:lit, 1)))))) add_tests("splat_lasgn_array", "Ruby" => "x = [*[1]]", "ParseTree" => s(:lasgn, :x, s(:array, s(:splat, s(:array, s(:lit, 1)))))) add_tests("splat_lit_1", "Ruby" => "[*1]", # UGH - damn MRI "ParseTree" => s(:array, s(:splat, s(:lit, 1)))) add_tests("splat_lit_n", "Ruby" => "[1, *2]", "ParseTree" => s(:array, s(:lit, 1), s(:splat, s(:lit, 2)))) add_tests("splat_next", "Ruby" => "next *[1]", "ParseTree" => s(:next, s(:svalue, s(:splat, s(:array, s(:lit, 1)))))) add_tests("splat_next_array", "Ruby" => "next [*[1]]", "ParseTree" => s(:next, s(:array, s(:splat, s(:array, s(:lit, 1)))))) add_tests("splat_return", "Ruby" => "return *[1]", "ParseTree" => s(:return, s(:svalue, s(:splat, s(:array, s(:lit, 1)))))) add_tests("splat_return_array", "Ruby" => "return [*[1]]", "ParseTree" => s(:return, s(:array, s(:splat, s(:array, s(:lit, 1)))))) add_tests("splat_super", "Ruby" => "super(*[1])", "ParseTree" => s(:super, s(:splat, s(:array, s(:lit, 1))))) add_tests("splat_super_array", "Ruby" => "super([*[1]])", "ParseTree" => s(:super, s(:array, s(:splat, s(:array, s(:lit, 1)))))) add_tests("splat_yield", "Ruby" => "yield(*[1])", "ParseTree" => s(:yield, s(:splat, s(:array, s(:lit, 1))))) add_tests("splat_yield_array", "Ruby" => "yield([*[1]])", "ParseTree" => s(:yield, s(:array, s(:splat, s(:array, s(:lit, 1)))))) add_tests("str", "Ruby" => '"x"', "ParseTree" => s(:str, "x")) add_tests("str_concat_newline", # FIX? make prettier? possible? "Ruby" => '"before" \\ " after"', "ParseTree" => s(:str, "before after"), "Ruby2Ruby" => '"before after"') add_tests("str_concat_space", "Ruby" => '"before" " after"', "ParseTree" => s(:str, "before after"), "Ruby2Ruby" => '"before after"') add_tests("str_heredoc", "Ruby" => "<<'EOM'\n blah\nblah\nEOM", "ParseTree" => s(:str, " blah\nblah\n"), "Ruby2Ruby" => "\" blah\\nblah\\n\"") add_tests("str_heredoc_call", "Ruby" => "<<'EOM'.strip\n blah\nblah\nEOM", "ParseTree" => s(:call, s(:str, " blah\nblah\n"), :strip), "Ruby2Ruby" => "\" blah\\nblah\\n\".strip") add_tests("str_heredoc_double", "Ruby" => "a += <<-H1 + b + <<-H2\n first\nH1\n second\nH2", "ParseTree" => s(:lasgn, :a, s(:call, s(:lvar, :a), :+, s(:call, s(:call, s(:str, " first\n"), :+, s(:call, nil, :b)), :+, s(:str, " second\n")))), "Ruby2Ruby" => "a = (a + ((\" first\\n\" + b) + \" second\\n\"))") add_tests("str_heredoc_empty", # yes... tarded "Ruby" => "<<'EOM'\nEOM", "ParseTree" => s(:str, ""), "Ruby2Ruby" => '""') add_tests("str_heredoc_indent", "Ruby" => "<<-EOM\n blah\nblah\n\n EOM", "ParseTree" => s(:str, " blah\nblah\n\n"), "Ruby2Ruby" => "\" blah\\nblah\\n\\n\"") add_tests("str_interp_file", "Ruby" => '"file = #{__FILE__}\n"', "ParseTree" => s(:str, "file = (string)\n"), "Ruby2Ruby" => '"file = (string)\\n"') add_tests("structure_extra_block_for_dvar_scoping", "Ruby" => "a.b do |c, d|\n unless e.f(c) then\n g = false\n d.h { |x, i| g = true }\n end\nend", "ParseTree" => s(:iter, s(:call, s(:call, nil, :a), :b), s(:args, :c, :d), s(:if, s(:call, s(:call, nil, :e), :f, s(:lvar, :c)), nil, s(:block, s(:lasgn, :g, s(:false)), s(:iter, s(:call, s(:lvar, :d), :h), s(:args, :x, :i), s(:lasgn, :g, s(:true))))))) add_tests("structure_remove_begin_1", "Ruby" => "a << begin\n b\n rescue\n c\n end", "ParseTree" => s(:call, s(:call, nil, :a), :<<, s(:rescue, s(:call, nil, :b), s(:resbody, s(:array), s(:call, nil, :c)))), "Ruby2Ruby" => "(a << (b rescue c))") add_tests("structure_remove_begin_2", "Ruby" => "a = if c\n begin\n b\n rescue\n nil\n end\n end\na", "ParseTree" => s(:block, s(:lasgn, :a, s(:if, s(:call, nil, :c), s(:rescue, s(:call, nil, :b), s(:resbody, s(:array), s(:nil))), nil)), s(:lvar, :a)), "Ruby2Ruby" => "a = b rescue nil if c\na\n") # OMG that's awesome add_tests("super_0", "Ruby" => "def x\n super()\nend", "ParseTree" => s(:defn, :x, s(:args), s(:super))) add_tests("super_1", "Ruby" => "def x\n super(4)\nend", "ParseTree" => s(:defn, :x, s(:args), s(:super, s(:lit, 4)))) add_tests("super_1_array", "Ruby" => "def x\n super([24, 42])\nend", "ParseTree" => s(:defn, :x, s(:args), s(:super, s(:array, s(:lit, 24), s(:lit, 42))))) add_tests("super_block_pass", "Ruby" => "super(a, &b)", "ParseTree" => s(:super, s(:call, nil, :a), s(:block_pass, s(:call, nil, :b)))) add_tests("super_block_splat", "Ruby" => "super(a, *b)", "ParseTree" => s(:super, s(:call, nil, :a), s(:splat, s(:call, nil, :b)))) add_tests("super_n", "Ruby" => "def x\n super(24, 42)\nend", "ParseTree" => s(:defn, :x, s(:args), s(:super, s(:lit, 24), s(:lit, 42)))) add_tests("svalue", "Ruby" => "a = *b", "ParseTree" => s(:lasgn, :a, s(:svalue, s(:splat, s(:call, nil, :b))))) add_tests("ternary_nil_no_space", "Ruby" => "1 ? nil: 1", "ParseTree" => s(:if, s(:lit, 1), s(:nil), s(:lit, 1)), "Ruby2Ruby" => "1 ? (nil) : (1)") add_tests("ternary_symbol_no_spaces", "Ruby" => "1?:x:1", "ParseTree" => s(:if, s(:lit, 1), s(:lit, :x), s(:lit, 1)), "Ruby2Ruby" => "1 ? (:x) : (1)") add_tests("to_ary", "Ruby" => "a, b = c", "ParseTree" => s(:masgn, s(:array, s(:lasgn, :a), s(:lasgn, :b)), s(:to_ary, s(:call, nil, :c)))) add_tests("true", "Ruby" => "true", "ParseTree" => s(:true)) add_tests("undef", "Ruby" => "undef :x", "ParseTree" => s(:undef, s(:lit, :x))) add_tests("undef_2", "Ruby" => "undef :x, :y", "ParseTree" => s(:block, s(:undef, s(:lit, :x)), s(:undef, s(:lit, :y))), "Ruby2Ruby" => "undef :x\nundef :y\n") add_tests("undef_3", "Ruby" => "undef :x, :y, :z", "ParseTree" => s(:block, s(:undef, s(:lit, :x)), s(:undef, s(:lit, :y)), s(:undef, s(:lit, :z))), "Ruby2Ruby" => "undef :x\nundef :y\nundef :z\n") add_tests("undef_block_1", "Ruby" => "f1\nundef :x\n", # TODO: don't like the extra return "ParseTree" => s(:block, s(:call, nil, :f1), s(:undef, s(:lit, :x)))) add_tests("undef_block_2", "Ruby" => "f1\nundef :x, :y", "ParseTree" => s(:block, s(:call, nil, :f1), s(:block, s(:undef, s(:lit, :x)), s(:undef, s(:lit, :y)))), "Ruby2Ruby" => "f1\n(undef :x\nundef :y)\n") add_tests("undef_block_3", "Ruby" => "f1\nundef :x, :y, :z", "ParseTree" => s(:block, s(:call, nil, :f1), s(:block, s(:undef, s(:lit, :x)), s(:undef, s(:lit, :y)), s(:undef, s(:lit, :z)))), "Ruby2Ruby" => "f1\n(undef :x\nundef :y\nundef :z)\n") add_tests("undef_block_3_post", "Ruby" => "undef :x, :y, :z\nf2", "ParseTree" => s(:block, s(:undef, s(:lit, :x)), s(:undef, s(:lit, :y)), s(:undef, s(:lit, :z)), s(:call, nil, :f2)), "Ruby2Ruby" => "undef :x\nundef :y\nundef :z\nf2\n") add_tests("undef_block_wtf", "Ruby" => "f1\nundef :x, :y, :z\nf2", "ParseTree" => s(:block, s(:call, nil, :f1), s(:block, s(:undef, s(:lit, :x)), s(:undef, s(:lit, :y)), s(:undef, s(:lit, :z))), s(:call, nil, :f2)), "Ruby2Ruby" => "f1\n(undef :x\nundef :y\nundef :z)\nf2\n") add_tests("unless_post", "Ruby" => "a unless b", "ParseTree" => s(:if, s(:call, nil, :b), nil, s(:call, nil, :a))) add_tests("unless_pre", "Ruby" => "unless b then a end", "ParseTree" => s(:if, s(:call, nil, :b), nil, s(:call, nil, :a)), "Ruby2Ruby" => "a unless b") add_tests("until_post", "Ruby" => "begin\n (1 + 1)\nend until false", "ParseTree" => s(:until, s(:false), s(:call, s(:lit, 1), :+, s(:lit, 1)), false)) add_tests("until_pre", "Ruby" => "until false do\n (1 + 1)\nend", "ParseTree" => s(:until, s(:false), s(:call, s(:lit, 1), :+, s(:lit, 1)), true)) add_tests("until_pre_mod", "Ruby" => "(1 + 1) until false", "ParseTree" => s(:until, s(:false), s(:call, s(:lit, 1), :+, s(:lit, 1)), true), "Ruby2Ruby" => "until false do\n (1 + 1)\nend") add_tests("valias", "Ruby" => "alias $y $x", "ParseTree" => s(:valias, :$y, :$x)) add_tests("vcall", "Ruby" => "method", "ParseTree" => s(:call, nil, :method)) add_tests("while_post", "Ruby" => "begin\n (1 + 1)\nend while false", "ParseTree" => s(:while, s(:false), s(:call, s(:lit, 1), :+, s(:lit, 1)), false)) add_tests("while_post2", "Ruby" => "begin\n (1 + 2)\n (3 + 4)\nend while false", "ParseTree" => s(:while, s(:false), s(:block, s(:call, s(:lit, 1), :+, s(:lit, 2)), s(:call, s(:lit, 3), :+, s(:lit, 4))), false)) add_tests("while_pre", "Ruby" => "while false do\n (1 + 1)\nend", "ParseTree" => s(:while, s(:false), s(:call, s(:lit, 1), :+, s(:lit, 1)), true)) add_tests("while_pre_mod", "Ruby" => "(1 + 1) while false", "ParseTree" => s(:while, s(:false), s(:call, s(:lit, 1), :+, s(:lit, 1)), true), "Ruby2Ruby" => "while false do\n (1 + 1)\nend") # FIX can be one liner add_tests("while_pre_nil", "Ruby" => "while false do\nend", "ParseTree" => s(:while, s(:false), nil, true)) add_tests("xstr", "Ruby" => "`touch 5`", "ParseTree" => s(:xstr, "touch 5")) add_tests("yield_0", "Ruby" => "yield", "ParseTree" => s(:yield)) add_tests("yield_1", "Ruby" => "yield(42)", "ParseTree" => s(:yield, s(:lit, 42))) add_tests("yield_array_0", "Ruby" => "yield([])", "ParseTree" => s(:yield, s(:array))) add_tests("yield_array_1", "Ruby" => "yield([42])", "ParseTree" => s(:yield, s(:array, s(:lit, 42)))) add_tests("yield_array_n", "Ruby" => "yield([42, 24])", "ParseTree" => s(:yield, s(:array, s(:lit, 42), s(:lit, 24)))) add_tests("yield_n", "Ruby" => "yield(42, 24)", "ParseTree" => s(:yield, s(:lit, 42), s(:lit, 24))) add_tests("zarray", "Ruby" => "a = []", "ParseTree" => s(:lasgn, :a, s(:array))) add_tests("zsuper", "Ruby" => "def x\n super\nend", "ParseTree" => s(:defn, :x, s(:args), s(:zsuper))) # TODO: discuss and decide which lit we like # it "converts a regexp to an sexp" do # "/blah/".to_sexp.should == s(:regex, "blah", 0) # "/blah/i".to_sexp.should == s(:regex, "blah", 1) # "/blah/u".to_sexp.should == s(:regex, "blah", 64) # end end # :startdoc: sexp_processor-4.17.0/lib/strict_sexp.rb0000444000004100000410000000755514443772242020376 0ustar www-datawww-data# :stopdoc: ## # I'm starting to warm up to this idea! # ENV["STRICT_SEXP"] turns on various levels of conformance checking # # 1 = sexp[0] => sexp_type # 1 = sexp.first => sexp_type # 1 = sexp[0] = x => sexp_type = x # 1 = sexp[1..-1] => sexp_body # 1 = sexp[1..-1] = x => sexp_body = x # 1 = sexp[-1] => last # 2 = sexp[1] => no # 2 = sexp[1] = x => no # 3 = sexp[n] => no # 3 = sexp[n] = x => no # 3 = sexp.node_name => no (ie, method_missing) # 4 = sexp.replace x => no # 4 = sexp.concat x => no # 4 = sexp.collect! => no # 4 = sexp.compact! => no # 4 = sexp.flatten! => no # 4 = sexp.map! => no # 4 = sexp.reject! => no # 4 = sexp.reverse! => no # 4 = sexp.rotate! => no # 4 = sexp.select! => no # 4 = sexp.shuffle! => no # 4 = sexp.slice! => no # 4 = sexp.sort! => no # 4 = sexp.sort_by! => no # 4 = sexp.uniq! => no # 4 = sexp.unshift => no # 4 = sexp.push => no # 4 = sexp.pop => no # 4 = sexp << => no class Sexp # alias :_concat :concat in sexp.rb so we have access to the original alias :safe_idx :[] alias :safe_asgn :[]= alias :sexp_type= :sexp_type= alias :sexp_body= :sexp_body= alias :shift :shift def self.nuke_method name, level return unless __strict >= level define_method name do |*args| raise "no mutation allowed on sexps: %s.%s %s" % [self, name, args] end end def self.__strict ENV["STRICT_SEXP"].to_i end def __strict self.class.__strict end undef_method :method_missing if __strict > 2 def method_missing msg, *args raise "don't call method_missing on Sexps: %p.(%s)" % [msg, args.inspect[1..-2]] end if __strict > 2 def [] i raise "no idx: #{inspect}[#{i}]" if __strict > 2 raise "no idx>1: #{inspect}[#{i}]" if Integer === i && i > 1 if __strict > 1 raise "use sexp_type" if i == 0 raise "use sexp_body" if i == (1..-1) raise "use last" if i == -1 self.safe_idx i end def []= i, v raise "use sexp_type=" if i == 0 raise "use sexp_body=" if i == (1..-1) raise "no asgn>1: #{inspect}[#{i}] = #{v.inspect}" if Integer === i && i > 1 if __strict > 1 raise "no asgn: #{inspect}[#{i}] = #{v.inspect}" if __strict > 2 self.safe_asgn i, v end def first(n = nil) raise "use sexp_type" unless n super end nuke_method :collect!, 4 nuke_method :compact!, 4 nuke_method :concat, 4 # HACK: using self.class.new.concat(...) for speed nuke_method :flatten!, 4 nuke_method :map!, 4 nuke_method :pop, 4 nuke_method :push, 4 nuke_method :reject!, 4 nuke_method :replace, 4 nuke_method :reverse!, 4 nuke_method :rotate!, 4 nuke_method :select!, 4 nuke_method :shuffle!, 4 nuke_method :slice!, 4 nuke_method :sort!, 4 nuke_method :sort_by!, 4 nuke_method :uniq!, 4 nuke_method :unshift, 4 nuke_method :<<, 5 nuke_method :shift, 5 def sexp_type safe_idx 0 end def sexp_body from = 1 self.new._concat(safe_idx(from..-1) || []) end def sexp_type= v self.safe_asgn 0, v end def sexp_body= v self.safe_asgn 1..-1, v end end unless Sexp.new.respond_to? :safe_asgn if ENV["STRICT_SEXP"] if ENV["SP_DEBUG"] && !ENV["STRICT_SEXP"] then class Sexp mutators = %i[ []= clear collect! compact! concat delete delete_at delete_if drop drop_while fill flatten! replace insert keep_if map! pop push reject! reverse! rotate! select! shift shuffle! slice! sort! sort_by! transpose uniq! unshift ] mutators.each do |method| define_method method do |*args| warn "Sexp modified by %p at %s" % [__method__, caller.first] if ENV["VERBOSE"] or (defined?(@hash) and @hash) super(*args) end end end end # :startdoc: sexp_processor-4.17.0/lib/sexp_processor.rb0000444000004100000410000004276614443772242021110 0ustar www-datawww-data$TESTING = false unless defined? $TESTING require "sexp" ## # SexpProcessor provides a uniform interface to process Sexps. # # In order to create your own SexpProcessor subclass you'll need # to call super in the initialize method, then set any of the # Sexp flags you want to be different from the defaults. # # SexpProcessor uses a Sexp's type to determine which process method # to call in the subclass. For Sexp s(:lit, 1) # SexpProcessor will call #process_lit, if it is defined. # # You can also specify a default method to call for any Sexp types # without a process_ method or use the default processor provided to # skip over them. # # Here is a simple example: # # class MyProcessor < SexpProcessor # def initialize # super # self.strict = false # end # # def process_lit(exp) # val = exp.shift # return val # end # end class SexpProcessor # duh VERSION = "4.17.0" ## # Automatically shifts off the Sexp type before handing the # Sexp to process_ attr_accessor :auto_shift_type ## # Return a stack of contexts. Most recent node is first. attr_reader :context ## # A Hash of Sexp types and Regexp. # # Print a debug message if the Sexp type matches the Hash key # and the Sexp's #inspect output matches the Regexp. attr_accessor :debug ## # A default method to call if a process_ method is not found # for the Sexp type. attr_accessor :default_method ## # Expected result class attr_accessor :expected ## # Raise an exception if the Sexp is not empty after processing attr_accessor :require_empty ## # Raise an exception if no process_ method is found for a Sexp. attr_accessor :strict ## # An array that specifies node types that are unsupported by this # processor. SexpProcessor will raise UnsupportedNodeError if you try # to process one of those node types. attr_accessor :unsupported ## # Emit a warning when the method in #default_method is called. attr_accessor :warn_on_default ## # A scoped environment to make you happy. attr_reader :env ## # Expand an array of directories into a flattened array of paths, eg: # # MyProcessor.run MyProcessor.expand_dirs_to_files ARGV def self.expand_dirs_to_files *dirs extensions = %w[rb rake] dirs.flatten.map { |p| if File.directory? p then Dir[File.join(p, "**", "*.{#{extensions.join ","}}")] else p end }.flatten.sort end ## # Cache processor methods per class. def self.processors @processors ||= {} end ## # Cache rewiter methods per class. def self.rewriters @rewriters ||= {} end ## # Creates a new SexpProcessor. Use super to invoke this # initializer from SexpProcessor subclasses, then use the # attributes above to customize the functionality of the # SexpProcessor def initialize @default_method = nil @warn_on_default = true @auto_shift_type = false @strict = false @unsupported = [:alloca, :cfunc, :cref, :ifunc, :last, :memo, :newline, :opt_n, :method] @unsupported_checked = false @debug = {} @expected = Sexp @require_empty = true @exceptions = {} # we do this on an instance basis so we can subclass it for # different processors. @processors = self.class.processors @rewriters = self.class.rewriters @context = [] if @processors.empty? public_methods.each do |name| case name when /^process_(.*)/ then @processors[$1.to_sym] = name.to_sym when /^rewrite_(.*)/ then @rewriters[$1.to_sym] = name.to_sym end end end end ## # Raise if +exp+ is not empty. def assert_empty meth, exp, exp_orig unless exp.empty? then msg = "exp not empty after #{self.class}.#{meth} on #{exp.inspect}" msg += " from #{exp_orig.inspect}" if $DEBUG raise NotEmptyError, msg end end ## # Rewrite +exp+ using rewrite_* method for +exp+'s sexp_type, if one # exists. def rewrite exp type = exp.sexp_type comments = exp.comments if @debug.key? type then str = exp.inspect puts "// DEBUG (original ): #{str}" if str =~ @debug[type] end in_context type do exp = exp.map { |sub| Array === sub ? rewrite(sub) : sub } end loop do meth = @rewriters[type] exp = self.send(meth, exp) if meth break unless Sexp === exp if @debug.key? type then str = exp.inspect puts "// DEBUG (rewritten): #{str}" if str =~ @debug[type] end old_type, type = type, exp.sexp_type break if old_type == type end exp.comments = comments exp end ## # Default Sexp processor. Invokes process_ methods matching # the Sexp type given. Performs additional checks as specified by # the initializer. def process exp return nil if exp.nil? unless Sexp === exp then raise SexpTypeError, "exp must be a Sexp, was #{exp.class}:#{exp.inspect}" end if self.context.empty? then p :rewriting unless debug.empty? exp = self.rewrite(exp) p :done_rewriting unless debug.empty? end unless @unsupported_checked then m = public_methods.grep(/^process_/) { |o| o.to_s.sub(/^process_/, "").to_sym } supported = m - (m - @unsupported) raise UnsupportedNodeError, "#{supported.inspect} shouldn't be in @unsupported" unless supported.empty? @unsupported_checked = true end result = self.expected.new type = exp.sexp_type raise "type should be a Symbol, not: #{exp.first.inspect}" unless Symbol === type in_context type do if @debug.key? type then str = exp.inspect puts "// DEBUG:(original ): #{str}" if str =~ @debug[type] end exp_orig = nil exp_orig = exp.deep_clone if $DEBUG or @debug.key? type or @exceptions.key?(type) raise UnsupportedNodeError, "'#{type}' is not a supported node type" if @unsupported.include? type # now do a pass with the real processor (or generic) meth = @processors[type] || @default_method if meth then if @warn_on_default and meth == @default_method then warn "WARNING: Using default method #{meth} for #{type}" end exp = exp.sexp_body if @auto_shift_type and meth != @default_method # HACK result = error_handler(type, exp_orig) { self.send meth, exp } if @debug.key? type then str = exp.inspect puts "// DEBUG (processed): #{str}" if str =~ @debug[type] end raise SexpTypeError, "Result of #{type} must be a #{@expected}, was #{result.class}:#{result.inspect}" unless @expected === result self.assert_empty(meth, exp, exp_orig) if @require_empty else unless @strict then until exp.empty? do sub_exp, *exp = exp # HACK sub_result = nil if Array === sub_exp then sub_result = error_handler(type, exp_orig) do process(sub_exp) end raise "Result is a bad type" unless Array === sub_exp raise "Result does not have a type in front: #{sub_exp.inspect}" unless Symbol === sub_exp.sexp_type unless sub_exp.empty? else sub_result = sub_exp end # result << sub_result result = result.class.new(*result, sub_result) # HACK end # NOTE: this is costly, but we are in the generic processor # so we shouldn't hit it too much with RubyToC stuff at least. result.c_type ||= exp.c_type if Sexp === exp and exp.respond_to?(:c_type) else msg = "Bug! Unknown node-type #{type.inspect} to #{self.class}" msg += " in #{exp_orig.inspect} from #{caller.inspect}" if $DEBUG raise UnknownNodeError, msg end end end result end ## # Raises unless the Sexp type for +list+ matches +typ+ def assert_type list, typ raise SexpTypeError, "Expected type #{typ.inspect} in #{list.inspect}" if not Array === list or list.sexp_type != typ end def error_handler type, exp = nil # :nodoc: yield rescue StandardError => err return @exceptions[type].call self, exp, err if @exceptions.key? type warn "#{err.class} Exception thrown while processing #{type} for sexp #{exp.inspect} #{caller.inspect}" if $DEBUG raise end ## # Registers an error handler for +node+ def on_error_in node_type, &block @exceptions[node_type] = block end ## # A fairly generic processor for a dummy node. Dummy nodes are used # when your processor is doing a complicated rewrite that replaces # the current sexp with multiple sexps. # # Bogus Example: # # def process_something(exp) # return s(:dummy, process(exp), s(:extra, 42)) # end def process_dummy exp result = @expected.new(:dummy) rescue @expected.new result << self.process(exp.shift) until exp.empty? result end ## # Add a scope level to the current env. Eg: # # def process_defn exp # name = exp.shift # args = process(exp.shift) # scope do # body = process(exp.shift) # # ... # end # end # # env[:x] = 42 # scope do # env[:x] # => 42 # env[:y] = 24 # end # env[:y] # => nil def scope &block env.scope(&block) end ## # Track a stack of contexts that the processor is in, pushing on # +type+ yielding, and then removing the context from the stack. def in_context type self.context.unshift type yield ensure self.context.shift end ## # I really hate this here, but I hate subdirs in my lib dir more... # I guess it is kinda like shaving... I'll split this out when it # itches too much... class Environment def initialize #:nodoc: @env = [] @env.unshift({}) end ## # Flatten out all scopes and return all key/value pairs. def all @env.reverse.inject { |env, scope| env.merge scope } end ## # Return the current number of scopes. def depth @env.length end # TODO: depth_of ## # Get +name+ from env at whatever scope it is defined in, or return nil. def [] name hash = @env.find { |closure| closure.key? name } hash[name] if hash end ## # If +name+ exists in the env, set it to +val+ in whatever scope # it is in. If it doesn't exist, set +name+ to +val+ in the # current scope. def []= name, val hash = @env.find { |closure| closure.key? name } || current hash[name] = val end ## # Get the current/top environment. def current @env.first end ## # Create a new scope and yield to the block passed. def scope @env.unshift({}) begin yield ensure @env.shift raise "You went too far unextending env" if @env.empty? end end end end ## # A simple subclass of SexpProcessor that defines a pattern I commonly # use: non-mutative and strict process that return assorted values; # AKA, an interpreter. class SexpInterpreter < SexpProcessor def initialize #:nodoc: super self.expected = Object self.require_empty = false self.strict = true end end ## # A simple subclass of SexpProcessor that tracks method and class # stacks for you. Use #method_name, #klass_name, or #signature to # refer to where you're at in processing. If you have to subclass # process_(class|module|defn|defs) you _must_ call super. class MethodBasedSexpProcessor < SexpProcessor @@no_class = :main @@no_method = :none ## # A stack of the classes/modules that are being processed attr_reader :class_stack ## # A stack of the methods that are being processed. You'd think it'd # only ever be 1 deep, but you'd be wrong. People do terrible things # in/to ruby. attr_reader :method_stack ## # A stack of the singleton classes that are being processed. attr_reader :sclass ## # A lookup table of all the method locations that have been # processed so far. attr_reader :method_locations def initialize #:nodoc: super @sclass = [] @class_stack = [] @method_stack = [] @method_locations = {} self.require_empty = false end ## # Adds name to the class stack, for the duration of the block def in_klass name if Sexp === name then name = case name.sexp_type when :colon2 then name = name.flatten name.delete :const name.delete :colon2 name.join("::") when :colon3 then name.last.to_s else raise "unknown type #{name.inspect}" end end @class_stack.unshift name with_new_method_stack do yield end ensure @class_stack.shift end ## # Adds name to the method stack, for the duration of the block def in_method name, file, line, line_max = nil method_name = Regexp === name ? name.inspect : name.to_s @method_stack.unshift method_name line_max = "-#{line_max}" if line_max @method_locations[signature] = "#{file}:#{line}#{line_max}" yield ensure @method_stack.shift end ## # Tracks whether we're in a singleton class or not. Doesn't track # actual receiver. def in_sklass @sclass.push true with_new_method_stack do yield end ensure @sclass.pop end ## # Returns the first class in the list, or @@no_class if there are # none. def klass_name name = @class_stack.first raise "you shouldn't see me" if Sexp === name if @class_stack.any? @class_stack.reverse.join("::").sub(/\([^\)]+\)$/, "") else @@no_class end end ## # Returns the first method in the list, or "#none" if there are # none. def method_name m = @method_stack.first || @@no_method m = "##{m}" unless m =~ /::/ m end ## # Process a class node until empty. Tracks all nesting. If you have # to subclass and override this method, you can call super with a # block. def process_class exp exp.shift unless auto_shift_type # node type in_klass exp.shift do if block_given? then yield else process_until_empty exp end end s() end ## # Process a method node until empty. Tracks your location. If you # have to subclass and override this method, you can clall super # with a block. def process_defn exp exp.shift unless auto_shift_type # node type name = @sclass.empty? ? exp.shift : "::#{exp.shift}" in_method name, exp.file, exp.line, exp.line_max do if block_given? then yield else process_until_empty exp end end s() end ## # Process a singleton method node until empty. Tracks your location. # If you have to subclass and override this method, you can clall # super with a block. def process_defs exp exp.shift unless auto_shift_type # node type process exp.shift # recv in_method "::#{exp.shift}", exp.file, exp.line, exp.line_max do if block_given? then yield else process_until_empty exp end end s() end ## # Process a module node until empty. Tracks all nesting. If you have # to subclass and override this method, you can clall super with a # block. def process_module exp exp.shift unless auto_shift_type # node type in_klass exp.shift do if block_given? then yield else process_until_empty exp end end s() end ## # Process a singleton class node until empty. Tracks all nesting. If # you have to subclass and override this method, you can clall super # with a block. def process_sclass exp exp.shift unless auto_shift_type # node type in_sklass do if block_given? then yield else process_until_empty exp end end s() end ## # Process each element of #exp in turn. def process_until_empty exp until exp.empty? sexp = exp.shift process sexp if Sexp === sexp end end ## # Returns the method signature for the current method. def signature "#{klass_name}#{method_name}" end ## # Reset the method stack for the duration of the block. Used for # class scoping. def with_new_method_stack old_method_stack, @method_stack = @method_stack, [] yield ensure @method_stack = old_method_stack end end class Object # :nodoc: ## # deep_clone is the usual Marshalling hack to make a deep copy. # It is rather slow, so use it sparingly. Helps with debugging # SexpProcessors since you usually shift off sexps. def deep_clone Marshal.load(Marshal.dump(self)) end end ## # SexpProcessor base exception class. class SexpProcessorError < StandardError; end ## # Raised by SexpProcessor if it sees a node type listed in its # unsupported list. class UnsupportedNodeError < SexpProcessorError; end ## # Raised by SexpProcessor if it is in strict mode and sees a node for # which there is no processor available. class UnknownNodeError < SexpProcessorError; end ## # Raised by SexpProcessor if a processor did not process every node in # a sexp and @require_empty is true. class NotEmptyError < SexpProcessorError; end ## # Raised if assert_type encounters an unexpected sexp type. class SexpTypeError < SexpProcessorError; end sexp_processor-4.17.0/lib/sexp_matcher.rb0000444000004100000410000005642614443772242020512 0ustar www-datawww-dataclass Sexp #:nodoc: ## # Verifies that +pattern+ is a Matcher and then dispatches to its # #=~ method. # # See Matcher.=~ def =~ pattern raise ArgumentError, "Not a pattern: %p" % [pattern] unless Matcher === pattern pattern =~ self end ## # Verifies that +pattern+ is a Matcher and then dispatches to its # #satisfy? method. # # TODO: rename match? def satisfy? pattern raise ArgumentError, "Not a pattern: %p" % [pattern] unless Matcher === pattern pattern.satisfy? self end ## # Verifies that +pattern+ is a Matcher and then dispatches to its #/ # method. # # TODO: rename grep? match_all ? find_all ? def / pattern raise ArgumentError, "Not a pattern: %p" % [pattern] unless Matcher === pattern pattern / self end ## # Recursively searches for the +pattern+ yielding the matches. def search_each pattern, &block # TODO: rename to grep? raise ArgumentError, "Needs a pattern" unless pattern.kind_of? Matcher return enum_for(:search_each, pattern) unless block_given? if pattern.satisfy? self then yield self end self.each_sexp do |subset| subset.search_each pattern, &block end end ## # Recursively searches for the +pattern+ yielding each match, and # replacing it with the result of the block. # def replace_sexp pattern, &block # TODO: rename to gsub? raise ArgumentError, "Needs a pattern" unless pattern.kind_of? Matcher return yield self if pattern.satisfy? self # TODO: Needs #new_from(*new_body) to copy file/line/comment self.class.new(*self.map { |subset| case subset when Sexp then subset.replace_sexp pattern, &block else subset end }) end ## # Matches an S-Expression. # # See Matcher for examples. def self.q *args Matcher.new(*args) end def self.s *args where = caller.first.split(/:/, 3).first(2).join ":" warn "DEPRECATED: use Sexp.q(...) instead. From %s" % [where] q(*args) end ## # Matches any single item. # # See Wild for examples. def self._ Wild.new end # TODO: reorder factory methods and classes to match ## # Matches all remaining input. # # See Remaining for examples. def self.___ Remaining.new end ## # Matches an expression or any expression that includes the child. # # See Include for examples. def self.include child # TODO: rename, name is generic ruby Include.new(child) end ## # Matches any atom. # # See Atom for examples. def self.atom Atom.new end ## # Matches when any of the sub-expressions match. # # This is also available via Matcher#|. # # See Any for examples. def self.any *args Any.new(*args) end ## # Matches only when all sub-expressions match. # # This is also available via Matcher#&. # # See All for examples. def self.all *args All.new(*args) end ## # Matches when sub-expression does not match. # # This is also available via Matcher#-@. # # See Not for examples. def self.not? arg Not.new arg end class << self alias - not? end # TODO: add Sibling factory method? ## # Matches anything that has a child matching the sub-expression. # # See Child for examples. def self.child child Child.new child end ## # Matches anything having the same sexp_type, which is the first # value in a Sexp. # # See Type for examples. def self.t name Type.new name end ## # Matches any atom who's string representation matches the patterns # passed in. # # See Pattern for examples. def self.m *values res = values.map { |value| case value when Regexp then value else re = Regexp.escape value.to_s Regexp.new "\\A%s\\Z" % re end } Pattern.new Regexp.union(*res) end ## # Matches an atom of the specified +klass+ (or module). # # See Pattern for examples. def self.k klass Klass.new klass end ## # Defines a family of objects that can be used to match sexps to # certain types of patterns, much like regexps can be used on # strings. Generally you won't use this class directly. # # You would normally create a matcher using the top-level #s method, # but with a block, calling into the Sexp factory methods. For example: # # s{ s(:class, m(/^Test/), _, ___) } # # This creates a matcher for classes whose names start with "Test". # It uses Sexp.m to create a Sexp::Matcher::Pattern matcher, Sexp._ # to create a Sexp::Matcher::Wild matcher, and Sexp.___ to create a # Sexp::Matcher::Remaining matcher. It works like this: # # s{ # start to create a pattern # s( # create a sexp matcher # :class. # for class nodes # m(/^Test/), # matching name slots that start with "Test" # _, # any superclass value # ___ # and whatever is in the class # ) # } # # Then you can use that with #=~, #/, Sexp#replace_sexp, and others. # # For more examples, see the various Sexp class methods, the examples, # and the tests supplied with Sexp. # # * For pattern creation, see factory methods: Sexp::_, Sexp::___, etc. # * For matching returning truthy/falsey results, see Sexp#=~. # * For case expressions, see Matcher#===. # * For getting all subtree matches, see Sexp#/. # # If rdoc didn't suck, these would all be links. class Matcher < Sexp ## # Should #=~ match sub-trees? def self.match_subs? @@match_subs end ## # Setter for +match_subs?+. def self.match_subs= o @@match_subs = o end self.match_subs = true ## # Does this matcher actually match +o+? Returns falsey if +o+ is # not a Sexp or if any sub-tree of +o+ is not satisfied by or # equal to its corresponding sub-matcher. # #-- # TODO: push this up to Sexp and make this the workhorse # TODO: do the same with ===/satisfy? def satisfy? o return unless o.kind_of?(Sexp) && (length == o.length || Matcher === last && last.greedy?) each_with_index.all? { |child, i| sexp = o.at i if Sexp === child then # TODO: when will this NOT be a matcher? sexp = o.sexp_body i if child.respond_to?(:greedy?) && child.greedy? child.satisfy? sexp else child == sexp end } end ## # Tree equivalent to String#=~, returns true if +self+ matches # +sexp+ as a whole or in a sub-tree (if +match_subs?+). # # TODO: maybe this should NOT be aliased to === ? # # TODO: example def =~ sexp raise ArgumentError, "Can't both be matchers: %p" % [sexp] if Matcher === sexp self.satisfy?(sexp) || (self.class.match_subs? && sexp.each_sexp.any? { |sub| self =~ sub }) end alias === =~ # TODO?: alias === satisfy? ## # Searches through +sexp+ for all sub-trees that match this # matcher and returns a MatchCollection for each match. # # TODO: redirect? # Example: # Q{ s(:b) } / s(:a, s(:b)) => [s(:b)] def / sexp raise ArgumentError, "can't both be matchers" if Matcher === sexp # TODO: move search_each into matcher? MatchCollection.new sexp.search_each(self).to_a end ## # Combines the Matcher with another Matcher, the resulting one will # be satisfied if either Matcher would be satisfied. # # TODO: redirect # Example: # s(:a) | s(:b) def | other Any.new self, other end ## # Combines the Matcher with another Matcher, the resulting one will # be satisfied only if both Matchers would be satisfied. # # TODO: redirect # Example: # t(:a) & include(:b) def & other All.new self, other end ## # Returns a Matcher that matches whenever this Matcher would not have matched # # Example: # -s(:a) def -@ Not.new self end ## # Returns a Matcher that matches if this has a sibling +o+ # # Example: # s(:a) >> s(:b) def >> other Sibling.new self, other end ## # Is this matcher greedy? Defaults to false. def greedy? false end def inspect # :nodoc: s = super s[0] = "q" s end def pretty_print q # :nodoc: q.group 1, "q(", ")" do q.seplist self do |v| q.pp v end end end ## # Parse a lispy string representation of a matcher into a Matcher. # See +Parser+. def self.parse s Parser.new(s).parse end ## # Converts from a lispy string to Sexp matchers in a safe manner. # # "(a 42 _ (c) [t x] ___)" => s{ s(:a, 42, _, s(:c), t(:x), ___) } class Parser ## # The stream of tokens to parse. See #lex. attr_accessor :tokens ## # Create a new Parser instance on +s+ def initialize s self.tokens = lex s end ## # Converts +s+ into a stream of tokens and adds them to +tokens+. def lex s s.scan %r%[()\[\]]|\"[^"]*\"|/[^/]*/|:?[\w?!=~-]+% end ## # Returns the next token and removes it from the stream or raises if empty. def next_token raise SyntaxError, "unbalanced input" if tokens.empty? tokens.shift end ## # Returns the next token without removing it from the stream. def peek_token tokens.first end ## # Parses tokens and returns a +Matcher+ instance. def parse result = parse_sexp until tokens.empty? result end ## # Parses a string into a sexp matcher: # # SEXP : "(" SEXP:args* ")" => Sexp.q(*args) # | "[" CMD:cmd sexp:args* "]" => Sexp.cmd(*args) # | "nil" => nil # | /\d+/:n => n.to_i # | "___" => Sexp.___ # | "_" => Sexp._ # | /^\/(.*)\/$/:re => Regexp.new re[0] # | /^"(.*)"$/:s => String.new s[0] # | UP_NAME:name => Object.const_get name # | NAME:name => name.to_sym # UP_NAME: /[A-Z]\w*/ # NAME : /:?[\w?!=~-]+/ # CMD : t | k | m | atom | not? | - | any | child | include def parse_sexp token = next_token case token when "(" then parse_list when "[" then parse_cmd when "nil" then nil when /^\d+$/ then token.to_i when "___" then Sexp.___ when "_" then Sexp._ when %r%^/(.*)/$% then re = $1 raise SyntaxError, "Not allowed: /%p/" % [re] unless re =~ /\A([\w()|.*+^$]+)\z/ Regexp.new re when /^"(.*)"$/ then $1 when /^([A-Z]\w*)$/ then Object.const_get $1 when /^:?([\w?!=~-]+)$/ then $1.to_sym else raise SyntaxError, "unhandled token: %p" % [token] end end ## # Parses a balanced list of expressions and returns the # equivalent matcher. def parse_list result = [] result << parse_sexp while peek_token && peek_token != ")" next_token # pop off ")" Sexp.q(*result) end ## # A collection of allowed commands to convert into matchers. ALLOWED = [:t, :m, :k, :atom, :not?, :-, :any, :child, :include].freeze ## # Parses a balanced command. A command is denoted by square # brackets and must conform to a whitelisted set of allowed # commands (see +ALLOWED+). def parse_cmd args = [] args << parse_sexp while peek_token && peek_token != "]" next_token # pop off "]" cmd = args.shift args = Sexp.q(*args) raise SyntaxError, "bad cmd: %p" % [cmd] unless ALLOWED.include? cmd result = Sexp.send cmd, *args result end end # class Parser end # class Matcher ## # Matches any single item. # # examples: # # s(:a) / s{ _ } #=> [s(:a)] # s(:a, s(s(:b))) / s{ s(_) } #=> [s(s(:b))] class Wild < Matcher ## # Matches any single element. def satisfy? o true end def inspect # :nodoc: "_" end def pretty_print q # :nodoc: q.text "_" end end ## # Matches all remaining input. If remaining comes before any other # matchers, they will be ignored. # # examples: # # s(:a) / s{ s(:a, ___ ) } #=> [s(:a)] # s(:a, :b, :c) / s{ s(:a, ___ ) } #=> [s(:a, :b, :c)] class Remaining < Matcher ## # Always satisfied once this is reached. Think of it as a var arg. def satisfy? o true end def greedy? true end def inspect # :nodoc: "___" end def pretty_print q # :nodoc: q.text "___" end end ## # Matches when any of the sub-expressions match. # # This is also available via Matcher#|. # # examples: # # s(:a) / s{ any(s(:a), s(:b)) } #=> [s(:a)] # s(:a) / s{ s(:a) | s(:b) } #=> [s(:a)] # same thing via | # s(:a) / s{ any(s(:b), s(:c)) } #=> [] class Any < Matcher ## # The collection of sub-matchers to match against. attr_reader :options ## # Create an Any matcher which will match any of the +options+. def initialize *options @options = options end ## # Satisfied when any sub expressions match +o+ def satisfy? o options.any? { |exp| Sexp === exp && exp.satisfy?(o) || exp == o } end def == o # :nodoc: super && self.options == o.options end def inspect # :nodoc: options.map(&:inspect).join(" | ") end def pretty_print q # :nodoc: q.group 1, "any(", ")" do q.seplist options do |v| q.pp v end end end end ## # Matches only when all sub-expressions match. # # This is also available via Matcher#&. # # examples: # # s(:a) / s{ all(s(:a), s(:b)) } #=> [] # s(:a, :b) / s{ t(:a) & include(:b)) } #=> [s(:a, :b)] class All < Matcher ## # The collection of sub-matchers to match against. attr_reader :options ## # Create an All matcher which will match all of the +options+. def initialize *options @options = options end ## # Satisfied when all sub expressions match +o+ def satisfy? o options.all? { |exp| exp.kind_of?(Sexp) ? exp.satisfy?(o) : exp == o } end def == o # :nodoc: super && self.options == o.options end def inspect # :nodoc: options.map(&:inspect).join(" & ") end def pretty_print q # :nodoc: q.group 1, "all(", ")" do q.seplist options do |v| q.pp v end end end end ## # Matches when sub-expression does not match. # # This is also available via Matcher#-@. # # examples: # # s(:a) / s{ not?(s(:b)) } #=> [s(:a)] # s(:a) / s{ -s(:b) } #=> [s(:a)] # s(:a) / s{ s(not? :a) } #=> [] class Not < Matcher ## # The value to negate in the match. attr_reader :value ## # Creates a Matcher which will match any Sexp that does not match the +value+ def initialize value @value = value end def == o # :nodoc: super && self.value == o.value end ## # Satisfied if a +o+ does not match the +value+ def satisfy? o !(value.kind_of?(Sexp) ? value.satisfy?(o) : value == o) end def inspect # :nodoc: "not?(%p)" % [value] end def pretty_print q # :nodoc: q.group 1, "not?(", ")" do q.pp value end end end ## # Matches anything that has a child matching the sub-expression # # example: # # s(s(s(s(s(:a))))) / s{ child(s(:a)) } #=> [s(s(s(s(s(:a))))), # s(s(s(s(:a)))), # s(s(s(:a))), # s(s(:a)), # s(:a)] class Child < Matcher ## # The child to match. attr_reader :child ## # Create a Child matcher which will match anything having a # descendant matching +child+. def initialize child @child = child end ## # Satisfied if matches +child+ or +o+ has a descendant matching # +child+. def satisfy? o child.satisfy?(o) || (o.kind_of?(Sexp) && o.search_each(child).any?) end def == o # :nodoc: super && self.child == o.child end def inspect # :nodoc: "child(%p)" % [child] end def pretty_print q # :nodoc: q.group 1, "child(", ")" do q.pp child end end end ## # Matches any atom (non-Sexp). # # examples: # # s(:a) / s{ s(atom) } #=> [s(:a)] # s(:a, s(:b)) / s{ s(atom) } #=> [s(:b)] class Atom < Matcher ## # Satisfied when +o+ is an atom. def satisfy? o !(o.kind_of? Sexp) end def inspect #:nodoc: "atom" end def pretty_print q # :nodoc: q.text "atom" end end ## # Matches any atom who's string representation matches the patterns # passed in. # # examples: # # s(:a) / s{ m('a') } #=> [s(:a)] # s(:a) / s{ m(/\w/,/\d/) } #=> [s(:a)] # s(:tests, s(s(:test_a), s(:test_b))) / s{ m(/test_\w/) } #=> [s(:test_a), # # TODO: maybe don't require non-sexps? This does respond to =~ now. class Pattern < Matcher ## # The regexp to match for the pattern. attr_reader :pattern def == o # :nodoc: super && self.pattern == o.pattern end ## # Create a Patten matcher which will match any atom that either # matches the input +pattern+. def initialize pattern @pattern = pattern end ## # Satisfied if +o+ is an atom, and +o+ matches +pattern+ def satisfy? o !o.kind_of?(Sexp) && o.to_s =~ pattern # TODO: question to_s end def inspect # :nodoc: "m(%p)" % pattern end def pretty_print q # :nodoc: q.group 1, "m(", ")" do q.pp pattern end end def eql? o super and self.pattern.eql? o.pattern end def hash [super, pattern].hash end end ## # Matches any atom that is an instance of the specified class or module. # # examples: # # s(:lit, 6.28) / s{ q(:lit, k(Float)) } #=> [s(:lit, 6.28)] class Klass < Pattern def satisfy? o o.kind_of? self.pattern end def inspect # :nodoc: "k(%p)" % pattern end def pretty_print q # :nodoc: q.group 1, "k(", ")" do q.pp pattern end end end ## # Matches anything having the same sexp_type, which is the first # value in a Sexp. # # examples: # # s(:a, :b) / s{ t(:a) } #=> [s(:a, :b)] # s(:a, :b) / s{ t(:b) } #=> [] # s(:a, s(:b, :c)) / s{ t(:b) } #=> [s(:b, :c)] class Type < Matcher attr_reader :sexp_type ## # Creates a Matcher which will match any Sexp who's type is +type+, where a type is # the first element in the Sexp. def initialize type @sexp_type = type end def == o # :nodoc: super && self.sexp_type == o.sexp_type end ## # Satisfied if the sexp_type of +o+ is +type+. def satisfy? o o.kind_of?(Sexp) && o.sexp_type == sexp_type end def inspect # :nodoc: "t(%p)" % sexp_type end def pretty_print q # :nodoc: q.group 1, "t(", ")" do q.pp sexp_type end end end ## # Matches an expression or any expression that includes the child. # # examples: # # s(:a, :b) / s{ include(:b) } #=> [s(:a, :b)] # s(s(s(:a))) / s{ include(:a) } #=> [s(:a)] class Include < Matcher ## # The value that should be included in the match. attr_reader :value ## # Creates a Matcher which will match any Sexp that contains the # +value+. def initialize value @value = value end ## # Satisfied if a +o+ is a Sexp and one of +o+'s elements matches # value def satisfy? o Sexp === o && o.any? { |c| # TODO: switch to respond_to?? Sexp === value ? value.satisfy?(c) : value == c } end def == o # :nodoc: super && self.value == o.value end def inspect # :nodoc: "include(%p)" % [value] end def pretty_print q # :nodoc: q.group 1, "include(", ")" do q.pp value end end end ## # See Matcher for sibling relations: <,<<,>>,> class Sibling < Matcher ## # The LHS of the matcher. attr_reader :subject ## # The RHS of the matcher. attr_reader :sibling ## # An optional distance requirement for the matcher. attr_reader :distance ## # Creates a Matcher which will match any pair of Sexps that are siblings. # Defaults to matching the immediate following sibling. def initialize subject, sibling, distance = nil @subject = subject @sibling = sibling @distance = distance end ## # Satisfied if o contains +subject+ followed by +sibling+ def satisfy? o # Future optimizations: # * Shortcut matching sibling subject_matches = index_matches(subject, o) return nil if subject_matches.empty? sibling_matches = index_matches(sibling, o) return nil if sibling_matches.empty? subject_matches.any? { |i1, _data_1| sibling_matches.any? { |i2, _data_2| distance ? (i2-i1 == distance) : i2 > i1 } } end def == o # :nodoc: super && self.subject == o.subject && self.sibling == o.sibling && self.distance == o.distance end def inspect # :nodoc: "%p >> %p" % [subject, sibling] end def pretty_print q # :nodoc: if distance then q.group 1, "sibling(", ")" do q.seplist [subject, sibling, distance] do |v| q.pp v end end else q.group 1 do q.pp subject q.text " >> " q.pp sibling end end end private def index_matches pattern, o indexes = [] return indexes unless o.kind_of? Sexp o.each_with_index do |e, i| data = {} if pattern.kind_of?(Sexp) ? pattern.satisfy?(e) : pattern == o[i] indexes << [i, data] end end indexes end end # class Sibling ## # Wraps the results of a Sexp query. MatchCollection defines # MatchCollection#/ so that you can chain queries. # # For instance: # res = s(:a, s(:b)) / s{ s(:a,_) } / s{ s(:b) } class MatchCollection < Array ## # See Traverse#search def / pattern inject(self.class.new) { |result, match| result.concat match / pattern } end def inspect # :nodoc: "MatchCollection.new(%s)" % self.to_a.inspect[1..-2] end alias :to_s :inspect # :nodoc: def pretty_print q # :nodoc: q.group 1, "MatchCollection.new(", ")" do q.seplist(self) {|v| q.pp v } end end end # class MatchCollection end # class Sexp sexp_processor-4.17.0/lib/unique.rb0000444000004100000410000000044114443772242017320 0ustar www-datawww-data## # Unique creates unique variable names. class Unique ## # Reset current count back to zero. Mainly used for testing. def self.reset @@curr = 0 end ## # Get the next unique variable name. def self.next @@curr += 1 "temp_#{@@curr}".intern end reset end sexp_processor-4.17.0/lib/composite_sexp_processor.rb0000444000004100000410000000217014443772242023153 0ustar www-datawww-datarequire "sexp_processor" ## # Implements the Composite pattern on SexpProcessor. Need we say more? # # Yeah... probably. Implements a SexpProcessor of SexpProcessors so # you can easily chain multiple to each other. At some stage we plan # on having all of them run +process+ and but only ever output # something when +generate+ is called, allowing for deferred final # processing. class CompositeSexpProcessor < SexpProcessor ## # The list o' processors to run. attr_reader :processors def initialize # :nodoc: super @processors = [] end ## # Add a +processor+ to the list of processors to run. def << processor raise ArgumentError, "Can only add sexp processors" unless SexpProcessor === processor || processor.respond_to?(:process) @processors << processor end ## # Run +exp+ through all of the processors, returning the final # result. def process exp @processors.each do |processor| exp = processor.process(exp) end exp end def on_error_in node_type, &block @processors.each do |processor| processor.on_error_in(node_type, &block) end end end sexp_processor-4.17.0/README.rdoc0000444000004100000410000000555014443772242016533 0ustar www-datawww-data= SexpProcessor home :: https://github.com/seattlerb/sexp_processor rdoc :: http://docs.seattlerb.org/sexp_processor == DESCRIPTION: sexp_processor branches from ParseTree bringing all the generic sexp processing tools with it. Sexp, SexpProcessor, Environment, etc... all for your language processing pleasure. == FEATURES/PROBLEMS: * Includes SexpProcessor and CompositeSexpProcessor. * Allows you to write very clean filters. * Includes MethodBasedSexpProcessor * Makes writing language processors even easier! * Sexp provides a simple and clean interface to creating and manipulating ASTs. * Includes new pattern matching system. == SYNOPSIS: You can use SexpProcessor to do all kinds of language processing. Here is a simple example of a simple documentation printer: class ArrrDoc < MethodBasedSexpProcessor def process_class exp super do puts "#{self.klass_name}: #{exp.comments}" end end def process_defn exp super do args, *_body = exp puts "#{self.method_name}(#{process_args args}): #{exp.comments}" end end end Sexp provides a lot of power with the new pattern matching system. Here is an example that parses all the test files using RubyParser and then quickly finds all the test methods and prints their names: >> require "ruby_parser"; >> rp = RubyParser.new; >> matcher = Sexp::Matcher.parse "(defn [m /^test_/] ___)" => q(:defn, m(/^test_/), ___) >> paths = Dir["test/**/*.rb"]; >> sexps = s(:block, *paths.map { |path| rp.process File.read(path), path }); >> (sexps / matcher).size => 189 ?> (sexps / matcher).map { |(_, name, *_rest)| name }.sort => [:test_all, :test_amp, :test_and_satisfy_eh, :test_any_search, ...] == REQUIREMENTS: * rubygems == INSTALL: * sudo gem install sexp_processor == 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. sexp_processor-4.17.0/Manifest.txt0000444000004100000410000000044614443772242017233 0ustar www-datawww-dataHistory.rdoc Manifest.txt README.rdoc Rakefile lib/composite_sexp_processor.rb lib/pt_testcase.rb lib/sexp.rb lib/sexp_matcher.rb lib/sexp_processor.rb lib/strict_sexp.rb lib/unique.rb test/test_composite_sexp_processor.rb test/test_environment.rb test/test_sexp.rb test/test_sexp_processor.rb