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 }