128 lines
4.5 KiB
Go
128 lines
4.5 KiB
Go
package parsekit
|
|
|
|
import (
|
|
"fmt"
|
|
"reflect"
|
|
"runtime"
|
|
)
|
|
|
|
// P holds the internal state of the parser.
|
|
type P struct {
|
|
state StateHandler // the function that handles the current state
|
|
nextState StateHandler // the function that will handle the next state
|
|
stack []StateHandler // state function stack, for nested parsing
|
|
input string // the scanned input
|
|
len int // the total length of the input in bytes
|
|
pos int // current byte scanning position in the input
|
|
newline bool // keep track of when we have scanned a newline
|
|
cursorRow int // current row number in the input
|
|
cursorColumn int // current column position in the input
|
|
expecting string // a description of what the current state expects to find
|
|
buffer stringBuffer // an efficient buffer, used to build string values
|
|
LastMatch string // a string representation of the last matched input data
|
|
items chan Item // channel of resulting Parser items
|
|
item Item // the current item as reached by Next() and retrieved by Get()
|
|
err *Error // an error when lexing failed, retrieved by Error()
|
|
}
|
|
|
|
// StateHandler defines the type of function that can be used to
|
|
// handle a parser state.
|
|
type StateHandler func(*P)
|
|
|
|
// New takes an input string and a start state,
|
|
// and initializes the parser for it.
|
|
func New(input string, start StateHandler) *P {
|
|
return &P{
|
|
input: input,
|
|
len: len(input),
|
|
nextState: start,
|
|
items: make(chan Item, 2),
|
|
}
|
|
}
|
|
|
|
// Next retrieves the next parsed item.
|
|
// When a valid item was found, then the boolean return parameter will be true.
|
|
// On error or when successfully reaching the end of the input, false is returned.
|
|
// When an error occurred, it will be set in the error return value, nil otherwise.
|
|
func (p *P) Next() (Item, *Error, bool) {
|
|
for {
|
|
select {
|
|
case i := <-p.items:
|
|
return p.makeReturnValues(i)
|
|
default:
|
|
p.runStatusHandler()
|
|
}
|
|
}
|
|
}
|
|
|
|
// runStatusHandler moves the parser, which is bascially a state machine,
|
|
// to its next status. It does so by invoking a function of the
|
|
// type StateHandler. This function represents the current status.
|
|
func (p *P) runStatusHandler() {
|
|
if state, ok := p.getNextStateHandler(); ok {
|
|
p.invokeNextStatusHandler(state)
|
|
}
|
|
}
|
|
|
|
// getNextStateHandler determintes the next StatusHandler to invoke in order
|
|
// to move the parsing state machine one step further.
|
|
//
|
|
// When implementing a parser, the StateHandler functions must provide
|
|
// a routing decision in every invocation. A routing decision is one
|
|
// of the following:
|
|
//
|
|
// * A route is specified explicitly, which means that the next StatusHandler
|
|
// function to invoke is registered during the StateHandler function
|
|
// invocation. For example: p.RouteTo(nextStatus)
|
|
//
|
|
// * A route is specified implicitly, which means that a previous StateHandler
|
|
// invocation has registered the followup route for the current state.
|
|
// For example: p.RouteTo(nextStatus).ThenTo(otherStatus)
|
|
// In this example, the nextStatus StateHandler will not have to specify
|
|
// a route explicitly, but otherStatus will be used implicitly after
|
|
// the nextStatus function has returned.
|
|
//
|
|
// * An expectation is registered by the StatusHandler.
|
|
// For example: p.Expects("a cool thing")
|
|
// When the StatusHandler returns without having specified a route, this
|
|
// expectation is used to generate an "unexpected input" error message.
|
|
//
|
|
// When no routing decision is provided by a StateHandler, then this is
|
|
// considered a bug in the state handler, and the parser will panic.
|
|
func (p *P) getNextStateHandler() (StateHandler, bool) {
|
|
switch {
|
|
case p.nextState != nil:
|
|
return p.nextState, true
|
|
case len(p.stack) > 0:
|
|
return p.popState(), true
|
|
case p.expecting != "":
|
|
p.UnexpectedInput()
|
|
return nil, false
|
|
default:
|
|
name := runtime.FuncForPC(reflect.ValueOf(p.state).Pointer()).Name()
|
|
panic(fmt.Sprintf("StateHandler %s did not provide a routing decision", name))
|
|
}
|
|
}
|
|
|
|
// invokeNextStatusHandler moves the parser state to the provided state
|
|
// and invokes the StatusHandler function.
|
|
func (p *P) invokeNextStatusHandler(state StateHandler) {
|
|
p.state = state
|
|
p.nextState = nil
|
|
p.expecting = ""
|
|
p.state(p)
|
|
}
|
|
|
|
func (p *P) makeReturnValues(i Item) (Item, *Error, bool) {
|
|
switch {
|
|
case i.Type == ItemEOF:
|
|
return i, nil, false
|
|
case i.Type == ItemError:
|
|
p.err = &Error{i.Value, p.cursorRow, p.cursorColumn}
|
|
return i, p.err, false
|
|
default:
|
|
p.item = i
|
|
return i, nil, true
|
|
}
|
|
}
|