go-toml/parsekit/parsekit.go

76 lines
2.0 KiB
Go

package parsekit
import (
"fmt"
"reflect"
"runtime"
)
// New takes an input string and a start state,
// and initializes the parser for it.
func New(input string, startState StateFn) *P {
return &P{
input: input,
len: len(input),
nextState: startState,
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:
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
}
default:
// When implementing a parser, a state function must provide
// a routing decision in every state function execution.
// When no route is specified, then it is considered a but
// in the parser implementation.
// An exception is when a function specified its expectation
// using the Expects() method. In that case, an unexpected
// input error is emitted.
if p.nextState == nil {
if p.expecting != "" {
p.UnexpectedInput()
continue
} else {
name := runtime.FuncForPC(reflect.ValueOf(p.state).Pointer()).Name()
panic(fmt.Sprintf("StateFn implementation bug: %s did not set next state or input expectation", name))
}
}
p.state = p.nextState
p.nextState = nil
p.expecting = ""
p.state(p)
}
}
}
// ToArray returns Parser items as an array (mainly intended for testing purposes)
// When an error occurs during scanning, a partial result will be
// returned, accompanied by the error that occurred.
func (p *P) ToArray() ([]Item, *Error) {
var items []Item
for {
item, err, more := p.Next()
if !more {
return items, err
}
items = append(items, item)
}
}