-
-
Notifications
You must be signed in to change notification settings - Fork 20
/
regexp.go
154 lines (132 loc) · 5.23 KB
/
regexp.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
package errlog
import (
"fmt"
"regexp"
"runtime/debug"
"strconv"
"strings"
"github.com/fatih/color"
"github.com/sirupsen/logrus"
)
var (
/*
Note for contributors/users : these regexp have been made by me, taking my own source code as example for finding the right one to use.
I use gofmt for source code formatting, that means this will work on most cases.
Unfortunately, I didn't check against other code formatting tools, so it may require some evolution.
Feel free to create an issue or send a PR.
*/
regexpParseStack = regexp.MustCompile(`((?:(?:[a-zA-Z._-]+)[/])*(?:[*a-zA-Z0-9_]*\.)+[a-zA-Z0-9_]+)\(((?:(?:0x[0-9a-f]+)|(?:...)[,\s]*)+)*\)[\s]+([/:\-a-zA-Z0-9\._]+)[:]([0-9]+)[\s](?:\+0x([0-9a-f]+))*`)
regexpHexNumber = regexp.MustCompile(`0x[0-9a-f]+`)
regexpFuncLine = regexp.MustCompile(`^func[\s][a-zA-Z0-9]+[(](.*)[)][\s]*{`)
regexpParseDebugLineFindFunc = regexp.MustCompile(`[\.]Debug[\(](.*)[/)]`)
regexpParseDebugLineParseVarName = regexp.MustCompile(`[\.]Debug[\(](.*)\)`)
regexpFindVarDefinition = func(varName string) *regexp.Regexp {
return regexp.MustCompile(fmt.Sprintf(`%s[\s\:]*={1}([\s]*[a-zA-Z0-9\._]+)`, varName))
}
)
// StackTraceItem represents parsed information of a stack trace item
type StackTraceItem struct {
CallingObject string
Args []string
SourcePathRef string
SourceLineRef int
MysteryNumber int64 // don't know what this is, no documentation found, if you know please let me know via a PR !
}
func parseStackTrace(deltaDepth int) []StackTraceItem {
fmt.Println(string(debug.Stack()))
return parseAnyStackTrace(string(debug.Stack()), deltaDepth)
}
func parseAnyStackTrace(stackStr string, deltaDepth int) []StackTraceItem {
stackArr := strings.Split(stackStr, "\n")
if len(stackArr) < 2*(2+deltaDepth) {
return nil
}
stack := strings.Join(stackArr[2*(2+deltaDepth):], "\n") //get stack trace and reduce to desired size
parsedRes := regexpParseStack.FindAllStringSubmatch(stack, -1)
sti := make([]StackTraceItem, len(parsedRes))
for i := range parsedRes {
args := regexpHexNumber.FindAllString(parsedRes[i][2], -1)
srcLine, err := strconv.Atoi(parsedRes[i][4])
if Debug(err) {
srcLine = -1
}
mysteryNumberStr := parsedRes[i][5]
mysteryNumber := int64(-25)
if mysteryNumberStr != "" {
mysteryNumber, err = strconv.ParseInt(parsedRes[i][5], 16, 32)
if Debug(err) {
mysteryNumber = -1
}
}
sti[i] = StackTraceItem{
CallingObject: parsedRes[i][1],
Args: args,
SourcePathRef: parsedRes[i][3],
SourceLineRef: srcLine,
MysteryNumber: mysteryNumber,
}
}
return sti
}
//findFuncLine finds line where func is declared
func findFuncLine(lines []string, lineNumber int) int {
for i := lineNumber; i > 0; i-- {
if regexpFuncLine.Match([]byte(lines[i])) {
return i
}
}
return -1
}
//findFailingLine finds line where <var> is defined, if Debug(<var>) is present on lines[debugLine]. funcLine serves as max
func findFailingLine(lines []string, funcLine int, debugLine int) (failingLineIndex, columnStart, columnEnd int) {
failingLineIndex = -1 //init error flag
//find var name
reMatches := regexpParseDebugLineParseVarName.FindStringSubmatch(lines[debugLine-1])
if len(reMatches) < 2 {
return
}
varName := reMatches[1]
//build regexp for finding var definition
reFindVar := regexpFindVarDefinition(varName)
//start to search for var definition
for i := debugLine; i >= funcLine && i > 0; i-- { // going reverse from debug line to funcLine
logrus.Debugf("%d: %s", i, lines[i]) // print line for debug
// early skipping some cases
if strings.Trim(lines[i], " \n\t") == "" { // skip if line is blank
logrus.Debugf(color.BlueString("%d: ignoring blank line", i))
continue
} else if len(lines[i]) >= 2 && lines[i][:2] == "//" { // skip if line is a comment line (note: comments of type '/*' can be stopped inline and code may be placed after it, therefore we should pass line if '/*' starts the line)
logrus.Debugf(color.BlueString("%d: ignoring comment line", i))
continue
}
//search for var definition
index := reFindVar.FindStringSubmatchIndex(lines[i])
if index == nil { //if not found, continue searching with next line
logrus.Debugf(color.BlueString("%d: var definition not found for '%s' (regexp no match).", i, varName))
continue
}
// At that point we found our definition
failingLineIndex = i //store the ressult
columnStart = index[0] //store columnStart
//now lets walk to columnEnd (because regexp is really bad at doing this)
//for this purpose, we count brackets from first opening, and stop when openedBrackets == closedBrackets
openedBrackets, closedBrackets := 0, 0
for j := index[1]; j < len(lines[i]); j++ {
if lines[i][j] == '(' {
openedBrackets++
} else if lines[i][j] == ')' {
closedBrackets++
}
if openedBrackets == closedBrackets { // that means every opened brackets are now closed (the first/last one is the one from the func call)
columnEnd = j // so we found our column end
return // so return the result
}
}
if columnEnd == 0 { //columnEnd was not found
logrus.Debugf("Fixing value of columnEnd (0). Defaulting to end of failing line.")
columnEnd = len(lines[i]) - 1
}
return
}
return
}