1 /** 2 * This module takes a `std.json` JSONValue and generates a `stdx.data.json` token stream. 3 * It provides Phobos backwards compatibility for `std_data_json`. 4 */ 5 module text.json.JsonValueRange; 6 7 import funkwerk.stdx.data.json.lexer; 8 import funkwerk.stdx.data.json.parser; 9 import std.algorithm : count; 10 import std.json; 11 import std.range : drop; 12 13 static assert(isJSONParserNodeInputRange!JsonValueRange); 14 15 struct JsonValueRange 16 { 17 public bool empty; 18 19 private JSONParserNode currentValue; 20 21 private ValueIterator[] iterators; 22 23 /** 24 * In order to allow us to be save()-less, it is not enough to just have the current iterator as array state! 25 * Consider the following case: [ A, B ], where A and B are objects. 26 * When decoding A, we are set to objectStart, but the current iterator is already A: that is, the iteration 27 * state on the level of [] is the previous state and thus must be saved also. 28 * Otherwise, when we finish parsing A, we will *consume* objectEnd and position ourselves at objectStart 29 * for B, advancing the state for the array iterator - which is one up from where we started. 30 */ 31 private ValueIterator current, previous; 32 33 private int level; 34 35 invariant(this.level <= cast(int) this.iterators.length); 36 37 public this(JSONValue value) 38 { 39 this.empty = false; 40 // current = 0, previous = -1 41 this.level = -2; 42 stepInto(value); 43 } 44 45 // For debugging. 46 string toString() 47 { 48 import std.format : format; 49 import std.algorithm : max; 50 51 return format!"JsonValueRange(%s, %s, %s: %s > %s > %s)"( 52 empty, currentValue, level, current, previous, iterators[0 .. max(0, level)]); 53 } 54 55 public @property ref JSONParserNode front() return 56 in (!empty) 57 { 58 return this.currentValue; 59 } 60 61 public JsonValueRange dup() const 62 { 63 JsonValueRange result; 64 65 result.empty = this.empty; 66 result.currentValue = this.currentValue; 67 result.iterators = this.iterators.dup; 68 result.current = current; 69 result.previous = previous; 70 result.level = level; 71 return result; 72 } 73 74 public void popFront() 75 in (!empty) 76 { 77 if (outOfValues) 78 { 79 empty = true; 80 return; 81 } 82 83 if (current.value.type == JSONType.object) 84 { 85 if (current.nextIndex == current.value.objectNoRef.length) 86 { 87 this.currentValue.kind = JSONParserNodeKind.objectEnd; 88 popState; 89 return; 90 } 91 92 if (!current.usedKey) 93 { 94 this.currentValue.key = current.value.objectNoRef.byKeyValue.drop(current.nextIndex).front.key; 95 current.usedKey = true; 96 return; 97 } 98 99 auto value = current.value.objectNoRef.byKeyValue.drop(current.nextIndex).front.value; 100 101 current.usedKey = false; 102 current.nextIndex++; 103 stepInto(value); 104 return; 105 } 106 else if (current.value.type == JSONType.array) 107 { 108 if (current.nextIndex == current.value.arrayNoRef.length) 109 { 110 this.currentValue.kind = JSONParserNodeKind.arrayEnd; 111 popState; 112 return; 113 } 114 auto value = current.value.arrayNoRef[current.nextIndex]; 115 116 current.nextIndex++; 117 stepInto(value); 118 } 119 else 120 { 121 import std.format : format; 122 123 assert(false, format!"unexpected value type: %s in %s"(current.value.type, this)); 124 } 125 } 126 127 private void stepInto(JSONValue value) 128 { 129 with (JSONType) final switch (value.type) 130 { 131 case null_: 132 this.currentValue.literal = JSONToken(null); 133 break; 134 case string: 135 this.currentValue.literal = JSONToken(value.str); 136 break; 137 case integer: 138 this.currentValue.literal = JSONToken(value.integer); 139 break; 140 case uinteger: 141 this.currentValue.literal = JSONToken(value.uinteger); 142 break; 143 case float_: 144 this.currentValue.literal = JSONToken(value.floating); 145 break; 146 case array: 147 this.currentValue.kind = JSONParserNodeKind.arrayStart; 148 pushState(value); 149 break; 150 case object: 151 this.currentValue.kind = JSONParserNodeKind.objectStart; 152 pushState(value); 153 break; 154 case true_: 155 this.currentValue.literal = JSONToken(true); 156 break; 157 case false_: 158 this.currentValue.literal = JSONToken(false); 159 break; 160 } 161 } 162 163 private void pushState(JSONValue value) 164 { 165 if (this.level >= 0) 166 { 167 if (this.level == this.iterators.length) 168 { 169 this.iterators ~= this.previous; 170 } 171 else 172 { 173 this.iterators[this.level] = this.previous; 174 } 175 } 176 this.previous = this.current; 177 this.current = ValueIterator(value); 178 this.level++; 179 } 180 181 private void popState() 182 { 183 this.level--; 184 if (!outOfValues) 185 { 186 this.current = this.previous; 187 if (this.level >= 0) // handle -1 case 188 { 189 this.previous = this.iterators[this.level]; 190 } 191 } 192 } 193 194 public bool outOfValues() const 195 { 196 return this.level == -2; 197 } 198 } 199 200 private struct ValueIterator 201 { 202 JSONValue value; 203 204 invariant(value.type == JSONType.array || value.type == JSONType.object); 205 206 // if `value` is an array or object, indicates the next element to be selected. 207 size_t nextIndex = 0; 208 209 bool usedKey; // objects are key, value, key, value => false, true, false, true... 210 }