-
Notifications
You must be signed in to change notification settings - Fork 20
/
shell_style.go
90 lines (81 loc) · 2.8 KB
/
shell_style.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
package quamina
import (
"encoding/json"
"errors"
"fmt"
"strings"
)
// readShellStyleSpecial parses a shellStyle object in a Pattern
func readShellStyleSpecial(pb *patternBuild, valsIn []typedVal) (pathVals []typedVal, err error) {
t, err := pb.jd.Token()
if err != nil {
return
}
pathVals = valsIn
shellString, ok := t.(string)
if !ok {
err = errors.New("value for `shellstyle` must be a string")
return
}
// no adjacent wildcards
if strings.Contains(shellString, "**") {
err = fmt.Errorf("adjacent '*' characters not allowed")
return
}
pathVals = append(pathVals, typedVal{vType: shellStyleType, val: `"` + shellString + `"`})
t, err = pb.jd.Token()
if err != nil {
return
}
switch t.(type) {
case json.Delim:
// } is all that will be returned
default:
err = errors.New("trailing garbage in shellstyle pattern")
}
return
}
// makeShellStyleFA does what it says. It is precisely equivalent to a regex with the only operator
// being a single ".*". Once we've implemented regular expressions we can use that to more or less eliminate this
func makeShellStyleFA(val []byte, printer printer) (start *smallTable, nextField *fieldMatcher) {
table := newSmallTable()
start = table
nextField = newFieldMatcher()
// for each byte in the pattern
valIndex := 0
for valIndex < len(val) {
ch := val[valIndex]
if ch == '*' {
// special-case handling for string ending in '*"' - transition to field match on any character.
// we know the trailing '"' will be there because of JSON syntax. We could use an epsilon state
// but then the matcher will process through all the rest of the bytes, when it doesn't need to
if valIndex == len(val)-2 {
step := &faState{
table: newSmallTable(),
fieldTransitions: []*fieldMatcher{nextField},
}
table.epsilon = []*faState{step}
printer.labelTable(table, fmt.Sprintf("prefix escape at %d", valIndex))
return
}
globStep := &faState{table: table}
printer.labelTable(table, fmt.Sprintf("gS at %d", valIndex))
table.epsilon = []*faState{globStep}
valIndex++
globNext := &faState{table: newSmallTable()}
printer.labelTable(globNext.table, fmt.Sprintf("gX on %c at %d", val[valIndex], valIndex))
table.addByteStep(val[valIndex], &faNext{states: []*faState{globNext}})
table = globNext.table
} else {
nextStep := &faState{table: newSmallTable()}
printer.labelTable(nextStep.table, fmt.Sprintf("on %c at %d", val[valIndex], valIndex))
table.addByteStep(ch, &faNext{states: []*faState{nextStep}})
table = nextStep.table
}
valIndex++
}
lastStep := &faState{table: newSmallTable(), fieldTransitions: []*fieldMatcher{nextField}}
printer.labelTable(lastStep.table, fmt.Sprintf("last step at %d", valIndex))
table.addByteStep(valueTerminator, &faNext{states: []*faState{lastStep}})
return
}