65 lines
2.3 KiB
Go
65 lines
2.3 KiB
Go
package parsekit
|
|
|
|
import (
|
|
"fmt"
|
|
"runtime"
|
|
"strings"
|
|
)
|
|
|
|
// Parser is the top-level struct that holds the configuration for a parser.
|
|
// The Parser can be instantiated using the parsekit.NewParser() method.
|
|
type Parser struct {
|
|
startHandler ParseHandler // the function that handles the very first state
|
|
}
|
|
|
|
// ParseHandler defines the type of function that must be implemented to handle
|
|
// a parsing state in a Parser state machine.
|
|
//
|
|
// A ParseHandler function gets a ParseAPI struct as its input. This struct holds
|
|
// all the internal state for the parsing state machine and provides the
|
|
// interface that the ParseHandler uses to interact with the parser.
|
|
type ParseHandler func(*ParseAPI)
|
|
|
|
// NewParser instantiates a new Parser.
|
|
//
|
|
// The Parser is a state machine-style recursive descent parser, in which
|
|
// ParseHandler functions are used to move the state machine forward during
|
|
// parsing. This style of parser is typically used for parsing programming
|
|
// languages and structured data formats (like json, xml, toml, etc.)
|
|
//
|
|
// To parse input data, use the method Parser.Execute().
|
|
func NewParser(startHandler ParseHandler) *Parser {
|
|
if startHandler == nil {
|
|
_, filepos := getCaller(1)
|
|
panic(fmt.Sprintf("parsekit.NewParser(): NewParser() called with nil input at %s", filepos))
|
|
}
|
|
return &Parser{startHandler: startHandler}
|
|
}
|
|
|
|
// Execute starts the parser for the provided input.
|
|
// When an error occurs during parsing, then this error is returned. Nil otherwise.
|
|
func (p *Parser) Execute(input string) *Error {
|
|
api := &ParseAPI{
|
|
tokenAPI: NewTokenAPI(strings.NewReader(input)),
|
|
loopCheck: map[string]bool{},
|
|
}
|
|
if api.Handle(p.startHandler) {
|
|
// Handle indicated that parsing could still continue, meaning that there
|
|
// was no error and that the parsing has not actively been Stop()-ed.
|
|
// However, at this point, the parsing really should have stopped.
|
|
// We'll see what happens when we tell the parser that EOF was expected.
|
|
// This might work if we're indeed at EOF. Otherwise, an error will be
|
|
// generated.
|
|
api.ExpectEndOfFile()
|
|
}
|
|
return api.err
|
|
}
|
|
|
|
func getCaller(depth int) (string, string) {
|
|
// No error handling, because we call this method ourselves with safe depth values.
|
|
pc, file, line, _ := runtime.Caller(depth + 1)
|
|
filepos := fmt.Sprintf("%s:%d", file, line)
|
|
caller := runtime.FuncForPC(pc)
|
|
return caller.Name(), filepos
|
|
}
|