2017-08-07 12:07:29 +02:00
|
|
|
// +build debug
|
|
|
|
|
|
|
|
package goparsify
|
|
|
|
|
|
|
|
import (
|
2017-08-10 13:04:14 +02:00
|
|
|
"bytes"
|
2017-08-07 12:07:29 +02:00
|
|
|
"fmt"
|
2017-08-10 13:04:14 +02:00
|
|
|
"io"
|
2017-08-07 12:07:29 +02:00
|
|
|
"sort"
|
2017-08-10 13:04:14 +02:00
|
|
|
"strconv"
|
2017-08-07 12:07:29 +02:00
|
|
|
"strings"
|
|
|
|
"time"
|
2017-08-10 13:04:14 +02:00
|
|
|
|
2021-05-07 21:52:01 +02:00
|
|
|
"kesim.org/goparsify/debug"
|
2017-08-07 12:07:29 +02:00
|
|
|
)
|
|
|
|
|
2017-08-10 13:04:14 +02:00
|
|
|
var log io.Writer = nil
|
2017-08-09 13:18:14 +02:00
|
|
|
var parsers []*debugParser
|
2017-08-10 13:04:14 +02:00
|
|
|
var pendingOpenLog = ""
|
|
|
|
var activeParsers []*debugParser
|
|
|
|
var longestLocation = 0
|
2017-08-07 12:07:29 +02:00
|
|
|
|
2017-08-09 13:18:14 +02:00
|
|
|
type debugParser struct {
|
2017-08-13 06:08:21 +02:00
|
|
|
Match string
|
|
|
|
Var string
|
|
|
|
Location string
|
|
|
|
Next Parser
|
|
|
|
Cumulative time.Duration
|
|
|
|
Self time.Duration
|
|
|
|
SelfStart time.Time
|
|
|
|
Calls int
|
|
|
|
Errors int
|
2017-08-10 13:04:14 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
func (dp *debugParser) Name() string {
|
2017-08-10 13:58:14 +02:00
|
|
|
if len(activeParsers) > 1 && activeParsers[len(activeParsers)-2].Var == dp.Var {
|
2017-08-10 13:04:14 +02:00
|
|
|
return dp.Match
|
|
|
|
}
|
|
|
|
return dp.Var
|
|
|
|
}
|
|
|
|
|
|
|
|
func (dp *debugParser) logf(ps *State, result *Result, format string, args ...interface{}) string {
|
|
|
|
buf := &bytes.Buffer{}
|
|
|
|
buf.WriteString(fmt.Sprintf("%"+strconv.Itoa(longestLocation)+"s | ", dp.Location))
|
|
|
|
buf.WriteString(fmt.Sprintf("%-15s", ps.Preview(15)))
|
|
|
|
buf.WriteString(" | ")
|
2017-08-13 11:50:41 +02:00
|
|
|
buf.WriteString(strings.Repeat(" ", len(activeParsers)-1))
|
2019-06-16 11:31:04 +02:00
|
|
|
//buf.WriteString(fmt.Sprintf(format, args...))
|
|
|
|
buf.WriteString(format)
|
|
|
|
buf.WriteString(fmt.Sprint(args...))
|
2017-08-10 13:04:14 +02:00
|
|
|
if ps.Errored() {
|
2017-08-13 11:50:41 +02:00
|
|
|
buf.WriteString(fmt.Sprintf(" did not find %s", ps.Error.expected))
|
2017-08-10 13:04:14 +02:00
|
|
|
} else if result != nil {
|
2017-08-13 13:20:41 +02:00
|
|
|
resultStr := strconv.Quote(result.String())
|
|
|
|
if len(resultStr) > 20 {
|
|
|
|
resultStr = resultStr[0:20]
|
|
|
|
}
|
|
|
|
buf.WriteString(fmt.Sprintf(" found %s", resultStr))
|
2017-08-10 13:04:14 +02:00
|
|
|
}
|
|
|
|
buf.WriteRune('\n')
|
|
|
|
return buf.String()
|
|
|
|
}
|
|
|
|
|
|
|
|
func (dp *debugParser) logStart(ps *State) {
|
|
|
|
if log != nil {
|
|
|
|
if pendingOpenLog != "" {
|
|
|
|
fmt.Fprint(log, pendingOpenLog)
|
|
|
|
pendingOpenLog = ""
|
|
|
|
}
|
2017-08-13 11:50:41 +02:00
|
|
|
pendingOpenLog = dp.logf(ps, nil, dp.Name()+" {")
|
2017-08-10 13:04:14 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (dp *debugParser) logEnd(ps *State, result *Result) {
|
|
|
|
if log != nil {
|
|
|
|
if pendingOpenLog != "" {
|
2019-06-16 12:14:02 +02:00
|
|
|
fmt.Fprint(log, dp.logf(ps, result, dp.Name()))
|
2017-08-10 13:04:14 +02:00
|
|
|
pendingOpenLog = ""
|
2017-08-13 11:50:41 +02:00
|
|
|
} else {
|
2019-06-16 12:14:02 +02:00
|
|
|
fmt.Fprint(log, dp.logf(ps, result, "}"))
|
2017-08-10 13:04:14 +02:00
|
|
|
}
|
|
|
|
}
|
2017-08-07 12:07:29 +02:00
|
|
|
}
|
|
|
|
|
2017-08-13 09:30:10 +02:00
|
|
|
func (dp *debugParser) Parse(ps *State, node *Result) {
|
2017-08-10 13:04:14 +02:00
|
|
|
activeParsers = append(activeParsers, dp)
|
2017-08-07 12:07:29 +02:00
|
|
|
start := time.Now()
|
2017-08-13 06:08:21 +02:00
|
|
|
dp.SelfStart = start
|
2017-08-07 12:07:29 +02:00
|
|
|
|
2017-08-10 13:04:14 +02:00
|
|
|
dp.logStart(ps)
|
2017-08-13 09:30:10 +02:00
|
|
|
dp.Next(ps, node)
|
|
|
|
dp.logEnd(ps, node)
|
2017-08-07 12:07:29 +02:00
|
|
|
|
2017-08-13 06:08:21 +02:00
|
|
|
dp.Cumulative += time.Since(start)
|
|
|
|
dp.Self += time.Since(dp.SelfStart)
|
2017-08-07 12:07:29 +02:00
|
|
|
dp.Calls++
|
2017-08-13 06:08:21 +02:00
|
|
|
if ps.Errored() {
|
|
|
|
dp.Errors++
|
|
|
|
}
|
2017-08-07 12:07:29 +02:00
|
|
|
|
2017-08-10 13:04:14 +02:00
|
|
|
activeParsers = activeParsers[0 : len(activeParsers)-1]
|
2017-08-07 12:07:29 +02:00
|
|
|
}
|
|
|
|
|
2017-08-09 13:18:14 +02:00
|
|
|
// NewParser should be called around the creation of every Parser.
|
|
|
|
// It does nothing normally and should incur no runtime overhead, but when building with -tags debug
|
2017-08-10 13:04:14 +02:00
|
|
|
// it will instrument every parser to collect valuable timing and debug information.
|
|
|
|
func NewParser(name string, p Parser) Parser {
|
|
|
|
description, location := debug.GetDefinition()
|
2017-08-07 12:07:29 +02:00
|
|
|
|
2017-08-09 13:18:14 +02:00
|
|
|
dp := &debugParser{
|
2017-08-10 13:04:14 +02:00
|
|
|
Match: name,
|
|
|
|
Var: description,
|
|
|
|
Location: location,
|
2017-08-13 06:08:21 +02:00
|
|
|
}
|
|
|
|
|
2017-08-13 09:30:10 +02:00
|
|
|
dp.Next = func(ps *State, ret *Result) {
|
2017-08-13 06:08:21 +02:00
|
|
|
dp.Self += time.Since(dp.SelfStart)
|
|
|
|
|
2017-08-13 09:30:10 +02:00
|
|
|
p(ps, ret)
|
2017-08-13 06:08:21 +02:00
|
|
|
|
|
|
|
dp.SelfStart = time.Now()
|
2017-08-10 13:04:14 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
if len(dp.Location) > longestLocation {
|
|
|
|
longestLocation = len(dp.Location)
|
2017-08-07 12:07:29 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
parsers = append(parsers, dp)
|
|
|
|
return dp.Parse
|
|
|
|
}
|
|
|
|
|
2017-08-10 13:04:14 +02:00
|
|
|
// EnableLogging will write logs to the given writer as the next parse happens
|
|
|
|
func EnableLogging(w io.Writer) {
|
|
|
|
log = w
|
|
|
|
}
|
|
|
|
|
|
|
|
// DisableLogging will stop writing logs
|
|
|
|
func DisableLogging() {
|
|
|
|
log = nil
|
|
|
|
}
|
|
|
|
|
2017-08-09 13:18:14 +02:00
|
|
|
// DumpDebugStats will print out the curring timings for each parser if built with -tags debug
|
2017-08-07 12:07:29 +02:00
|
|
|
func DumpDebugStats() {
|
|
|
|
sort.Slice(parsers, func(i, j int) bool {
|
2017-08-13 07:42:51 +02:00
|
|
|
return parsers[i].Cumulative >= parsers[j].Cumulative
|
2017-08-07 12:07:29 +02:00
|
|
|
})
|
|
|
|
|
2017-08-13 07:42:51 +02:00
|
|
|
fmt.Println()
|
|
|
|
fmt.Println("| var name | matches | total time | self time | calls | errors | location ")
|
|
|
|
fmt.Println("| -------------------- | -------------------- | --------------- | --------------- | ---------- | ---------- | ----------")
|
2017-08-07 12:07:29 +02:00
|
|
|
for _, parser := range parsers {
|
2017-08-13 07:42:51 +02:00
|
|
|
fmt.Printf("| %20s | %20s | %15s | %15s | %10d | %10d | %s\n", parser.Var, parser.Match, parser.Cumulative.String(), parser.Self.String(), parser.Calls, parser.Errors, parser.Location)
|
2017-08-07 12:07:29 +02:00
|
|
|
}
|
|
|
|
}
|