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  }