143 lines
4.7 KiB
Go
143 lines
4.7 KiB
Go
package parsekit
|
|
|
|
import (
|
|
"fmt"
|
|
)
|
|
|
|
// Item represents an item that can be emitted from the parser.
|
|
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())
|
|
}
|
|
|
|
// 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)
|
|
}
|
|
|
|
// 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 *ParseAPI) 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 *ParseAPI) string {
|
|
if p.expecting == "" {
|
|
return ""
|
|
}
|
|
return fmt.Sprintf(" (expected %s)", p.expecting)
|
|
}
|