blob: 8208cbcaf24dbdde0b8b41503bebac4593b73d47 [file] [log] [blame]
Marcel van Lohuizen68a054b2019-10-15 13:44:35 +02001// Copyright 2019 CUE Authors
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15package regexp
16
17import (
Marcel van Lohuizen68a054b2019-10-15 13:44:35 +020018 "regexp"
Marcel van Lohuizen42346df2019-12-19 14:09:54 +010019
20 "cuelang.org/go/cue/errors"
Marcel van Lohuizen68a054b2019-10-15 13:44:35 +020021)
22
23var errNoMatch = errors.New("no match")
24
Roger Peppe2db865d2020-01-16 12:00:16 +000025// Valid reports whether the given regular expression
26// is valid.
27func Valid(pattern string) (bool, error) {
28 _, err := regexp.Compile(pattern)
29 return err == nil, err
30}
31
Marcel van Lohuizen68a054b2019-10-15 13:44:35 +020032// Find returns a string holding the text of the leftmost match in s of
33// the regular expression. It returns bottom if there was no match.
34func Find(pattern, s string) (string, error) {
35 re, err := regexp.Compile(pattern)
36 if err != nil {
37 return "", err
38 }
39 m := re.FindStringIndex(s)
40 if m == nil {
41 return "", errNoMatch
42 }
43 return s[m[0]:m[1]], nil
44}
45
46// FindAll returns a list of all successive matches of the expression. It
47// matches successive non-overlapping matches of the entire expression. Empty
48// matches abutting a preceding match are ignored. The return value is a list
49// containing the successive matches. The integer argument n indicates the
50// maximum number of matches to return for n >= 0, or all matches otherwise. It
51// returns bottom for no match.
52func FindAll(pattern, s string, n int) ([]string, error) {
53 re, err := regexp.Compile(pattern)
54 if err != nil {
55 return nil, err
56 }
57 m := re.FindAllString(s, n)
58 if m == nil {
59 return nil, errNoMatch
60 }
61 return m, nil
62}
63
64// FindSubmatch returns a list of strings holding the text of the leftmost match
65// of the regular expression in s and the matches, if any, of its
66// subexpressions. Submatches are matches of parenthesized subexpressions (also
67// known as capturing groups) within the regular expression, numbered from left
68// to right in order of opening parenthesis. Submatch 0 is the match of the
69// entire expression, submatch 1 the match of the first parenthesized
70// subexpression, and so on. It returns bottom for no match.
71func FindSubmatch(pattern, s string) ([]string, error) {
72 re, err := regexp.Compile(pattern)
73 if err != nil {
74 return nil, err
75 }
76 m := re.FindStringSubmatch(s)
77 if m == nil {
78 return nil, errNoMatch
79 }
80 return m, nil
81}
82
83// FindAllSubmatch finds successive matches as returned by FindSubmatch,
84// observing the rules of FindAll. It returns bottom for no match.
85func FindAllSubmatch(pattern, s string, n int) ([][]string, error) {
86 re, err := regexp.Compile(pattern)
87 if err != nil {
88 return nil, err
89 }
90 m := re.FindAllStringSubmatch(s, n)
91 if m == nil {
92 return nil, errNoMatch
93 }
94 return m, nil
95}
96
97var errNoNamedGroup = errors.New("no named groups")
98
99// FindNamedSubmatch is like FindSubmatch, but returns a map with the names used
100// in capturing groups.
101//
102// Example:
103// regexp.MapSubmatch(#"Hello (?P<person>\w*)!"#, "Hello World!")
104// Output:
105// [{person: "World"}]
106//
107func FindNamedSubmatch(pattern, s string) (map[string]string, error) {
108 re, err := regexp.Compile(pattern)
109 if err != nil {
110 return nil, err
111 }
112 names := re.SubexpNames()
113 if len(names) == 0 {
114 return nil, errNoNamedGroup
115 }
116 m := re.FindStringSubmatch(s)
117 if m == nil {
118 return nil, errNoMatch
119 }
120 r := make(map[string]string, len(names)-1)
121 for k, name := range names {
122 if name != "" {
123 r[name] = m[k]
124 }
125 }
126 return r, nil
127}
128
129// FindAllNamedSubmatch is like FindAllSubmatch, but returns a map with the
130// named used in capturing groups. See FindNamedSubmatch for an example on
131// how to use named groups.
132func FindAllNamedSubmatch(pattern, s string, n int) ([]map[string]string, error) {
133 re, err := regexp.Compile(pattern)
134 if err != nil {
135 return nil, err
136 }
137 names := re.SubexpNames()
138 if len(names) == 0 {
139 return nil, errNoNamedGroup
140 }
141 m := re.FindAllStringSubmatch(s, n)
142 if m == nil {
143 return nil, errNoMatch
144 }
145 result := make([]map[string]string, len(m))
146 for i, m := range m {
147 r := make(map[string]string, len(names)-1)
148 for k, name := range names {
149 if name != "" {
150 r[name] = m[k]
151 }
152 }
153 result[i] = r
154 }
155 return result, nil
156}