remove branch predictor in Any

fixes #2
This commit is contained in:
Adam Scarr 2018-06-11 11:54:35 +10:00
parent 981f4347b1
commit 8a5260ae50
2 changed files with 19 additions and 40 deletions

View File

@ -36,7 +36,6 @@ func NoAutoWS(parser Parserish) Parser {
func Any(parsers ...Parserish) Parser { func Any(parsers ...Parserish) Parser {
parserfied := ParsifyAll(parsers...) parserfied := ParsifyAll(parsers...)
// Records which parser was successful for each byte, and will use it first next time. // 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) { return NewParser("Any()", func(ps *State, node *Result) {
ps.WS(ps) ps.WS(ps)
@ -45,13 +44,6 @@ func Any(parsers ...Parserish) Parser {
return return
} }
startpos := ps.Pos startpos := ps.Pos
predictorChar := ps.Input[startpos]
predicted := predictor[predictorChar]
parserfied[predicted](ps, node)
if !ps.Errored() {
return
}
longestError := ps.Error longestError := ps.Error
if ps.Cut <= startpos { if ps.Cut <= startpos {
@ -60,10 +52,7 @@ func Any(parsers ...Parserish) Parser {
return return
} }
for i, parser := range parserfied { for _, parser := range parserfied {
if i == predicted {
continue
}
parser(ps, node) parser(ps, node)
if ps.Errored() { if ps.Errored() {
if ps.Error.pos >= longestError.pos { if ps.Error.pos >= longestError.pos {
@ -75,7 +64,6 @@ func Any(parsers ...Parserish) Parser {
ps.Recover() ps.Recover()
continue continue
} }
predictor[predictorChar] = i
return return
} }

View File

@ -4,6 +4,8 @@ import (
"testing" "testing"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"os"
"fmt"
) )
func TestSeq(t *testing.T) { func TestSeq(t *testing.T) {
@ -62,38 +64,27 @@ func TestAny(t *testing.T) {
require.Equal(t, 0, p2.Pos) require.Equal(t, 0, p2.Pos)
}) })
t.Run("branch prediction", func(t *testing.T) { t.Run("overlapping longest match", func(t *testing.T) {
p := Any("hello", Seq("{", Cut(), "world", "}"), Seq("[", Cut(), "a", "]")) EnableLogging(os.Stdout)
// warm up the predictor p := Many(Any("ab", "a"))
_, _ = Run(p, "hello")
_, _ = Run(p, "{world}")
t.Run("matches", func(t *testing.T) { t.Run("a ab", func(t *testing.T) {
node, ps := runParser("hello world!", p) node, ps := runParser("a ab", p)
require.Equal(t, "hello", node.Token)
require.Equal(t, 5, ps.Pos) 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) { t.Run("ab a", func(t *testing.T) {
_, ps := runParser("help world!", p) node, ps := runParser("ab a", 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("errors with cuts", func(t *testing.T) { fmt.Println(node)
_, ps := runParser("{world", p)
require.Equal(t, "offset 6: expected }", ps.Error.Error()) require.False(t, ps.Errored())
require.Equal(t, 6, ps.Error.Pos()) require.Equal(t, "ab", node.Child[0].Token)
require.Equal(t, 0, ps.Pos) 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)
}) })
}) })
} }