|
| 1 | +/* MiniJSON - a small self-contained JSON reader. |
| 2 | + * |
| 3 | + * Parses UTF-8 JSON text into a Value DOM (object/array/string/number/ |
| 4 | + * boolean/null). Used by the ShEx shape-map and manifest readers; not a |
| 5 | + * general-purpose serializer. |
| 6 | + */ |
| 7 | + |
| 8 | +#ifndef SWOBJ_MINIJSON_H |
| 9 | +#define SWOBJ_MINIJSON_H |
| 10 | + |
| 11 | +#include <map> |
| 12 | +#include <vector> |
| 13 | +#include <string> |
| 14 | +#include <sstream> |
| 15 | +#include <stdexcept> |
| 16 | + |
| 17 | +namespace w3c_sw { |
| 18 | +namespace MiniJSON { |
| 19 | + |
| 20 | + struct ParseError : public std::runtime_error { |
| 21 | + ParseError (std::string what) : std::runtime_error("JSON: " + what) { } |
| 22 | + }; |
| 23 | + |
| 24 | + class Value { |
| 25 | + public: |
| 26 | + enum Type { Null_T, Bool_T, Number_T, String_T, Array_T, Object_T }; |
| 27 | + Type type; |
| 28 | + bool boolean; |
| 29 | + double number; |
| 30 | + std::string string; // also holds the raw number text |
| 31 | + std::vector<Value> array; |
| 32 | + std::vector<std::pair<std::string, Value> > object; // insertion-ordered |
| 33 | + |
| 34 | + Value () : type(Null_T), boolean(false), number(0) { } |
| 35 | + |
| 36 | + bool isNull () const { return type == Null_T; } |
| 37 | + bool isString () const { return type == String_T; } |
| 38 | + bool isArray () const { return type == Array_T; } |
| 39 | + bool isObject () const { return type == Object_T; } |
| 40 | + |
| 41 | + const Value* get (const std::string& key) const { |
| 42 | + for (std::vector<std::pair<std::string, Value> >::const_iterator it = object.begin(); |
| 43 | + it != object.end(); ++it) |
| 44 | + if (it->first == key) |
| 45 | + return &it->second; |
| 46 | + return NULL; |
| 47 | + } |
| 48 | + std::string getString (const std::string& key, std::string dflt = "") const { |
| 49 | + const Value* v = get(key); |
| 50 | + return v != NULL && v->type == String_T ? v->string : dflt; |
| 51 | + } |
| 52 | + }; |
| 53 | + |
| 54 | + class Parser { |
| 55 | + const std::string& text; |
| 56 | + size_t pos; |
| 57 | + |
| 58 | + public: |
| 59 | + Parser (const std::string& text) : text(text), pos(0) { } |
| 60 | + |
| 61 | + Value parse () { |
| 62 | + Value ret = parseValue(); |
| 63 | + skipWS(); |
| 64 | + if (pos != text.size()) |
| 65 | + throw ParseError("trailing content at offset " + offsetStr()); |
| 66 | + return ret; |
| 67 | + } |
| 68 | + |
| 69 | + private: |
| 70 | + std::string offsetStr () const { |
| 71 | + std::stringstream ss; |
| 72 | + ss << pos; |
| 73 | + return ss.str(); |
| 74 | + } |
| 75 | + void skipWS () { |
| 76 | + while (pos < text.size() && |
| 77 | + (text[pos] == ' ' || text[pos] == '\t' || text[pos] == '\r' || text[pos] == '\n')) |
| 78 | + ++pos; |
| 79 | + } |
| 80 | + char peek () { |
| 81 | + skipWS(); |
| 82 | + if (pos >= text.size()) |
| 83 | + throw ParseError("unexpected end of input"); |
| 84 | + return text[pos]; |
| 85 | + } |
| 86 | + void expect (char c) { |
| 87 | + if (peek() != c) |
| 88 | + throw ParseError(std::string("expected '") + c + "' at offset " + offsetStr()); |
| 89 | + ++pos; |
| 90 | + } |
| 91 | + bool tryConsume (char c) { |
| 92 | + if (pos < text.size() && peek() == c) { |
| 93 | + ++pos; |
| 94 | + return true; |
| 95 | + } |
| 96 | + return false; |
| 97 | + } |
| 98 | + |
| 99 | + Value parseValue () { |
| 100 | + switch (peek()) { |
| 101 | + case '{': return parseObject(); |
| 102 | + case '[': return parseArray(); |
| 103 | + case '"': { |
| 104 | + Value v; |
| 105 | + v.type = Value::String_T; |
| 106 | + v.string = parseString(); |
| 107 | + return v; |
| 108 | + } |
| 109 | + case 't': case 'f': return parseKeyword(); |
| 110 | + case 'n': return parseKeyword(); |
| 111 | + default: return parseNumber(); |
| 112 | + } |
| 113 | + } |
| 114 | + |
| 115 | + Value parseObject () { |
| 116 | + Value v; |
| 117 | + v.type = Value::Object_T; |
| 118 | + expect('{'); |
| 119 | + if (tryConsume('}')) |
| 120 | + return v; |
| 121 | + do { |
| 122 | + if (peek() != '"') |
| 123 | + throw ParseError("expected object key at offset " + offsetStr()); |
| 124 | + std::string key = parseString(); |
| 125 | + expect(':'); |
| 126 | + v.object.push_back(std::make_pair(key, parseValue())); |
| 127 | + } while (tryConsume(',')); |
| 128 | + expect('}'); |
| 129 | + return v; |
| 130 | + } |
| 131 | + |
| 132 | + Value parseArray () { |
| 133 | + Value v; |
| 134 | + v.type = Value::Array_T; |
| 135 | + expect('['); |
| 136 | + if (tryConsume(']')) |
| 137 | + return v; |
| 138 | + do { |
| 139 | + v.array.push_back(parseValue()); |
| 140 | + } while (tryConsume(',')); |
| 141 | + expect(']'); |
| 142 | + return v; |
| 143 | + } |
| 144 | + |
| 145 | + std::string parseString () { |
| 146 | + expect('"'); |
| 147 | + std::string ret; |
| 148 | + while (true) { |
| 149 | + if (pos >= text.size()) |
| 150 | + throw ParseError("unterminated string"); |
| 151 | + char c = text[pos++]; |
| 152 | + if (c == '"') |
| 153 | + return ret; |
| 154 | + if (c != '\\') { |
| 155 | + ret += c; |
| 156 | + continue; |
| 157 | + } |
| 158 | + if (pos >= text.size()) |
| 159 | + throw ParseError("unterminated escape"); |
| 160 | + char e = text[pos++]; |
| 161 | + switch (e) { |
| 162 | + case '"': ret += '"'; break; |
| 163 | + case '\\': ret += '\\'; break; |
| 164 | + case '/': ret += '/'; break; |
| 165 | + case 'b': ret += '\b'; break; |
| 166 | + case 'f': ret += '\f'; break; |
| 167 | + case 'n': ret += '\n'; break; |
| 168 | + case 'r': ret += '\r'; break; |
| 169 | + case 't': ret += '\t'; break; |
| 170 | + case 'u': { |
| 171 | + if (pos + 4 > text.size()) |
| 172 | + throw ParseError("truncated \\u escape"); |
| 173 | + unsigned long cp = strtoul(text.substr(pos, 4).c_str(), NULL, 16); |
| 174 | + pos += 4; |
| 175 | + // surrogate pairs |
| 176 | + if (cp >= 0xD800 && cp <= 0xDBFF && pos + 6 <= text.size() |
| 177 | + && text[pos] == '\\' && text[pos+1] == 'u') { |
| 178 | + unsigned long lo = strtoul(text.substr(pos+2, 4).c_str(), NULL, 16); |
| 179 | + if (lo >= 0xDC00 && lo <= 0xDFFF) { |
| 180 | + cp = 0x10000 + ((cp - 0xD800) << 10) + (lo - 0xDC00); |
| 181 | + pos += 6; |
| 182 | + } |
| 183 | + } |
| 184 | + // utf-8 encode |
| 185 | + if (cp < 0x80) |
| 186 | + ret += (char)cp; |
| 187 | + else if (cp < 0x800) { |
| 188 | + ret += (char)(0xC0 | (cp >> 6)); |
| 189 | + ret += (char)(0x80 | (cp & 0x3F)); |
| 190 | + } else if (cp < 0x10000) { |
| 191 | + ret += (char)(0xE0 | (cp >> 12)); |
| 192 | + ret += (char)(0x80 | ((cp >> 6) & 0x3F)); |
| 193 | + ret += (char)(0x80 | (cp & 0x3F)); |
| 194 | + } else { |
| 195 | + ret += (char)(0xF0 | (cp >> 18)); |
| 196 | + ret += (char)(0x80 | ((cp >> 12) & 0x3F)); |
| 197 | + ret += (char)(0x80 | ((cp >> 6) & 0x3F)); |
| 198 | + ret += (char)(0x80 | (cp & 0x3F)); |
| 199 | + } |
| 200 | + break; |
| 201 | + } |
| 202 | + default: |
| 203 | + throw ParseError(std::string("invalid escape \\") + e); |
| 204 | + } |
| 205 | + } |
| 206 | + } |
| 207 | + |
| 208 | + Value parseKeyword () { |
| 209 | + Value v; |
| 210 | + if (text.compare(pos, 4, "true") == 0) { |
| 211 | + v.type = Value::Bool_T; |
| 212 | + v.boolean = true; |
| 213 | + pos += 4; |
| 214 | + } else if (text.compare(pos, 5, "false") == 0) { |
| 215 | + v.type = Value::Bool_T; |
| 216 | + v.boolean = false; |
| 217 | + pos += 5; |
| 218 | + } else if (text.compare(pos, 4, "null") == 0) { |
| 219 | + v.type = Value::Null_T; |
| 220 | + pos += 4; |
| 221 | + } else |
| 222 | + throw ParseError("unexpected token at offset " + offsetStr()); |
| 223 | + return v; |
| 224 | + } |
| 225 | + |
| 226 | + Value parseNumber () { |
| 227 | + size_t start = pos; |
| 228 | + if (pos < text.size() && text[pos] == '-') |
| 229 | + ++pos; |
| 230 | + while (pos < text.size() && |
| 231 | + ((text[pos] >= '0' && text[pos] <= '9') || text[pos] == '.' |
| 232 | + || text[pos] == 'e' || text[pos] == 'E' || text[pos] == '+' || text[pos] == '-')) |
| 233 | + ++pos; |
| 234 | + if (pos == start) |
| 235 | + throw ParseError("unexpected character at offset " + offsetStr()); |
| 236 | + Value v; |
| 237 | + v.type = Value::Number_T; |
| 238 | + v.string = text.substr(start, pos - start); |
| 239 | + v.number = atof(v.string.c_str()); |
| 240 | + return v; |
| 241 | + } |
| 242 | + }; |
| 243 | + |
| 244 | + inline Value parse (const std::string& text) { |
| 245 | + Parser p(text); |
| 246 | + return p.parse(); |
| 247 | + } |
| 248 | + |
| 249 | +} // namespace MiniJSON |
| 250 | +} // namespace w3c_sw |
| 251 | + |
| 252 | +#endif // !SWOBJ_MINIJSON_H |
0 commit comments