Marcel van Lohuizen | 68a054b | 2019-10-15 13:44:35 +0200 | [diff] [blame] | 1 | // 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 | |
| 15 | package regexp |
| 16 | |
| 17 | import ( |
Marcel van Lohuizen | 68a054b | 2019-10-15 13:44:35 +0200 | [diff] [blame] | 18 | "regexp" |
Marcel van Lohuizen | 42346df | 2019-12-19 14:09:54 +0100 | [diff] [blame] | 19 | |
| 20 | "cuelang.org/go/cue/errors" |
Marcel van Lohuizen | 68a054b | 2019-10-15 13:44:35 +0200 | [diff] [blame] | 21 | ) |
| 22 | |
| 23 | var errNoMatch = errors.New("no match") |
| 24 | |
Roger Peppe | 2db865d | 2020-01-16 12:00:16 +0000 | [diff] [blame] | 25 | // Valid reports whether the given regular expression |
| 26 | // is valid. |
| 27 | func Valid(pattern string) (bool, error) { |
| 28 | _, err := regexp.Compile(pattern) |
| 29 | return err == nil, err |
| 30 | } |
| 31 | |
Marcel van Lohuizen | 68a054b | 2019-10-15 13:44:35 +0200 | [diff] [blame] | 32 | // 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. |
| 34 | func 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. |
| 52 | func 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. |
| 71 | func 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. |
| 85 | func 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 | |
| 97 | var 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 | // |
| 107 | func 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. |
| 132 | func 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 | } |