From 0dc37ae5bc10cc0669f88ab9febbc039a28f23d1 Mon Sep 17 00:00:00 2001 From: Adam Scarr Date: Sun, 13 Aug 2017 16:56:12 +1000 Subject: [PATCH] Remove a few allocs from Run --- .gitignore | 1 + errors.go | 26 ++++++++++++++++++++++++++ parser.go | 5 ++--- perf_test.go | 1 + scripts/benchalloc.sh | 12 ++++++++++++ state.go | 14 -------------- 6 files changed, 42 insertions(+), 17 deletions(-) create mode 100644 errors.go create mode 100644 scripts/benchalloc.sh diff --git a/.gitignore b/.gitignore index 321d49b..dee607c 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,4 @@ cpu.out mem.out /vendor +trace.log diff --git a/errors.go b/errors.go new file mode 100644 index 0000000..3f8787c --- /dev/null +++ b/errors.go @@ -0,0 +1,26 @@ +package goparsify + +import "fmt" + +// Error represents a parse error. These will often be set, the parser will back up a little and +// find another viable path. In general when combining errors the longest error should be returned. +type Error struct { + pos int + expected string +} + +// Pos is the offset into the document the error was found +func (e *Error) Pos() int { return e.pos } + +// Error satisfies the golang error interface +func (e *Error) Error() string { return fmt.Sprintf("offset %d: expected %s", e.pos, e.expected) } + +// UnparsedInputError is returned by Run when not all of the input was consumed. There may still be a valid result +type UnparsedInputError struct { + remaining string +} + +// Error satisfies the golang error interface +func (e UnparsedInputError) Error() string { + return "left unparsed: " + e.remaining +} diff --git a/parser.go b/parser.go index 87705ad..86f2e05 100644 --- a/parser.go +++ b/parser.go @@ -1,7 +1,6 @@ package goparsify import ( - "errors" "fmt" "regexp" "strings" @@ -90,11 +89,11 @@ func Run(parser Parserish, input string, ws ...VoidParser) (result interface{}, ps.AutoWS() if ps.Error.expected != "" { - return ret.Result, ps.Error + return ret.Result, &ps.Error } if ps.Get() != "" { - return ret.Result, errors.New("left unparsed: " + ps.Get()) + return ret.Result, UnparsedInputError{ps.Get()} } return ret.Result, nil diff --git a/perf_test.go b/perf_test.go index d6a0092..7a2911e 100644 --- a/perf_test.go +++ b/perf_test.go @@ -5,6 +5,7 @@ import "testing" func BenchmarkAny(b *testing.B) { p := Any("hello", "goodbye", "help") + b.ResetTimer() for i := 0; i < b.N; i++ { _, _ = Run(p, "hello") _, _ = Run(p, "hello world") diff --git a/scripts/benchalloc.sh b/scripts/benchalloc.sh new file mode 100644 index 0000000..8409938 --- /dev/null +++ b/scripts/benchalloc.sh @@ -0,0 +1,12 @@ +#!/bin/bash + +if [ $# != 1 ] ; then + echo Run this in a directory containing benchmarks and pass it the name of a benchmark. It will dump allocations out to trace.log + exit +fi + +set -eu + +go test -c + +GODEBUG=allocfreetrace=1 ./$(basename $(pwd)).test.exe -test.run=none -test.bench=$1 -test.benchmem -test.benchtime=1ns 2> >(sed -n '/benchmark.go:75/,$p' > trace.log) diff --git a/state.go b/state.go index 8aab3e2..e567777 100644 --- a/state.go +++ b/state.go @@ -1,24 +1,10 @@ package goparsify import ( - "fmt" "unicode" "unicode/utf8" ) -// Error represents a parse error. These will often be set, the parser will back up a little and -// find another viable path. In general when combining errors the longest error should be returned. -type Error struct { - pos int - expected string -} - -// Pos is the offset into the document the error was found -func (e Error) Pos() int { return e.pos } - -// Error satisfies the golang error interface -func (e Error) Error() string { return fmt.Sprintf("offset %d: expected %s", e.pos, e.expected) } - // State is the current parse state. It is entirely public because parsers are expected to mutate it during the parse. type State struct { // The full input string