mirror of https://github.com/gobwas/glob.git
333 lines
7.2 KiB
Go
333 lines
7.2 KiB
Go
|
package parser
|
||
|
|
||
|
import (
|
||
|
"fmt"
|
||
|
"github.com/gobwas/glob/lexer"
|
||
|
"reflect"
|
||
|
"testing"
|
||
|
)
|
||
|
|
||
|
type stubLexer struct {
|
||
|
tokens []lexer.Token
|
||
|
pos int
|
||
|
}
|
||
|
|
||
|
func (s *stubLexer) Next() (ret lexer.Token) {
|
||
|
if s.pos == len(s.tokens) {
|
||
|
return lexer.Token{lexer.EOF, ""}
|
||
|
}
|
||
|
ret = s.tokens[s.pos]
|
||
|
s.pos++
|
||
|
return
|
||
|
}
|
||
|
|
||
|
func TestParseString(t *testing.T) {
|
||
|
for id, test := range []struct {
|
||
|
tokens []lexer.Token
|
||
|
tree Node
|
||
|
}{
|
||
|
{
|
||
|
//pattern: "abc",
|
||
|
tokens: []lexer.Token{
|
||
|
lexer.Token{lexer.Text, "abc"},
|
||
|
lexer.Token{lexer.EOF, ""},
|
||
|
},
|
||
|
tree: &PatternNode{
|
||
|
node: node{
|
||
|
children: []Node{
|
||
|
&TextNode{Text: "abc"},
|
||
|
},
|
||
|
},
|
||
|
},
|
||
|
},
|
||
|
{
|
||
|
//pattern: "a*c",
|
||
|
tokens: []lexer.Token{
|
||
|
lexer.Token{lexer.Text, "a"},
|
||
|
lexer.Token{lexer.Any, "*"},
|
||
|
lexer.Token{lexer.Text, "c"},
|
||
|
lexer.Token{lexer.EOF, ""},
|
||
|
},
|
||
|
tree: &PatternNode{
|
||
|
node: node{
|
||
|
children: []Node{
|
||
|
&TextNode{Text: "a"},
|
||
|
&AnyNode{},
|
||
|
&TextNode{Text: "c"},
|
||
|
},
|
||
|
},
|
||
|
},
|
||
|
},
|
||
|
{
|
||
|
//pattern: "a**c",
|
||
|
tokens: []lexer.Token{
|
||
|
lexer.Token{lexer.Text, "a"},
|
||
|
lexer.Token{lexer.Super, "**"},
|
||
|
lexer.Token{lexer.Text, "c"},
|
||
|
lexer.Token{lexer.EOF, ""},
|
||
|
},
|
||
|
tree: &PatternNode{
|
||
|
node: node{
|
||
|
children: []Node{
|
||
|
&TextNode{Text: "a"},
|
||
|
&SuperNode{},
|
||
|
&TextNode{Text: "c"},
|
||
|
},
|
||
|
},
|
||
|
},
|
||
|
},
|
||
|
{
|
||
|
//pattern: "a?c",
|
||
|
tokens: []lexer.Token{
|
||
|
lexer.Token{lexer.Text, "a"},
|
||
|
lexer.Token{lexer.Single, "?"},
|
||
|
lexer.Token{lexer.Text, "c"},
|
||
|
lexer.Token{lexer.EOF, ""},
|
||
|
},
|
||
|
tree: &PatternNode{
|
||
|
node: node{
|
||
|
children: []Node{
|
||
|
&TextNode{Text: "a"},
|
||
|
&SingleNode{},
|
||
|
&TextNode{Text: "c"},
|
||
|
},
|
||
|
},
|
||
|
},
|
||
|
},
|
||
|
{
|
||
|
//pattern: "[!a-z]",
|
||
|
tokens: []lexer.Token{
|
||
|
lexer.Token{lexer.RangeOpen, "["},
|
||
|
lexer.Token{lexer.Not, "!"},
|
||
|
lexer.Token{lexer.RangeLo, "a"},
|
||
|
lexer.Token{lexer.RangeBetween, "-"},
|
||
|
lexer.Token{lexer.RangeHi, "z"},
|
||
|
lexer.Token{lexer.RangeClose, "]"},
|
||
|
lexer.Token{lexer.EOF, ""},
|
||
|
},
|
||
|
tree: &PatternNode{
|
||
|
node: node{
|
||
|
children: []Node{
|
||
|
&RangeNode{Lo: 'a', Hi: 'z', Not: true},
|
||
|
},
|
||
|
},
|
||
|
},
|
||
|
},
|
||
|
{
|
||
|
//pattern: "[az]",
|
||
|
tokens: []lexer.Token{
|
||
|
lexer.Token{lexer.RangeOpen, "["},
|
||
|
lexer.Token{lexer.Text, "az"},
|
||
|
lexer.Token{lexer.RangeClose, "]"},
|
||
|
lexer.Token{lexer.EOF, ""},
|
||
|
},
|
||
|
tree: &PatternNode{
|
||
|
node: node{
|
||
|
children: []Node{
|
||
|
&ListNode{Chars: "az"},
|
||
|
},
|
||
|
},
|
||
|
},
|
||
|
},
|
||
|
{
|
||
|
//pattern: "{a,z}",
|
||
|
tokens: []lexer.Token{
|
||
|
lexer.Token{lexer.TermsOpen, "{"},
|
||
|
lexer.Token{lexer.Text, "a"},
|
||
|
lexer.Token{lexer.Separator, ","},
|
||
|
lexer.Token{lexer.Text, "z"},
|
||
|
lexer.Token{lexer.TermsClose, "}"},
|
||
|
lexer.Token{lexer.EOF, ""},
|
||
|
},
|
||
|
tree: &PatternNode{
|
||
|
node: node{
|
||
|
children: []Node{
|
||
|
&AnyOfNode{node: node{children: []Node{
|
||
|
&PatternNode{
|
||
|
node: node{children: []Node{
|
||
|
&TextNode{Text: "a"},
|
||
|
}},
|
||
|
},
|
||
|
&PatternNode{
|
||
|
node: node{children: []Node{
|
||
|
&TextNode{Text: "z"},
|
||
|
}},
|
||
|
},
|
||
|
}}},
|
||
|
},
|
||
|
},
|
||
|
},
|
||
|
},
|
||
|
{
|
||
|
//pattern: "/{z,ab}*",
|
||
|
tokens: []lexer.Token{
|
||
|
lexer.Token{lexer.Text, "/"},
|
||
|
lexer.Token{lexer.TermsOpen, "{"},
|
||
|
lexer.Token{lexer.Text, "z"},
|
||
|
lexer.Token{lexer.Separator, ","},
|
||
|
lexer.Token{lexer.Text, "ab"},
|
||
|
lexer.Token{lexer.TermsClose, "}"},
|
||
|
lexer.Token{lexer.Any, "*"},
|
||
|
lexer.Token{lexer.EOF, ""},
|
||
|
},
|
||
|
tree: &PatternNode{
|
||
|
node: node{
|
||
|
children: []Node{
|
||
|
&TextNode{Text: "/"},
|
||
|
&AnyOfNode{node: node{children: []Node{
|
||
|
&PatternNode{
|
||
|
node: node{children: []Node{
|
||
|
&TextNode{Text: "z"},
|
||
|
}},
|
||
|
},
|
||
|
&PatternNode{
|
||
|
node: node{children: []Node{
|
||
|
&TextNode{Text: "ab"},
|
||
|
}},
|
||
|
},
|
||
|
}}},
|
||
|
&AnyNode{},
|
||
|
},
|
||
|
},
|
||
|
},
|
||
|
},
|
||
|
{
|
||
|
//pattern: "{a,{x,y},?,[a-z],[!qwe]}",
|
||
|
tokens: []lexer.Token{
|
||
|
lexer.Token{lexer.TermsOpen, "{"},
|
||
|
lexer.Token{lexer.Text, "a"},
|
||
|
lexer.Token{lexer.Separator, ","},
|
||
|
lexer.Token{lexer.TermsOpen, "{"},
|
||
|
lexer.Token{lexer.Text, "x"},
|
||
|
lexer.Token{lexer.Separator, ","},
|
||
|
lexer.Token{lexer.Text, "y"},
|
||
|
lexer.Token{lexer.TermsClose, "}"},
|
||
|
lexer.Token{lexer.Separator, ","},
|
||
|
lexer.Token{lexer.Single, "?"},
|
||
|
lexer.Token{lexer.Separator, ","},
|
||
|
lexer.Token{lexer.RangeOpen, "["},
|
||
|
lexer.Token{lexer.RangeLo, "a"},
|
||
|
lexer.Token{lexer.RangeBetween, "-"},
|
||
|
lexer.Token{lexer.RangeHi, "z"},
|
||
|
lexer.Token{lexer.RangeClose, "]"},
|
||
|
lexer.Token{lexer.Separator, ","},
|
||
|
lexer.Token{lexer.RangeOpen, "["},
|
||
|
lexer.Token{lexer.Not, "!"},
|
||
|
lexer.Token{lexer.Text, "qwe"},
|
||
|
lexer.Token{lexer.RangeClose, "]"},
|
||
|
lexer.Token{lexer.TermsClose, "}"},
|
||
|
lexer.Token{lexer.EOF, ""},
|
||
|
},
|
||
|
tree: &PatternNode{
|
||
|
node: node{
|
||
|
children: []Node{
|
||
|
&AnyOfNode{node: node{children: []Node{
|
||
|
&PatternNode{
|
||
|
node: node{children: []Node{
|
||
|
&TextNode{Text: "a"},
|
||
|
}},
|
||
|
},
|
||
|
&PatternNode{
|
||
|
node: node{children: []Node{
|
||
|
&AnyOfNode{node: node{children: []Node{
|
||
|
&PatternNode{
|
||
|
node: node{children: []Node{
|
||
|
&TextNode{Text: "x"},
|
||
|
}},
|
||
|
},
|
||
|
&PatternNode{
|
||
|
node: node{children: []Node{
|
||
|
&TextNode{Text: "y"},
|
||
|
}},
|
||
|
},
|
||
|
}}},
|
||
|
}},
|
||
|
},
|
||
|
&PatternNode{
|
||
|
node: node{children: []Node{
|
||
|
&SingleNode{},
|
||
|
}},
|
||
|
},
|
||
|
&PatternNode{
|
||
|
node: node{
|
||
|
children: []Node{
|
||
|
&RangeNode{Lo: 'a', Hi: 'z', Not: false},
|
||
|
},
|
||
|
},
|
||
|
},
|
||
|
&PatternNode{
|
||
|
node: node{
|
||
|
children: []Node{
|
||
|
&ListNode{Chars: "qwe", Not: true},
|
||
|
},
|
||
|
},
|
||
|
},
|
||
|
}}},
|
||
|
},
|
||
|
},
|
||
|
},
|
||
|
},
|
||
|
} {
|
||
|
lexer := &stubLexer{tokens: test.tokens}
|
||
|
result, err := Parse(lexer)
|
||
|
if err != nil {
|
||
|
t.Errorf("[%d] unexpected error: %s", id, err)
|
||
|
}
|
||
|
if !reflect.DeepEqual(test.tree, result) {
|
||
|
t.Errorf("[%d] Parse():\nact:\t%s\nexp:\t%s\n", id, result, test.tree)
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
const abstractNodeImpl = "nodeImpl"
|
||
|
|
||
|
func nodeEqual(a, b Node) error {
|
||
|
if (a == nil || b == nil) && a != b {
|
||
|
return fmt.Errorf("nodes are not equal: exp %s, act %s", a, b)
|
||
|
}
|
||
|
|
||
|
aValue, bValue := reflect.Indirect(reflect.ValueOf(a)), reflect.Indirect(reflect.ValueOf(b))
|
||
|
aType, bType := aValue.Type(), bValue.Type()
|
||
|
if aType != bType {
|
||
|
return fmt.Errorf("nodes are not equal: exp %s, act %s", aValue.Type(), bValue.Type())
|
||
|
}
|
||
|
|
||
|
for i := 0; i < aType.NumField(); i++ {
|
||
|
var eq bool
|
||
|
|
||
|
f := aType.Field(i).Name
|
||
|
if f == abstractNodeImpl {
|
||
|
continue
|
||
|
}
|
||
|
|
||
|
af, bf := aValue.FieldByName(f), bValue.FieldByName(f)
|
||
|
|
||
|
switch af.Kind() {
|
||
|
case reflect.String:
|
||
|
eq = af.String() == bf.String()
|
||
|
case reflect.Bool:
|
||
|
eq = af.Bool() == bf.Bool()
|
||
|
default:
|
||
|
eq = fmt.Sprint(af) == fmt.Sprint(bf)
|
||
|
}
|
||
|
|
||
|
if !eq {
|
||
|
return fmt.Errorf("nodes<%s> %q fields are not equal: exp %q, act %q", aType, f, af, bf)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
for i, aDesc := range a.Children() {
|
||
|
if len(b.Children())-1 < i {
|
||
|
return fmt.Errorf("node does not have enough children (got %d children, wanted %d-th token)", len(b.Children()), i)
|
||
|
}
|
||
|
|
||
|
bDesc := b.Children()[i]
|
||
|
|
||
|
if err := nodeEqual(aDesc, bDesc); err != nil {
|
||
|
return err
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return nil
|
||
|
}
|