Skip to content

Instantly share code, notes, and snippets.

@wconrad
Created July 7, 2012 13:37
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save wconrad/3066509 to your computer and use it in GitHub Desktop.
Save wconrad/3066509 to your computer and use it in GitHub Desktop.
big_pattern_matcher
# -*- coding: utf-8 -*-
# Given multiple regular expressions and a string to match them
# against, call a method on a receiver that corresponds to the matched
# expression.
#
# see example.rb
class BigPatternMatcher
def initialize(patterns_and_functions, no_match_function)
@regex = make_big_regex(patterns_and_functions)
@no_match_function = no_match_function
end
def match(s, context)
match = @regex.match(s)
context.send(function_name(match), match)
end
private
FUNC_GROUP_PREFIX = "func_"
FUNC_GROUP_REGEX = /^#{FUNC_GROUP_PREFIX}(.*)$/
def function_name(match)
if match
match.names.grep(FUNC_GROUP_REGEX).find do |name|
match[name]
end[FUNC_GROUP_REGEX, 1]
else
@no_match_function
end
end
def make_big_regex(patterns_and_functions)
patterns = patterns_and_functions.map do |pattern, function|
/(?<#{FUNC_GROUP_PREFIX}#{function}>#{pattern.source})/
end
Regexp.union(patterns)
end
end
# -*- coding: utf-8 -*-
require_relative 'big_pattern_matcher'
describe BigPatternMatcher do
let(:receiver) do
Class.new do
attr_reader :method
def method_missing(method, *args)
if @match_group
@match_group[method]
else
@method = method
@match_group = args.first
end
end
end.new
end
let(:matcher) {BigPatternMatcher.new(patterns, :no_match)}
before(:each) do
matcher.match(string, receiver)
end
subject {receiver}
context "when no patterns" do
let(:patterns) do
[]
end
let(:string) {''}
its(:method) {should == :no_match}
end
context "when two simple patterns with no match groups" do
let(:patterns) do
[
[/foo/, :foo],
[/bar/, :bar],
]
end
context 'when matching the first pattern' do
let(:string) {'abc foo xyz'}
its(:method) {should == :foo}
end
context 'when matching the second pattern' do
let(:string) {'abc bar xyz'}
its(:method) {should == :bar}
end
context 'when both patterns present' do
let(:string) {'foobar'}
its(:method) {should == :foo}
end
end
context "when patterns with match groups" do
let(:patterns) do
[
[/foo(?<number>\d+)/, :foo],
[/bar(?<number>\d+)/, :bar],
]
end
context 'when matching the first pattern' do
let(:string) {'foo123'}
its(:method) {should == :foo}
its(:number) {should == '123'}
end
context 'when matching the second pattern' do
let(:string) {'bar456'}
its(:method) {should == :bar}
its(:number) {should == '456'}
end
end
end
#!/usr/bin/env ruby
# -*- coding: utf-8 -*-
require_relative 'big_pattern_matcher'
PATTERNS_AND_FUNCTIONS = [
[/ABC(?<value>\d+)/, :foo],
[/DEF(?<value>\d+)/, :bar],
]
def foo(match)
p ["foo", match[:value]]
end
def bar(match)
p ["bar", match[:value]]
end
def no_match(match)
p "no match"
end
matcher = BigPatternMatcher.new(PATTERNS_AND_FUNCTIONS, :no_match)
matcher.match('blah ABC1 blah', self) # => ["foo", "1"
matcher.match('blah DEF2 blah', self) # => ["bar", "2"]
matcher.match('blah blah', self) # => "no_match"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment