require 'test/unit' require 'pattern_match' class PatternMatchTest < Test::Unit::TestCase def test_single_literal_arguments Foo.class_eval do def f(n) match(n) do with(0) {0} end end end foo = Foo.new assert_equal 0, foo.f(0) end def test_multiple_literal_arguments Foo.class_eval do def f(*args) match args do with([0]) {0} with([1]) {1} otherwise {2} end end end foo = Foo.new assert_equal 0, foo.f(0) assert_equal 1, foo.f(1) end def test_class_arguments Foo.class_eval do def f(*args) match args do with([String]) {0} with([Fixnum]) {1} otherwise {2} end end end foo = Foo.new assert_equal 0, foo.f("") assert_equal 1, foo.f(1) end def test_array_arguments Foo.class_eval do def f(*args) match args do with([0,1]) {0} with([0,String]) {1} otherwise {2} end end end foo = Foo.new assert_equal 0, foo.f(0,1) assert_equal 1, foo.f(0,"") assert_equal 2, foo.f(0,2) assert_equal 2, foo.f(1,"") end def test_hash_arg Foo.class_eval do def f(hash) match hash do with(:a => 1) {1} otherwise {-1} end end end foo = Foo.new assert_equal 1, foo.f(:a => 1) assert_equal -1, foo.f(:a => 2) assert_equal -1, foo.f(:b => 2) end def test_hash_containing_classes Foo.class_eval do def f(hash) match hash do with(lit(:a) => Fixnum) {1} with(Symbol => "a") {2} otherwise {-1} end end end foo = Foo.new assert_equal 1, foo.f(:a => 1) assert_equal 2, foo.f(:b => "a") assert_equal -1, foo.f(:c => 2) end def test_literal_args Foo.class_eval do def f(*args) match(args) do with([lit(:x)]) {0} with([lit(String)]) {1} otherwise {-1} end end end foo = Foo.new assert_equal 0, foo.f(:x) assert_equal -1, foo.f(1) assert_equal 1, foo.f(String) assert_equal -1, foo.f("a") end def test_named_arguments Foo.class_eval do def f(*args) match args do with([:x]) {x} with([:x,:y]) {x+y} otherwise {2} end end end foo = Foo.new assert_equal 1, foo.f(1) assert_equal 3, foo.f(1,2) assert_equal 2, foo.f(1,2,3) end def test_empty_name_matches_anything Foo.class_eval do def f(*args) match args do with([1,2]) {3} with([:x,:_]) {x} otherwise {-1} end end end foo = Foo.new assert_equal 3, foo.f(1,2) assert_equal 1, foo.f(1,3) assert_equal -1, foo.f(1) end def test_empty_name_isnt_usable_in_block Foo.class_eval do def f(*args) match args do with([:first,:_]) {first+_} end end end foo = Foo.new assert_raises NameError do foo.f(1,2) end end def test_call_other_method_in_class Foo.class_eval do def double(x) x * 2 end def bar(n) match n do with(1) { double(n) } with(2) { double(n)+1 } otherwise { double(0) } end end end foo = Foo.new assert_equal 2, foo.bar(1) assert_equal 5, foo.bar(2) assert_equal 0, foo.bar(3) end def test_recursive_call Foo.class_eval do def fib(n) match n do with(0) {0} with(1) {1} otherwise {fib(n-1) + fib(n-2)} end end end foo = Foo.new assert_equal 0, foo.fib(0) assert_equal 1, foo.fib(1) assert_equal 1, foo.fib(2) assert_equal 2, foo.fib(3) assert_equal 3, foo.fib(4) end def test_error_if_no_match Foo.class_eval do def f(n) match(n) do with(1) {1} end end end foo = Foo.new assert_equal 1, foo.f(1) assert_raises Matchable::NoMatchFoundError do foo.f(2) end end def test_array_destructuring_bind_first_and_rest Foo.class_eval do def sum(arr) match(arr) do with(:x %:xs) { x+sum(xs) } otherwise {0} end end end foo = Foo.new assert_equal 6, foo.sum([1,2,3]) assert_equal 1, foo.sum([1]) end def test_array_destructuring_bind_first_two_and_rest Foo.class_eval do def pairs(arr) match(arr) do with(:x % :y % :xs) { [[x,y]] + pairs(xs)} otherwise {[]} end end end foo = Foo.new assert_equal [[1,2],[3,4]], foo.pairs([1,2,3,4]) end def test_array_destructuring_too_many_names Foo.class_eval do def drop_two(arr) match(arr) do with(:x % :y % :xs) { xs } otherwise { -1 } end end end foo = Foo.new assert_equal [], foo.drop_two([1,2]) assert_equal [], foo.drop_two([1]) end def test_naming_array Foo.class_eval do def f(arr) match(arr) do with(:inc & [1,2,3]) { inc.first} with(:dec & [3,2,1]) { dec.last } otherwise {-1} end end end foo = Foo.new assert_equal 1, foo.f([1,2,3]) assert_equal 1, foo.f([3,2,1]) assert_equal -1, foo.f([1,1,1]) end def test_naming_value_match Foo.class_eval do def f(arr) match(arr) do with(:num & 5) { num+1} with(:str & "hey") { str + "!" } otherwise {-1} end end end foo = Foo.new assert_equal 6, foo.f(5) assert_equal "hey!", foo.f("hey") end def test_naming_class_match Foo.class_eval do def f(arr) match(arr) do with(:num & Fixnum) { num + 1 } with(:str & String) { str + "!"} otherwise {-1} end end end foo = Foo.new assert_equal 6, foo.f(5) assert_equal "hey!", foo.f("hey") end def test_naming_hash Foo.class_eval do def f(arr) match(arr) do with(:hash & {:a => 1}) { hash[:a] } otherwise {-1} end end end foo = Foo.new assert_equal 1, foo.f(:a => 1) end def test_naming_hash_key Foo.class_eval do def f(arr) match(arr) do with({(:key & :a) => 1}) { key } otherwise {-1} end end end foo = Foo.new assert_equal :a, foo.f(:a => 1) end def test_naming_hash_value Foo.class_eval do def f(arr) match(arr) do with({:a => (:val & 1)}) {val} otherwise {-1} end end end foo = Foo.new assert_equal 1, foo.f(:a => 1) end def test_nested_naming Foo.class_eval do def f(arg) match(arg) do with(:arr & [1,2,:three & 3]) { arr << three} with(:arr & [1,2,:last]) { last } end end end foo = Foo.new assert_equal [1,2,3,3], foo.f([1,2,3]) assert_equal 4, foo.f([1,2,4]) end def test_example__take1 Foo.class_eval do def take1(arr,num=nil) match([arr,num]) do with([[],:_]) {[]} with([:_, 0]) {[]} with([(:x % :xs),Fixnum]) { [x] + take1(xs,num-1)} end end end foo = Foo.new assert_equal [], foo.take1([],4) assert_equal [], foo.take1([1,2,3],0) assert_equal [1], foo.take1([1,2,3],1) assert_equal [1,2], foo.take1([1,2,3],2) end def test_example__print Foo.class_eval do def disp(tree) match tree do with(:num & Fixnum) {num} with([:left, :op & String, :right]){"(#{disp(left)} #{op} #{disp(right)})"} otherwise {"Invalid tree structure"} end end end foo = Foo.new assert_equal "Invalid tree structure", foo.disp([]) assert_equal "Invalid tree structure", foo.disp([1,2]) assert_equal "(1 + 2)", foo.disp([1,'+',2]) assert_equal "((5 * 10) + 2)", foo.disp([[5,'*',10],'+',2]) assert_equal "((5 * 10) + (6 / 3))", foo.disp([[5,'*',10],'+',[6, '/', 3]]) end def test_example__map Foo.class_eval do def map(proc,arr) match([proc,arr]) do with([:_,[]]) {[]} with([:f,:x % :xs]) { [f.call(x)] + map(f,xs) } end end end foo = Foo.new inc = Proc.new {|x| x+1} assert_equal [], foo.map(inc,[]) assert_equal [2,3,4], foo.map(inc,[1,2,3]) end end class Foo include Matchable end