129 lines
3.8 KiB
Go
129 lines
3.8 KiB
Go
package parsekit
|
|
|
|
import "fmt"
|
|
|
|
// On checks if the input at the current cursor position matches the provided
|
|
// TokenHandler. On must be chained with another method that tells the parser
|
|
// what action to perform when a match was found:
|
|
//
|
|
// 1) On(...).Skip() - Only move cursor forward, ignore the matched runes.
|
|
//
|
|
// 2) On(...).Accept() - Move cursor forward, add runes to parsers's string buffer.
|
|
//
|
|
// 3) On(...).Stay() - Do nothing, the cursor stays at the same position.
|
|
//
|
|
// So an example chain could look like this:
|
|
//
|
|
// p.On(parsekit.A.Whitespace).Skip()
|
|
//
|
|
// The chain as a whole returns a boolean that indicates whether or not at match
|
|
// was found. When no match was found, false is returned and Skip() and Accept()
|
|
// will have no effect. Because of this, typical use of an On() chain is as
|
|
// expression for a conditional statement (if, switch/case, for). E.g.:
|
|
//
|
|
// // Skip multiple exclamation marks.
|
|
// for p.On(parsekit.A.Excl).Skip() { }
|
|
//
|
|
// // Fork a route based on the input.
|
|
// switch {
|
|
// case p.On(parsekit.A.Excl).Stay()
|
|
// p.RouteTo(stateHandlerA)
|
|
// case p.On(parsekit.A.Colon).Stay():
|
|
// p.RouteTo(stateHandlerB)
|
|
// default:
|
|
// p.RouteTo(stateHandlerC)
|
|
// }
|
|
//
|
|
// // When there's a "hi" on input, then say hello.
|
|
// if p.On(parsekit.C.Str("hi")).Accept() {
|
|
// fmt.Println("Hello!")
|
|
// }
|
|
func (p *ParseAPI) On(tokenHandler TokenHandler) *ParseAPIOnAction {
|
|
p.panicWhenStoppedOrInError()
|
|
p.checkForLoops()
|
|
if tokenHandler == nil {
|
|
panic("ParseHandler bug: tokenHandler argument for On() is nil")
|
|
}
|
|
|
|
p.result = nil
|
|
p.tokenAPI.result = NewResult()
|
|
fork := p.tokenAPI.Fork()
|
|
ok := tokenHandler(fork)
|
|
|
|
return &ParseAPIOnAction{
|
|
parseAPI: p,
|
|
tokenAPI: fork,
|
|
ok: ok,
|
|
}
|
|
}
|
|
|
|
// ParseAPIOnAction is a struct that is used for building the On()-method chain.
|
|
// The On() method will return an initialized struct of this type.
|
|
type ParseAPIOnAction struct {
|
|
parseAPI *ParseAPI
|
|
tokenAPI *TokenAPI
|
|
ok bool
|
|
}
|
|
|
|
// Accept tells the parser to move the cursor past a match that was found,
|
|
// and to make the TokenHandler results available in the ParseAPI through
|
|
// the Result() method.
|
|
//
|
|
// Returns true in case a match was found.
|
|
// When no match was found, then no action is taken and false is returned.
|
|
func (a *ParseAPIOnAction) Accept() bool {
|
|
if a.ok {
|
|
a.tokenAPI.Merge()
|
|
a.parseAPI.result = a.tokenAPI.root.result
|
|
a.tokenAPI.root.result = NewResult()
|
|
a.tokenAPI.root.detachChilds()
|
|
if a.tokenAPI.offset > 0 {
|
|
a.tokenAPI.root.FlushReaderBuffer(a.tokenAPI.offset)
|
|
a.parseAPI.initLoopCheck()
|
|
}
|
|
}
|
|
return a.ok
|
|
}
|
|
|
|
// Skip tells the parser to move the cursor past a match that was found,
|
|
// without making the results available through the ParseAPI.
|
|
//
|
|
// Returns true in case a match was found.
|
|
// When no match was found, then no action is taken and false is returned.
|
|
func (a *ParseAPIOnAction) Skip() bool {
|
|
if a.ok {
|
|
a.tokenAPI.root.cursor = a.tokenAPI.cursor
|
|
a.tokenAPI.root.result = NewResult()
|
|
a.tokenAPI.root.detachChilds()
|
|
if a.tokenAPI.offset > 0 {
|
|
a.tokenAPI.root.FlushReaderBuffer(a.tokenAPI.offset)
|
|
a.parseAPI.initLoopCheck()
|
|
}
|
|
}
|
|
return a.ok
|
|
}
|
|
|
|
// Stay tells the parser to not move the cursor after finding a match.
|
|
// Returns true in case a match was found, false otherwise.
|
|
func (a *ParseAPIOnAction) Stay() bool {
|
|
if a.ok {
|
|
a.tokenAPI.root.result = NewResult()
|
|
a.tokenAPI.root.detachChilds()
|
|
}
|
|
return a.ok
|
|
}
|
|
|
|
// Result returns a Result struct, containing results as produced by the
|
|
// last ParseAPI.On() call.
|
|
func (p *ParseAPI) Result() *Result {
|
|
result := p.result
|
|
if p.result == nil {
|
|
caller, filepos := getCaller(1)
|
|
panic(fmt.Sprintf(
|
|
"parsekit.ParseAPI.Result(): Result() called without calling "+
|
|
"ParseAPI.Accept() on beforehand to make the result available "+
|
|
"from %s at %s", caller, filepos))
|
|
}
|
|
return result
|
|
}
|