Pastie now auto-senses if line-wrap is a bad or good idea. Feedback?
## mark a section (Learn more)
class Ematcher < BlankSlate class Placeholder attr :name def initialize(name); @name = name; end end module AnyMany class Any; end class Many; end def _; Any; end def __; Many; end end extend AnyMany include AnyMany def method_missing(sym, *args) case args.length when 0 then case @mode when :build then Placeholder.new(sym) when :match then super when :yield then match(sym) else super end when 1 then return super unless @mode == :yield && ((name = sym.to_s).last == "=") name = name.chop.to_sym return super unless @match.key?(name) @match[name] = args[0] else super end end private def match(sym) raise ArgumentError, "No match #{sym}" unless @match.key?(sym) @match[sym] end def in_mode(mode, &block) begin @mode = mode yield ensure @mode = :build end end public def on(pattern, &block) return if @match @match = in_mode(:match) do Ematcher.match(pattern, input) end in_mode(:yield, &block) if @match end attr :input def self.match(pattern, input, env = {}) return env if pattern == __ return env if pattern == _ if pattern.is_a?(Placeholder) return nil if env.key?(pattern.name) && env[pattern.name] != input return env.update(pattern.name => input) end if input.is_a?(Array) && pattern.is_a?(Array) if pattern.length != input.length return nil if pattern.length < input.length return nil if pattern.length > input.length+1 return nil if pattern.last != __ end return pattern.zip(input).inject(env) do |env, (p, i)| env && match(p, i, env) end end input == pattern && env end def initialize(input) @input = input.dup.freeze end def matching(&block) @mode = :build Proc.new.bind(self).call @match end end def matching!(input, &block) matching(input, &block) || raise(ArgumentError, "No match for #{input.inspect}") end def matching(input, &block) Ematcher.new(input).matching(&block) end m = matching! [ 1, [ :a, 2 ], 3 ] do on [ a ] do puts [ "1", a ].inspect end on [ a, _, a ] do puts [ "2", a ].inspect end on [ a, [ :a, 3 ], z ] do puts [ "3", a, z ].inspect end on [ a, [ b, 2 ], z ] do puts [ "4", a, b, z ].inspect self.z = :z end on [ a, [ :a, 2 ] , z ] do puts [ "5", a, z ].inspect end end puts m.inspect
This paste will be private.
From the Design Piracy series on my blog: