87 lines
2.7 KiB
Go
87 lines
2.7 KiB
Go
package parsekit
|
|
|
|
import (
|
|
"unicode/utf8"
|
|
)
|
|
|
|
// peek returns but does not advance the cursor to the next rune(s) in the input.
|
|
// Returns the rune, its width in bytes and a boolean.
|
|
// The boolean will be false in case no upcoming rune can be peeked
|
|
// (end of data or invalid UTF8 character).
|
|
func (p *P) peek(offsetInBytes int) (rune, int, bool) {
|
|
r, w := utf8.DecodeRuneInString(p.input[p.pos+offsetInBytes:])
|
|
return handleRuneError(r, w)
|
|
}
|
|
|
|
// peekMulti takes a peek at multiple upcoming runes in the input.
|
|
// Returns a slice of runes, a slice containing their respective
|
|
// widths in bytes and a boolean.
|
|
// The boolean will be false in case less runes can be peeked than
|
|
// the requested amount (end of data or invalid UTF8 character).
|
|
func (p *P) peekMulti(amount int) ([]rune, []int, bool) {
|
|
var runes []rune
|
|
var widths []int
|
|
offset := 0
|
|
for i := 0; i < amount; i++ {
|
|
r, w := utf8.DecodeRuneInString(p.input[p.pos+offset:])
|
|
r, w, ok := handleRuneError(r, w)
|
|
runes = append(runes, r)
|
|
widths = append(widths, w)
|
|
offset += w
|
|
if !ok {
|
|
return runes, widths, false
|
|
}
|
|
}
|
|
return runes, widths, true
|
|
}
|
|
|
|
// progress moves the cursor forward in the input, returning one rune
|
|
// for every specified pattern. The cursor will only be moved forward when
|
|
// all requested patterns can be satisfied.
|
|
// Returns true when all patterns were satisfied and the cursor was
|
|
// moved forward, false otherwise.
|
|
// A callback function can be provided to specify what to do with
|
|
// the runes that are encountered in the input.
|
|
func (p *P) progress(callback func(rune), patterns ...interface{}) bool {
|
|
if runes, widths, ok := p.Match(patterns...); ok {
|
|
for i, r := range runes {
|
|
callback(r)
|
|
p.advanceCursor(r, widths[i])
|
|
}
|
|
return true
|
|
}
|
|
return false
|
|
}
|
|
|
|
// advanceCursor advances the rune cursor one position in the
|
|
// input data. While doing so, it keeps tracks of newlines,
|
|
// so we can report on row + column positions on error.
|
|
func (p *P) advanceCursor(r rune, w int) {
|
|
p.pos += w
|
|
if p.newline {
|
|
p.cursorColumn = 0
|
|
p.cursorRow++
|
|
} else {
|
|
p.cursorColumn++
|
|
}
|
|
p.newline = r == '\n'
|
|
}
|
|
|
|
// handleRuneError is used to normale rune value in case of errors.
|
|
// When an error occurs, then utf8.RuneError will be in the rune.
|
|
// This can however indicate one of two situations:
|
|
// * w == 0: end of file is reached
|
|
// * w == 1: invalid UTF character on input
|
|
// This function lets these two cases return respectively the
|
|
// package's own EOF or INVALID runes, to make it easy for client
|
|
// code to distinct between these two cases.
|
|
func handleRuneError(r rune, w int) (rune, int, bool) {
|
|
if r == utf8.RuneError {
|
|
if w == 0 {
|
|
return EOF, 0, false
|
|
}
|
|
return INVALID, w, false
|
|
}
|
|
return r, w, true
|
|
}
|