github.com/tada-team/tdproto@v1.51.57/tdmarkup/scanner.go (about) 1 package tdmarkup 2 3 import ( 4 "fmt" 5 "strings" 6 ) 7 8 const EOF = rune(0) 9 10 func NewScanner(text string) *Scanner { 11 return &Scanner{runes: []rune(text)} 12 } 13 14 type Scanner struct { 15 runes []rune 16 pos int 17 } 18 19 // Current position 20 func (s Scanner) Position() int { return s.pos } 21 22 // Length in runes 23 func (s Scanner) Length() int { return len(s.runes) } 24 25 // Change current position 26 func (s *Scanner) Rewind(i int) { 27 if i < 0 || i > s.Length() { 28 panic(fmt.Errorf("index must be between 0 and %d, %d given", s.Length(), i)) 29 } 30 s.pos = i 31 } 32 33 // Diff 34 func (s Scanner) Since(i int) int { 35 if s.pos < i { 36 panic(fmt.Errorf("index must be between less than %d, %d given", s.pos, i)) 37 } 38 return s.pos - i 39 } 40 41 // Number of runes 42 func (s Scanner) Rest() int { 43 return s.Length() - s.pos 44 } 45 46 // Previous rune 47 func (s Scanner) Prev() rune { 48 if s.pos < 2 { 49 return EOF 50 } 51 return s.runes[s.pos-2] 52 } 53 54 // Current rune: FIXME: strange logic, but it works 55 func (s Scanner) Current() rune { 56 if s.pos < 1 { 57 return EOF 58 } 59 return s.runes[s.pos-1] 60 } 61 62 // Returns next rune without rewind 63 func (s Scanner) Next() rune { 64 if s.pos >= s.Length() { 65 return EOF 66 } 67 return s.runes[s.pos] 68 } 69 70 // Returns next rune and rewind current position 71 func (s *Scanner) TakeNext() rune { 72 ch := s.Next() 73 if ch != EOF { 74 s.pos++ 75 } 76 return ch 77 } 78 79 func (s *MarkupScanner) ScanUntil(cl []rune) string { 80 start := s.Position() 81 82 var b strings.Builder 83 //b.Grow(s.Length() - start) // slower! 84 85 switch len(cl) { 86 case 0: 87 panic("invalid close operator length: 0") 88 case 1: 89 r := cl[0] 90 for s.Rest() > 0 { 91 b.WriteRune(s.TakeNext()) 92 if s.Current() == r { 93 return b.String() 94 } 95 } 96 s.Rewind(start) 97 return "" 98 default: 99 if s.Rest() < len(cl) { 100 return "" 101 } 102 for _, r := range cl { 103 if s.Next() != r { 104 s.Rewind(start) 105 return "" 106 } 107 b.WriteRune(s.TakeNext()) 108 } 109 } 110 111 return b.String() 112 }