diff --git a/combinator.go b/combinator.go index ca62f30..d7be3f9 100644 --- a/combinator.go +++ b/combinator.go @@ -39,11 +39,11 @@ func Any(parsers ...Parserish) Parser { predictor := [255]int{} return NewParser("Any()", func(ps *State, node *Result) { + ps.WS(ps) if ps.Pos >= len(ps.Input) { ps.ErrorHere("!EOF") return } - longestError := Error{} startpos := ps.Pos predictorChar := ps.Input[startpos] predicted := predictor[predictorChar] @@ -53,9 +53,7 @@ func Any(parsers ...Parserish) Parser { return } - if ps.Error.pos >= longestError.pos { - longestError = ps.Error - } + longestError := ps.Error if ps.Cut <= startpos { ps.Recover() } else { @@ -159,7 +157,6 @@ func Bind(parser Parserish, val interface{}) Parser { return } node.Result = val - return } } diff --git a/debugon.go b/debugon.go index 302b0ec..ceb3c33 100644 --- a/debugon.go +++ b/debugon.go @@ -49,7 +49,11 @@ func (dp *debugParser) logf(ps *State, result *Result, format string, args ...in if ps.Errored() { buf.WriteString(fmt.Sprintf(" did not find %s", ps.Error.expected)) } else if result != nil { - buf.WriteString(fmt.Sprintf(" found %s", result.String())) + resultStr := strconv.Quote(result.String()) + if len(resultStr) > 20 { + resultStr = resultStr[0:20] + } + buf.WriteString(fmt.Sprintf(" found %s", resultStr)) } buf.WriteRune('\n') return buf.String() diff --git a/json/json_test.go b/json/json_test.go index f020a40..13f2e40 100644 --- a/json/json_test.go +++ b/json/json_test.go @@ -4,6 +4,8 @@ import ( stdlibJson "encoding/json" "testing" + "os" + parsecJson "github.com/prataprc/goparsec/json" "github.com/stretchr/testify/require" "github.com/vektah/goparsify" @@ -53,6 +55,7 @@ func BenchmarkUnmarshalParsec(b *testing.B) { } func BenchmarkUnmarshalParsify(b *testing.B) { + goparsify.EnableLogging(os.Stdout) for i := 0; i < b.N; i++ { _, err := Unmarshal(benchmarkString) require.NoError(b, err) diff --git a/readme.md b/readme.md index ba35397..853dd6b 100644 --- a/readme.md +++ b/readme.md @@ -9,16 +9,18 @@ Run(parser, input, ASCIIWhitespace) ``` ### benchmarks -I dont have many benchmarks set up yet, but the json parser keeps up with the stdlib for raw speed: +I dont have many benchmarks set up yet, but the json parser is 50% faster than the stdlib. ``` -$ go test -bench=. -benchtime=2s -benchmem ./json -BenchmarkUnmarshalParsec-8 50000 66012 ns/op 50462 B/op 1318 allocs/op -BenchmarkUnmarshalParsify-8 100000 46713 ns/op 44543 B/op 332 allocs/op -BenchmarkUnmarshalStdlib-8 100000 46967 ns/op 13952 B/op 262 allocs/op +$ go test -bench=. -benchmem -benchtime=5s ./json -run=none +BenchmarkUnmarshalParsec-8 100000 65682 ns/op 50464 B/op 1318 allocs/op +BenchmarkUnmarshalParsify-8 200000 32656 ns/op 42094 B/op 220 allocs/op +BenchmarkUnmarshalStdlib-8 200000 48023 ns/op 13952 B/op 262 allocs/op PASS -ok github.com/vektah/goparsify/json 14.424s +ok github.com/vektah/goparsify/json 24.314s ``` +Most of the remaining small allocs are from putting things in `interface{}` and are pretty unavoidable. https://www.darkcoding.net/software/go-the-price-of-interface/ is a good read. + ### debugging parsers When a parser isnt working as you intended you can build with debugging and enable logging to get a detailed log of exactly what the parser is doing. diff --git a/result.go b/result.go index cdad054..21440eb 100644 --- a/result.go +++ b/result.go @@ -5,6 +5,7 @@ import ( "strings" ) +// TrashResult is used in places where the result isnt wanted, but something needs to be passed in to satisfy the interface. var TrashResult = &Result{} // Result is the output of a parser. Usually only one of its fields will be set and should be though of @@ -16,6 +17,7 @@ type Result struct { Result interface{} } +// String stringifies a node. This is only called from debug code. func (r Result) String() string { if r.Result != nil { if rs, ok := r.Result.(fmt.Stringer); ok { diff --git a/state.go b/state.go index 1b7e9f2..5d35ac8 100644 --- a/state.go +++ b/state.go @@ -1,6 +1,7 @@ package goparsify import ( + "strconv" "unicode" "unicode/utf8" ) @@ -72,11 +73,14 @@ func (s *State) Preview(x int) string { if s.Pos >= len(s.Input) { return "" } - if len(s.Input)-s.Pos >= x { - return s.Input[s.Pos : s.Pos+x] + + quoted := strconv.Quote(s.Get()) + quoted = quoted[1 : len(quoted)-1] + if len(quoted) >= x { + return quoted[0:x] } - return s.Input[s.Pos:] + return quoted } // ErrorHere raises an error at the current position.