glob/parser.go

128 lines
2.4 KiB
Go

package glob
import (
"errors"
"fmt"
"github.com/gobwas/glob/match"
)
func parseAll(source, separators string) ([]token, error) {
lexer := newLexer(source)
var tokens []token
for parser := parserMain; parser != nil; {
var err error
tokens, parser, err = parser(lexer, separators)
if err != nil {
return nil, err
}
}
return tokens, nil
}
type parseFn func(*lexer, string) ([]token, parseFn, error)
func parserMain(lexer *lexer, separators string) ([]token, parseFn, error) {
var (
prev *token
tokens []token
)
for item := lexer.nextItem(); ; {
var t token
if item.t == item_eof {
break
}
switch item.t {
case item_eof:
return tokens, nil, nil
case item_error:
return nil, nil, errors.New(item.s)
case item_text:
t = token{match.Raw{item.s}, item.s}
case item_any:
if prev != nil && prev.matcher.Kind() == match.KindMultipleSeparated {
// remove simple any and replace it with super_any
tokens = tokens[:len(tokens)-1]
t = token{match.Any{""}, item.s}
} else {
t = token{match.Any{separators}, item.s}
}
case item_single:
t = token{match.Single{separators}, item.s}
case item_range_open:
return tokens, parserRange, nil
}
prev = &t
}
return tokens, nil, nil
}
func parserRange(lexer *lexer, separators string) ([]token, parseFn, error) {
var (
not bool
lo rune
hi rune
chars string
)
for item := lexer.nextItem(); ; {
switch item.t {
case item_eof:
return nil, nil, errors.New("unexpected end")
case item_error:
return nil, nil, errors.New(item.s)
case item_range_not:
not = true
case item_range_lo:
r := []rune(item.s)
if len(r) != 1 {
return nil, nil, fmt.Errorf("unexpected length of lo character")
}
lo = r[0]
case item_range_minus:
//
case item_range_hi:
r := []rune(item.s)
if len(r) != 1 {
return nil, nil, fmt.Errorf("unexpected length of hi character")
}
hi = r[0]
case item_range_chars:
chars = item.s
case item_range_close:
isRange := lo != 0 && hi != 0
isChars := chars == ""
if !(isChars != isRange) {
return nil, nil, fmt.Errorf("parse error: unexpected lo, hi, chars in range")
}
if isRange {
return []token{token{match.Between{lo, hi, not}, ""}}, parserMain, nil
} else {
return []token{token{match.RangeList{chars, not}, ""}}, parserMain, nil
}
}
}
}