package parsekit import ( "strings" ) // AtEndOfFile returns true when there is no more data available in the input. func (p *P) AtEndOfFile() bool { return p.pos >= p.len } // AtEndOfLine returns true when the cursor is either at the end of the line // or at the end of the file. The cursor is not moved to a new position // by this method. func (p *P) AtEndOfLine() bool { return p.AtEndOfFile() || p.Upcoming("\r", "\n") || p.Upcoming("\n") } // SkipEndOfLine returns true when the cursor is either at the end of the line // or at the end of the file. Additionally, when not at the end of the file, // the cursor is moved forward to beyond the newline. func (p *P) SkipEndOfLine() bool { return p.AtEndOfFile() || p.SkipMatching("\r", "\n") || p.SkipMatching("\n") } // AcceptEndOfLine returns true when the cursor is either at the end of the line // or at the end of the file. When not at the end of the file, a normalized // newline (only a '\n' character, even with '\r\n' on the input) // is added to the string buffer. func (p *P) AcceptEndOfLine() bool { if p.AtEndOfFile() { return true } if p.SkipEndOfLine() { p.buffer.writeRune('\n') return true } return false } // Match checks if the upcoming runes satisfy all provided patterns. // It returns a slice of runes that were found, a slice containing // their respective byte widths, and a boolean indicating whether // or not all provided patterns were satisfied by the input data. func (p *P) Match(patterns ...string) ([]rune, []int, bool) { peeked, widths, ok := p.peekMulti(len(patterns)) if ok { for i, r := range patterns { if strings.IndexRune(r, peeked[i]) < 0 { return peeked, widths, false } } return peeked, widths, true } return peeked, widths, false } // Upcoming checks if the upcoming runes satisfy all provided patterns. // Returns true if all provided patterns are satisfied. // This is basically the same as the Match method, but with only // the boolean return parameter for programmer convenciency. func (p *P) Upcoming(patterns ...string) bool { _, _, ok := p.Match(patterns...) return ok } // AcceptAny adds the next rune from the input to the string buffer. // If no rune could be read (end of file or invalid UTF8 data), // then false is returned. func (p *P) AcceptAny() bool { if r, ok := p.next(); ok { p.buffer.writeRune(r) return true } return false } // AcceptMatching adds the next runes to the string buffer, but only // if the upcoming runes satisfy the provided patterns. // When runes were added then true is returned, false otherwise. func (p *P) AcceptMatching(patterns ...string) bool { return p.progress(func(r rune) { p.buffer.writeRune(r) }, patterns...) } // AcceptConsecutive adds consecutive runes from the input to the string // buffer, as long as they exist in the pattern. // If any runes were added then true is returned, false otherwise. func (p *P) AcceptConsecutive(pattern string) bool { accepted := false for p.AcceptMatching(pattern) { accepted = true } return accepted } // SkipMatching skips runes, but only when all provided patterns are satisfied. // Returns true when one or more runes were skipped. func (p *P) SkipMatching(patterns ...string) bool { if runes, widths, ok := p.Match(patterns...); ok { for i, r := range runes { p.advanceCursor(r, widths[i]) } return true } return false } // SkipConsecutive skips consecutive runes from the provided pattern. // Returns true when one or more runes were skipped. func (p *P) SkipConsecutive(pattern string) bool { didSkip := false for p.SkipMatching(pattern) { didSkip = true } return didSkip }