blob: 8208cbcaf24dbdde0b8b41503bebac4593b73d47 [file] [log] [blame]
// 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
import (
"regexp"
"cuelang.org/go/cue/errors"
)
var errNoMatch = errors.New("no match")
// Valid reports whether the given regular expression
// is valid.
func Valid(pattern string) (bool, error) {
_, err := regexp.Compile(pattern)
return err == nil, err
}
// Find returns a string holding the text of the leftmost match in s of
// the regular expression. It returns bottom if there was 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 returns a list of all successive matches of the expression. It
// matches successive non-overlapping matches of the entire expression. Empty
// matches abutting a preceding match are ignored. The return value is a list
// containing the successive matches. The integer argument n indicates the
// maximum number of matches to return for n >= 0, or all matches otherwise. It
// returns bottom for 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
}
// FindSubmatch returns a list of strings holding the text of the leftmost match
// of the regular expression in s and the matches, if any, of its
// subexpressions. 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. It returns bottom for 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
}
// FindAllSubmatch finds successive matches as returned by FindSubmatch,
// observing the rules of FindAll. It returns bottom for 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
}
var errNoNamedGroup = errors.New("no named groups")
// FindNamedSubmatch is like FindSubmatch, but returns a map with the names used
// in capturing groups.
//
// Example:
// regexp.MapSubmatch(#"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
}
// FindAllNamedSubmatch is like FindAllSubmatch, but returns a map 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
}