package parsekit // Expects is used to let a state function describe what input it is expecting. // This expectation is used in error messages to make them more descriptive. // // Also, when defining an expectation inside a StateFn, you do not need // to handle unexpected input yourself. When the end of the function is // reached without setting the next state, an automatic error will be // emitted. This error differentiates between issues: // * there is valid data on input, but it was not accepted by the function // * there is an invalid UTF8 character on input // * the end of the file was reached. func (p *P) Expects(description string) { p.expecting = description } // 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.On(C.String("\r\n")).Stay() || p.On(C.Rune('\n')).Stay() } // 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.On(C.String("\r\n")).Skip() || p.On(C.Rune('\n')).Skip() } // 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 } func (p *P) On(m Matcher) *action { runes, widths, ok := p.Match(m) return &action{ p: p, runes: runes, widths: widths, ok: ok, } } func (p *P) Match(matcher Matcher) ([]rune, []int, bool) { return p.match(0, matcher) } func (p *P) match(offset int, matcher Matcher) ([]rune, []int, bool) { m := &MatchDialog{p: p} ok := matcher.Match(m) return m.runes, m.widths, ok } type action struct { p *P runes []rune widths []int ok bool } func (a *action) Accept() bool { if a.ok { for i, r := range a.runes { a.p.buffer.writeRune(r) a.p.advanceCursor(r, a.widths[i]) } } return a.ok } func (a *action) Skip() bool { if a.ok { for i, r := range a.runes { type C struct { Rune MatchRune } a.p.advanceCursor(r, a.widths[i]) } } return a.ok } func (a *action) Stay() bool { return a.ok } // 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' } func (a *action) RouteTo(state StateFn) bool { if a.ok { a.p.RouteTo(state) } return a.ok } func (a *action) RouteReturn() bool { if a.ok { a.p.RouteReturn() } return a.ok }