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 void popFront() 62 in (!empty) 63 { 64 if (outOfValues) 65 { 66 empty = true; 67 return; 68 } 69 70 if (current.value.type == JSONType.object) 71 { 72 if (current.nextIndex == current.value.objectNoRef.length) 73 { 74 this.currentValue.kind = JSONParserNodeKind.objectEnd; 75 popState; 76 return; 77 } 78 79 if (!current.usedKey) 80 { 81 this.currentValue.key = current.value.objectNoRef.byKeyValue.drop(current.nextIndex).front.key; 82 current.usedKey = true; 83 return; 84 } 85 86 auto value = current.value.objectNoRef.byKeyValue.drop(current.nextIndex).front.value; 87 88 current.usedKey = false; 89 current.nextIndex++; 90 stepInto(value); 91 return; 92 } 93 else if (current.value.type == JSONType.array) 94 { 95 if (current.nextIndex == current.value.arrayNoRef.length) 96 { 97 this.currentValue.kind = JSONParserNodeKind.arrayEnd; 98 popState; 99 return; 100 } 101 auto value = current.value.arrayNoRef[current.nextIndex]; 102 103 current.nextIndex++; 104 stepInto(value); 105 } 106 else 107 { 108 import std.format : format; 109 110 assert(false, format!"unexpected value type: %s"(current.value.type)); 111 } 112 } 113 114 private void stepInto(JSONValue value) 115 { 116 alias Token = JSONToken!string; 117 118 with (JSONType) final switch (value.type) 119 { 120 case null_: 121 this.currentValue.literal = Token(null); 122 break; 123 case string: 124 this.currentValue.literal = Token(value.str); 125 break; 126 case integer: 127 this.currentValue.literal = Token(value.integer); 128 break; 129 case uinteger: 130 this.currentValue.literal = Token(value.uinteger); 131 break; 132 case float_: 133 this.currentValue.literal = Token(value.floating); 134 break; 135 case array: 136 this.currentValue.kind = JSONParserNodeKind.arrayStart; 137 pushState(value); 138 break; 139 case object: 140 this.currentValue.kind = JSONParserNodeKind.objectStart; 141 pushState(value); 142 break; 143 case true_: 144 this.currentValue.literal = Token(true); 145 break; 146 case false_: 147 this.currentValue.literal = Token(false); 148 break; 149 } 150 } 151 152 private void pushState(JSONValue value) 153 { 154 if (this.level >= 0) 155 { 156 if (this.level == this.iterators.length) 157 { 158 this.iterators ~= this.previous; 159 } 160 else 161 { 162 this.iterators[this.level] = this.previous; 163 } 164 } 165 this.previous = this.current; 166 this.current = ValueIterator(value); 167 this.level++; 168 } 169 170 private void popState() 171 { 172 this.level--; 173 if (!outOfValues) 174 { 175 this.current = this.previous; 176 if (this.level >= 0) // handle -1 case 177 { 178 this.previous = this.iterators[this.level]; 179 } 180 } 181 } 182 183 public bool outOfValues() const 184 { 185 return this.level == -2; 186 } 187 } 188 189 private struct ValueIterator 190 { 191 JSONValue value; 192 193 invariant(value.type == JSONType.array || value.type == JSONType.object); 194 195 // if `value` is an array or object, indicates the next element to be selected. 196 size_t nextIndex = 0; 197 198 bool usedKey; // objects are key, value, key, value => false, true, false, true... 199 }