110 lines
3.9 KiB
Go
110 lines
3.9 KiB
Go
// 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.Skip(a.StrNoCase("hello")) {
|
|
p.SetError("the greeting is not being friendly")
|
|
return
|
|
}
|
|
if !p.Skip(c.Seq(c.Optional(a.Blanks), a.Comma, c.Optional(a.Blanks))) {
|
|
p.SetError("the greeting is not properly separated")
|
|
return
|
|
}
|
|
if p.Accept(m.TrimSpace(c.OneOrMore(a.AnyRune.Except(a.Excl)))) {
|
|
h.greetee = p.Result.String()
|
|
if h.greetee == "" {
|
|
p.SetError("the name cannot be empty")
|
|
return
|
|
}
|
|
} else {
|
|
p.SetError("the greeting is targeted at thin air")
|
|
return
|
|
}
|
|
if !p.Skip(a.Excl) {
|
|
p.SetError("the greeting is not loud enough")
|
|
} else if !p.Skip(a.EndOfFile) {
|
|
p.SetError("too much stuff going on after the closing '!'")
|
|
} else {
|
|
p.Stop()
|
|
}
|
|
}
|