#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 |