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 }