github.com/yankunsam/loki/v2@v2.6.3-0.20220817130409-389df5235c27/pkg/logql/log/pattern/ast.go (about) 1 package pattern 2 3 import ( 4 "fmt" 5 "unicode/utf8" 6 ) 7 8 type node interface { 9 fmt.Stringer 10 } 11 12 type expr []node 13 14 func (e expr) hasCapture() bool { 15 return e.captureCount() != 0 16 } 17 18 func (e expr) validate() error { 19 if !e.hasCapture() { 20 return ErrNoCapture 21 } 22 // Consecutive captures are not allowed. 23 for i, n := range e { 24 if i+1 >= len(e) { 25 break 26 } 27 if _, ok := n.(capture); ok { 28 if _, ok := e[i+1].(capture); ok { 29 return fmt.Errorf("found consecutive capture '%s': %w", n.String()+e[i+1].String(), ErrInvalidExpr) 30 } 31 } 32 } 33 34 caps := e.captures() 35 uniq := map[string]struct{}{} 36 for _, c := range caps { 37 if _, ok := uniq[c]; ok { 38 return fmt.Errorf("duplicate capture name (%s): %w", c, ErrInvalidExpr) 39 } 40 uniq[c] = struct{}{} 41 } 42 return nil 43 } 44 45 func (e expr) captures() (captures []string) { 46 for _, n := range e { 47 if c, ok := n.(capture); ok && !c.isUnamed() { 48 captures = append(captures, c.Name()) 49 } 50 } 51 return 52 } 53 54 func (e expr) captureCount() (count int) { 55 return len(e.captures()) 56 } 57 58 type capture string 59 60 func (c capture) String() string { 61 return "<" + string(c) + ">" 62 } 63 64 func (c capture) Name() string { 65 return string(c) 66 } 67 68 func (c capture) isUnamed() bool { 69 return string(c) == underscore 70 } 71 72 type literals []byte 73 74 func (l literals) String() string { 75 return string(l) 76 } 77 78 func runesToLiterals(rs []rune) literals { 79 res := make([]byte, len(rs)*utf8.UTFMax) 80 count := 0 81 for _, r := range rs { 82 count += utf8.EncodeRune(res[count:], r) 83 } 84 res = res[:count] 85 return res 86 }