forked from Shiloh/githaven
6ddd3b0b47
* Fix validate() function to handle errors in embedded anon structs * Implement webhook branch filter See #2025, #3998.
158 lines
2.9 KiB
Go
Vendored
158 lines
2.9 KiB
Go
Vendored
package ast
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"github.com/gobwas/glob/syntax/lexer"
|
|
"unicode/utf8"
|
|
)
|
|
|
|
type Lexer interface {
|
|
Next() lexer.Token
|
|
}
|
|
|
|
type parseFn func(*Node, Lexer) (parseFn, *Node, error)
|
|
|
|
func Parse(lexer Lexer) (*Node, error) {
|
|
var parser parseFn
|
|
|
|
root := NewNode(KindPattern, nil)
|
|
|
|
var (
|
|
tree *Node
|
|
err error
|
|
)
|
|
for parser, tree = parserMain, root; parser != nil; {
|
|
parser, tree, err = parser(tree, lexer)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
|
|
return root, nil
|
|
}
|
|
|
|
func parserMain(tree *Node, lex Lexer) (parseFn, *Node, error) {
|
|
for {
|
|
token := lex.Next()
|
|
switch token.Type {
|
|
case lexer.EOF:
|
|
return nil, tree, nil
|
|
|
|
case lexer.Error:
|
|
return nil, tree, errors.New(token.Raw)
|
|
|
|
case lexer.Text:
|
|
Insert(tree, NewNode(KindText, Text{token.Raw}))
|
|
return parserMain, tree, nil
|
|
|
|
case lexer.Any:
|
|
Insert(tree, NewNode(KindAny, nil))
|
|
return parserMain, tree, nil
|
|
|
|
case lexer.Super:
|
|
Insert(tree, NewNode(KindSuper, nil))
|
|
return parserMain, tree, nil
|
|
|
|
case lexer.Single:
|
|
Insert(tree, NewNode(KindSingle, nil))
|
|
return parserMain, tree, nil
|
|
|
|
case lexer.RangeOpen:
|
|
return parserRange, tree, nil
|
|
|
|
case lexer.TermsOpen:
|
|
a := NewNode(KindAnyOf, nil)
|
|
Insert(tree, a)
|
|
|
|
p := NewNode(KindPattern, nil)
|
|
Insert(a, p)
|
|
|
|
return parserMain, p, nil
|
|
|
|
case lexer.Separator:
|
|
p := NewNode(KindPattern, nil)
|
|
Insert(tree.Parent, p)
|
|
|
|
return parserMain, p, nil
|
|
|
|
case lexer.TermsClose:
|
|
return parserMain, tree.Parent.Parent, nil
|
|
|
|
default:
|
|
return nil, tree, fmt.Errorf("unexpected token: %s", token)
|
|
}
|
|
}
|
|
return nil, tree, fmt.Errorf("unknown error")
|
|
}
|
|
|
|
func parserRange(tree *Node, lex Lexer) (parseFn, *Node, error) {
|
|
var (
|
|
not bool
|
|
lo rune
|
|
hi rune
|
|
chars string
|
|
)
|
|
for {
|
|
token := lex.Next()
|
|
switch token.Type {
|
|
case lexer.EOF:
|
|
return nil, tree, errors.New("unexpected end")
|
|
|
|
case lexer.Error:
|
|
return nil, tree, errors.New(token.Raw)
|
|
|
|
case lexer.Not:
|
|
not = true
|
|
|
|
case lexer.RangeLo:
|
|
r, w := utf8.DecodeRuneInString(token.Raw)
|
|
if len(token.Raw) > w {
|
|
return nil, tree, fmt.Errorf("unexpected length of lo character")
|
|
}
|
|
lo = r
|
|
|
|
case lexer.RangeBetween:
|
|
//
|
|
|
|
case lexer.RangeHi:
|
|
r, w := utf8.DecodeRuneInString(token.Raw)
|
|
if len(token.Raw) > w {
|
|
return nil, tree, fmt.Errorf("unexpected length of lo character")
|
|
}
|
|
|
|
hi = r
|
|
|
|
if hi < lo {
|
|
return nil, tree, fmt.Errorf("hi character '%s' should be greater than lo '%s'", string(hi), string(lo))
|
|
}
|
|
|
|
case lexer.Text:
|
|
chars = token.Raw
|
|
|
|
case lexer.RangeClose:
|
|
isRange := lo != 0 && hi != 0
|
|
isChars := chars != ""
|
|
|
|
if isChars == isRange {
|
|
return nil, tree, fmt.Errorf("could not parse range")
|
|
}
|
|
|
|
if isRange {
|
|
Insert(tree, NewNode(KindRange, Range{
|
|
Lo: lo,
|
|
Hi: hi,
|
|
Not: not,
|
|
}))
|
|
} else {
|
|
Insert(tree, NewNode(KindList, List{
|
|
Chars: chars,
|
|
Not: not,
|
|
}))
|
|
}
|
|
|
|
return parserMain, tree, nil
|
|
}
|
|
}
|
|
}
|