package parsekit import ( "fmt" "strings" ) // ItemType represents the type of a parser Item. 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, s string) { p.items <- Item{t, s} 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()) } // EmitLiteralTrim passes a Parser item to the client, including // accumulated string buffer data as a literal string with whitespace // trimmed from it. func (p *P) EmitLiteralTrim(t ItemType) { p.Emit(t, strings.TrimSpace(p.buffer.asLiteralString())) } // EmitInterpreted passes a Parser item to the client, including // accumulated string buffer data a Go doubled quoted interpreted string // (handling escape codes like \n, \t, \uXXXX, etc.) // This method might return an error, in case there is data in the // string buffer that is not valid for string interpretation. func (p *P) EmitInterpreted(t ItemType) error { 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 err } p.Emit(t, s) return nil } // 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("Error method called on the parser, but no error was set") } return err.Message } func (err *Error) ErrorFull() string { message := err.Error() return fmt.Sprintf("%s after line %d, column %d", message, 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 parser implementation 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 == EOF: p.EmitError("unexpected end of file%s", fmtExpects(p)) case r == INVALID: p.EmitError("invalid UTF8 character in input%s", fmtExpects(p)) default: panic("Unhandled output from peek()") } } func fmtExpects(p *P) string { if p.expecting == "" { return "" } return fmt.Sprintf(" (expected %s)", p.expecting) }