package parsekit import ( "fmt" ) // 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 // Item represents an item that can be emitted from the parser. type Item struct { Type ItemType Value string } // Emit passes a Parser item to the client, including the provided string. func (p *P) Emit(t ItemType, v string) { p.items <- Item{t, v} p.buffer.reset() } // EmitLiteral passes a Parser item to the client, including accumulated // string buffer data as a literal string. func (p *P) EmitLiteral(t ItemType) { p.Emit(t, p.buffer.asLiteralString()) } // BufLiteral retrieves the contents of the parser buffer (all the runes that // were added to it using P.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 *P) BufLiteral() string { return p.buffer.asLiteralString() } // BufInterpreted retrieves the contents of the parser buffer (all the runes that // were added to it using P.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 *P) 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 *P) EmitInterpreted(t ItemType) 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 } p.Emit(t, s) return true } // 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 *P) EmitError(format string, args ...interface{}) { message := fmt.Sprintf(format, args...) p.Emit(ItemError, message) } // UnexpectedInput is used by a StateHandler function to emit an error item // that tells the client that an unexpected rune was encountered in the input. func (p *P) UnexpectedInput() { 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 *P) string { if p.expecting == "" { return "" } return fmt.Sprintf(" (expected %s)", p.expecting) }