#include "DtsodV24.h" #include "../String/StringBuilder.h" #define ARR_SZ_START 64 typedef struct DeserializeSharedData{ const char* sh_text_first; char* sh_text; bool sh_partOfDollarList; bool sh_calledRecursively; allocator_ptr sh_tmp_al; } DeserializeSharedData; #define text shared->sh_text #define partOfDollarList shared->sh_partOfDollarList #define calledRecursively shared->sh_calledRecursively #define tmp_al shared->sh_tmp_al // special func for throwing error messages about wrong characters in deserializing text Maybe ERROR_WRONGCHAR(const char c, char* _text, char* text_first, const char* srcfile, i32 line, const char* funcname){ char errBuf[68]; for(u8 n=0; n at:\n" " \"%s\"\n" "\\___[%s:%d] %s()", c,errBuf, srcfile,line,funcname); safethrow(errmsg,;); } #define safethrow_wrongchar(C, freeMem) { freeMem; return ERROR_WRONGCHAR(C, text, shared->sh_text_first, __FILE__,__LINE__,__func__); } Maybe __SkipComment(DeserializeSharedData* shared) { char c; while ((c=*++text) != '\n') if (!c) safethrow(ERR_ENDOFSTR,;); return MaybeNull; } #define SkipComment() __SkipComment(shared) Maybe __ReadName(DeserializeSharedData* shared){ char c; string nameStr={text+1,0}; while ((c=*++text)) switch (c){ case ' ': case '\t': case '\r': case '\n': if(nameStr.length!=0) safethrow_wrongchar(c,;); nameStr.ptr++; break; case '=': case ';': case '\'': case '"': case '[': case ']': case '{': safethrow_wrongchar(c,;); case '#': ; char _c=c; char* _text=text; try(SkipComment(),_,;); if(nameStr.length!=0){ text=_text; safethrow_wrongchar(_c,;); } nameStr.ptr=text+1; // skips '\n' break; case '}': if(!calledRecursively || nameStr.length!=0) safethrow_wrongchar(c,;); return SUCCESS(UniHeapPtr(char,NULL)); case ':': return SUCCESS(UniHeapPtr(char,string_extract(tmp_al, nameStr))); case '$': if(nameStr.length!=0) safethrow_wrongchar(c,;); nameStr.ptr++; partOfDollarList=true; break; default: nameStr.length++; break; } if(nameStr.length>0) safethrow(ERR_ENDOFSTR,;); return SUCCESS(UniHeapPtr(char,NULL)); } #define ReadName() __ReadName(shared) Maybe __deserialize(char** _text, bool _calledRecursively, allocator_ptr _tmp_al); Maybe __ReadValue(DeserializeSharedData* shared, bool* readingList); #define ReadValue(rL) __ReadValue(shared, rL) // returns part of without quotes Maybe __ReadString(DeserializeSharedData* shared){ char c; bool prevIsBackslash=false; StringBuilder _sb; StringBuilder* b=&_sb; StringBuilder_construct(b, tmp_al); while ((c=*++text)){ if(c=='"') { if(prevIsBackslash) { // replacing <\"> with <"> StringBuilder_rmchar(b); StringBuilder_append_char(b,c); prevIsBackslash=false; } else { char* str=StringBuilder_build(b).ptr; return SUCCESS(UniHeapPtr(char,str)); } } else { prevIsBackslash= c=='\\' && !prevIsBackslash; StringBuilder_append_char(b,c); } } safethrow(ERR_ENDOFSTR, StringBuilder_destruct(b)); } #define ReadString() __ReadString(shared) Maybe __ReadList(DeserializeSharedData* shared){ Autoarr(Unitype) list; Autoarr_construct(&list, Unitype, ARR_SZ_START, NULL); bool readingList=true; while (true){ try(ReadValue((&readingList)), m_val, Autoarr_destruct(&list)) Autoarr_add(&list, m_val.value); if (!readingList){ if(Unitype_isUniNull(m_val.value)) Autoarr_pop(&list); break; } } return SUCCESS(UniHeapPtr(Autoarr_Unitype, &list)); }; #define ReadList() __ReadList(shared) Maybe __ParseValue(DeserializeSharedData* shared, string str){ const string trueStr={"true",4}; const string falseStr={"false",5}; switch(str.ptr[str.length-1]){ // Bool case 'e': if(string_compare(str,trueStr)) return SUCCESS(UniTrue); else if(string_compare(str,falseStr)) return SUCCESS(UniFalse); else safethrow_wrongchar(*str.ptr,;); // Float64 case 'f': { char* _c=string_extract(tmp_al, str); Unitype rez=UniFloat64(strtod(_c,NULL)); // allocator_free(tmp_al,_c); return SUCCESS(rez); } // UInt64 case 'u': { u64 lu=0; char* _c=string_extract(tmp_al, str); if(sscanf(_c, IFWIN("%llu", "%lu"), &lu)!=1){ char err[64]; sprintf_s(err, sizeof(err), "can't parse to int: <%s>", _c); safethrow(err, /*allocator_free(tmp_al, _c)*/); } // allocator_free(tmp_al, _c); return SUCCESS(UniUInt64(lu)); } // Int64 case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': { i64 li=0; char* _c=string_extract(tmp_al, str); if(sscanf(_c, IFWIN("%lli", "%li"), &li)!=1){ char err[64]; sprintf_s(err, sizeof(err),"can't parse to int: <%s>",_c); // safethrow(err,allocator_free(tmp_al, _c)); } // allocator_free(tmp_al, _c); return SUCCESS(UniInt64(li)); } // wrong type default: safethrow_wrongchar(str.ptr[str.length-1],;); } safethrow(ERR_ENDOFSTR,;); }; #define ParseValue(str) __ParseValue(shared, str) Maybe __ReadValue(DeserializeSharedData* shared, bool* readingList){ char c; string valueStr={text+1,0}; Unitype value=UniNull; bool spaceAfterVal=false; while ((c=*++text)) switch (c){ case ' ': case '\t': case '\r': case '\n': if(valueStr.length!=0) spaceAfterVal=true; else valueStr.ptr++; break; case '=': case ':': case '}': case '$': case '\'': safethrow_wrongchar(c,Unitype_destruct(&value)); case '#':; char _c=c; char* _text=text; try(SkipComment(),_,;); if(valueStr.length!=0){ text=_text; safethrow_wrongchar(_c,Unitype_destruct(&value)); } valueStr.ptr=text+1; // skips '\n' break; case '"': if(valueStr.length!=0) safethrow_wrongchar(c,Unitype_destruct(&value)); try(ReadString(),maybeString,;) value=maybeString.value; break; case '{': if(valueStr.length!=0) safethrow_wrongchar(c,Unitype_destruct(&value)); ++text; // skips '{' try(__deserialize(&text,true,tmp_al), val, Unitype_destruct(&value)) value=val.value; break; case '[': if(valueStr.length!=0) safethrow_wrongchar(c,Unitype_destruct(&value)); try(ReadList(),maybeList,Unitype_destruct(&value)) value=maybeList.value; break; case ']': if(!readingList) safethrow_wrongchar(c,Unitype_destruct(&value)); *readingList=false; goto return_value; case ';': case ',': return_value: if(valueStr.length!=0){ if(!Unitype_isUniNull(value)) safethrow_wrongchar(c,Unitype_destruct(&value)); try(ParseValue(valueStr),maybeParsed,;) value=maybeParsed.value; } return SUCCESS(value); default: if(spaceAfterVal) safethrow_wrongchar(c,Unitype_destruct(&value)); valueStr.length++; break; } safethrow(ERR_ENDOFSTR,;); } Maybe __deserialize(char** _text, bool _calledRecursively, allocator_ptr _tmp_al) { DeserializeSharedData _shared={ .sh_text_first=*_text, .sh_text=*_text, .sh_partOfDollarList=false, .sh_calledRecursively=_calledRecursively, .sh_tmp_al=_tmp_al }; DeserializeSharedData* shared=&_shared; Hashtable* dict=Hashtable_create(); text--; while(true){ try(ReadName(), maybeName, Hashtable_destruct(dict)) if(!maybeName.value.VoidPtr) // end of file or '}' in recursive call goto END; char* nameCPtr=maybeName.value.VoidPtr; try(ReadValue(NULL), val, { Hashtable_destruct(dict); // do not use, free call order is incorrect // allocator_free(tmp_al, nameCPtr); }) { if(partOfDollarList){ partOfDollarList=false; Autoarr(Unitype)* list; Unitype lu; if(Hashtable_tryGet(dict,nameCPtr, &lu)){ list=(Autoarr(Unitype)*)lu.VoidPtr; // Key is not used in that case, because it is already added // to the table with the first dollar list item. // do not use, free call order is incorrect // allocator_free(tmp_al, nameCPtr); } else{ list=allocator_alloc(tmp_al, sizeof(*list)); Autoarr_construct(list, Unitype, ARR_SZ_START, NULL); Hashtable_add(dict,nameCPtr,UniHeapPtr(Autoarr_Unitype,list)); } Autoarr_add(list,val.value); } else Hashtable_add(dict,nameCPtr,val.value); } } END: *_text=text; return SUCCESS(UniHeapPtr(Hashtable,dict)); } Maybe DtsodV24_deserialize(char* _text) { LinearAllocator _tmp_al; LinearAllocator_construct(&_tmp_al, 1024); Maybe m=__deserialize(&_text, false, (allocator_ptr)&_tmp_al); LinearAllocator_destruct(&_tmp_al); return m; }