// This is the same as the other hello examples, except that in this // implementation the state machine is implemented using a combination of some // Handlers and only a single state, in which multiple API.On() calls // are combined to do all the work in one go. // // Note that things are much easier to implement using custom Handlers (see // the other helloParserCombinator example for this). Doing this implementation // is mainly for your learning pleasure. // // One big difference between the parser/combinator-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. package examples import ( "fmt" "git.makaay.nl/mauricem/go-parsekit/parse" "git.makaay.nl/mauricem/go-parsekit/tokenize" ) 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 at line 1, column 6 // [4] Input: "" Error: the greeting is not being friendly at start of file // [5] Input: " " Error: the greeting is not being friendly at start of file // [6] Input: "hello" Error: the greeting is not properly separated at line 1, column 6 // [7] Input: "hello," Error: the greeting is targeted at thin air at line 1, column 7 // [8] Input: "hello , " Error: the greeting is targeted at thin air at line 1, column 9 // [9] Input: "hello , Droopy" Error: the greeting is not loud enough at line 1, column 15 // [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 at start of file // [13] Input: "hello,!" Error: the greeting is targeted at thin air at line 1, column 7 // [14] Input: "HELLO, Buster! Eat this!" Error: too much stuff going on after the closing '!' at line 1, column 15 } // ――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――― // Implementation of the parser // ――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――― type helloparser2 struct { greetee string } func (h *helloparser2) Parse(input string) (string, error) { parseHello := parse.New(h.start) err := parseHello(input) return h.greetee, err } func (h *helloparser2) start(p *parse.API) { c, a, m := tokenize.C, tokenize.A, tokenize.M if !p.Accept(a.StrNoCase("hello")) { p.Error("the greeting is not being friendly") return } if !p.Accept(c.Seq(c.Opt(a.Blank), a.Comma, c.Opt(a.Blank))) { p.Error("the greeting is not properly separated") return } if p.Accept(m.TrimSpace(c.OneOrMore(c.Except(a.Excl, a.AnyRune)))) { h.greetee = p.Result().String() if h.greetee == "" { p.Error("the name cannot be empty") return } } else { p.Error("the greeting is targeted at thin air") return } if !p.Accept(a.Excl) { p.Error("the greeting is not loud enough") } else if !p.Accept(a.EndOfFile) { p.Error("too much stuff going on after the closing '!'") } else { p.Stop() } }