go-parsekit/examples/example_helloManyStateParse...

152 lines
4.4 KiB
Go

// In this example, a parser is created that is able to parse input that looks
// like "Hello, <name>!", 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()
}
}