1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133
|
# frozen_string_literal: true
# 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<String>] 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<String>] the array of strings to match
# @param pattern [String, Regexp, Puppet::Pops::Types::PPatternType, Puppet::Pops::PRegexpType, Array] the pattern
# @return [Array<Array<String, nil>>] 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_PTypeAliasType(alias_t, s)
match(s, alias_t.resolved_type)
end
def match_PVariantType(var_t, s)
# Find first matching type (or error out if one of the variants is not acceptable)
result = nil
var_t.types.find { |t| result = match(s, t) }
result
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)
result = regexp.match(s)
result.to_a if result
end
end
|