forked from timbray/quamina
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathanything_but.go
143 lines (135 loc) · 4.5 KB
/
anything_but.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
package quamina
import (
"encoding/json"
"errors"
"fmt"
"io"
)
func readAnythingButSpecial(pb *patternBuild, valsIn []typedVal) (pathVals []typedVal, err error) {
t, err := pb.jd.Token()
if err != nil {
return
}
pathVals = valsIn
fieldCount := 0
delim, ok := t.(json.Delim)
if (!ok) || delim != '[' {
err = errors.New("value for anything-but must be an array")
return
}
done := false
val := typedVal{vType: anythingButType}
for !done {
t, err = pb.jd.Token()
if err == io.EOF {
err = errors.New("anything-but list truncated")
return
} else if err != nil {
return
}
switch tt := t.(type) {
case json.Delim:
if tt == ']' {
done = true
} else {
err = fmt.Errorf("spurious %c in anything-but list", tt)
}
case string:
fieldCount++
val.list = append(val.list, []byte(`"`+tt+`"`))
default:
err = errors.New("malformed anything-but list")
done = true
}
}
if err != nil {
return
}
if fieldCount == 0 {
err = errors.New("empty list in 'anything-but' pattern")
return
}
pathVals = append(pathVals, val)
// this has to be a '}' or you're going to get an err from the tokenizer, so no point looking at the value
_, err = pb.jd.Token()
return
}
// makeMultiFieldAnythingButAutomaton exists to handle constructs such as
//
// {"x": [ {"anything-but": [ "a", "b" ] } ] }
//
// Making a succession of anything-but automata for each of "a" and "b" and then merging them turns out not
// to work because what the caller means is really an AND - everything that matches neither "a" nor "b". So
// in principle we could intersect automata, which is probably the right answer, but for the moment we can
// build like makeAnythingButAutomaton but do it for several vals in parallel
func makeMultiAnythingButAutomaton(vals [][]byte, useThisTransition *fieldMatcher) (*smallTable[*dfaStep], *fieldMatcher) {
var nextField *fieldMatcher
if useThisTransition != nil {
nextField = useThisTransition
} else {
nextField = newFieldMatcher()
}
ret, _ := oneMultiAnythingButStep(vals, 0, nextField), nextField
return ret, nextField
}
// oneMultiAnythingButStep - spookeh
func oneMultiAnythingButStep(vals [][]byte, index int, nextField *fieldMatcher) *smallTable[*dfaStep] {
success := &dfaStep{table: newSmallTable[*dfaStep](), fieldTransitions: []*fieldMatcher{nextField}}
var u unpackedTable[*dfaStep]
for i := range u {
u[i] = success
}
// for the char at position 'index' in each val
nextSteps := make(map[byte][][]byte)
lastSteps := make(map[byte]bool)
for _, val := range vals {
lastIndex := len(val) - 1
switch {
case index < lastIndex:
utf8Byte := val[index]
step := nextSteps[utf8Byte]
nextSteps[utf8Byte] = append(step, val)
case index == lastIndex:
lastSteps[val[index]] = true
case index > lastIndex:
// no-op
}
}
for utf8Byte, valList := range nextSteps {
u[utf8Byte] = &dfaStep{table: oneMultiAnythingButStep(valList, index+1, nextField)}
}
for utf8Byte := range lastSteps {
lastStep := &dfaStep{table: newSmallTable[*dfaStep]()} // note no transition
u[utf8Byte] = &dfaStep{table: makeSmallDfaTable(success, []byte{valueTerminator}, []*dfaStep{lastStep})}
}
table := newSmallTable[*dfaStep]()
table.pack(&u)
return table
}
/*
// makeAnythingButAutomaton produces a DFA that matches anything but the byte sequence in val.
// For each byte in val with value Z, we produce a table that leads to a nextField match on all non-Z values,
// and to another such table for Z. After all the bytes have matched, a match on valueTerminator leads to
// an empty table with no field Transitions, all others to a nexField match
// [no longer used but retaining for now]
func makeAnythingButAutomaton(val []byte, useThisTransition *fieldMatcher) (*smallTable[*dfaStep], *fieldMatcher) {
var nextField *fieldMatcher
if useThisTransition != nil {
nextField = useThisTransition
} else {
nextField = newFieldMatcher()
}
return oneAnythingButStep(val, 0, nextField), nextField
}
func oneAnythingButStep(val []byte, index int, nextField *fieldMatcher) *smallTable[*dfaStep] {
var nextStep *dfaStep
success := &dfaStep{table: newSmallTable[*dfaStep](), fieldTransitions: []*fieldMatcher{nextField}}
if index == len(val)-1 {
lastStep := &dfaStep{table: newSmallTable[*dfaStep]()} // note no transition
nextStep = &dfaStep{table: makeSmallDfaTable(success, []byte{valueTerminator}, []*dfaStep{lastStep})}
} else {
nextStep = &dfaStep{table: oneAnythingButStep(val, index+1, nextField)}
}
return makeSmallDfaTable(success, []byte{val[index]}, []*dfaStep{nextStep})
}
*/