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!string 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!string 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 alias Token = JSONToken!string; 130 131 with (JSONType) final switch (value.type) 132 { 133 case null_: 134 this.currentValue.literal = Token(null); 135 break; 136 case string: 137 this.currentValue.literal = Token(value.str); 138 break; 139 case integer: 140 this.currentValue.literal = Token(value.integer); 141 break; 142 case uinteger: 143 this.currentValue.literal = Token(value.uinteger); 144 break; 145 case float_: 146 this.currentValue.literal = Token(value.floating); 147 break; 148 case array: 149 this.currentValue.kind = JSONParserNodeKind.arrayStart; 150 pushState(value); 151 break; 152 case object: 153 this.currentValue.kind = JSONParserNodeKind.objectStart; 154 pushState(value); 155 break; 156 case true_: 157 this.currentValue.literal = Token(true); 158 break; 159 case false_: 160 this.currentValue.literal = Token(false); 161 break; 162 } 163 } 164 165 private void pushState(JSONValue value) 166 { 167 if (this.level >= 0) 168 { 169 if (this.level == this.iterators.length) 170 { 171 this.iterators ~= this.previous; 172 } 173 else 174 { 175 this.iterators[this.level] = this.previous; 176 } 177 } 178 this.previous = this.current; 179 this.current = ValueIterator(value); 180 this.level++; 181 } 182 183 private void popState() 184 { 185 this.level--; 186 if (!outOfValues) 187 { 188 this.current = this.previous; 189 if (this.level >= 0) // handle -1 case 190 { 191 this.previous = this.iterators[this.level]; 192 } 193 } 194 } 195 196 public bool outOfValues() const 197 { 198 return this.level == -2; 199 } 200 } 201 202 private struct ValueIterator 203 { 204 JSONValue value; 205 206 invariant(value.type == JSONType.array || value.type == JSONType.object); 207 208 // if `value` is an array or object, indicates the next element to be selected. 209 size_t nextIndex = 0; 210 211 bool usedKey; // objects are key, value, key, value => false, true, false, true... 212 }