#define JSONT_INVALID 0 #define JSONT_STRING 1 #define JSONT_INTEGER 2 #define JSONT_FLOAT 3 #define JSONT_ARRAY 4 #define JSONT_BOOL 5 #define JSONT_OBJ 6 #define JSONT_NULL 7 #define JSON_HASHTABLE_SIZE 1024 #define HTT_JSON 0x00100 //identical to HTT_DICT_WORD class CJSONDataEntry:CQueue { U8 type; I64 int_data; F64 float_data; U8 *string_data; Bool bool_data; CJSONDataEntry *list_data; CHashTable *hash_table; }; class CJSONDataHash:CHash { CJSONDataEntry *data; }; U8 **JSONGetKeys(CHashTable *table) { I64 i, count = 0; CHash *temp_hash; U8 **keys; I64 key_index = 0; for (i = 0; i <= table->mask; i++) //mask is table length 0-based if (temp_hash = table->body[i]) //if temp_hash exists count++; keys = CAlloc(sizeof(U8*) * count); // alloc string list for (i = 0; i <= table->mask; i++) if (temp_hash = table->body[i]) { keys[key_index] = StrNew(temp_hash->str);//add key string to list key_index++; } return keys; } U0 JSONIndentLine(I64 indent) { I64 i; // for (i = 0; i < indent; i++) {" ";} for (i = 0; i < indent; i++) {" ";} } U0 JSONDataRep(CJSONDataEntry *data, I64 indent=0) { U8 **keys;// = JSONGetKeys(data->hash_table); I64 index; I64 count;// = MSize(keys) / sizeof(U8*); CJSONDataEntry *entry;// = data->list_data->next;//one after head. CJSONDataHash *temp_hash; JSONIndentLine(indent); switch (data->type) { case JSONT_INVALID: "Invalid JSON.\n"; break; case JSONT_STRING: "%s\n", data->string_data; break; case JSONT_INTEGER: "%d\n", data->int_data; break; case JSONT_FLOAT: "%.9f\n", data->float_data; break; case JSONT_BOOL: "%Z\n", data->bool_data, "ST_FALSE_TRUE"; break; case JSONT_NULL: "Null.\n"; break; case JSONT_ARRAY: "Array:\n"; JSONIndentLine(indent); "[\n"; entry = data->list_data->next;//one after head. while (entry != data->list_data)//head ignored, stop on head. { JSONDataRep(entry, indent + 1); // recursive Rep on the list entry entry = entry->next; } JSONIndentLine(indent); "]\n"; break; case JSONT_OBJ: "Object.\n"; JSONIndentLine(indent); "{\n"; keys = JSONGetKeys(data->hash_table); count = MSize(keys) / sizeof(U8*); for (index = 0; index < count; index++) { JSONIndentLine(indent); "Key: %s\n", keys[index]; temp_hash = HashFind(keys[index], data->hash_table, HTT_JSON); JSONDataRep(temp_hash->data, indent + 1); } JSONIndentLine(indent); "}\n"; break; } } CJSONDataEntry *JSONParse(CCompCtrl *cc) { CJSONDataEntry *result = CAlloc(sizeof(CJSONDataEntry)); I64 tk, last_tk; Bool is_done = FALSE; CJSONDataEntry *temp_entry;// = JSONParse(cc); CJSONDataHash *temp_hash = CAlloc(sizeof(CJSONDataHash)); while (tk = Lex(cc)) { // ClassRep(cc); switch (tk) { case '}': LexExcept(cc, "Expected Value, got '}'."); case TK_STR: result->type = JSONT_STRING; result->string_data = StrNew(cc->cur_str); is_done = TRUE; break; case TK_I64: //todo, LexExcept on 0x or 0b vals. result->type = JSONT_INTEGER; result->int_data = cc->cur_i64; // LexPush(cc); // "got hex val, token string is %s\n",cc->cur_str; // ClassRep(cc); // Break; is_done = TRUE; break; case TK_F64: result->type = JSONT_FLOAT; result->float_data = cc->cur_f64; is_done = TRUE; break; case TK_IDENT: if (!StrCompare(cc->cur_str, "true") || !StrCompare(cc->cur_str, "false")) { result->type = JSONT_BOOL; if (!StrCompare(cc->cur_str, "true")) result->bool_data = TRUE; if (!StrCompare(cc->cur_str, "false")) result->bool_data = FALSE; is_done = TRUE; } if (!StrCompare(cc->cur_str, "null")) { result->type = JSONT_NULL; is_done = TRUE; } is_done = TRUE; break; case '[': result->type = JSONT_ARRAY; result->list_data = CAlloc(sizeof(CJSONDataEntry)); QueueInit(result->list_data); lex_listitem: /* CCompCtrl* temp_cc = NULL; MemCopy(temp_cc, cc, CompCtrlSize(cc)); tk = Lex(temp_cc); // CompCtrlDel(temp_cc); */ last_tk = tk; LexPush(cc); tk = Lex(cc); if (last_tk == ',' && tk == ']') LexExcept(cc, "Expected List value, got ']'"); if (tk == ']') goto lex_listdone; if (tk == ',') LexExcept(cc, "Expected List Value, got comma."); LexPopRestore(cc); temp_entry = JSONParse(cc); QueueInsert(temp_entry, result->list_data->last); tk = Lex(cc); if (tk == ',') goto lex_listitem; lex_listdone: // LexPopNoRestore(cc); is_done = TRUE; break; case '{': result->type = JSONT_OBJ; result->hash_table = HashTableNew(JSON_HASHTABLE_SIZE); lex_objkey: //lex next. expect TK_STR. Make a temp_hash. last_tk = tk; tk = Lex(cc); if (last_tk == ',' && tk == '}') LexExcept(cc, "Expected Key after comma."); if (tk == '}') goto lex_objdone; if (tk != TK_STR) LexExcept(cc, "Expected Key String."); temp_hash = CAlloc(sizeof(CJSONDataHash)); //set hash type and StrNew with cc->cur_str into hash str. temp_hash->type = HTT_JSON; temp_hash->str = StrNew(cc->cur_str); //lex next. expect ':'. tk = Lex(cc); if (tk != ':') LexExcept(cc, "Expected ':' after Key String."); //now expect JSONDataEntry-able value next. //Recursive JSONParse into hash data member. temp_hash->data = JSONParse(cc); //JSONParse leaves off on the last token. e.g. int, tk will //still be TK_I64. //add hash to result hash_table. HashAdd(temp_hash, result->hash_table); // "Debugging.\nHash added with str:%s.\n",temp_hash->str; // ClassRep(temp_hash->data); //lex next. expect ',' or '}'. tk = Lex(cc); if (tk != ',' && tk != '}') LexExcept(cc, "Expected ',' or '}' after Object Value."); //if ',' ... Terry's parsing code would imply labels and gotos. //i wonder if there's a better, less BASIC way. if (tk == ',') goto lex_objkey; lex_objdone: is_done = TRUE; break; } if (is_done) break; } return result; } U0 Test(U8 *filename) { CCompCtrl *cc = CompCtrlNew(MStrPrint("#include \"%s\"", filename)); CJSONDataEntry *result = JSONParse(cc); JSONDataRep(result); } Cd(__DIR__); Test("JSON1.TXT");