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 routeStack []StateHandler // route stack, for handling 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 cursorLine 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), cursorLine: 1, cursorColumn: 1, 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.routeStack) > 0: return p.popRoute(), 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.cursorLine, p.cursorColumn} return i, p.err, false default: p.item = i return i, nil, true } }