@@ -21,19 +21,105 @@ auto FlatDfsItem::Init(const PathSegment& segment) -> AdvanceResult {
21
21
}
22
22
23
23
auto FlatDfsItem::Advance (const PathSegment& segment) -> AdvanceResult {
24
- return AdvanceResult{};
24
+ if (state_ == kInit ) {
25
+ return Init (segment);
26
+ }
27
+
28
+ if (!ShouldIterateAll (segment.type ()))
29
+ return Exhausted ();
30
+
31
+ ++state_;
32
+ auto vec = obj ().AsVector ();
33
+ if (state_ >= vec.size ())
34
+ return Exhausted ();
35
+ return Next (vec[state_]);
25
36
}
26
37
27
38
FlatDfs FlatDfs::Traverse (absl::Span<const PathSegment> path, const flexbuffers::Reference root,
28
39
const PathFlatCallback& callback) {
29
40
DCHECK (!path.empty ());
30
41
FlatDfs dfs;
31
42
43
+ if (path.size () == 1 ) {
44
+ dfs.PerformStep (path[0 ], root, callback);
45
+ return dfs;
46
+ }
47
+
48
+ using ConstItem = FlatDfsItem;
49
+ vector<ConstItem> stack;
50
+ stack.emplace_back (root);
51
+
52
+ do {
53
+ unsigned segment_index = stack.back ().segment_idx ();
54
+ const auto & path_segment = path[segment_index];
55
+
56
+ // init or advance the current object
57
+ ConstItem::AdvanceResult res = stack.back ().Advance (path_segment);
58
+ if (res && !res->first .IsNull ()) {
59
+ const flexbuffers::Reference next = res->first ;
60
+ DVLOG (2 ) << " Handling now " << next.GetType () << " " << next.ToString ();
61
+
62
+ // We descent only if next is object or an array.
63
+ if (IsRecursive (next.GetType ())) {
64
+ unsigned next_seg_id = res->second ;
65
+
66
+ if (next_seg_id + 1 < path.size ()) {
67
+ stack.emplace_back (next, next_seg_id);
68
+ } else {
69
+ // terminal step
70
+ // TODO: to take into account MatchStatus
71
+ // for `json.set foo $.a[10]` or for `json.set foo $.*.b`
72
+ dfs.PerformStep (path[next_seg_id], next, callback);
73
+ }
74
+ }
75
+ } else {
76
+ stack.pop_back ();
77
+ }
78
+ } while (!stack.empty ());
79
+
32
80
return dfs;
33
81
}
34
82
35
83
auto FlatDfs::PerformStep (const PathSegment& segment, const flexbuffers::Reference node,
36
84
const PathFlatCallback& callback) -> nonstd::expected<void, MatchStatus> {
85
+ switch (segment.type ()) {
86
+ case SegmentType::IDENTIFIER: {
87
+ if (!node.IsMap ())
88
+ return make_unexpected (MISMATCH);
89
+ auto map = node.AsMap ();
90
+ flexbuffers::Reference value = map[segment.identifier ().c_str ()];
91
+ if (!value.IsNull ()) {
92
+ DoCall (callback, string_view{segment.identifier ()}, value);
93
+ }
94
+ } break ;
95
+ case SegmentType::INDEX: {
96
+ if (!node.IsVector ())
97
+ return make_unexpected (MISMATCH);
98
+ auto vec = node.AsVector ();
99
+ if (segment.index () >= vec.size ()) {
100
+ return make_unexpected (OUT_OF_BOUNDS);
101
+ }
102
+ DoCall (callback, nullopt, vec[segment.index ()]);
103
+ } break ;
104
+
105
+ case SegmentType::DESCENT:
106
+ case SegmentType::WILDCARD: {
107
+ auto vec = node.AsVector (); // always succeeds
108
+ auto keys = node.AsMap ().Keys (); // always succeeds
109
+ string str;
110
+ for (size_t i = 0 ; i < vec.size (); ++i) {
111
+ flexbuffers::Reference key = keys[i];
112
+ optional<string_view> opt_key;
113
+ if (key.IsString ()) {
114
+ str = key.ToString ();
115
+ opt_key = str;
116
+ }
117
+ DoCall (callback, opt_key, vec[i]);
118
+ }
119
+ } break ;
120
+ default :
121
+ LOG (DFATAL) << " Unknown segment " << SegmentName (segment.type ());
122
+ }
37
123
return {};
38
124
}
39
125
0 commit comments