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 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230
|
// Copyright 2019 CUE Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// Package regexp implements regular expression search.
//
// The syntax of the regular expressions accepted is the same
// general syntax used by Perl, Python, and other languages.
// More precisely, it is the syntax accepted by RE2 and described at
// https://golang.org/s/re2syntax, except for \C.
// For an overview of the syntax, run
//
// go doc regexp/syntax
//
// The regexp implementation provided by this package is
// guaranteed to run in time linear in the size of the input.
// (This is a property not guaranteed by most open source
// implementations of regular expressions.) For more information
// about this property, see
//
// https://swtch.com/~rsc/regexp/regexp1.html
//
// or any book about automata theory.
//
// All characters are UTF-8-encoded code points.
//
// The regexp package functions match a regular expression and identify
// the matched text. Their names are matched by this regular expression:
//
// Find(All)?(Submatch)?
//
// If 'All' is present, the routine matches successive non-overlapping
// matches of the entire expression. Empty matches abutting a preceding
// match are ignored. The return value is a slice containing the successive
// return values of the corresponding non-'All' routine. These routines take
// an extra integer argument, n. If n >= 0, the function returns at most n
// matches/submatches; otherwise, it returns all of them.
//
// If 'Submatch' is present, the return value is a slice identifying the
// successive submatches of the expression. Submatches are matches of
// parenthesized subexpressions (also known as capturing groups) within the
// regular expression, numbered from left to right in order of opening
// parenthesis. Submatch 0 is the match of the entire expression, submatch 1
// the match of the first parenthesized subexpression, and so on.
package regexp
import (
"regexp"
"cuelang.org/go/cue/errors"
)
var errNoMatch = errors.New("no match")
// Find returns a list holding the text of the leftmost match in b of the regular expression.
// A return value of bottom indicates no match.
func Find(pattern, s string) (string, error) {
re, err := regexp.Compile(pattern)
if err != nil {
return "", err
}
m := re.FindStringIndex(s)
if m == nil {
return "", errNoMatch
}
return s[m[0]:m[1]], nil
}
// FindAll is the 'All' version of Find; it returns a list of all successive
// matches of the expression, as defined by the 'All' description in the
// package comment.
// A return value of bottom indicates no match.
func FindAll(pattern, s string, n int) ([]string, error) {
re, err := regexp.Compile(pattern)
if err != nil {
return nil, err
}
m := re.FindAllString(s, n)
if m == nil {
return nil, errNoMatch
}
return m, nil
}
// FindAllNamedSubmatch is like FindAllSubmatch, but returns a list of maps
// with the named used in capturing groups. See FindNamedSubmatch for an
// example on how to use named groups.
func FindAllNamedSubmatch(pattern, s string, n int) ([]map[string]string, error) {
re, err := regexp.Compile(pattern)
if err != nil {
return nil, err
}
names := re.SubexpNames()
if len(names) == 0 {
return nil, errNoNamedGroup
}
m := re.FindAllStringSubmatch(s, n)
if m == nil {
return nil, errNoMatch
}
result := make([]map[string]string, len(m))
for i, m := range m {
r := make(map[string]string, len(names)-1)
for k, name := range names {
if name != "" {
r[name] = m[k]
}
}
result[i] = r
}
return result, nil
}
var errNoNamedGroup = errors.New("no named groups")
// FindAllSubmatch is the 'All' version of FindSubmatch; it returns a list
// of all successive matches of the expression, as defined by the 'All'
// description in the package comment.
// A return value of bottom indicates no match.
func FindAllSubmatch(pattern, s string, n int) ([][]string, error) {
re, err := regexp.Compile(pattern)
if err != nil {
return nil, err
}
m := re.FindAllStringSubmatch(s, n)
if m == nil {
return nil, errNoMatch
}
return m, nil
}
// FindNamedSubmatch is like FindSubmatch, but returns a map with the names used
// in capturing groups.
//
// Example:
//
// regexp.FindNamedSubmatch(#"Hello (?P<person>\w*)!"#, "Hello World!")
//
// Output:
//
// [{person: "World"}]
func FindNamedSubmatch(pattern, s string) (map[string]string, error) {
re, err := regexp.Compile(pattern)
if err != nil {
return nil, err
}
names := re.SubexpNames()
if len(names) == 0 {
return nil, errNoNamedGroup
}
m := re.FindStringSubmatch(s)
if m == nil {
return nil, errNoMatch
}
r := make(map[string]string, len(names)-1)
for k, name := range names {
if name != "" {
r[name] = m[k]
}
}
return r, nil
}
// FindSubmatch returns a list of lists holding the text of the leftmost
// match of the regular expression in b and the matches, if any, of its
// subexpressions, as defined by the 'Submatch' descriptions in the package
// comment.
// A return value of bottom indicates no match.
func FindSubmatch(pattern, s string) ([]string, error) {
re, err := regexp.Compile(pattern)
if err != nil {
return nil, err
}
m := re.FindStringSubmatch(s)
if m == nil {
return nil, errNoMatch
}
return m, nil
}
// ReplaceAll returns a copy of src, replacing variables in repl with
// corresponding matches drawn from src, according to the following rules.
//
// In the template repl, a variable is denoted by a substring of the form $name
// or ${name}, where name is a non-empty sequence of letters, digits, and
// underscores. A purely numeric name like $1 refers to the submatch with the
// corresponding index; other names refer to capturing parentheses named with
// the (?P<name>...) syntax. A reference to an out of range or unmatched index
// or a name that is not present in the regular expression is replaced with an
// empty slice.
//
// In the $name form, name is taken to be as long as possible: $1x is
// equivalent to ${1x}, not ${1}x, and, $10 is equivalent to ${10}, not ${1}0.
//
// To insert a literal $ in the output, use $$ in the template.
func ReplaceAll(pattern, src, repl string) (string, error) {
re, err := regexp.Compile(pattern)
if err != nil {
return "", err
}
return re.ReplaceAllString(src, repl), nil
}
// ReplaceAllLiteral returns a copy of src, replacing matches of the regexp
// pattern with the replacement string repl. The replacement repl is substituted
// directly.
func ReplaceAllLiteral(pattern, src, repl string) (string, error) {
re, err := regexp.Compile(pattern)
if err != nil {
return "", err
}
return re.ReplaceAllLiteralString(src, repl), nil
}
// Valid reports whether the given regular expression
// is valid.
func Valid(pattern string) (bool, error) {
_, err := regexp.Compile(pattern)
return err == nil, err
}
|