Improve debugging output

This commit is contained in:
Adam Scarr 2017-08-13 19:50:41 +10:00
parent a4677a5834
commit a733d0ae13
5 changed files with 133 additions and 75 deletions

View File

@ -44,16 +44,13 @@ func (dp *debugParser) logf(ps *State, result *Result, format string, args ...in
buf.WriteString(fmt.Sprintf("%"+strconv.Itoa(longestLocation)+"s | ", dp.Location)) buf.WriteString(fmt.Sprintf("%"+strconv.Itoa(longestLocation)+"s | ", dp.Location))
buf.WriteString(fmt.Sprintf("%-15s", ps.Preview(15))) buf.WriteString(fmt.Sprintf("%-15s", ps.Preview(15)))
buf.WriteString(" | ") buf.WriteString(" | ")
output := ""
if ps.Errored() {
output = "fail"
} else if result != nil {
output = result.Token
}
buf.WriteString(fmt.Sprintf("%-10s | ", output))
buf.WriteString(strings.Repeat(" ", len(activeParsers)-1)) buf.WriteString(strings.Repeat(" ", len(activeParsers)-1))
buf.WriteString(fmt.Sprintf(format, args...)) buf.WriteString(fmt.Sprintf(format, args...))
buf.WriteString(fmt.Sprintf(" > %#v", result)) 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()))
}
buf.WriteRune('\n') buf.WriteRune('\n')
return buf.String() return buf.String()
} }
@ -64,7 +61,7 @@ func (dp *debugParser) logStart(ps *State) {
fmt.Fprint(log, pendingOpenLog) fmt.Fprint(log, pendingOpenLog)
pendingOpenLog = "" pendingOpenLog = ""
} }
pendingOpenLog = dp.logf(ps, nil, dp.Name()) pendingOpenLog = dp.logf(ps, nil, dp.Name()+" {")
} }
} }
@ -73,6 +70,8 @@ func (dp *debugParser) logEnd(ps *State, result *Result) {
if pendingOpenLog != "" { if pendingOpenLog != "" {
fmt.Fprintf(log, dp.logf(ps, result, dp.Name())) fmt.Fprintf(log, dp.logf(ps, result, dp.Name()))
pendingOpenLog = "" pendingOpenLog = ""
} else {
fmt.Fprintf(log, dp.logf(ps, result, "}"))
} }
} }
} }

View File

@ -7,17 +7,6 @@ import (
"unicode/utf8" "unicode/utf8"
) )
var TrashResult = &Result{}
// Result is the output of a parser. Usually only one of its fields will be set and should be though of
// more as a union type. having it avoids interface{} littered all through the parsing code and makes
// the it easy to do the two most common operations, getting a token and finding a child.
type Result struct {
Token string
Child []Result
Result interface{}
}
// Parser is the workhorse of parsify. A parser takes a State and returns a result, consuming some // Parser is the workhorse of parsify. A parser takes a State and returns a result, consuming some
// of the State in the process. // of the State in the process.
// Given state is shared there are a few rules that should be followed: // Given state is shared there are a few rules that should be followed:
@ -64,10 +53,6 @@ func Parsify(p Parserish) Parser {
} }
case string: case string:
return Exact(p) return Exact(p)
case VoidParser:
return func(ptr *State, node *Result) {
p(ptr)
}
case func(*State): case func(*State):
return func(ptr *State, node *Result) { return func(ptr *State, node *Result) {
p(ptr) p(ptr)

125
readme.md
View File

@ -28,58 +28,81 @@ When a parser isnt working as you intended you can build with debugging and enab
This works great with tests, eg in the goparsify source tree This works great with tests, eg in the goparsify source tree
``` ```
$ cd html adam:goparsify(master)$ go test -tags debug ./html -v
$ go test -tags debug -parselogs === RUN TestParse
html.go:50 | <body>hello <p | | tag html.go:48 | <body>hello <p | tag {
html.go:45 | <body>hello <p | | tstart html.go:43 | <body>hello <p | tstart {
html.go:45 | body>hello <p c | < | < html.go:43 | body>hello <p c | < found <
html.go:20 | >hello <p color | body | identifier html.go:20 | >hello <p color | identifier found body
html.go:35 | >hello <p color | | attrs html.go:33 | >hello <p color | attrs {
html.go:34 | >hello <p color | | attr html.go:32 | >hello <p color | attr {
html.go:20 | >hello <p color | fail | identifier html.go:20 | >hello <p color | identifier did not find [a-zA-Z][a-zA-Z0-9]*
html.go:45 | hello <p color= | > | > html.go:32 | >hello <p color | } did not find [a-zA-Z][a-zA-Z0-9]*
html.go:26 | hello <p color= | | elements html.go:33 | >hello <p color | } found
html.go:25 | hello <p color= | | element html.go:43 | hello <p color= | > found >
html.go:21 | <p color="blue" | hello | text html.go:43 | hello <p color= | } found [<,body,,map[string]string{},>]
html.go:25 | <p color="blue" | | element html.go:24 | hello <p color= | elements {
html.go:21 | <p color="blue" | fail | text html.go:23 | hello <p color= | element {
html.go:50 | <p color="blue" | | tag html.go:21 | <p color="blue" | text found hello
html.go:45 | <p color="blue" | | tstart html.go:23 | <p color="blue" | } found "hello "
html.go:45 | p color="blue"> | < | < html.go:23 | <p color="blue" | element {
html.go:20 | color="blue">w | p | identifier html.go:21 | <p color="blue" | text did not find <>
html.go:35 | color="blue">w | | attrs html.go:48 | <p color="blue" | tag {
html.go:34 | color="blue">w | | attr html.go:43 | <p color="blue" | tstart {
html.go:20 | ="blue">world</ | color | identifier html.go:43 | p color="blue"> | < found <
html.go:34 | "blue">world</p | = | = html.go:20 | color="blue">w | identifier found p
html.go:34 | >world</p></bod | | string literal html.go:33 | color="blue">w | attrs {
html.go:34 | >world</p></bod | | attr html.go:32 | color="blue">w | attr {
html.go:20 | >world</p></bod | fail | identifier html.go:20 | ="blue">world</ | identifier found color
html.go:45 | world</p></body | > | > html.go:32 | "blue">world</p | = found =
html.go:26 | world</p></body | | elements html.go:32 | >world</p></bod | string literal found "blue"
html.go:25 | world</p></body | | element html.go:32 | >world</p></bod | } found [color,=,"blue"]
html.go:21 | </p></body> | world | text html.go:32 | >world</p></bod | attr {
html.go:25 | </p></body> | | element html.go:20 | >world</p></bod | identifier did not find [a-zA-Z][a-zA-Z0-9]*
html.go:21 | </p></body> | fail | text html.go:32 | >world</p></bod | } did not find [a-zA-Z][a-zA-Z0-9]*
html.go:50 | </p></body> | | tag html.go:33 | >world</p></bod | } found [[color,=,"blue"]]
html.go:45 | </p></body> | | tstart html.go:43 | world</p></body | > found >
html.go:45 | /p></body> | < | < html.go:43 | world</p></body | } found [<,p,,map[string]string{"color":"blue"},>]
html.go:20 | /p></body> | fail | identifier html.go:24 | world</p></body | elements {
html.go:46 | </p></body> | | tend html.go:23 | world</p></body | element {
html.go:46 | p></body> | </ | </ html.go:21 | </p></body> | text found world
html.go:20 | ></body> | p | identifier html.go:23 | </p></body> | } found "world"
html.go:46 | </body> | > | > html.go:23 | </p></body> | element {
html.go:25 | </body> | | element html.go:21 | </p></body> | text did not find <>
html.go:21 | </body> | fail | text html.go:48 | </p></body> | tag {
html.go:50 | </body> | | tag html.go:43 | </p></body> | tstart {
html.go:45 | </body> | | tstart html.go:43 | /p></body> | < found <
html.go:45 | /body> | < | < html.go:20 | /p></body> | identifier did not find [a-zA-Z][a-zA-Z0-9]*
html.go:20 | /body> | fail | identifier html.go:43 | </p></body> | } did not find [a-zA-Z][a-zA-Z0-9]*
html.go:46 | </body> | | tend html.go:48 | </p></body> | } did not find [a-zA-Z][a-zA-Z0-9]*
html.go:46 | body> | </ | </ html.go:23 | </p></body> | } did not find [a-zA-Z][a-zA-Z0-9]*
html.go:20 | > | body | identifier html.go:24 | </p></body> | } found ["world"]
html.go:46 | | > | > html.go:44 | </p></body> | tend {
html.go:44 | p></body> | </ found </
html.go:20 | ></body> | identifier found p
html.go:44 | </body> | > found >
html.go:44 | </body> | } found [</,,p,>]
html.go:48 | </body> | } found "hello "
html.go:23 | </body> | } found html.htmlTag{Name:"p", Attributes:map[string]string{"color":"blue"}, Body:[]interface {}{"world"}}
html.go:23 | </body> | element {
html.go:48 | </body> | tag {
html.go:43 | </body> | tstart {
html.go:43 | /body> | < found <
html.go:20 | /body> | identifier did not find [a-zA-Z][a-zA-Z0-9]*
html.go:43 | </body> | } did not find [a-zA-Z][a-zA-Z0-9]*
html.go:48 | </body> | } did not find [a-zA-Z][a-zA-Z0-9]*
html.go:21 | </body> | text did not find <>
html.go:23 | </body> | } did not find [a-zA-Z][a-zA-Z0-9]*
html.go:24 | </body> | } found ["hello ",html.htmlTag{Name:"p", Attributes:map[string]string{"color":"blue"}, Body:[]interface {}{"world"}}]
html.go:44 | </body> | tend {
html.go:44 | body> | </ found </
html.go:20 | > | identifier found body
html.go:44 | | > found >
html.go:44 | | } found [</,,body,>]
html.go:48 | | } found [[<,body,,map[string]string{},>],,[]interface {}{"hello ", html.htmlTag{Name:"p", Attributes:map[string]string{"color":"blue"}, Body:[]interface {}{"world"}}},[</,,body,>]]
--- PASS: TestParse (0.00s)
PASS PASS
ok github.com/vektah/goparsify/html 0.118s ok github.com/vektah/goparsify/html 0.117s
``` ```
### debugging performance ### debugging performance

36
result.go Normal file
View File

@ -0,0 +1,36 @@
package goparsify
import (
"fmt"
"strings"
)
var TrashResult = &Result{}
// Result is the output of a parser. Usually only one of its fields will be set and should be though of
// more as a union type. having it avoids interface{} littered all through the parsing code and makes
// the it easy to do the two most common operations, getting a token and finding a child.
type Result struct {
Token string
Child []Result
Result interface{}
}
func (r Result) String() string {
if r.Result != nil {
if rs, ok := r.Result.(fmt.Stringer); ok {
return rs.String()
}
return fmt.Sprintf("%#v", r.Result)
}
if len(r.Child) > 0 {
children := []string{}
for _, child := range r.Child {
children = append(children, child.String())
}
return "[" + strings.Join(children, ",") + "]"
}
return r.Token
}

15
result_test.go Normal file
View File

@ -0,0 +1,15 @@
package goparsify
import (
"math/big"
"testing"
"github.com/stretchr/testify/require"
)
func TestResult_String(t *testing.T) {
require.Equal(t, "Hello", Result{Token: "Hello"}.String())
require.Equal(t, "[Hello,World]", Result{Child: []Result{{Token: "Hello"}, {Token: "World"}}}.String())
require.Equal(t, "10", Result{Result: 10}.String())
require.Equal(t, "10", Result{Result: big.NewInt(10)}.String())
}