diff --git a/examples/example_basiccalculator1_test.go b/examples/example_basiccalculator1_test.go index 3ce04c6..6755cde 100644 --- a/examples/example_basiccalculator1_test.go +++ b/examples/example_basiccalculator1_test.go @@ -57,8 +57,8 @@ func Example_basicCalculator1() { // An error is returned in case the calculation failed. func ComputeSimple(calculation string) (int64, error) { calculator := &simpleCalculator{op: +1} - parser := parse.New(calculator.number) - err := parser(calculation) + parseCalculation := parse.New(calculator.number) + err := parseCalculation(calculation) return calculator.Result, err } diff --git a/examples/example_basiccalculator2_test.go b/examples/example_basiccalculator2_test.go index ad2d597..43c42c0 100644 --- a/examples/example_basiccalculator2_test.go +++ b/examples/example_basiccalculator2_test.go @@ -78,8 +78,8 @@ type calculator struct { // calculation fails for some reason. func Compute(input string) (float64, error) { calc := &calculator{} - parser := parse.New(calc.calculation) - err := parser(input) + parseCalculation := parse.New(calc.calculation) + err := parseCalculation(input) return calc.result, err } diff --git a/examples/example_helloManyStateParser_test.go b/examples/example_helloManyStateParser_test.go index 392848a..21204c6 100644 --- a/examples/example_helloManyStateParser_test.go +++ b/examples/example_helloManyStateParser_test.go @@ -9,7 +9,7 @@ // // 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 +// 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. @@ -76,8 +76,8 @@ type helloparser1 struct { } func (h *helloparser1) Parse(input string) (string, error) { - parser := parse.New(h.start) - err := parser(input) + parseHello := parse.New(h.start) + err := parseHello(input) return h.greetee, err } @@ -92,12 +92,10 @@ func (h *helloparser1) start(p *parse.API) { func (h *helloparser1) comma(p *parse.API) { a := tokenize.A - switch { - case p.Accept(a.Blanks): - p.Handle(h.comma) - case p.Accept(a.Comma): + p.Accept(a.Blanks) + if p.Accept(a.Comma) { p.Handle(h.startName) - default: + } else { p.Expected("comma") } } diff --git a/examples/example_helloSingleStateParser_test.go b/examples/example_helloSingleStateParser_test.go index ffe6a48..722287c 100644 --- a/examples/example_helloSingleStateParser_test.go +++ b/examples/example_helloSingleStateParser_test.go @@ -74,8 +74,8 @@ type helloparser2 struct { } func (h *helloparser2) Parse(input string) (string, error) { - parser := parse.New(h.start) - err := parser(input) + parseHello := parse.New(h.start) + err := parseHello(input) return h.greetee, err } diff --git a/examples/examples_state_test.go b/examples/examples_state_test.go index 550c0ab..3825292 100644 --- a/examples/examples_state_test.go +++ b/examples/examples_state_test.go @@ -3,7 +3,7 @@ // // Here, we create a custom type 'Chunks', which is an alias // for []string. We add a Handler method directly to that type -// and let the parsing code fill the slice with strings during parsing. +// and let the parsing code append items to the slice during parsing. package examples @@ -20,12 +20,12 @@ func (l *Chunks) AddChopped(s string, chunkSize int) error { c, a := tokenize.C, tokenize.A chunkOfRunes := c.MinMax(1, chunkSize, a.AnyRune) - parser := parse.New(func(p *parse.API) { + parseChunks := parse.New(func(p *parse.API) { for p.Accept(chunkOfRunes) { *l = append(*l, p.Result().String()) } }) - return parser(s) + return parseChunks(s) } func Example_usingSliceAsParserState() { diff --git a/go.mod b/go.mod index 446c749..c336e8c 100644 --- a/go.mod +++ b/go.mod @@ -1,3 +1,3 @@ module git.makaay.nl/mauricem/go-parsekit -go 1.12 \ No newline at end of file +go 1.12 diff --git a/parse/api.go b/parse/api.go index 9645e5d..9af8792 100644 --- a/parse/api.go +++ b/parse/api.go @@ -65,7 +65,7 @@ func (p *API) invokeHandler(name string, tokenHandler tokenize.Handler) (*tokeni p.panicWhenStoppedOrInError(name) p.checkForLoops() if tokenHandler == nil { - callerPanic(2, "parse.API.%s(): %s() called with nil tokenHandler argument at {caller}", name, name) + callerPanic(2, "parsekit.parse.API.%s(): %s() called with nil tokenHandler argument at {caller}", name, name) } p.result = nil @@ -93,7 +93,7 @@ func (p *API) panicWhenStoppedOrInError(name string) { after = "Stop()" } - callerPanic(2, "parse.API.%s(): Illegal call to %s() at {caller}: "+ + callerPanic(2, "parsekit.parse.API.%s(): Illegal call to %s() at {caller}: "+ "no calls allowed after API.%s", name, name, after) } @@ -115,7 +115,7 @@ func (p *API) initLoopCheck() { func (p *API) checkForLoops() { filepos := callerFilepos(3) if _, ok := p.loopCheck[filepos]; ok { - callerPanic(3, "parse.API: Loop detected in parser at {caller}") + callerPanic(3, "parsekit.parse.API: Loop detected in parser at {caller}") } p.loopCheck[filepos] = true } @@ -128,7 +128,7 @@ func (p *API) checkForLoops() { func (p *API) Result() *tokenize.Result { result := p.result if p.result == nil { - callerPanic(1, "parse.API.Result(): Result() called "+ + callerPanic(1, "parsekit.parse.API.Result(): Result() called "+ "at {caller} without calling API.Peek() or API.Accept() on beforehand") } return result @@ -154,7 +154,7 @@ func (p *API) Handle(parseHandler Handler) bool { func (p *API) panicWhenHandlerNil(parseHandler Handler) { if parseHandler == nil { - callerPanic(2, "parse.API.Handle(): Handle() called with nil input at {caller}") + callerPanic(2, "parsekit.parse.API.Handle(): Handle() called with nil input at {caller}") } } diff --git a/parse/parse_test.go b/parse/parse_test.go index 386a98b..40b8a95 100644 --- a/parse/parse_test.go +++ b/parse/parse_test.go @@ -200,7 +200,7 @@ func TestGivenNullHandler_HandlePanics(t *testing.T) { parse.AssertPanic(t, parse.PanicT{ Function: func() { parser("") }, Regexp: true, - Expect: `parse\.API\.Handle\(\): Handle\(\) called with nil input ` + + Expect: `parsekit\.parse\.API\.Handle\(\): Handle\(\) called with nil input ` + `at /.*/parse_test\.go:\d+`}) } func TestGivenNilHandler_AcceptPanics(t *testing.T) { @@ -210,7 +210,7 @@ func TestGivenNilHandler_AcceptPanics(t *testing.T) { parse.AssertPanic(t, parse.PanicT{ Function: func() { p("") }, Regexp: true, - Expect: `parse\.API\.Accept\(\): Accept\(\) called with nil ` + + Expect: `parsekit\.parse\.API\.Accept\(\): Accept\(\) called with nil ` + `tokenHandler argument at /.*/parse_test\.go:\d+`}) } @@ -221,7 +221,7 @@ func TestGivenNilHandler_PeekPanics(t *testing.T) { parse.AssertPanic(t, parse.PanicT{ Function: func() { p("") }, Regexp: true, - Expect: `parse\.API\.Peek\(\): Peek\(\) called with nil ` + + Expect: `parsekit\.parse\.API\.Peek\(\): Peek\(\) called with nil ` + `tokenHandler argument at /.*/parse_test\.go:\d+`}) } @@ -236,7 +236,7 @@ func TestGivenStoppedParser_HandlePanics(t *testing.T) { parse.AssertPanic(t, parse.PanicT{ Function: func() { p("") }, Regexp: true, - Expect: `parse\.API\.Handle\(\): Illegal call to Handle\(\) ` + + Expect: `parsekit\.parse\.API\.Handle\(\): Illegal call to Handle\(\) ` + `at /.*/parse_test\.go:\d+: no calls allowed after API\.Stop\(\)`}) } @@ -251,7 +251,7 @@ func TestGivenParserWithErrorSet_HandlePanics(t *testing.T) { parse.AssertPanic(t, parse.PanicT{ Function: func() { p("") }, Regexp: true, - Expect: `parse\.API\.Handle\(\): Illegal call to Handle\(\) ` + + Expect: `parsekit\.parse\.API\.Handle\(\): Illegal call to Handle\(\) ` + `at /.*/parse_test\.go:\d+: no calls allowed after API\.Error\(\)`}) } @@ -262,7 +262,7 @@ func TestGivenParserWithoutCallToPeekOrAccept_ResultPanics(t *testing.T) { parse.AssertPanic(t, parse.PanicT{ Function: func() { p("") }, Regexp: true, - Expect: `parse\.API\.Result\(\): Result\(\) called at ` + + Expect: `parsekit\.parse\.API\.Result\(\): Result\(\) called at ` + `/.*/parse_test.go:\d+ without calling API.Peek\(\) or API.Accept\(\) on beforehand`}) } @@ -307,7 +307,7 @@ func TestGivenLoopingParserDefinition_ParserPanics(t *testing.T) { parse.AssertPanic(t, parse.PanicT{ Function: func() { parser("Het houdt niet op, niet vanzelf") }, Regexp: true, - Expect: `parse\.API: Loop detected in parser at /.*/parse_test.go:\d+`}) + Expect: `parsekit\.parse\.API: Loop detected in parser at /.*/parse_test.go:\d+`}) } // This test incorporates an actual loop bug that I dropped on myself and @@ -333,5 +333,5 @@ func TestGivenLoopingParserDefinition2_ParserPanics(t *testing.T) { parse.AssertPanic(t, parse.PanicT{ Function: func() { parser("This will end soon") }, Regexp: true, - Expect: `parse\.API: Loop detected in parser at .*/parse_test.go:\d+`}) + Expect: `parsekit\.parse\.API: Loop detected in parser at .*/parse_test.go:\d+`}) } diff --git a/tokenize/api_test.go b/tokenize/api_test.go index a21d2e1..3bdb4c7 100644 --- a/tokenize/api_test.go +++ b/tokenize/api_test.go @@ -6,6 +6,102 @@ import ( "git.makaay.nl/mauricem/go-parsekit/tokenize" ) +func ExampleNewAPI() { + tokenize.NewAPI("The input that the API will handle") + + // Output: +} + +func ExampleAPI_NextRune() { + api := tokenize.NewAPI("The input that the API will handle") + r, err := api.NextRune() + + fmt.Printf("Rune read from input; %c\n", r) + fmt.Printf("The error: %v\n", err) + fmt.Printf("API results: %q\n", api.Result().String()) + + // Output: + // Rune read from input; T + // The error: + // API results: "" +} + +func ExampleAPI_Accept() { + api := tokenize.NewAPI("The input that the API will handle") + api.NextRune() // reads 'T' + api.Accept() // adds 'T' to the API results + api.NextRune() // reads 'h' + api.Accept() // adds 'h' to the API results + api.NextRune() // reads 'e', but it is not added to the API results + + fmt.Printf("API results: %q\n", api.Result().String()) + + // Output: + // API results: "Th" +} + +func ExampleAPI_Result() { + api := tokenize.NewAPI("") + + result := api.Result() + + result.AddRunes("Some runes") + result.AddRunes([]rune{' ', 'a', 'd', 'd', 'e', 'd'}) + result.AddRunes(' ', 'i', 'n', ' ', "various ways") + fmt.Printf("API result first 10 runes: %q\n", api.Result().Runes()[0:10]) + fmt.Printf("API result runes as string: %q\n", api.Result().String()) + + result.SetRunes("new ", "set ", "of ", 'r', 'u', 'n', 'e', 's') + fmt.Printf("API result runes as string: %q\n", api.Result().String()) + fmt.Printf("API result runes: %q\n", api.Result().Runes()) + fmt.Printf("API third rune: %q\n", api.Result().Rune(2)) + + result.AddTokens(&tokenize.Token{ + Runes: []rune("demo 1"), + Type: 42, + Value: "towel"}) + result.AddTokens(&tokenize.Token{ + Runes: []rune("demo 2"), + Type: 73, + Value: "Zaphod"}) + fmt.Printf("API result tokens: %v\n", api.Result().Tokens()) + fmt.Printf("API second result token: %v\n", api.Result().Token(1)) + + // Output: + // API result first 10 runes: ['S' 'o' 'm' 'e' ' ' 'r' 'u' 'n' 'e' 's'] + // API result runes as string: "Some runes added in various ways" + // API result runes as string: "new set of runes" + // API result runes: ['n' 'e' 'w' ' ' 's' 'e' 't' ' ' 'o' 'f' ' ' 'r' 'u' 'n' 'e' 's'] + // API third rune: 'w' + // API result tokens: [42("demo 1", value = (string)towel) 73("demo 2", value = (string)Zaphod)] + // API second result token: 73("demo 2", value = (string)Zaphod) +} + +func ExampleAPI_Reset() { + api := tokenize.NewAPI("Very important input!") + + api.NextRune() + api.Accept() + api.NextRune() + api.Accept() + fmt.Printf("API results: %q at %s\n", api.Result().String(), api.Result().Cursor()) + + // Reset clears the results, but keeps the cursor position. + api.Reset() + fmt.Printf("API results: %q at %s\n", api.Result().String(), api.Result().Cursor()) + + api.NextRune() + api.Accept() + api.NextRune() + api.Accept() + fmt.Printf("API results: %q at %s\n", api.Result().String(), api.Result().Cursor()) + + // Output: + // API results: "Ve" at line 1, column 3 + // API results: "" at line 1, column 3 + // API results: "ry" at line 1, column 5 +} + func ExampleAPI_Fork() { // This custom Handler checks for input 'a', 'b' or 'c'. abcHandler := func(t *tokenize.API) bool { @@ -44,6 +140,26 @@ func ExampleAPI_Fork() { // mismatch at start of file } +func ExampleAPI_Dispose() { + api := tokenize.NewAPI("My uninspired piece of input") + + child := api.Fork() + // ... do stuff with child ... + child.NextRune() + child.Accept() + child.NextRune() + child.Accept() + // ... dispose of the child results ... + child.Dispose() + + // The parent still reads from the start of the input. + r, _ := api.NextRune() + fmt.Printf("Rune read from parent: %c\n", r) + + // Output: + // Rune read from parent: M +} + func ExampleAPI_Merge() { tokenHandler := func(t *tokenize.API) bool { child1 := t.Fork() diff --git a/tokenize/result.go b/tokenize/result.go index 79d141b..26f9476 100644 --- a/tokenize/result.go +++ b/tokenize/result.go @@ -68,9 +68,9 @@ func (r *Result) ClearRunes() { } // SetRunes replaces the Runes from the Result with the provided input. -func (r *Result) SetRunes(s interface{}) { +func (r *Result) SetRunes(s ...interface{}) { r.ClearRunes() - r.addRunes(s) + r.addRunes(s...) } // AddRunes is used to add runes to the Result. @@ -114,7 +114,7 @@ func (r *Result) ClearTokens() { } // SetTokens replaces the Tokens from the Result with the provided tokens. -func (r *Result) SetTokens(tokens []*Token) { +func (r *Result) SetTokens(tokens ...*Token) { r.ClearTokens() for _, t := range tokens { r.AddTokens(t)