| #include <stdlib.h>␍␊ |
| #include <stdio.h>␍␊ |
| #include <limits.h>␍␊ |
| ␍␊ |
| #include "ptypes.h"␍␊ |
| #include "pstreams.h"␍␊ |
| ␍␊ |
| ␍␊ |
| PTYPES_BEGIN␍␊ |
| ␍␊ |
| pt::string json::JsonEncode(const pt::variant & v)␍␊ |
| {␍␊ |
| ␉pt::string builder;␍␊ |
| ␉bool success = SerializeValue(v, builder);␍␊ |
| ␍␊ |
| ␉if (success)␍␊ |
| ␉␉return builder;␍␊ |
| ␉else␍␊ |
| ␉␉return "";␍␊ |
| }␍␊ |
| ␍␊ |
| bool json::SerializeValue(const pt::variant & value, pt::string & builder)␍␊ |
| {␍␊ |
| ␉bool success = true;␍␊ |
| ␉if (pt::isstring(value))␍␊ |
| ␉{␍␊ |
| ␉␉success = SerializeString((pt::string)value, builder);␍␊ |
| ␉} else if (pt::isobject(value)) {␍␊ |
| ␉␉pt::tparray * arr = (pt::tparray *)(pt::component *)value;␍␊ |
| ␉␉if (pt::isassoc((*arr)))␍␊ |
| ␉␉{␍␊ |
| ␉␉␉success = SerializeObject(*arr, builder);␍␊ |
| ␉␉} else if (pt::islist((*arr))) {␍␊ |
| ␉␉␉success = SerializeArray(*arr, builder);␍␊ |
| ␉␉}␍␊ |
| ␉} else if (pt::isbool(value)) {␍␊ |
| ␉␉if ((bool)value)␍␊ |
| ␉␉␉builder += "true";␍␊ |
| ␉␉else␍␊ |
| ␉␉␉builder += "false";␍␊ |
| ␉} else if (pt::isint(value)) {␍␊ |
| ␉␉success = SerializeNumber(value, builder);␍␊ |
| ␉} else if (pt::isnull(value)) {␍␊ |
| ␉␉builder += "null";␍␊ |
| ␉} else {␍␊ |
| ␉␉success = false;␍␊ |
| ␉}␍␊ |
| ␉return success;␍␊ |
| }␍␊ |
| ␍␊ |
| bool json::SerializeNumber(large number, string & builder)␍␊ |
| {␍␊ |
| ␉builder += pt::itostring(number);␍␊ |
| ␉return true;␍␊ |
| }␍␊ |
| ␍␊ |
| bool json::SerializeArray(const tparray & anArray, string & builder)␍␊ |
| {␍␊ |
| ␉builder += "[";␍␊ |
| ␍␊ |
| ␉bool first = true;␍␊ |
| ␉variant val;␍␊ |
| ␉for(int i = 0; anext(anArray, i, val);)␍␊ |
| ␉{␍␊ |
| ␉␉if (!first)␍␊ |
| ␉␉␉builder += ", ";␍␊ |
| ␍␊ |
| ␉␉if (!SerializeValue(val, builder))␍␊ |
| ␉␉{␍␊ |
| ␉␉␉return false;␍␊ |
| ␉␉}␍␊ |
| ␍␊ |
| ␉␉first = false;␍␊ |
| ␉}␍␊ |
| ␍␊ |
| ␉builder += "]";␍␊ |
| ␉return true;␍␊ |
| }␍␊ |
| ␍␊ |
| bool json::SerializeObject(const tparray & anObject, string & builder)␍␊ |
| {␍␊ |
| ␉builder += "{";␍␊ |
| ␍␊ |
| ␉bool first = true;␍␊ |
| ␉pt::variant key, val;␍␊ |
| ␉for(int i = 0; anext(anObject, i, val, key);)␍␊ |
| ␉{␍␊ |
| ␉␉if (!first)␍␊ |
| ␉␉{␍␊ |
| ␉␉␉builder += ", ";␍␊ |
| ␉␉}␍␊ |
| ␉␉SerializeString(key, builder);␍␊ |
| ␉␉builder += ":";␍␊ |
| ␉␉if (!SerializeValue(val, builder))␍␊ |
| ␉␉{␍␊ |
| ␉␉␉return false;␍␊ |
| ␉␉}␍␊ |
| ␍␊ |
| ␉␉first = false;␍␊ |
| ␉}␍␊ |
| ␍␊ |
| ␉builder += "}";␍␊ |
| ␍␊ |
| ␉return true;␍␊ |
| }␍␊ |
| ␍␊ |
| bool json::SerializeString(const string & aString, string & builder)␍␊ |
| {␍␊ |
| ␉builder += '"';␍␊ |
| ␍␊ |
| ␉for(int i = 0; i < length(aString); i++)␍␊ |
| ␉{␍␊ |
| ␉␉char c = aString[i];␍␊ |
| ␍␊ |
| ␉␉if (c == '"') {␍␊ |
| ␉␉␉builder += "\\\"";␍␊ |
| ␉␉} else if (c == '\\') {␍␊ |
| ␉␉␉builder += "\\\\";␍␊ |
| ␉␉} else if (c == '\b') {␍␊ |
| ␉␉␉builder += "\\b";␍␊ |
| ␉␉} else if (c == '\f') {␍␊ |
| ␉␉␉builder += "\\f";␍␊ |
| ␉␉} else if (c == '\n') {␍␊ |
| ␉␉␉builder += "\\n";␍␊ |
| ␉␉} else if (c == '\r') {␍␊ |
| ␉␉␉builder += "\\r";␍␊ |
| ␉␉} else if (c == '\t') {␍␊ |
| ␉␉␉builder += "\\t";␍␊ |
| ␉␉} else {␍␊ |
| ␉␉␉int codepoint = (int)c;␍␊ |
| ␉␉␉if ((codepoint >= 32) && (codepoint <= 126)) {␍␊ |
| ␉␉␉␉builder += c;␍␊ |
| ␉␉␉} else {␍␊ |
| ␉␉␉␉builder += "\\u" + pt::itostring(codepoint, 16, 4, '0');␍␊ |
| ␉␉␉}␍␊ |
| ␉␉}␍␊ |
| ␉}␍␊ |
| ␍␊ |
| ␉builder += '"';␍␊ |
| ␉return true;␍␊ |
| }␍␊ |
| ␍␊ |
| #ifndef USE_VARIANT␍␊ |
| pt::tparray * json::ParseArray(const string & json, int & index, bool & success)␍␊ |
| #else␍␊ |
| pt::variant json::ParseArray(const string & json, int & index, bool & success)␍␊ |
| #endif␍␊ |
| {␍␊ |
| ␉#ifndef USE_VARIANT␍␊ |
| ␉tparray * ret = new tparray();␍␊ |
| ␉#else␍␊ |
| ␉variant ret;␍␊ |
| ␉#endif␍␊ |
| ␉NextToken(json, index);␍␊ |
| ␍␊ |
| ␉bool done = false;␍␊ |
| ␉while (!done) {␍␊ |
| ␉␉int token = LookAhead(json, index);␍␊ |
| ␉␉if (token == json::TOKEN_NONE)␍␊ |
| ␉␉{␍␊ |
| ␉␉␉success = false;␍␊ |
| ␉␉␉return ret;␍␊ |
| ␉␉} else if (token == json::TOKEN_COMMA) {␍␊ |
| ␉␉␉NextToken(json, index);␍␊ |
| ␉␉} else if (token == json::TOKEN_SQUARED_CLOSE) {␍␊ |
| ␉␉␉NextToken(json, index);␍␊ |
| ␉␉␉break;␍␊ |
| ␉␉} else {␍␊ |
| ␉␉␉variant v = ParseValue(json, index, success);␍␊ |
| ␉␉␉␍␊ |
| ␉␉␉if (!success)␍␊ |
| ␉␉␉␉return ret; // NULL␍␊ |
| ␍␊ |
| ␉␉␉#ifndef USE_VARIANT␍␊ |
| ␉␉␉add((*ret), v);␍␊ |
| ␉␉␉#else␍␊ |
| ␉␉␉add(ret, v);␍␊ |
| ␉␉␉#endif␍␊ |
| ␍␊ |
| ␉␉}␍␊ |
| ␉}␍␊ |
| ␍␊ |
| ␉return ret;␍␊ |
| }␍␊ |
| ␍␊ |
| #ifndef USE_VARIANT␍␊ |
| pt::tparray * json::ParseObject(const string & json, int & index, bool & success)␍␊ |
| #else␍␊ |
| pt::variant json::ParseObject(const string & json, int & index, bool & success)␍␊ |
| #endif␍␊ |
| {␍␊ |
| ␉#ifndef USE_VARIANT␍␊ |
| ␉tparray * ret = new tparray();␍␊ |
| ␉#else␍␊ |
| ␉pt::variant ret;␍␊ |
| ␉#endif␍␊ |
| ␉int token;␍␊ |
| ␍␊ |
| ␉NextToken(json, index);␍␊ |
| ␍␊ |
| ␉bool done = false;␍␊ |
| ␉while (!done) {␍␊ |
| ␉␉token = LookAhead(json, index);␍␊ |
| ␉␉if (token == json::TOKEN_NONE)␍␊ |
| ␉␉{␍␊ |
| ␉␉␉success = false;␍␊ |
| ␉␉␉return ret; // NULL␍␊ |
| ␉␉} else if (token == json::TOKEN_COMMA) {␍␊ |
| ␉␉␉NextToken(json, index);␍␊ |
| ␉␉} else if (token == json::TOKEN_CURLY_CLOSE) {␍␊ |
| ␉␉␉NextToken(json, index);␍␊ |
| ␉␉␉return ret; // NULL␍␊ |
| ␉␉} else {␍␊ |
| ␍␊ |
| ␉␉␉// Missing support for numbers as keys␍␊ |
| ␉␉␉string name = ParseString(json, index, success);␍␊ |
| ␉␉␉if (!success) {␍␊ |
| ␉␉␉␉success = false;␍␊ |
| ␉␉␉␉return ret; //NULL␍␊ |
| ␉␉␉}␍␊ |
| ␍␊ |
| ␉␉␉token = NextToken(json, index);␍␊ |
| ␉␉␉if (token != json::TOKEN_COLON)␍␊ |
| ␉␉␉{␍␊ |
| ␉␉␉␉success = false;␍␊ |
| ␉␉␉␉return ret; //NULL␍␊ |
| ␉␉␉}␍␊ |
| ␍␊ |
| ␉␉␉variant obj = ParseValue(json, index, success);␍␊ |
| ␉␉␉if (!success) {␍␊ |
| ␉␉␉␉success = false;␍␊ |
| ␉␉␉␉return ret; //NULL␍␊ |
| ␉␉␉}␍␊ |
| ␍␊ |
| ␉␉␉#ifndef USE_VARIANT␍␊ |
| ␉␉␉add((*ret), name, obj);␍␊ |
| ␉␉␉#else␍␊ |
| ␉␉␉put(ret, name, obj);␍␊ |
| ␉␉␉#endif␍␊ |
| ␉␉␉//put(ret, name, obj);␍␊ |
| ␉␉}␍␊ |
| ␉}␍␊ |
| ␍␊ |
| ␉return ret;␍␊ |
| }␍␊ |
| ␍␊ |
| pt::string json::ParseString(const pt::string & json, int & index, bool & success)␍␊ |
| {␍␊ |
| ␉pt::string ret;␍␊ |
| ␉char c;␍␊ |
| ␍␊ |
| ␉EatWhiteSpace(json, index);␍␊ |
| ␍␊ |
| ␉c = json[index++];␍␊ |
| ␍␊ |
| ␉bool complete = false;␍␊ |
| ␉while (!complete) {␍␊ |
| ␉␉if (index == length(json))␍␊ |
| ␉␉␉break;␍␊ |
| ␍␊ |
| ␉␉c = json[index++];␍␊ |
| ␉␉if (c == '"') {␍␊ |
| ␉␉␉complete = true;␍␊ |
| ␉␉␉break;␍␊ |
| ␉␉} else if (c == '\\') {␍␊ |
| ␉␉␉if (index == length(json))␍␊ |
| ␉␉␉␉break;␍␊ |
| ␍␊ |
| ␉␉␉c = json[index++];␍␊ |
| ␉␉␉//clever programming␍␊ |
| ␉␉␉bool brk = false;␍␊ |
| ␉␉␉int remainingLength;␍␊ |
| ␉␉␉switch(c)␍␊ |
| ␉␉␉{␍␊ |
| ␉␉␉␉case 'b':␍␊ |
| ␉␉␉␉␉ret += '\b';␍␊ |
| ␉␉␉␉␉break;␍␊ |
| ␉␉␉␉case 'f':␍␊ |
| ␉␉␉␉␉ret += '\f';␍␊ |
| ␉␉␉␉␉break;␍␊ |
| ␉␉␉␉case 'n':␍␊ |
| ␉␉␉␉␉ret += '\n';␍␊ |
| ␉␉␉␉␉break;␍␊ |
| ␉␉␉␉case 'r':␍␊ |
| ␉␉␉␉␉ret += '\r';␍␊ |
| ␉␉␉␉␉break;␍␊ |
| ␉␉␉␉case 't':␍␊ |
| ␉␉␉␉␉ret += '\t';␍␊ |
| ␉␉␉␉␉break;␍␊ |
| ␉␉␉␉case 'u':␍␊ |
| ␉␉␉␉␉remainingLength = length(json) - index;␍␊ |
| ␉␉␉␉␉if (remainingLength >= 4)␍␊ |
| ␉␉␉␉␉{␍␊ |
| ␉␉␉␉␉␉pt::large codePoint = pt::stringtoi(copy(json, index, 4));␍␊ |
| ␉␉␉␉␉␉ret += (char)codePoint;␍␊ |
| ␉␉␉␉␉␉index += 4;␍␊ |
| ␉␉␉␉␉} else {␍␊ |
| ␉␉␉␉␉␉brk = true;␍␊ |
| ␉␉␉␉␉}␍␊ |
| ␉␉␉␉␉break;␍␊ |
| ␉␉␉␉case '"':␍␊ |
| ␉␉␉␉case '\\':␍␊ |
| ␉␉␉␉case '/':␍␊ |
| ␉␉␉␉␉ret += c;␍␊ |
| ␉␉␉␉␉break;␍␊ |
| ␍␊ |
| ␉␉␉␉␍␊ |
| ␉␉␉}␍␊ |
| ␉␉␉if (brk)␍␊ |
| ␉␉␉␉break;␍␊ |
| ␉␉} else {␍␊ |
| ␉␉␉ret += c;␍␊ |
| ␉␉}␍␊ |
| ␉}␍␊ |
| ␍␊ |
| ␉if (!complete) {␍␊ |
| ␉␉success = false;␍␊ |
| ␉␉return "";␍␊ |
| ␉}␍␊ |
| ␍␊ |
| ␉return ret;␍␊ |
| }␍␊ |
| ␍␊ |
| pt::large json::ParseNumber(const string & json, int & index, bool & success)␍␊ |
| {␍␊ |
| ␉EatWhiteSpace(json, index);␍␊ |
| ␍␊ |
| ␉int lastIndex = GetLastIndexOfNumber(json, index);␍␊ |
| ␉int charLength = (lastIndex - index) + 1;␍␊ |
| ␍␊ |
| ␉pt::large number = stringtoi(copy(json, index, charLength));␍␊ |
| ␍␊ |
| ␉index = lastIndex + 1;␍␊ |
| ␉return number;␍␊ |
| }␍␊ |
| ␍␊ |
| int json::GetLastIndexOfNumber(const string & json, int index)␍␊ |
| {␍␊ |
| ␉int lastIndex;␍␊ |
| ␉cset num = "0-9eE~43~45~46";␍␊ |
| ␉for(lastIndex = index; lastIndex < length(json); lastIndex++)␍␊ |
| ␉{␍␊ |
| ␉␉if (!(json[lastIndex] & num))␍␊ |
| ␉␉␉break;␍␊ |
| ␉}␍␊ |
| ␉return lastIndex - 1;␍␊ |
| }␍␊ |
| ␍␊ |
| ␍␊ |
| pt::variant json::JsonDecode(const pt::string & json, bool & success)␍␊ |
| {␍␊ |
| ␍␊ |
| ␉success = true;␍␊ |
| ␉if (!pt::isempty(json))␍␊ |
| ␉{␍␊ |
| ␉␉int index = 0;␍␊ |
| ␉␉pt::variant ret = ParseValue(json, index, success);␍␊ |
| ␉␉return ret;␍␊ |
| ␉} else {␍␊ |
| ␉␉return NULL;␍␊ |
| ␉}␍␊ |
| }␍␊ |
| ␍␊ |
| pt::variant json::ParseValue(const pt::string & json, int & index, bool & success)␍␊ |
| {␍␊ |
| ␉switch(LookAhead(json, index)) {␍␊ |
| ␉␉case json::TOKEN_STRING:␍␊ |
| ␉␉␉return ParseString(json, index, success);␍␊ |
| ␉␉case json::TOKEN_NUMBER:␍␊ |
| ␉␉␉return ParseNumber(json, index, success);␍␊ |
| ␉␉case json::TOKEN_CURLY_OPEN:␍␊ |
| ␉␉␉return ParseObject(json, index, success);␍␊ |
| ␉␉case json::TOKEN_SQUARED_OPEN:␍␊ |
| ␉␉␉return ParseArray(json, index, success);␍␊ |
| ␉␉case json::TOKEN_TRUE:␍␊ |
| ␉␉␉NextToken(json, index);␍␊ |
| ␉␉␉return true;␍␊ |
| ␉␉case json::TOKEN_FALSE:␍␊ |
| ␉␉␉NextToken(json, index);␍␊ |
| ␉␉␉return false;␍␊ |
| ␉␉case json::TOKEN_NULL:␍␊ |
| ␉␉␉NextToken(json, index);␍␊ |
| ␉␉␉return NULL;␍␊ |
| ␉␉case json::TOKEN_NONE:␍␊ |
| ␉␉␉break;␍␊ |
| ␍␊ |
| ␉}␍␊ |
| ␉␍␊ |
| ␉success = false;␍␊ |
| ␉return NULL;␍␊ |
| }␍␊ |
| ␍␊ |
| int json::LookAhead(const pt::string & json, int index)␍␊ |
| {␍␊ |
| ␉// why is this needed?␍␊ |
| ␉int saveIndex = index;␍␊ |
| ␉return NextToken(json, saveIndex);␍␊ |
| }␍␊ |
| ␍␊ |
| int json::NextToken(const pt::string & json, int & index)␍␊ |
| {␍␊ |
| ␉EatWhiteSpace(json, index);␍␊ |
| ␍␊ |
| ␉if (index == length(json))␍␊ |
| ␉{␍␊ |
| ␉␉return json::TOKEN_NONE;␍␊ |
| ␉}␍␊ |
| ␍␊ |
| ␉char c = json[index];␍␊ |
| ␉index++;␍␊ |
| ␉switch (c)␍␊ |
| ␉{␍␊ |
| ␉␉case '{':␍␊ |
| ␉␉␉return json::TOKEN_CURLY_OPEN;␍␊ |
| ␉␉case '}':␍␊ |
| ␉␉␉return json::TOKEN_CURLY_CLOSE;␍␊ |
| ␉␉case '[':␍␊ |
| ␉␉␉return json::TOKEN_SQUARED_OPEN;␍␊ |
| ␉␉case ']':␍␊ |
| ␉␉␉return json::TOKEN_SQUARED_CLOSE;␍␊ |
| ␉␉case ',':␍␊ |
| ␉␉␉return json::TOKEN_COMMA;␍␊ |
| ␉␉case '"':␍␊ |
| ␉␉␉return json::TOKEN_STRING;␍␊ |
| ␉␉case '0':␍␊ |
| ␉␉case '1':␍␊ |
| ␉␉case '2':␍␊ |
| ␉␉case '3':␍␊ |
| ␉␉case '4':␍␊ |
| ␉␉case '5':␍␊ |
| ␉␉case '6':␍␊ |
| ␉␉case '7':␍␊ |
| ␉␉case '8':␍␊ |
| ␉␉case '9':␍␊ |
| ␉␉case '-': //huh? - negative duh␍␊ |
| ␉␉␉return json::TOKEN_NUMBER;␍␊ |
| ␉␉case ':':␍␊ |
| ␉␉␉return json::TOKEN_COLON;␍␊ |
| ␉}␍␊ |
| ␉index--;␍␊ |
| ␍␊ |
| ␉int remainingLength = length(json) - index;␍␊ |
| ␍␊ |
| ␍␊ |
| ␉// some clever shortcuts....␍␊ |
| ␉if (remainingLength >= 5)␍␊ |
| ␉{␍␊ |
| ␉␉if (copy(json, index, 5) == "false")␍␊ |
| ␉␉{␍␊ |
| ␉␉␉index += 5;␍␊ |
| ␉␉␉return json::TOKEN_FALSE;␍␊ |
| ␉␉}␍␊ |
| ␉}␍␊ |
| ␍␊ |
| ␉if (remainingLength >= 4)␍␊ |
| ␉{␍␊ |
| ␉␉if (copy(json, index, 4) == "true")␍␊ |
| ␉␉{␍␊ |
| ␉␉␉index += 4;␍␊ |
| ␉␉␉return json::TOKEN_TRUE;␍␊ |
| ␉␉}␍␊ |
| ␍␊ |
| ␉␉if (copy(json, index, 4) == "null")␍␊ |
| ␉␉{␍␊ |
| ␉␉␉index += 4;␍␊ |
| ␉␉␉return json::TOKEN_NULL;␍␊ |
| ␉␉}␍␊ |
| ␉}␍␊ |
| ␍␊ |
| ␉return json::TOKEN_NONE;␍␊ |
| }␍␊ |
| ␍␊ |
| void json::EatWhiteSpace(const pt::string & json, int & index)␍␊ |
| {␍␊ |
| ␉pt::cset wspace = "~09~0d~0a ";␍␊ |
| ␉for(; index < pt::length(json); index++)␍␊ |
| ␉{␍␊ |
| ␉␉if (!(json[index] & wspace))␍␊ |
| ␉␉␉break;␍␊ |
| ␉}␍␊ |
| }␍␊ |
| ␍␊ |
| PTYPES_END |