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 private int level; 24 25 invariant(this.level < cast(int) this.iterators.length); 26 27 public this(JSONValue value) 28 { 29 this.empty = false; 30 this.level = -1; 31 stepInto(value); 32 } 33 34 public @property ref JSONParserNode!string front() 35 in (!empty) 36 { 37 return this.currentValue; 38 } 39 40 private void stepInto(JSONValue value) 41 { 42 alias Token = JSONToken!string; 43 44 with (JSONType) final switch (value.type) 45 { 46 case null_: 47 this.currentValue.literal = Token(null); 48 break; 49 case string: 50 this.currentValue.literal = Token(value.str); 51 break; 52 case integer: 53 this.currentValue.literal = Token(value.integer); 54 break; 55 case uinteger: 56 this.currentValue.literal = Token(value.uinteger); 57 break; 58 case float_: 59 this.currentValue.literal = Token(value.floating); 60 break; 61 case array: 62 this.currentValue.kind = JSONParserNodeKind.arrayStart; 63 pushState(value); 64 break; 65 case object: 66 this.currentValue.kind = JSONParserNodeKind.objectStart; 67 pushState(value); 68 break; 69 case true_: 70 this.currentValue.literal = Token(true); 71 break; 72 case false_: 73 this.currentValue.literal = Token(false); 74 break; 75 } 76 } 77 78 private void pushState(JSONValue value) 79 { 80 if (this.level + 1 == this.iterators.length) 81 { 82 this.iterators ~= ValueIterator(value); 83 } 84 else 85 { 86 this.iterators[this.level + 1] = ValueIterator(value); 87 } 88 this.level++; 89 } 90 91 public void popFront() 92 in (!empty) 93 { 94 if (outOfValues) 95 { 96 empty = true; 97 return; 98 } 99 100 if (current.value.type == JSONType.object) 101 { 102 if (current.nextIndex == current.value.objectNoRef.length) 103 { 104 this.currentValue.kind = JSONParserNodeKind.objectEnd; 105 popState; 106 return; 107 } 108 109 if (!current.usedKey) 110 { 111 this.currentValue.key = current.value.objectNoRef.byKeyValue.drop(current.nextIndex).front.key; 112 current.usedKey = true; 113 return; 114 } 115 116 auto value = current.value.objectNoRef.byKeyValue.drop(current.nextIndex).front.value; 117 118 current.usedKey = false; 119 current.nextIndex++; 120 stepInto(value); 121 return; 122 } 123 else if (current.value.type == JSONType.array) 124 { 125 if (current.nextIndex == current.value.arrayNoRef.length) 126 { 127 this.currentValue.kind = JSONParserNodeKind.arrayEnd; 128 popState; 129 return; 130 } 131 stepInto(current.value.arrayNoRef[current.nextIndex++]); 132 } 133 else 134 { 135 assert(false, "unexpected value type"); 136 } 137 } 138 139 private void popState() 140 { 141 this.level--; 142 } 143 144 private ref ValueIterator current() 145 in (!outOfValues) 146 { 147 return this.iterators[this.level]; 148 } 149 150 public bool outOfValues() const 151 { 152 return this.level == -1; 153 } 154 } 155 156 private struct ValueIterator 157 { 158 JSONValue value; 159 160 invariant(value.type == JSONType.array || value.type == JSONType.object); 161 162 // if `value` is an array or object, indicates the next element to be selected. 163 size_t nextIndex = 0; 164 165 bool usedKey; // objects are key, value, key, value => false, true, false, true... 166 }