PATM: PATtern Matcher for Ruby

Usage

require 'patm'
# With case(simple but slow)
def match(obj)
  p = Patm
  _xs = Patm._xs
  case obj
  when m = Patm.match([:x, p._1, p._2])
    [m._2, m._1]
  when m = Patm.match([1, _xs&p._1])
    m._1
  end
end

match([1, 2, 3])
# => [2, 3]

match([:x, :y, :z])
# => [:z, :y]

match([])
# => nil
# With DSL
class A
  extend ::Patm::DSL

  define_matcher :match1 do|r|
    p = Patm
    r.on [:x, p._1, p._2] do|m|
      [m._1, m._2]
    end
  end

  define_matcher :match2 do|r|
    r.on [:a, Patm._xs & Patm._1] do|m, _self|
      _self.match1(m._1)
    end
    # ...
  end
end

A.new.match1([:x, 1, 2])
# => [1, 2]
# With pre-built Rule
rule = Patm::Rule.new do|r|
  p = Patm
  _xs = Patm._xs
  r.on [:x, p._1, p._2] do|m|
    [m._2, m._1]
  end
  r.on [1, _xs&p._1] do|m|
    m._1
  end
end

rule.apply([1,2,3])
# => [2, 3]

rule.apply([:x, :y, :z])
# => [:z, :y]

rule.apply([])
# => nil
# With cached rules
class A
  def initialize
    @rules = Patm::RuleCache.new
  end

  def match1(obj)
    @rules.match(:match1, obj) do|r|
      p = Patm
      r.on [:x, p._1, p._2] do|m|
        [m._1, m._2]
      end
    end
  end

  def match2(obj)
    @rules.match(:match2, obj) do|r|
      # ...
    end
  end
end

Patterns

1, :x, String, ...

Normal pattern matches if pattern === value is true.

Array

[1, 2, _xs] matches [1, 2], [1, 2, 3], [1, 2, 3, 4], etc. [1, _xs, 2] matches [1, 2], [1, 10, 2], etc.

Note: More than one _xs in same array is invalid.

Capture

_1, _2, etc matches any value, and capture the value as correspond match group.

Compose

_1&[_any, _any] matches any two element array, and capture the array as _1. Patm.or(1, 2) matches 1 or 2.

Changes

1.0.0

  • DSL
  • Compile is enabled by default
  • Change interface

0.1.0

  • Faster matching with pattern compilation
  • Fix StackOverflow bug for [Patm.or()]

0.0.1

  • Initial release