// This is the same as the example HelloWorldUsingParser1, except that in this // implementation the state machine is implemented using a combination of some // TokenHandlers and only a single state, in which multiple ParseAPI.On() calls // are combined to do all the work in one go. // // Note that things are much easier to implement using custom TokenHandlers (see // the other HelloWorldUsingMatcher example for this). Doing this implementation // is mainly for your learning pleasure. // // One big difference between the Matcher-based example and this one, is that // this parser reports errors much more fine-grained. This might or might not be // useful for your specific use case.:0 package parsekit_test import ( "fmt" "git.makaay.nl/mauricem/go-parsekit" ) func Example_helloWorldUsingParser2() { parser := &helloparser2{} for i, input := range []string{ "Hello, world!", "HELLO ,Johnny!", "hello , Bob123!", "hello Pizza!", "", " ", "hello", "hello,", "hello , ", "hello , Droopy", "hello , Droopy!", "hello , \t \t Droopy \t !", "Oh no!", "hello,!", "HELLO, Buster! Eat this!", } { name, err := parser.Parse(input) if err != nil { fmt.Printf("[%d] Input: %q Error: %s\n", i, input, err) } else { fmt.Printf("[%d] Input: %q Output: %s\n", i, input, name) } } // Output: // [0] Input: "Hello, world!" Output: world // [1] Input: "HELLO ,Johnny!" Output: Johnny // [2] Input: "hello , Bob123!" Output: Bob123 // [3] Input: "hello Pizza!" Error: the greeting is not properly separated // [4] Input: "" Error: the greeting is not being friendly // [5] Input: " " Error: the greeting is not being friendly // [6] Input: "hello" Error: the greeting is not properly separated // [7] Input: "hello," Error: the greeting is targeted at thin air // [8] Input: "hello , " Error: the greeting is targeted at thin air // [9] Input: "hello , Droopy" Error: the greeting is not loud enough // [10] Input: "hello , Droopy!" Output: Droopy // [11] Input: "hello , \t \t Droopy \t !" Output: Droopy // [12] Input: "Oh no!" Error: the greeting is not being friendly // [13] Input: "hello,!" Error: the greeting is targeted at thin air // [14] Input: "HELLO, Buster! Eat this!" Error: too much stuff going on after the closing '!' } // --------------------------------------------------------------------------- // Implementation of the parser // --------------------------------------------------------------------------- type helloparser2 struct { greetee string } func (h *helloparser2) Parse(input string) (string, *parsekit.Error) { parser := parsekit.NewParser(h.start) err := parser.Execute(input) return h.greetee, err } // Note: // For efficiency, we could have either: // // 1) added a return after every call to p.EmitError() // 2) done an 'else if' for every 'if' after the first // // For code readability, I omitted these however. The ParseAPI knows it // should ignore any upcoming call after an error has been set, so after // an error the p.On() calls will be invoked, however they will always // return false. func (h *helloparser2) start(p *parsekit.ParseAPI) { c, a, m := parsekit.C, parsekit.A, parsekit.M if !p.On(c.StrNoCase("hello")).Skip() { p.EmitError("the greeting is not being friendly") } else if !p.On(c.Seq(c.Opt(a.Whitespace), a.Comma, c.Opt(a.Whitespace))).Skip() { p.EmitError("the greeting is not properly separated") } else if !p.On(m.TrimSpace(c.OneOrMore(c.Except(a.Excl, a.AnyRune)))).Accept() { p.EmitError("the greeting is targeted at thin air") } else if !p.On(a.Excl).Skip() { p.EmitError("the greeting is not loud enough") } else if !p.On(a.EndOfFile).Stay() { p.EmitError("too much stuff going on after the closing '!'") } else { h.greetee = p.BufLiteral() if h.greetee == "" { p.EmitError("the name cannot be empty") } p.Stop() } }