121 lines
3.6 KiB
Go
121 lines
3.6 KiB
Go
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
|
|
}
|