From 8a5260ae50486c5e424d9778b7ec824b27842b9d Mon Sep 17 00:00:00 2001 From: Adam Scarr Date: Mon, 11 Jun 2018 11:54:35 +1000 Subject: [PATCH] remove branch predictor in Any fixes #2 --- combinator.go | 14 +------------- combinator_test.go | 45 ++++++++++++++++++--------------------------- 2 files changed, 19 insertions(+), 40 deletions(-) diff --git a/combinator.go b/combinator.go index 38145ee..5b0d584 100644 --- a/combinator.go +++ b/combinator.go @@ -36,7 +36,6 @@ func NoAutoWS(parser Parserish) Parser { func Any(parsers ...Parserish) Parser { parserfied := ParsifyAll(parsers...) // Records which parser was successful for each byte, and will use it first next time. - predictor := [255]int{} return NewParser("Any()", func(ps *State, node *Result) { ps.WS(ps) @@ -45,13 +44,6 @@ func Any(parsers ...Parserish) Parser { return } startpos := ps.Pos - predictorChar := ps.Input[startpos] - predicted := predictor[predictorChar] - - parserfied[predicted](ps, node) - if !ps.Errored() { - return - } longestError := ps.Error if ps.Cut <= startpos { @@ -60,10 +52,7 @@ func Any(parsers ...Parserish) Parser { return } - for i, parser := range parserfied { - if i == predicted { - continue - } + for _, parser := range parserfied { parser(ps, node) if ps.Errored() { if ps.Error.pos >= longestError.pos { @@ -75,7 +64,6 @@ func Any(parsers ...Parserish) Parser { ps.Recover() continue } - predictor[predictorChar] = i return } diff --git a/combinator_test.go b/combinator_test.go index c1eedb3..aa16e21 100644 --- a/combinator_test.go +++ b/combinator_test.go @@ -4,6 +4,8 @@ import ( "testing" "github.com/stretchr/testify/require" + "os" + "fmt" ) func TestSeq(t *testing.T) { @@ -62,38 +64,27 @@ func TestAny(t *testing.T) { require.Equal(t, 0, p2.Pos) }) - t.Run("branch prediction", func(t *testing.T) { - p := Any("hello", Seq("{", Cut(), "world", "}"), Seq("[", Cut(), "a", "]")) - // warm up the predictor - _, _ = Run(p, "hello") - _, _ = Run(p, "{world}") + t.Run("overlapping longest match", func(t *testing.T) { + EnableLogging(os.Stdout) + p := Many(Any("ab", "a")) - t.Run("matches", func(t *testing.T) { - node, ps := runParser("hello world!", p) - require.Equal(t, "hello", node.Token) - require.Equal(t, 5, ps.Pos) + t.Run("a ab", func(t *testing.T) { + node, ps := runParser("a ab", p) + + require.False(t, ps.Errored()) + require.Equal(t, "a", node.Child[0].Token) + require.Equal(t, "ab", node.Child[1].Token) }) - t.Run("errors", func(t *testing.T) { - _, ps := runParser("help world!", p) - require.Equal(t, "offset 0: expected [", ps.Error.Error()) - require.Equal(t, 0, ps.Error.Pos()) - require.Equal(t, 0, ps.Pos) - }) + t.Run("ab a", func(t *testing.T) { + node, ps := runParser("ab a", p) - t.Run("errors with cuts", func(t *testing.T) { - _, ps := runParser("{world", p) - require.Equal(t, "offset 6: expected }", ps.Error.Error()) - require.Equal(t, 6, ps.Error.Pos()) - require.Equal(t, 0, ps.Pos) - }) + fmt.Println(node) + + require.False(t, ps.Errored()) + require.Equal(t, "ab", node.Child[0].Token) + require.Equal(t, "a", node.Child[1].Token) - t.Run("misprededicted cut", func(t *testing.T) { - // This should probably only happen when the predictor is cold - _, ps := runParser("[a", p) - require.Equal(t, "offset 2: expected ]", ps.Error.Error()) - require.Equal(t, 2, ps.Error.Pos()) - require.Equal(t, 0, ps.Pos) }) }) }