// In this example, a parser is created that is able to parse input that looks // like "Hello, !", and that extracts the name from it. // // This implementation uses a state-based Parser for it, and it does not // implement any custom parser/combinator Handler functions. Note that // things are much easier to implement using custom Handlers (see the // helloParserCombinator example for this). Doing this fully parser-based // 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. If you need error reporting // like this, then also take a look at the helloSingleState example, which does // the same thing as this version, only more concise. package examples import ( "fmt" "strings" "git.makaay.nl/mauricem/go-parsekit/parse" "git.makaay.nl/mauricem/go-parsekit/tokenize" ) func Example_helloWorldUsingParser1() { for i, input := range []string{ "Oh!", "Hello, world!", "HELLO ,Johnny!", "hello , Bob123!", "hello Pizza!", "", " ", "hello", "hello,", "hello , ", "hello , Droopy", "hello , Droopy!", "hello , \t \t Droopy \t !", "Oh no!", "hello,!", "hello, \t!", } { name, err := (&helloparser1{}).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) } } // [0] Input: "Oh!" Error: unexpected input (expected hello) // [1] Input: "Hello, world!" Output: world // [2] Input: "HELLO ,Johnny!" Output: Johnny // [3] Input: "hello , Bob123!" Output: Bob123 // [4] Input: "hello Pizza!" Error: unexpected input (expected comma) // [5] Input: "" Error: unexpected end of file (expected hello) // [6] Input: " " Error: unexpected input (expected hello) // [7] Input: "hello" Error: unexpected end of file (expected comma) // [8] Input: "hello," Error: unexpected end of file (expected name) // [9] Input: "hello , " Error: unexpected end of file (expected name) // [10] Input: "hello , Droopy" Error: unexpected end of file (expected exclamation mark) // [11] Input: "hello , Droopy!" Output: Droopy // [12] Input: "hello , \t \t Droopy \t !" Output: Droopy // [13] Input: "Oh no!" Error: unexpected input (expected hello) // [14] Input: "hello,!" Error: The name cannot be empty // [15] Input: "hello, \t!" Error: The name cannot be empty } // ――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――― // Implementation of the parser // ――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――― type helloparser1 struct { greetee string } func (h *helloparser1) Parse(input string) (string, error) { parseHello := parse.New(h.start) err := parseHello(input) return h.greetee, err } func (h *helloparser1) start(p *parse.API) { a := tokenize.A if p.Skip(a.StrNoCase("hello")) { p.Handle(h.comma) } else { p.Expected("hello") } } func (h *helloparser1) comma(p *parse.API) { a := tokenize.A p.Skip(a.Blanks) if p.Skip(a.Comma) { p.Handle(h.startName) } else { p.Expected("comma") } } func (h *helloparser1) startName(p *parse.API) { a := tokenize.A p.Skip(a.Blanks) if p.Peek(a.AnyRune) { p.Handle(h.name) } else { p.Expected("name") } } func (h *helloparser1) name(p *parse.API) { a := tokenize.A switch { case p.Peek(a.Excl): p.Handle(h.exclamation) case p.Accept(a.AnyRune): h.greetee += p.Result.String() p.Handle(h.name) default: p.Expected("exclamation mark") } } func (h *helloparser1) exclamation(p *parse.API) { a := tokenize.A if p.Skip(a.Excl) { p.Handle(h.end) } else { p.Expected("exclamation") } } // Here we could have used p.ExpectEndOfFile() as well, but a slightly // different route was taken to implement a more friendly 'end of greeting' // error message. func (h *helloparser1) end(p *parse.API) { var a = tokenize.A if !p.Skip(a.EndOfFile) { p.Expected("end of greeting") return } h.greetee = strings.TrimSpace(h.greetee) if h.greetee == "" { p.SetError("The name cannot be empty") } else { p.Stop() } }