From 4594587e3b7d1b474ce2444e821294c63cabe3da Mon Sep 17 00:00:00 2001 From: Adam Scarr Date: Mon, 7 Aug 2017 22:38:34 +1000 Subject: [PATCH] More perf tweaks --- combinator.go | 73 ++++++++++++++------------------------------ combinator_test.go | 29 ------------------ json/profile/json.go | 22 ++++++------- parser.go | 2 +- parser_test.go | 6 ++++ 5 files changed, 40 insertions(+), 92 deletions(-) diff --git a/combinator.go b/combinator.go index 48aef09..f072913 100644 --- a/combinator.go +++ b/combinator.go @@ -16,17 +16,16 @@ func And(parsers ...Parserish) Parser { parserfied := ParsifyAll(parsers...) return NewParser("And()", func(ps *State) Node { - var nodes = make([]Node, 0, len(parserfied)) + result := Node{Children: make([]Node, len(parserfied))} startpos := ps.Pos - for _, parser := range parserfied { - node := parser(ps) + for i, parser := range parserfied { + result.Children[i] = parser(ps) if ps.Errored() { 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 { - return NewParser("Kleene()", manyImpl(0, opScan, nil, sepScan...)) -} - -func KleeneUntil(opScan Parserish, untilScan Parserish, sepScan ...Parserish) Parser { - return NewParser("KleeneUntil()", manyImpl(0, opScan, untilScan, sepScan...)) + return NewParser("Kleene()", manyImpl(0, opScan, sepScan...)) } 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 { - return NewParser("ManyUntil()", manyImpl(1, opScan, untilScan, sepScan...)) -} - -func manyImpl(min int, op Parserish, until Parserish, sep ...Parserish) Parser { - opParser := Parsify(op) - untilParser := Parsify(until) - sepParser := Nil +func manyImpl(min int, op Parserish, sep ...Parserish) Parser { + var opParser = Parsify(op) + var sepParser Parser if len(sep) > 0 { sepParser = Parsify(sep[0]) } return func(ps *State) Node { - var node Node - nodes := make([]Node, 0, 20) + var result Node startpos := ps.Pos for { - tempPos := ps.Pos - 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) + node := opParser(ps) if ps.Errored() { - if len(nodes) < min { + if len(result.Children) < min { ps.Pos = startpos - return Node{} + return result } ps.ClearError() - break + return result } + result.Children = append(result.Children, node) - nodes = append(nodes, node) - - node = sepParser(ps) - if ps.Errored() { - ps.ClearError() - break + if sepParser != nil { + sepParser(ps) + if ps.Errored() { + ps.ClearError() + return result + } } } - return Node{Children: nodes} } } @@ -143,7 +117,6 @@ func Maybe(parser Parserish) Parser { node := parserfied(ps) if ps.Errored() { ps.ClearError() - 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 { node := p(ps) if ps.Errored() { - return Node{} + return node } return f(node) }) diff --git a/combinator_test.go b/combinator_test.go index 10ff788..4ac96b1 100644 --- a/combinator_test.go +++ b/combinator_test.go @@ -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 { Name string } diff --git a/json/profile/json.go b/json/profile/json.go index a03d047..0579fce 100644 --- a/json/profile/json.go +++ b/json/profile/json.go @@ -34,7 +34,16 @@ func main() { max := 1000000 if *memprofile != "" { 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++ { @@ -43,15 +52,4 @@ func main() { panic(err) } } - - if *memprofile != "" { - f, err := os.Create(*memprofile) - if err != nil { - log.Fatal(err) - } - - pprof.WriteHeapProfile(f) - f.Close() - return - } } diff --git a/parser.go b/parser.go index 2477bb4..62383b9 100644 --- a/parser.go +++ b/parser.go @@ -85,7 +85,7 @@ func Exact(match string) Parser { matchByte := match[0] return NewParser(match, func(ps *State) Node { ps.AutoWS() - if ps.Input[ps.Pos] != matchByte { + if ps.Pos >= len(ps.Input) || ps.Input[ps.Pos] != matchByte { ps.ErrorHere(match) return Node{} } diff --git a/parser_test.go b/parser_test.go index 594fcae..caa2f8b 100644 --- a/parser_test.go +++ b/parser_test.go @@ -72,6 +72,12 @@ func TestExact(t *testing.T) { require.Equal(t, "o", ps.Error.Expected) 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) {