#require 'facets/functor' class String # Create a mask. def mask(re=nil) Mask.new(self,re) end # = Mask # class Mask ESC = "\032" # ASCII SUBSTITUTE def self.[](string, re=nil) new(string, re) end private def initialize(string, re=nil) @to_str = string.dup mask!(re) if re end def convert(other) case other when Mask other else self.class.new(other.to_s, re) end end public # The underlying string object. def to_str @to_str end # def to_s to_str end # def inspect to_str.inspect end # def [](*a) to_str[*a] end def mask(re) self.class.new(to_str,re) end def mask!(re) to_str.gsub!(re){ |s| ESC * s.size } end # Mask subtraction. Where the characters are the same, # the result is "empty", where they differ the result # reflects the last string. # # "abc..123" "ab..789." # - "ab..789." - "abc..123" # ---------- ---------- # "....789." "..c..123" # def -(other) other = convert(other) i = 0 o = '' while i < to_str.size if to_str[i,1] == other[i,1] o << ESC else o << other[i,1] end i += 1 end self.class.new(o) end # Mask ADD. As long as there is a value other # then empty the character filters though. # The last to_str takes precedence. # # "abc..123" "ab..789." # + "ab..789." + "abc..123" # ---------- ---------- # "abc.7893" "abc.7123" # def +(other) other = convert(other) i = 0 o = '' while i < to_str.size if other[i,1] == ESC o << to_str[i,1] else o << other[i,1] end i += 1 end self.class.new(o) end # Mask OR is the same as ADD. alias_method :|, :+ # Mask XAND. Where the characters are the same, the # result is the same, where they differ the result # reflects the later. # # "abc..123" "ab..789." # * "ab..789." * "abc..123" # ---------- ---------- # "ab..789." "abc..123" # def *(other) other = convert(other) i = 0 o = '' while i < to_str.size if (c = to_str[i,1]) == other[i,1] o << c else o << other[i,1] end i += 1 end self.class.new(o) end # Mask AND. Only where they are # then same filters through. # # "abc..123" "ab..789." # & "ab..789." | "abc..123" # ---------- ---------- # "ab......" "ab......" # def &(other) other = convert(other) i = 0 o = '' while i < to_str.size if (c = to_str[i,1]) == other[i,1] o << c else o << ESC end i += 1 end self.class.new(o) end # Mask XOR operation. Only where there # is an empty slot will the value filter. # # "abc..123" "ab..789." # | "ab..789." | "abc..123" # ---------- ---------- # "..c.7..3" "..c.7..3" # def ^(other) other = convert(other) i = 0 o = '' while i < to_str.size if to_str[i,1] == ESC o << other[i,1] elsif other[i,1] == ESC o << to_str[i,1] else o << ESC end i += 1 end self.class.new(o) end # def ==(other) case other when Mask to_str == other.to_str else to_str == other.to_s end end # Apply a method to the internal string and return # a new mask. def apply(s=nil, *a, &b) if s to_str.send(s,*a,&b).to_mask else @_self ||= Functor.new do |op, *a| to_str.send(op,*a).to_mask end end end # def replace(string) @to_str = string.to_s end # #def instance_delegate # @to_str #end # Functor on the interal string. #def self # @_self ||= Functor.new do |op, *a| # @to_str = @to_str.send(op, *a) # end #end # Delegate any missing methods to underlying string. # def method_missing(s, *a, &b) begin to_str.send(s, *a, &b) rescue NoMethodError super(s, *a, &b) end end end end