More documentation and examples.

This commit is contained in:
Maurice Makaay 2019-06-12 16:17:13 +00:00
parent 1a280233b0
commit cdfc4ce52c
10 changed files with 148 additions and 34 deletions

View File

@ -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
}

View File

@ -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
}

View File

@ -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")
}
}

View File

@ -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
}

View File

@ -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() {

2
go.mod
View File

@ -1,3 +1,3 @@
module git.makaay.nl/mauricem/go-parsekit
go 1.12
go 1.12

View File

@ -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}")
}
}

View File

@ -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+`})
}

View File

@ -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: <nil>
// 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() {
// <nil> 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()

View File

@ -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)