Project

patm

0.01
No commit activity in last 3 years
No release in over 3 years
Pattern matching library for plain data structure
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
2025
 Dependencies

Development

~> 0.9
~> 2.14
~> 0.7.1
 Project Readme

PATM: PATtern Matcher for Ruby

Build Status Code Climate

PATM is extremely faster pattern match library.

Features: Match value/classes, Capture, Array/Hash decomposition.

Usage

require 'patm'
# 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
    # ...
    r.else do
      nil
    end
  end
end

A.new.match1([:x, 1, 2])
# => [1, 2]
# 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 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

DSL

class PatternMatcher
  extend Patm::DSL

  define_matcher(:match) do|r| # r is instance of Patm::Rule
    # r.on( PATTERN ) {|match, _self|
    #   First argument is instance of Patm::Match. Use it to access captured value.
    #   ex. m._1, m._2, ..., m[capture_name]
    #
    #   Second argument is instance of the class. Use it to access other methods.
    #   ex. _self.other_method
    # }
    #
    # r.else {|value, _self|
    #   First argument is the value. Second is instance of the class.
    # }
  end
end

matcher = PatternMatcher.new

matcher.match(1)

Patterns

Value

Value patterns such as 1, :x, String, ... matches if pattern === target_value is true.

Struct

A = Struct.new(:x, :y)

# ...
define_matcher :match_struct do|r|
  # use Patm[struct_class].( ... ) for match struct
  # argument is a Hash(member_name => pattern) or patterns that correspond struct members.
  r.on Patm[A].(x: 1, y: Patm._1) {|m| m._1 }
  r.on Patm[A].(2, Patm._1) {|m| m._1 }
end

Array

[1, 2, _any] matches [1, 2, 3], [1, 2, :x], etc.

[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.

Hash

{a: 1} matches {a: 1}, {a: 1, b: 2}, etc.

{a: 1, Patm.exact => true} matches only {a: 1}.

{a: 1, b: Patm.opt(2)} matches {a: 1}, {a: 1, b: 2}.

Capture

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

Pattern#[capture_name] also used for capture.Patm._any[:foo] capture any value as foo.

Captured values are accessible through Match#_1, _2, ... and Match#[capture_name]

Compose

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

Performance

see benchmark code for details

Machine: MacBook Air(Late 2010) C2D 1.8GHz, OS X 10.9.2

RUBY_VERSION: 2.1.2 p95

Benchmark: Empty(x10000)
                    user     system      total        real
manual          0.010000   0.000000   0.010000 (  0.012252)
patm            0.060000   0.000000   0.060000 (  0.057050)
pattern_match   1.710000   0.010000   1.720000 (  1.765749)

Benchmark: SimpleConst(x10000)
                    user     system      total        real
manual          0.020000   0.000000   0.020000 (  0.018274)
patm            0.060000   0.000000   0.060000 (  0.075068)
patm_case       0.160000   0.000000   0.160000 (  0.161002)
pattern_match   1.960000   0.020000   1.980000 (  2.007936)

Benchmark: ArrayDecomposition(x10000)
                    user     system      total        real
manual          0.050000   0.000000   0.050000 (  0.047948)
patm            0.250000   0.000000   0.250000 (  0.254039)
patm_case       1.710000   0.000000   1.710000 (  1.765656)
pattern_match  12.890000   0.060000  12.950000 ( 13.343334)

Benchmark: VarArray(x10000)
                    user     system      total        real
manual          0.050000   0.000000   0.050000 (  0.052425)
patm            0.210000   0.000000   0.210000 (  0.223190)
patm_case       1.440000   0.000000   1.440000 (  1.587535)
pattern_match  10.050000   0.070000  10.120000 ( 10.898683)

Changes

3.1.0

  • Struct matcher

3.0.0

  • If given value can't match any pattern and no else, Patm::NoMatchError raised(Instead of return nil).
  • RuleCache is now obsoleted. Use DSL.
  • More optimized compiler

2.0.1

  • Bugfix: About pattern Patm._1 & Array.
  • Bugfix: Compiler bug fix.

2.0.0

  • Named capture
  • Patm::GROUPS is obsolete. Use pattern[number_or_name] or Patm._1, _2, ... instead.
  • More optimize for compiled pattern.
  • Hash patterns

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