package parsekit import ( "fmt" ) // Item represents an item that can be emitted from a ParseHandler function. type Item struct { Type ItemType Value string } // ItemType represents the type of a parser Item. // // When creating your own ItemType values, then make use of positive integer // values. Negative values are possible, but they are reserved for internal // use by parsekit. type ItemType int // ItemEOF is a built-in parser item type that is used for flagging that the // end of the input was reached. const ItemEOF ItemType = -1 // ItemError is a built-in parser item type that is used for flagging that // an error has occurred during parsing. const ItemError ItemType = -2 // Emit passes a Parser item to the client, including the provided string. func (p *ParseAPI) Emit(t ItemType, v string) { p.items = append(p.items, Item{t, v}) p.buffer.reset() } // BufLiteral retrieves the contents of the parser's string buffer (all the // runes that were added to it using ParseAPI.Accept()) as a literal string. // // Literal means that if the input had for example the subsequent runes '\' and 'n' // in it, then the literal string would have a backslash and an 'n' it in, not a // linefeed (ASCII char 10). // // Retrieving the buffer contents will not affect the buffer itself. New runes can // still be added to it. Only when calling P.Emit(), the buffer will be cleared. func (p *ParseAPI) BufLiteral() string { return p.buffer.asLiteralString() } // EmitLiteral passes a parser Item to the client, including the accumulated // string buffer data as a literal string. func (p *ParseAPI) EmitLiteral(t ItemType) { p.Emit(t, p.BufLiteral()) } // BufClear clears the contents of the parser string buffer. func (p *ParseAPI) BufClear() { p.buffer.reset() } // BufInterpreted retrieves the contents of the parser's string buffer (all // the runes that were added to it using ParseAPI.Accept()) as an // interpreted string. // // Interpreted means that the contents are treated as a Go double quoted // interpreted string (handling escape codes like \n, \t, \uXXXX, etc.). if the // input had for example the subsequent runes '\' and 'n' in it, then the interpreted // string would have an actual linefeed (ASCII char 10) in it. // // This method returns a boolean value, indicating whether or not the string // interpretation was successful. On invalid string data, an error will // automatically be emitted and the boolean return value will be false. // // Retrieving the buffer contents will not affect the buffer itself. New runes can // still be added to it. Only when calling P.Emit(), the buffer will be cleared. func (p *ParseAPI) BufInterpreted() (string, bool) { s, err := p.buffer.asInterpretedString() if err != nil { p.EmitError( "invalid string: %s (%s, forgot to escape a double quote or backslash maybe?)", p.buffer.asLiteralString(), err) return "", false } return s, true } // EmitInterpreted passes a Parser item to the client, including accumulated // string buffer data a Go double quoted interpreted string (handling escape // codes like \n, \t, \uXXXX, etc.) // This method returns a boolean value, indicating whether or not the string // interpretation was successful. On invalid string data, an error will // automatically be emitted and false will be returned. func (p *ParseAPI) EmitInterpreted(t ItemType) bool { if s, ok := p.BufInterpreted(); ok { p.Emit(t, s) return true } return false } // Error is used as the error type when parsing errors occur. // The error includes some extra meta information to allow for useful // error messages to the user. type Error struct { Message string Line int Column int } func (err *Error) Error() string { if err == nil { panic("internal parser error: Error() method called on the parser, but no error was set") } return err.Message } // ErrorFull returns the current error message, including information about // the position in the input where the error occurred. func (err *Error) ErrorFull() string { return fmt.Sprintf("%s after line %d, column %d", err, err.Line, err.Column) } // EmitError emits a parser error item to the client. func (p *ParseAPI) EmitError(format string, args ...interface{}) { message := fmt.Sprintf(format, args...) p.Emit(ItemError, message) } // EmitEOF emits an EOF to the client. In effect, this will stop the parsing process. func (p *ParseAPI) EmitEOF() { p.Emit(ItemEOF, "EOF") } // UnexpectedInput is used by a ParseHandler function to emit an error item // that tells the client that an unexpected rune was encountered in the input. func (p *ParseAPI) UnexpectedInput() { // When some previous parsing step yielded an error, skip this operation. if p.err != nil { return } r, _, ok := p.peek(0) switch { case ok: p.EmitError("unexpected character %q%s", r, fmtExpects(p)) case r == eofRune: p.EmitError("unexpected end of file%s", fmtExpects(p)) case r == invalidRune: p.EmitError("invalid UTF8 character in input%s", fmtExpects(p)) default: panic("parsekit bug: Unhandled output from peek()") } } func fmtExpects(p *ParseAPI) string { if p.expecting == "" { return "" } return fmt.Sprintf(" (expected %s)", p.expecting) }