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 StateHandler, 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 } // On checks if the current input matches the provided Matcher. // It returns a MatchAction struct, which provides methods that // can be used to tell the parser what to do with a match. // // The intended way to use this, is by chaining some methods, // for example: p.On(...).Accept() // The chained methods will as a whole return a boolean value, // indicating whether or not a match was found and processed. func (p *P) On(m Matcher) *MatchAction { runes, widths, ok := p.match(m) p.LastMatch = string(runes) return &MatchAction{ p: p, runes: runes, widths: widths, ok: ok, } } // Match checks if the provided Matcher matches the current input. // Returns a slice of matching runes, a slice of their respective // byte widths and a boolean. // The boolean will be false and the slices will be empty in case // the input did not match. func (p *P) match(matcher Matcher) ([]rune, []int, bool) { m := &MatchDialog{p: p} ok := matcher.Match(m) return m.runes, m.widths, ok } type MatchAction struct { p *P runes []rune widths []int ok bool } // Accept tells the parser to move the cursor past a match that was found, // and to store the input that matched in the string buffer. // Returns true in case a match was found. // When no match was found, then no action is taken and false is returned. func (a *MatchAction) 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 } // Skip tells the parser to move the cursor past a match that was found, // without storing the actual match in the string buffer. // Returns true in case a match was found. // When no match was found, then no action is taken and false is returned. func (a *MatchAction) 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 } // Stay tells the parser to not move the cursor after finding a match. // Returns true in case a match was found, false otherwise. func (a *MatchAction) Stay() bool { return a.ok } // RouteTo is a shortcut for p.On(...).Stay() + p.RouteTo(...). func (a *MatchAction) RouteTo(state StateHandler) bool { if a.ok { a.p.RouteTo(state) } return a.ok } // RouteReturn is a shortcut for p.On(...).Stay() + p.RouteReturn(). func (a *MatchAction) RouteReturn() bool { if a.ok { a.p.RouteReturn() } 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' }