package parse_test import ( "fmt" "testing" "git.makaay.nl/mauricem/go-parsekit/parse" "git.makaay.nl/mauricem/go-parsekit/tokenize" ) func ExampleNew_usingAcceptedRunes() { // Easy access to the tokenize definitions. a := tokenize.A matches := []string{} parser := parse.New(func(p *parse.API) { for p.Accept(a.AnyRune) { matches = append(matches, p.Result.String()) } p.ExpectEndOfFile() }) err := parser("¡Any will dö!") fmt.Printf("Matches = %q, Error = %v\n", matches, err) // Output: // Matches = ["¡" "A" "n" "y" " " "w" "i" "l" "l" " " "d" "ö" "!"], Error = } func ExampleNew_usingTokens() { // Easy access to the tokenize definitions. c, a, tok := tokenize.C, tokenize.A, tokenize.T parser := parse.New(func(p *parse.API) { if p.Accept(c.OneOrMore(tok.Rune("RUNE", a.AnyRune))) { fmt.Printf("Runes accepted: %q\n", p.Result.String()) fmt.Printf("Tokens:\n") tokens := p.Result.Tokens() for i, token := range tokens { fmt.Printf("[%d] %s\n", i, token) } } p.ExpectEndOfFile() }) parser("¡ök!") // Output: // Runes accepted: "¡ök!" // Tokens: // [0] RUNE('¡') // [1] RUNE('ö') // [2] RUNE('k') // [3] RUNE('!') } func ExampleAPI_Expected() { parser := parse.New(func(p *parse.API) { p.Expected("a thing") }) err := parser("Whatever, this parser will never be happy...") fmt.Printf("Error: %s\n", err) // Output: // Error: unexpected input (expected a thing) at start of file } func ExampleAPI_Accept_inIfStatement() { parser := parse.New(func(p *parse.API) { // When a case-insensitive match on "Yowza!" is found by the // tokenizer, then Accept() will make the result available // through API.Result() if p.Accept(tokenize.A.StrNoCase("Yowza!")) { // Result.String() returns a string containing all // accepted runes that were matched against. fmt.Println(p.Result.String()) } }) parser("YOWZA!") // Output: // YOWZA! } func ExampleAPI_Accept_inSwitchStatement() { var result string a := tokenize.A parser := parse.New(func(p *parse.API) { for loop := true; loop; { switch { case p.Accept(a.Char('X')): // NOOP, skip this rune case p.Accept(a.AnyRune): result += p.Result.String() default: loop = false } } }) parser("HXeXllXoXX, XXwoXrlXXXd!") fmt.Println(result) // Output: // Hello, world! } func ExampleAPI_Stop() { c, a := tokenize.C, tokenize.A parser := parse.New(func(p *parse.API) { fmt.Printf("First word: ") for p.Accept(c.Not(a.Space)) { fmt.Printf("%s", p.Result.String()) } p.Stop() }) parser("Input with spaces") // Output: // First word: Input } func ExampleAPI_Stop_notCalledAndNoInputPending() { c, a := tokenize.C, tokenize.A parser := parse.New(func(p *parse.API) { fmt.Printf("Word: ") for p.Accept(c.Not(a.Space)) { fmt.Printf("%s", p.Result.String()) } fmt.Printf("\n") }) err := parser("Troglodyte") fmt.Printf("Error is nil: %t\n", err == nil) // Output: // Word: Troglodyte // Error is nil: true } func ExampleAPI_Stop_notCalledButInputPending() { c, a := tokenize.C, tokenize.A parser := parse.New(func(p *parse.API) { fmt.Printf("First word: ") for p.Accept(c.Not(a.Space)) { fmt.Printf("%s", p.Result.String()) } fmt.Printf("\n") }) err := parser("Input with spaces") fmt.Printf("Error: %s\n", err) // Output: // First word: Input // Error: unexpected input (expected end of file) at line 1, column 6 } func ExampleAPI_Peek() { // Definition of a fantasy serial number format. c, a := tokenize.C, tokenize.A serialnr := c.Seq(a.Asterisk, a.ASCIIUpper, a.ASCIIUpper, a.Digits) // This handler is able to handle serial numbers. serialnrHandler := func(p *parse.API) { if p.Accept(serialnr) { fmt.Println(p.Result.String()) } } // Start could function as a sort of dispatcher, handing over // control to the correct Handler function, based on the input. start := func(p *parse.API) { if p.Peek(tokenize.A.Asterisk) { p.Handle(serialnrHandler) return } // ... other cases could go here ... } parser := parse.New(start) parser("#XX1234") parser("*ay432566") parser("*ZD987112") // Output: // *ZD987112 } func TestGivenNullHandler_NewPanics(t *testing.T) { parse.AssertPanic(t, parse.PanicT{ Function: func() { parse.New(nil) }, Regexp: true, Expect: `parsekit\.parse\.New\(\): New\(\) called ` + `with nil input at /.*/parse_test\.go:\d+`}) } func TestGivenNullHandler_HandlePanics(t *testing.T) { brokenHandler := func(p *parse.API) { p.Handle(nil) } parser := parse.New(brokenHandler) parse.AssertPanic(t, parse.PanicT{ Function: func() { parser("") }, Regexp: true, Expect: `parsekit\.parse\.API\.Handle\(\): Handle\(\) called with nil input ` + `at /.*/parse_test\.go:\d+`}) } func TestGivenNilHandler_AcceptPanics(t *testing.T) { p := parse.New(func(p *parse.API) { p.Accept(nil) }) parse.AssertPanic(t, parse.PanicT{ Function: func() { p("") }, Regexp: true, Expect: `parsekit\.parse\.API\.Accept\(\): Accept\(\) called with nil ` + `tokenHandler argument at /.*/parse_test\.go:\d+`}) } func TestGivenNilHandler_PeekPanics(t *testing.T) { p := parse.New(func(p *parse.API) { p.Peek(nil) }) parse.AssertPanic(t, parse.PanicT{ Function: func() { p("") }, Regexp: true, Expect: `parsekit\.parse\.API\.Peek\(\): Peek\(\) called with nil ` + `tokenHandler argument at /.*/parse_test\.go:\d+`}) } func TestGivenStoppedParser_HandlePanics(t *testing.T) { otherHandler := func(p *parse.API) { panic("This is not the handler you're looking for") } p := parse.New(func(p *parse.API) { p.Stop() p.Handle(otherHandler) }) parse.AssertPanic(t, parse.PanicT{ Function: func() { p("") }, Regexp: true, Expect: `parsekit\.parse\.API\.Handle\(\): Illegal call to Handle\(\) ` + `at /.*/parse_test\.go:\d+: no calls allowed after API\.Stop\(\)`}) } func TestGivenParserWithErrorSet_HandlePanics(t *testing.T) { otherHandler := func(p *parse.API) { panic("This is not the handler you're looking for") } p := parse.New(func(p *parse.API) { p.SetError("It ends here") p.Handle(otherHandler) }) parse.AssertPanic(t, parse.PanicT{ Function: func() { p("") }, Regexp: true, Expect: `parsekit\.parse\.API\.Handle\(\): Illegal call to Handle\(\) ` + `at /.*/parse_test\.go:\d+: no calls allowed after API\.Error\(\)`}) } func TestGivenParserWhichIsNotStopped_WithNoMoreInput_FallbackExpectEndOfFileKicksIn(t *testing.T) { p := parse.New(func(p *parse.API) {}) err := p("") parse.AssertTrue(t, err == nil, "err") } func TestGivenParserWhichIsNotStopped_WithMoreInput_ProducesError(t *testing.T) { p := parse.New(func(p *parse.API) {}) err := p("x") parse.AssertEqual(t, "unexpected input (expected end of file) at start of file", err.Error(), "err") }