From f7d1e28fa166dd1f9804c1f94285313ec2ce152f Mon Sep 17 00:00:00 2001 From: Maurice Makaay Date: Sun, 26 May 2019 11:32:45 +0000 Subject: [PATCH] Dropped the whole channel support for the parser internals. It seems a nice idea to work with, but it's hard to predict how much buffering the channel should have to make all parsers work. When the buffer is too small, then a StateHandler that emits more items than can be buffered will end up in a deadlock and stall the application. It is easy enough to replace the channel with a slice of Items, so I did. --- example_dutchpostcode_test.go | 2 +- parsekit.go | 19 +++++++++---------- statehandler.go | 2 +- statehandler_emit.go | 2 +- 4 files changed, 12 insertions(+), 13 deletions(-) diff --git a/example_dutchpostcode_test.go b/example_dutchpostcode_test.go index 38bad4f..36364e6 100644 --- a/example_dutchpostcode_test.go +++ b/example_dutchpostcode_test.go @@ -1,4 +1,4 @@ -// In this example, a Parser is created which can parse and normalize Dutch postcodes +// In this example, a Paparserrser is created which can parse and normalize Dutch postcodes // The implementation uses only TokenHandler functions and does not implement a // full-fledged state-based Parser for it. package parsekit_test diff --git a/parsekit.go b/parsekit.go index 35fd3d4..fc325d9 100644 --- a/parsekit.go +++ b/parsekit.go @@ -30,7 +30,7 @@ type ParseRun struct { } // Parse starts a parse run on the provided input data. -// To retrieve parser Items from the run, make use of the ParseRun.Next() method. +// To retrieve emitted parser Items from the run, make use of the ParseRun.Next() method. func (p *Parser) Parse(input string) *ParseRun { return &ParseRun{ p: &ParseAPI{ @@ -39,7 +39,6 @@ func (p *Parser) Parse(input string) *ParseRun { cursorLine: 1, cursorColumn: 1, nextState: p.startState, - items: make(chan Item, 2), }, } } @@ -53,15 +52,15 @@ func (p *Parser) Parse(input string) *ParseRun { func (run *ParseRun) Next() (Item, *Error, bool) { // State handling loop: we handle states, until an Item is ready to be returned. for { - select { - // If a state handler has emitted an (error) Item, then the state handling - // loop is stopped and the Item is returned to the caller. - case i := <-run.p.items: - return run.makeReturnValues(i) - // Otherwise, the next state handler is looked up and invoked. - default: - run.runNextStateHandler() + // If a state handler has emitted one or more parser Items, then the next + // available Item is returned to the caller. + if len(run.p.items) > 0 { + item, rest := run.p.items[0], run.p.items[1:] + run.p.items = rest + return run.makeReturnValues(item) } + // Otherwise, the next state handler is looked up and invoked. + run.runNextStateHandler() } } diff --git a/statehandler.go b/statehandler.go index c49d16b..16201be 100644 --- a/statehandler.go +++ b/statehandler.go @@ -24,7 +24,7 @@ type ParseAPI struct { newline bool // keep track of when we have scanned a newline expecting string // a description of what the current state expects to find (see P.Expects()) buffer stringBuffer // an efficient buffer, used to build string values (see P.Accept()) - items chan Item // channel of resulting Parser items (see P.Emit()) + items []Item // a slice of resulting Parser items (see P.Emit()) item Item // the current item as reached by Next() and retrieved by Get() err *Error // an error when lexing failed, retrieved by Error() diff --git a/statehandler_emit.go b/statehandler_emit.go index f4a7e0f..0442f77 100644 --- a/statehandler_emit.go +++ b/statehandler_emit.go @@ -27,7 +27,7 @@ const ItemError ItemType = -2 // Emit passes a Parser item to the client, including the provided string. func (p *ParseAPI) Emit(t ItemType, v string) { - p.items <- Item{t, v} + p.items = append(p.items, Item{t, v}) p.buffer.reset() }