# Matches a regular expression against a string and returns an array containing the match # and any matched capturing groups. # # The first argument is a string or array of strings. The second argument is either a # regular expression, regular expression represented as a string, or Regex or Pattern # data type that the function matches against the first argument. # # The returned array contains the entire match at index 0, and each captured group at # subsequent index values. If the value or expression being matched is an array, the # function returns an array with mapped match results. # # If the function doesn't find a match, it returns 'undef'. # # @example Matching a regular expression in a string # # ```puppet # $matches = "abc123".match(/[a-z]+[1-9]+/) # # $matches contains [abc123] # ``` # # @example Matching a regular expressions with grouping captures in a string # # ```puppet # $matches = "abc123".match(/([a-z]+)([1-9]+)/) # # $matches contains [abc123, abc, 123] # ``` # # @example Matching a regular expression with grouping captures in an array of strings # # ```puppet # $matches = ["abc123","def456"].match(/([a-z]+)([1-9]+)/) # # $matches contains [[abc123, abc, 123], [def456, def, 456]] # ``` # # @since 4.0.0 # Puppet::Functions.create_function(:match) do dispatch :match do param 'String', :string param 'Variant[Any, Type]', :pattern end dispatch :enumerable_match do param 'Array[String]', :string param 'Variant[Any, Type]', :pattern end def initialize(closure_scope, loader) super # Make this visitor shared among all instantiations of this function since it is faster. # This can be used because it is not possible to replace # a puppet runtime (where this function is) without a reboot. If you model a function in a module after # this class, use a regular instance variable instead to enable reloading of the module without reboot # @@match_visitor ||= Puppet::Pops::Visitor.new(self, "match", 1, 1) end # Matches given string against given pattern and returns an Array with matches. # @param string [String] the string to match # @param pattern [String, Regexp, Puppet::Pops::Types::PPatternType, Puppet::Pops::PRegexpType, Array] the pattern # @return [Array] matches where first match is the entire match, and index 1-n are captures from left to right # def match(string, pattern) @@match_visitor.visit_this_1(self, pattern, string) end # Matches given Array[String] against given pattern and returns an Array with mapped match results. # # @param array [Array] the array of strings to match # @param pattern [String, Regexp, Puppet::Pops::Types::PPatternType, Puppet::Pops::PRegexpType, Array] the pattern # @return [Array>] Array with matches (see {#match}), non matching entries produce a nil entry # def enumerable_match(array, pattern) array.map {|s| match(s, pattern) } end protected def match_Object(obj, s) msg = _("match() expects pattern of T, where T is String, Regexp, Regexp[r], Pattern[p], or Array[T]. Got %{klass}") % { klass: obj.class } raise ArgumentError, msg end def match_String(pattern_string, s) do_match(s, Regexp.new(pattern_string)) end def match_Regexp(regexp, s) do_match(s, regexp) end def match_PRegexpType(regexp_t, s) raise ArgumentError, _("Given Regexp Type has no regular expression") unless regexp_t.pattern do_match(s, regexp_t.regexp) end def match_PPatternType(pattern_t, s) # Since we want the actual match result (not just a boolean), an iteration over # Pattern's regular expressions is needed. (They are of PRegexpType) result = nil pattern_t.patterns.find {|pattern| result = match(s, pattern) } result end # Returns the first matching entry def match_Array(array, s) result = nil array.flatten.find {|entry| result = match(s, entry) } result end private def do_match(s, regexp) if result = regexp.match(s) result.to_a end end end