More perf tweaks

This commit is contained in:
Adam Scarr 2017-08-07 22:38:34 +10:00
parent b4f5fb423e
commit 4594587e3b
5 changed files with 40 additions and 92 deletions

View File

@ -16,17 +16,16 @@ func And(parsers ...Parserish) Parser {
parserfied := ParsifyAll(parsers...) parserfied := ParsifyAll(parsers...)
return NewParser("And()", func(ps *State) Node { return NewParser("And()", func(ps *State) Node {
var nodes = make([]Node, 0, len(parserfied)) result := Node{Children: make([]Node, len(parserfied))}
startpos := ps.Pos startpos := ps.Pos
for _, parser := range parserfied { for i, parser := range parserfied {
node := parser(ps) result.Children[i] = parser(ps)
if ps.Errored() { if ps.Errored() {
ps.Pos = startpos ps.Pos = startpos
return Node{} return result
} }
nodes = append(nodes, node)
} }
return Node{Children: nodes} return result
}) })
} }
@ -71,68 +70,43 @@ func Any(parsers ...Parserish) Parser {
} }
func Kleene(opScan Parserish, sepScan ...Parserish) Parser { func Kleene(opScan Parserish, sepScan ...Parserish) Parser {
return NewParser("Kleene()", manyImpl(0, opScan, nil, sepScan...)) return NewParser("Kleene()", manyImpl(0, opScan, sepScan...))
}
func KleeneUntil(opScan Parserish, untilScan Parserish, sepScan ...Parserish) Parser {
return NewParser("KleeneUntil()", manyImpl(0, opScan, untilScan, sepScan...))
} }
func Many(opScan Parserish, sepScan ...Parserish) Parser { func Many(opScan Parserish, sepScan ...Parserish) Parser {
return NewParser("Many()", manyImpl(1, opScan, nil, sepScan...)) return NewParser("Many()", manyImpl(1, opScan, sepScan...))
} }
func ManyUntil(opScan Parserish, untilScan Parserish, sepScan ...Parserish) Parser { func manyImpl(min int, op Parserish, sep ...Parserish) Parser {
return NewParser("ManyUntil()", manyImpl(1, opScan, untilScan, sepScan...)) var opParser = Parsify(op)
} var sepParser Parser
func manyImpl(min int, op Parserish, until Parserish, sep ...Parserish) Parser {
opParser := Parsify(op)
untilParser := Parsify(until)
sepParser := Nil
if len(sep) > 0 { if len(sep) > 0 {
sepParser = Parsify(sep[0]) sepParser = Parsify(sep[0])
} }
return func(ps *State) Node { return func(ps *State) Node {
var node Node var result Node
nodes := make([]Node, 0, 20)
startpos := ps.Pos startpos := ps.Pos
for { for {
tempPos := ps.Pos node := opParser(ps)
if untilParser != nil {
node = untilParser(ps)
if !ps.Errored() {
ps.Pos = tempPos
if len(nodes) < min {
ps.Pos = startpos
ps.ErrorHere("something else")
return Node{}
}
break
}
ps.ClearError()
}
node = opParser(ps)
if ps.Errored() { if ps.Errored() {
if len(nodes) < min { if len(result.Children) < min {
ps.Pos = startpos ps.Pos = startpos
return Node{} return result
} }
ps.ClearError() ps.ClearError()
break return result
} }
result.Children = append(result.Children, node)
nodes = append(nodes, node) if sepParser != nil {
sepParser(ps)
node = sepParser(ps) if ps.Errored() {
if ps.Errored() { ps.ClearError()
ps.ClearError() return result
break }
} }
} }
return Node{Children: nodes}
} }
} }
@ -143,7 +117,6 @@ func Maybe(parser Parserish) Parser {
node := parserfied(ps) node := parserfied(ps)
if ps.Errored() { if ps.Errored() {
ps.ClearError() ps.ClearError()
return Node{}
} }
return node return node
@ -156,7 +129,7 @@ func Map(parser Parserish, f func(n Node) Node) Parser {
return NewParser("Map()", func(ps *State) Node { return NewParser("Map()", func(ps *State) Node {
node := p(ps) node := p(ps)
if ps.Errored() { if ps.Errored() {
return Node{} return node
} }
return f(node) return f(node)
}) })

View File

@ -134,35 +134,6 @@ func TestMany(t *testing.T) {
}) })
} }
func TestKleeneUntil(t *testing.T) {
t.Run("Matches sequence with sep", func(t *testing.T) {
node, p2 := runParser("a,b,c,d,e,fg", KleeneUntil(Chars("abcde"), "d", ","))
assertSequence(t, node, "a", "b", "c")
require.Equal(t, "d,e,fg", p2.Get())
})
t.Run("Breaks if separator does not match", func(t *testing.T) {
node, p2 := runParser("a,b,c,d,e,fg", KleeneUntil(Chars("abcdefg", 1, 1), "y", ","))
assertSequence(t, node, "a", "b", "c", "d", "e", "f")
require.Equal(t, "g", p2.Get())
})
}
func TestManyUntil(t *testing.T) {
t.Run("Matches sequence until", func(t *testing.T) {
node, p2 := runParser("a,b,c,d,e,", ManyUntil(Chars("abcdefg"), "d", ","))
assertSequence(t, node, "a", "b", "c")
require.Equal(t, 6, p2.Pos)
})
t.Run("Returns error until matches early", func(t *testing.T) {
_, p2 := runParser("a,b,c,d,e,", ManyUntil(Chars("abc"), "a", ","))
require.Equal(t, "offset 0: Expected something else", p2.Error.Error())
require.Equal(t, 0, p2.Pos)
require.Equal(t, "a,b,c,d,e,", p2.Get())
})
}
type htmlTag struct { type htmlTag struct {
Name string Name string
} }

View File

@ -34,7 +34,16 @@ func main() {
max := 1000000 max := 1000000
if *memprofile != "" { if *memprofile != "" {
runtime.MemProfileRate = 1 runtime.MemProfileRate = 1
max = 10000 max = 100000
defer func() {
f, err := os.Create(*memprofile)
if err != nil {
log.Fatal(err)
}
pprof.WriteHeapProfile(f)
f.Close()
}()
} }
for i := 0; i < max; i++ { for i := 0; i < max; i++ {
@ -43,15 +52,4 @@ func main() {
panic(err) panic(err)
} }
} }
if *memprofile != "" {
f, err := os.Create(*memprofile)
if err != nil {
log.Fatal(err)
}
pprof.WriteHeapProfile(f)
f.Close()
return
}
} }

View File

@ -85,7 +85,7 @@ func Exact(match string) Parser {
matchByte := match[0] matchByte := match[0]
return NewParser(match, func(ps *State) Node { return NewParser(match, func(ps *State) Node {
ps.AutoWS() ps.AutoWS()
if ps.Input[ps.Pos] != matchByte { if ps.Pos >= len(ps.Input) || ps.Input[ps.Pos] != matchByte {
ps.ErrorHere(match) ps.ErrorHere(match)
return Node{} return Node{}
} }

View File

@ -72,6 +72,12 @@ func TestExact(t *testing.T) {
require.Equal(t, "o", ps.Error.Expected) require.Equal(t, "o", ps.Error.Expected)
require.Equal(t, 0, ps.Pos) require.Equal(t, 0, ps.Pos)
}) })
t.Run("eof char", func(t *testing.T) {
_, ps := runParser("", Exact("o"))
require.Equal(t, "o", ps.Error.Expected)
require.Equal(t, 0, ps.Pos)
})
} }
func TestChars(t *testing.T) { func TestChars(t *testing.T) {