201
vendor/github.com/yashtewari/glob-intersection/LICENSE
generated
vendored
Normal file
201
vendor/github.com/yashtewari/glob-intersection/LICENSE
generated
vendored
Normal file
@@ -0,0 +1,201 @@
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "{}"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright {yyyy} {name of copyright owner}
|
||||
|
||||
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.
|
||||
26
vendor/github.com/yashtewari/glob-intersection/README.md
generated
vendored
Normal file
26
vendor/github.com/yashtewari/glob-intersection/README.md
generated
vendored
Normal file
@@ -0,0 +1,26 @@
|
||||
# glob-intersection
|
||||
Go package to check if the set of non-empty strings matched by the intersection of two regexp-style globs is non-empty.
|
||||
|
||||
### Examples
|
||||
- `gintersect.NonEmpty("a.a.", ".b.b")` is `true` because both globs match the string `abab`.
|
||||
- `gintersect.NonEmpty("[a-z]+", "[0-9]*)` is `false` because there are no non-empty strings that both globs match.
|
||||
|
||||
### Limitations
|
||||
|
||||
- It is assumed that all input is rooted at the beginning and the end, i.e, starts and ends with the regexp symbols `^` and `$` respectively. This is done because any non-rooted expressions will always match a non-empty set of non-empty strings.
|
||||
- The only special symbols are:
|
||||
- `.` for any character.
|
||||
- `+` for 1 or more of the preceding expression.
|
||||
- `*` for 0 or more of the preceding expression.
|
||||
- `[` and `]` to define regexp-style character classes.
|
||||
- `-` to specify Unicode ranges inside character class definitions.
|
||||
- `\` escapes any special symbol, including itself.
|
||||
|
||||
### Complexity
|
||||
|
||||
Complexity is exponential in the number of flags (`+` or `*`) present in the glob with the smaller flag count.
|
||||
Benchmarks (see [`non_empty_bench_test.go`](/non_empty_bench_test.go)) reveal that inputs where one of the globs has <= 10 flags, and both globs have 100s of characters, will run in less than a nanosecond. This should be ok for most use cases.
|
||||
|
||||
### Acknowledgements
|
||||
|
||||
[This StackOverflow discussion](https://stackoverflow.com/questions/18695727/algorithm-to-find-out-whether-the-matches-for-two-glob-patterns-or-regular-expr) for fleshing out the logic.
|
||||
182
vendor/github.com/yashtewari/glob-intersection/glob.go
generated
vendored
Normal file
182
vendor/github.com/yashtewari/glob-intersection/glob.go
generated
vendored
Normal file
@@ -0,0 +1,182 @@
|
||||
// Package gintersect provides methods to check whether the intersection of several globs matches a non-empty set of strings.
|
||||
package gintersect
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Glob represents a glob.
|
||||
type Glob []Token
|
||||
|
||||
// NewGlob constructs a Glob from the given string by tokenizing and then simplifying it, or reports errors if any.
|
||||
func NewGlob(input string) (Glob, error) {
|
||||
tokens, err := Tokenize([]rune(input))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
tokens = Simplify(tokens)
|
||||
|
||||
return Glob(tokens), nil
|
||||
}
|
||||
|
||||
// TokenType is the type of a Token.
|
||||
type TokenType uint
|
||||
|
||||
const (
|
||||
TTCharacter TokenType = iota
|
||||
TTDot
|
||||
TTSet
|
||||
)
|
||||
|
||||
// Flag applies to a token.
|
||||
type Flag uint
|
||||
|
||||
func (f Flag) String() (s string) {
|
||||
for r, flag := range flagRunes {
|
||||
if f == flag {
|
||||
s = string(r)
|
||||
break
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
const (
|
||||
FlagNone = iota
|
||||
FlagPlus
|
||||
FlagStar
|
||||
)
|
||||
|
||||
// Token is the element that makes up a Glob.
|
||||
type Token interface {
|
||||
Type() TokenType
|
||||
Flag() Flag
|
||||
SetFlag(Flag)
|
||||
// Equal describes whether the given Token is exactly equal to this one, barring differences in flags.
|
||||
Equal(Token) bool
|
||||
String() string
|
||||
}
|
||||
|
||||
// token is the base for all structs implementing Token.
|
||||
type token struct {
|
||||
ttype TokenType
|
||||
flag Flag
|
||||
}
|
||||
|
||||
func (t token) Type() TokenType {
|
||||
return t.ttype
|
||||
}
|
||||
|
||||
func (t token) Flag() Flag {
|
||||
return t.flag
|
||||
}
|
||||
|
||||
func (t *token) SetFlag(f Flag) {
|
||||
t.flag = f
|
||||
}
|
||||
|
||||
// character is a specific rune. It implements Token.
|
||||
type character struct {
|
||||
token
|
||||
r rune
|
||||
}
|
||||
|
||||
func NewCharacter(r rune) Token {
|
||||
return &character{
|
||||
token: token{ttype: TTCharacter},
|
||||
r: r,
|
||||
}
|
||||
}
|
||||
|
||||
func (c character) Equal(other Token) bool {
|
||||
if c.Type() != other.Type() {
|
||||
return false
|
||||
}
|
||||
|
||||
o := other.(*character)
|
||||
return c.Rune() == o.Rune()
|
||||
}
|
||||
|
||||
func (c character) String() string {
|
||||
return fmt.Sprintf("{character: %s flag: %s}", string(c.Rune()), c.Flag().String())
|
||||
}
|
||||
|
||||
func (c character) Rune() rune {
|
||||
return c.r
|
||||
}
|
||||
|
||||
// dot is any character. It implements Token.
|
||||
type dot struct {
|
||||
token
|
||||
}
|
||||
|
||||
func NewDot() Token {
|
||||
return &dot{
|
||||
token: token{ttype: TTDot},
|
||||
}
|
||||
}
|
||||
|
||||
func (d dot) Equal(other Token) bool {
|
||||
if d.Type() != other.Type() {
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func (d dot) String() string {
|
||||
return fmt.Sprintf("{dot flag: %s}", d.Flag().String())
|
||||
}
|
||||
|
||||
// set is a set of characters (similar to regexp character class).
|
||||
// It implements Token.
|
||||
type set struct {
|
||||
token
|
||||
runes map[rune]bool
|
||||
}
|
||||
|
||||
func NewSet(runes []rune) Token {
|
||||
m := map[rune]bool{}
|
||||
for _, r := range runes {
|
||||
m[r] = true
|
||||
}
|
||||
return &set{
|
||||
token: token{ttype: TTSet},
|
||||
runes: m,
|
||||
}
|
||||
}
|
||||
|
||||
func (s set) Equal(other Token) bool {
|
||||
if s.Type() != other.Type() {
|
||||
return false
|
||||
}
|
||||
|
||||
o := other.(*set)
|
||||
r1, r2 := s.Runes(), o.Runes()
|
||||
|
||||
if len(r1) != len(r2) {
|
||||
return false
|
||||
}
|
||||
|
||||
for k, _ := range r1 {
|
||||
if _, ok := r2[k]; !ok {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func (s set) String() string {
|
||||
rs := make([]string, 0, 30)
|
||||
for r, _ := range s.Runes() {
|
||||
rs = append(rs, string(r))
|
||||
}
|
||||
return fmt.Sprintf("{set: %s flag: %s}", strings.Join(rs, ""), s.Flag().String())
|
||||
}
|
||||
|
||||
func (s set) Runes() map[rune]bool {
|
||||
return s.runes
|
||||
}
|
||||
91
vendor/github.com/yashtewari/glob-intersection/match.go
generated
vendored
Normal file
91
vendor/github.com/yashtewari/glob-intersection/match.go
generated
vendored
Normal file
@@ -0,0 +1,91 @@
|
||||
package gintersect
|
||||
|
||||
import (
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
var (
|
||||
errBadImplementation = errors.New("this logical path is invalid")
|
||||
)
|
||||
|
||||
// Match implements single-Token matching, ignoring flags.
|
||||
// Example: [a-d] and [b-e] match, while [a-z] and [0-9] do not.
|
||||
func Match(t1 Token, t2 Token) bool {
|
||||
var temp Token
|
||||
if t1.Type() > t2.Type() {
|
||||
temp = t1
|
||||
t1 = t2
|
||||
t2 = temp
|
||||
}
|
||||
|
||||
switch t1.Type() {
|
||||
case TTCharacter:
|
||||
ch := t1.(*character)
|
||||
|
||||
switch t2.Type() {
|
||||
case TTCharacter:
|
||||
return matchCharacters(ch, t2.(*character))
|
||||
case TTDot:
|
||||
return matchCharacterDot(ch, t2.(*dot))
|
||||
case TTSet:
|
||||
return matchCharacterSet(ch, t2.(*set))
|
||||
default:
|
||||
panic(errBadImplementation)
|
||||
}
|
||||
|
||||
case TTDot:
|
||||
d := t1.(*dot)
|
||||
|
||||
switch t2.Type() {
|
||||
case TTDot:
|
||||
return matchDots(d, t2.(*dot))
|
||||
case TTSet:
|
||||
return matchDotSet(d, t2.(*set))
|
||||
default:
|
||||
panic(errBadImplementation)
|
||||
}
|
||||
|
||||
case TTSet:
|
||||
switch t2.Type() {
|
||||
case TTSet:
|
||||
return matchSets(t1.(*set), t2.(*set))
|
||||
default:
|
||||
panic(errBadImplementation)
|
||||
}
|
||||
|
||||
default:
|
||||
panic(errBadImplementation)
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
func matchCharacters(a *character, b *character) bool {
|
||||
return a.Rune() == b.Rune()
|
||||
}
|
||||
|
||||
func matchCharacterDot(a *character, b *dot) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func matchCharacterSet(a *character, b *set) bool {
|
||||
_, ok := b.Runes()[a.Rune()]
|
||||
return ok
|
||||
}
|
||||
|
||||
func matchDots(a *dot, b *dot) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func matchDotSet(a *dot, b *set) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func matchSets(a *set, b *set) bool {
|
||||
for k, _ := range a.Runes() {
|
||||
if _, ok := b.Runes()[k]; ok {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
154
vendor/github.com/yashtewari/glob-intersection/non_empty.go
generated
vendored
Normal file
154
vendor/github.com/yashtewari/glob-intersection/non_empty.go
generated
vendored
Normal file
@@ -0,0 +1,154 @@
|
||||
package gintersect
|
||||
|
||||
// NonEmpty is true if the intersection of lhs and rhs matches a non-empty set of non-empty str1ngs.
|
||||
func NonEmpty(lhs string, rhs string) (bool, error) {
|
||||
g1, err := NewGlob(lhs)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
g2, err := NewGlob(rhs)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
var match bool
|
||||
g1, g2, match = trimGlobs(g1, g2)
|
||||
if !match {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
return intersectNormal(g1, g2), nil
|
||||
}
|
||||
|
||||
// trimGlobs removes matching prefixes and suffixes from g1, g2, or returns false if prefixes/suffixes don't match.
|
||||
func trimGlobs(g1, g2 Glob) (Glob, Glob, bool) {
|
||||
var l, r1, r2 int
|
||||
|
||||
// Trim from the beginning until a flagged Token or a mismatch is found.
|
||||
for l = 0; l < len(g1) && l < len(g2) && g1[l].Flag() == FlagNone && g2[l].Flag() == FlagNone; l++ {
|
||||
if !Match(g1[l], g2[l]) {
|
||||
return nil, nil, false
|
||||
}
|
||||
}
|
||||
|
||||
// Leave one prefix Token untrimmed to avoid empty Globs because those will break the algorithm.
|
||||
if l > 0 {
|
||||
l--
|
||||
}
|
||||
|
||||
// Trim from the end until a flagged Token or a mismatch is found.
|
||||
for r1, r2 = len(g1)-1, len(g2)-1; r1 >= 0 && r1 >= l && r2 >= 0 && r2 >= l && g1[r1].Flag() == FlagNone && g2[r2].Flag() == FlagNone; r1, r2 = r1-1, r2-1 {
|
||||
if !Match(g1[r1], g2[r2]) {
|
||||
return nil, nil, false
|
||||
}
|
||||
}
|
||||
|
||||
// Leave one suffix Token untrimmed to avoid empty Globs because those will break the algorithm.
|
||||
if r1 < len(g1)-1 {
|
||||
r1++
|
||||
r2++
|
||||
}
|
||||
|
||||
return g1[l : r1+1], g2[l : r2+1], true
|
||||
}
|
||||
|
||||
// All uses of `intersection exists` below mean that the intersection of the globs matches a non-empty set of non-empty strings.
|
||||
|
||||
// intersectNormal accepts two globs and returns a boolean describing whether their intersection exists.
|
||||
// It traverses g1, g2 while ensuring that their Tokens match.
|
||||
// If a flagged Token is encountered, flow of control is handed off to intersectSpecial.
|
||||
func intersectNormal(g1, g2 Glob) bool {
|
||||
var i, j int
|
||||
for i, j = 0, 0; i < len(g1) && j < len(g2); i, j = i+1, j+1 {
|
||||
if g1[i].Flag() == FlagNone && g2[j].Flag() == FlagNone {
|
||||
if !Match(g1[i], g2[j]) {
|
||||
return false
|
||||
}
|
||||
} else {
|
||||
return intersectSpecial(g1[i:], g2[j:])
|
||||
}
|
||||
}
|
||||
|
||||
if i == len(g1) && j == len(g2) {
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// intersectSpecial accepts two globs such that at least one starts with a flagged Token.
|
||||
// It returns a boolean describing whether their intersection exists.
|
||||
// It hands flow of control to intersectPlus or intersectStar correctly.
|
||||
func intersectSpecial(g1, g2 Glob) bool {
|
||||
if g1[0].Flag() != FlagNone { // If g1 starts with a Token having a Flag.
|
||||
switch g1[0].Flag() {
|
||||
case FlagPlus:
|
||||
return intersectPlus(g1, g2)
|
||||
case FlagStar:
|
||||
return intersectStar(g1, g2)
|
||||
}
|
||||
} else { // If g2 starts with a Token having a Flag.
|
||||
switch g2[0].Flag() {
|
||||
case FlagPlus:
|
||||
return intersectPlus(g2, g1)
|
||||
case FlagStar:
|
||||
return intersectStar(g2, g1)
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// intersectPlus accepts two globs such that plussed[0].Flag() == FlagPlus.
|
||||
// It returns a boolean describing whether their intersection exists.
|
||||
// It ensures that at least one token in other maches plussed[0], before handing flow of control to intersectSpecial.
|
||||
func intersectPlus(plussed, other Glob) bool {
|
||||
if !Match(plussed[0], other[0]) {
|
||||
return false
|
||||
}
|
||||
return intersectStar(plussed, other[1:])
|
||||
}
|
||||
|
||||
// intersectStar accepts two globs such that starred[0].Flag() == FlagStar.
|
||||
// It returns a boolean describing whether their intersection exists.
|
||||
// It gobbles up Tokens from other until the Tokens remaining in other intersect with starred[1:]
|
||||
func intersectStar(starred, other Glob) bool {
|
||||
// starToken, nextToken are the token having FlagStar and the one that follows immediately after, respectively.
|
||||
var starToken, nextToken Token
|
||||
|
||||
starToken = starred[0]
|
||||
if len(starred) > 1 {
|
||||
nextToken = starred[1]
|
||||
}
|
||||
|
||||
for i, t := range other {
|
||||
// Start gobbl1ng up tokens in other while they match starToken.
|
||||
if nextToken != nil && Match(t, nextToken) {
|
||||
// When a token in other matches the token after starToken, stop gobbl1ng and try to match the two all the way.
|
||||
allTheWay := intersectNormal(starred[1:], other[i:])
|
||||
// If they match all the way, the Globs intersect.
|
||||
if allTheWay {
|
||||
return true
|
||||
} else {
|
||||
// If they don't match all the way, then the current token from other should still match starToken.
|
||||
if !Match(t, starToken) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Only move forward if this token can be gobbled up by starToken.
|
||||
if !Match(t, starToken) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If there was no token following starToken, and everything from other was gobbled, the Globs intersect.
|
||||
if nextToken == nil {
|
||||
return true
|
||||
}
|
||||
|
||||
// If everything from other was gobbles but there was a nextToken to match, they don't intersect.
|
||||
return false
|
||||
}
|
||||
43
vendor/github.com/yashtewari/glob-intersection/simplify.go
generated
vendored
Normal file
43
vendor/github.com/yashtewari/glob-intersection/simplify.go
generated
vendored
Normal file
@@ -0,0 +1,43 @@
|
||||
package gintersect
|
||||
|
||||
// Simplify accepts a Token slice and returns a equivalient Token slice that is shorter/simpler.
|
||||
// The only simplification currently applied is removing redundant flagged Tokens.
|
||||
// TODO: Remove unflagged Tokens next to equivalen Tokens with FlagPlus. Example: tt+t == t+
|
||||
func Simplify(tokens []Token) []Token {
|
||||
if len(tokens) == 0 {
|
||||
return tokens
|
||||
}
|
||||
simple := make([]Token, 1, len(tokens))
|
||||
simple[0] = tokens[0]
|
||||
|
||||
latest := simple[0]
|
||||
|
||||
for i := 1; i < len(tokens); i++ {
|
||||
handled := false
|
||||
// Possible simplifications to apply if there is a flag.
|
||||
if tokens[i].Flag() != FlagNone && latest.Flag() != FlagNone {
|
||||
// If the token contents are the same, then apply simplification.
|
||||
if tokens[i].Equal(latest) {
|
||||
var flag Flag
|
||||
// FlagPlus takes precedence, because:
|
||||
// t+t* == t+
|
||||
// t*t+ == t+
|
||||
if tokens[i].Flag() == FlagPlus || latest.Flag() == FlagPlus {
|
||||
flag = FlagPlus
|
||||
} else {
|
||||
flag = FlagStar
|
||||
}
|
||||
|
||||
simple[len(simple)-1].SetFlag(flag)
|
||||
handled = true
|
||||
}
|
||||
}
|
||||
|
||||
if !handled {
|
||||
latest = tokens[i]
|
||||
simple = append(simple, tokens[i])
|
||||
}
|
||||
}
|
||||
|
||||
return simple
|
||||
}
|
||||
84
vendor/github.com/yashtewari/glob-intersection/test_samples.go
generated
vendored
Normal file
84
vendor/github.com/yashtewari/glob-intersection/test_samples.go
generated
vendored
Normal file
@@ -0,0 +1,84 @@
|
||||
package gintersect
|
||||
|
||||
var (
|
||||
samplesInitialized = false
|
||||
|
||||
testCharacters map[rune]Token
|
||||
testCharactersPlus map[rune]Token
|
||||
testCharactersStar map[rune]Token
|
||||
|
||||
testDot, testDotPlus, testDotStar Token
|
||||
|
||||
testLowerAlphaSet, testLowerAlphaSetPlus, lowerAplhaSetStar Token
|
||||
testUpperAlphaSet, testUpperAlphaSetPlus, testUpperAlphaSetStar Token
|
||||
testNumSet, testNumSetPlus, testNumSetStar Token
|
||||
testSymbolSet, testSymbolSetPlus, testSymbolSetStar Token
|
||||
|
||||
testEmptySet Token
|
||||
)
|
||||
|
||||
func initializeTestSamples() {
|
||||
if samplesInitialized {
|
||||
return
|
||||
}
|
||||
|
||||
testCharacters, testCharactersPlus, testCharactersStar = make(map[rune]Token), make(map[rune]Token), make(map[rune]Token)
|
||||
|
||||
testDot, testDotPlus, testDotStar = NewDot(), NewDot(), NewDot()
|
||||
testDotPlus.SetFlag(FlagPlus)
|
||||
testDotStar.SetFlag(FlagStar)
|
||||
|
||||
var runes []rune
|
||||
runes = makeRunes('a', 'z')
|
||||
|
||||
testLowerAlphaSet, testLowerAlphaSetPlus, lowerAplhaSetStar = NewSet(runes), NewSet(runes), NewSet(runes)
|
||||
testLowerAlphaSetPlus.SetFlag(FlagPlus)
|
||||
lowerAplhaSetStar.SetFlag(FlagStar)
|
||||
|
||||
runes = makeRunes('A', 'Z')
|
||||
|
||||
testUpperAlphaSet, testUpperAlphaSetPlus, testUpperAlphaSetStar = NewSet(runes), NewSet(runes), NewSet(runes)
|
||||
testUpperAlphaSetPlus.SetFlag(FlagPlus)
|
||||
testUpperAlphaSetStar.SetFlag(FlagStar)
|
||||
|
||||
runes = makeRunes('0', '9')
|
||||
|
||||
testNumSet, testNumSetPlus, testNumSetStar = NewSet(runes), NewSet(runes), NewSet(runes)
|
||||
testNumSetPlus.SetFlag(FlagPlus)
|
||||
testNumSetStar.SetFlag(FlagStar)
|
||||
|
||||
runes = makeRunes('!', '/')
|
||||
|
||||
testSymbolSet, testSymbolSetPlus, testSymbolSetStar = NewSet(runes), NewSet(runes), NewSet(runes)
|
||||
testSymbolSetPlus.SetFlag(FlagPlus)
|
||||
testSymbolSetStar.SetFlag(FlagStar)
|
||||
|
||||
testEmptySet = NewSet([]rune{})
|
||||
|
||||
samplesInitialized = true
|
||||
}
|
||||
|
||||
func makeRunes(from rune, to rune) []rune {
|
||||
runes := make([]rune, 0, 30)
|
||||
for r := from; r <= to; r++ {
|
||||
runes = append(runes, r)
|
||||
addToCharacters(r)
|
||||
}
|
||||
|
||||
return runes
|
||||
}
|
||||
|
||||
func addToCharacters(r rune) {
|
||||
var t Token
|
||||
|
||||
t = NewCharacter(r)
|
||||
testCharacters[r] = t
|
||||
|
||||
t = NewCharacter(r)
|
||||
t.SetFlag(FlagPlus)
|
||||
testCharactersPlus[r] = t
|
||||
|
||||
t = NewCharacter(r)
|
||||
t.SetFlag(FlagStar)
|
||||
testCharactersStar[r] = t
|
||||
}
|
||||
251
vendor/github.com/yashtewari/glob-intersection/tokenize.go
generated
vendored
Normal file
251
vendor/github.com/yashtewari/glob-intersection/tokenize.go
generated
vendored
Normal file
@@ -0,0 +1,251 @@
|
||||
package gintersect
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
// Modifier is a special character that affects lexical analysis.
|
||||
type Modifier uint
|
||||
|
||||
const (
|
||||
ModifierBackslash Modifier = iota
|
||||
)
|
||||
|
||||
var (
|
||||
// Special runes.
|
||||
tokenTypeRunes = map[rune]TokenType{
|
||||
'.': TTDot,
|
||||
'[': TTSet,
|
||||
']': TTSet,
|
||||
}
|
||||
flagRunes = map[rune]Flag{
|
||||
'+': FlagPlus,
|
||||
'*': FlagStar,
|
||||
}
|
||||
modifierRunes = map[rune]Modifier{
|
||||
'\\': ModifierBackslash,
|
||||
}
|
||||
|
||||
// Errors.
|
||||
ErrInvalidInput = errors.New("the input provided is invalid")
|
||||
errEndOfInput = errors.New("reached end of input")
|
||||
)
|
||||
|
||||
// Tokenize converts a rune slice into a Token slice.
|
||||
func Tokenize(input []rune) ([]Token, error) {
|
||||
tokens := []Token{}
|
||||
for i, t, err := nextToken(0, input); err != errEndOfInput; i, t, err = nextToken(i, input) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
tokens = append(tokens, t)
|
||||
}
|
||||
|
||||
return tokens, nil
|
||||
}
|
||||
|
||||
// nextToken yields the Token starting at the given index of input, and newIndex at which the next Token should start.
|
||||
func nextToken(index int, input []rune) (newIndex int, token Token, err error) {
|
||||
var r rune
|
||||
var escaped bool
|
||||
|
||||
newIndex, r, escaped, err = nextRune(index, input)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if !escaped {
|
||||
if ttype, ok := tokenTypeRunes[r]; ok {
|
||||
switch ttype {
|
||||
case TTDot:
|
||||
token = NewDot()
|
||||
|
||||
case TTSet:
|
||||
if r == ']' {
|
||||
err = errors.Wrap(ErrInvalidInput, invalidInputMessage(input, newIndex, "set-close ']' with no preceding '['"))
|
||||
return
|
||||
}
|
||||
|
||||
newIndex, token, err = nextTokenSet(newIndex, input)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
default:
|
||||
panic(errors.Wrapf(errBadImplementation, "encountered unhandled token type: %v", ttype))
|
||||
}
|
||||
|
||||
} else if _, ok := flagRunes[r]; ok {
|
||||
err = errors.Wrap(ErrInvalidInput, invalidInputMessage(input, newIndex, "flag '%s' must be preceded by a non-flag", string(r)))
|
||||
return
|
||||
|
||||
} else if m, ok := modifierRunes[r]; ok {
|
||||
panic(errors.Wrapf(errBadImplementation, "encountered unhandled modifier: %v", m))
|
||||
} else {
|
||||
// Nothing special to do.
|
||||
token = NewCharacter(r)
|
||||
}
|
||||
} else {
|
||||
// Nothing special to do.
|
||||
token = NewCharacter(r)
|
||||
}
|
||||
|
||||
var f Flag
|
||||
newIndex, f, err = nextFlag(newIndex, input)
|
||||
if err == errEndOfInput {
|
||||
// Let this err be passed in the next cycle, after the current token is consumed.
|
||||
err = nil
|
||||
} else if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
token.SetFlag(f)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// nextTokenSet yields a Token having type TokenSet and starting at the given index of input.
|
||||
// The next Token/Flag should start at newIndex.
|
||||
func nextTokenSet(index int, input []rune) (newIndex int, t Token, err error) {
|
||||
var r, prev rune
|
||||
var escaped bool
|
||||
|
||||
runes := make([]rune, 0, 30)
|
||||
complete, prevExists := false, false
|
||||
|
||||
newIndex, r, escaped, err = nextRune(index, input)
|
||||
// If errEndOfInput is encountered, flow of control proceeds to the end of the function,
|
||||
// where the error is handled.
|
||||
if err != nil && err != errEndOfInput {
|
||||
return
|
||||
}
|
||||
|
||||
for ; err != errEndOfInput; newIndex, r, escaped, err = nextRune(newIndex, input) {
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if !escaped {
|
||||
// Handle symbols.
|
||||
switch r {
|
||||
case '-':
|
||||
if !prevExists {
|
||||
err = errors.Wrap(ErrInvalidInput, invalidInputMessage(input, newIndex, "range character '-' must be preceded by a Unicode character"))
|
||||
return
|
||||
}
|
||||
if newIndex >= len(input)-1 {
|
||||
err = errors.Wrap(ErrInvalidInput, invalidInputMessage(input, newIndex, "range character '-' must be followed by a Unicode character"))
|
||||
return
|
||||
}
|
||||
|
||||
// Get the next rune to know the extent of the range.
|
||||
newIndex, r, escaped, err = nextRune(newIndex, input)
|
||||
|
||||
if !escaped {
|
||||
if r == ']' || r == '-' {
|
||||
err = errors.Wrap(ErrInvalidInput, invalidInputMessage(input, newIndex, "range character '-' cannot be followed by a special symbol"))
|
||||
return
|
||||
}
|
||||
}
|
||||
if r < prev {
|
||||
err = errors.Wrap(ErrInvalidInput, invalidInputMessage(input, newIndex, "range is out of order: '%s' comes before '%s' in Unicode", string(r), string(prev)))
|
||||
return
|
||||
}
|
||||
|
||||
for x := prev; x <= r; x++ {
|
||||
runes = append(runes, x)
|
||||
}
|
||||
|
||||
prevExists = false
|
||||
|
||||
case ']':
|
||||
complete = true
|
||||
|
||||
// Nothing special to do.
|
||||
default:
|
||||
runes = append(runes, r)
|
||||
prev, prevExists = r, true
|
||||
}
|
||||
} else {
|
||||
// Nothing special to do.
|
||||
runes = append(runes, r)
|
||||
prev, prevExists = r, true
|
||||
}
|
||||
|
||||
// Don't move the index forward if the set is complete.
|
||||
if complete {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// End of input is reached before the set completes.
|
||||
if !complete {
|
||||
err = errors.Wrap(ErrInvalidInput, invalidInputMessage(input, newIndex, "found [ without matching ]"))
|
||||
} else {
|
||||
t = NewSet(runes)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// nextFlag yields the Flag starting at the given index of input, if any.
|
||||
// The next Token should start at newIndex.
|
||||
func nextFlag(index int, input []rune) (newIndex int, f Flag, err error) {
|
||||
var escaped, ok bool
|
||||
var r rune
|
||||
|
||||
f = FlagNone
|
||||
|
||||
newIndex, r, escaped, err = nextRune(index, input)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if !escaped {
|
||||
// Revert back to index for later consumption.
|
||||
if f, ok = flagRunes[r]; !ok {
|
||||
newIndex = index
|
||||
}
|
||||
} else {
|
||||
// Revert back to index for later consumption.
|
||||
newIndex = index
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// nextRune yields the rune starting (with modifiers) at the given index of input, with boolean escaped describing whether the rune is escaped.
|
||||
// The next rune should start at newIndex.
|
||||
func nextRune(index int, input []rune) (newIndex int, r rune, escaped bool, err error) {
|
||||
if index >= len(input) {
|
||||
newIndex = index
|
||||
err = errEndOfInput
|
||||
return
|
||||
}
|
||||
|
||||
if m, ok := modifierRunes[input[index]]; ok {
|
||||
switch m {
|
||||
|
||||
case ModifierBackslash:
|
||||
if index < len(input)-1 {
|
||||
newIndex, r, escaped = index+2, input[index+1], true
|
||||
} else if index == len(input)-1 {
|
||||
err = errors.Wrap(ErrInvalidInput, invalidInputMessage(input, index, "input ends with a \\ (escape) character"))
|
||||
}
|
||||
default:
|
||||
panic(errors.Wrapf(errBadImplementation, "encountered unhandled modifier: %v", m))
|
||||
}
|
||||
} else {
|
||||
newIndex, r, escaped = index+1, input[index], false
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// invalidInputMessage wraps the message describing invalid input with the input itself and index at which it is invalid.
|
||||
func invalidInputMessage(input []rune, index int, message string, args ...interface{}) string {
|
||||
return fmt.Sprintf("input:%s, pos:%d, %s", string(input), index, fmt.Sprintf(message, args...))
|
||||
}
|
||||
Reference in New Issue
Block a user