-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathparser.go
151 lines (125 loc) · 3.18 KB
/
parser.go
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
package glob
import (
"regexp"
"strings"
"github.com/lukasholzer/go-glob/internal"
)
func cleanPattern(input string) string {
// windows path segments to posix characters
input = strings.ReplaceAll(input, "\\", "/")
input = strings.Replace(input, "***", "*", 1)
input = strings.Replace(input, "**/**", "**", 1)
input = strings.Replace(input, "**/**/**", "**", 1)
return input
}
type ParsedPattern struct {
// regExp is the regular expression as string that falls out of the parsedPattern
stringPattern string
isBaseSet bool
// input is the original glob pattern
Input string
RegExp *regexp.Regexp
IsGlobstar bool
// base is the base folder that can be used for matching a glob.
// For example if a glob starts with `src/**/*.ts` we don't need to crawl all
// folders in the current working directory as we see the `src` as base folder
Base string
}
func (p *ParsedPattern) String() string {
return p.RegExp.String()
}
func (p *ParsedPattern) setBase(str string) {
if !p.isBaseSet {
p.Base = str
p.isBaseSet = true
}
}
func (p *ParsedPattern) Compile() (*ParsedPattern, error) {
re, err := regexp.Compile(`^` + p.stringPattern + `$`)
if err != nil {
return nil, err
}
p.RegExp = re
return p, nil
}
func (p *ParsedPattern) add(char string) {
p.stringPattern += char
}
func Parse(input string) (*ParsedPattern, error) {
// remove duplicate or redundant parts in the pattern
// and transform to posix separators
input = cleanPattern(input)
parsed := ParsedPattern{
Input: input,
IsGlobstar: false,
}
for i := 0; i < len(input); i++ {
cur := string(input[i])
// store the previous character if we have some
// var prevChar string
// if i > 0 {
// prevChar = string(input[i-1])
// }
// store the next character if we have some
var nextChar string
if i < len(input)-1 {
nextChar = string(input[i+1])
}
switch cur {
case "/":
fallthrough
case "$":
fallthrough
case "^":
fallthrough
case "+":
fallthrough
case ".":
fallthrough
case "(":
fallthrough
case ")":
fallthrough
case "=":
fallthrough
case "!":
fallthrough
case "|":
// escape the following characters as they have a special meaning in regexp
parsed.add(`\` + cur)
// special characters are starting now
case "?":
parsed.add(`.`)
case "*":
// consider everything until now as base
parsed.setBase(input[0:i])
// count consecutive stars to determine if it is a globstar `**` or single star
starCount := 1
if nextChar == "*" {
starCount++
i++
}
// for string(input[i+1]) == "*" {
// starCount++
// i++
// }
// isStartOfSegment := prevChar == `/` || len(prevChar) == 0
// isEndOfSegment := nextChar == `/` || len(nextChar) == 0
isGlobstar := starCount > 1 // && isStartOfSegment && isEndOfSegment
if !parsed.IsGlobstar && isGlobstar {
parsed.IsGlobstar = true
}
if isGlobstar {
// it's a globstar, so match zero or more path segments
parsed.add(internal.GLOBSTER)
i++ // move over the "/"
} else {
// it's not a globstar, so only match one path segment
parsed.add(internal.STAR)
}
default:
parsed.add(cur)
}
}
return parsed.Compile()
}