libcw/cwObject.cpp

1138 lines
39 KiB
C++
Raw Permalink Normal View History

//| Copyright: (C) 2020-2024 Kevin Larke <contact AT larke DOT org>
//| License: GNU GPL version 3.0 or above. See the accompanying LICENSE file.
2019-12-19 03:24:12 +00:00
#include <type_traits>
#include "cwCommon.h"
#include "cwLog.h"
#include "cwCommonImpl.h"
#include "cwMem.h"
#include "cwFile.h"
#include "cwTest.h"
2019-12-19 03:24:12 +00:00
#include "cwLex.h"
#include "cwText.h"
#include "cwNumericConvert.h"
#include "cwObject.h"
#include "cwObjectTemplate.h"
namespace cw
{
enum
{
kLCurlyLexTId = cw::lex::kUserLexTId+1,
2019-12-19 03:24:12 +00:00
kRCurlyLexTId,
kLHardLexTId,
kRHardLexTId,
kColonLexTId,
kCommaLexTId,
kTrueLexTId,
kFalseLexTId,
kNullLexTId,
kSegmentedIdLexTId, // id with embedded periods
2019-12-19 03:24:12 +00:00
};
idLabelPair_t _objTokenArray[] =
{
{ kLCurlyLexTId, "{" },
{ kRCurlyLexTId, "}" },
{ kLHardLexTId, "[" },
{ kRHardLexTId, "]" },
{ kColonLexTId, ":" },
{ kCommaLexTId, "," },
{ kTrueLexTId, "true"},
{ kFalseLexTId, "false"},
{ kNullLexTId, "null" },
{ lex::kErrorLexTId,""}
2019-12-19 03:24:12 +00:00
};
unsigned _udiff( unsigned n, unsigned i )
{
return i>=n ? 0 : n-i;
}
unsigned _lexSegmentedIdMatcher( const char* cp, unsigned cn )
{
unsigned i = 0;
if( isalpha(cp[0]) || (cp[0]== '_'))
{
i = 1;
for(; i<cn; ++i)
if( !isalnum(cp[i]) && (cp[i] != '_') && (cp[i] != '.') )
break;
}
return i;
}
2019-12-19 03:24:12 +00:00
void _objTypeFree( object_t* o )
{
o->type->free_value(o);
mem::release(o);
}
2019-12-19 03:24:12 +00:00
void _objTypeFreeValue( object_t* o )
{}
void _objTypeFreeValueString( object_t* o )
2019-12-19 03:24:12 +00:00
{
mem::release( o->u.str );
2019-12-19 03:24:12 +00:00
}
2019-12-19 03:24:12 +00:00
const char* _objTypeIdToLabel( objTypeId_t tid );
rc_t _objTypeValueFromChar( const object_t* o, unsigned tid, void* dst ) { return getObjectValue(o->u.c,tid, dst,o->type->label); }
rc_t _objTypeValueFromInt8( const object_t* o, unsigned tid, void* dst ) { return getObjectValue(o->u.i8,tid, dst,o->type->label); }
rc_t _objTypeValueFromUInt8( const object_t* o, unsigned tid, void* dst ) { return getObjectValue(o->u.u8,tid, dst,o->type->label); }
rc_t _objTypeValueFromInt16( const object_t* o, unsigned tid, void* dst ) { return getObjectValue(o->u.i16,tid,dst,o->type->label); }
rc_t _objTypeValueFromUInt16( const object_t* o, unsigned tid, void* dst ) { return getObjectValue(o->u.u16,tid,dst,o->type->label); }
rc_t _objTypeValueFromInt32( const object_t* o, unsigned tid, void* dst ) { return getObjectValue(o->u.i32,tid,dst,o->type->label); }
rc_t _objTypeValueFromUInt32( const object_t* o, unsigned tid, void* dst ) { return getObjectValue(o->u.u32,tid,dst,o->type->label); }
rc_t _objTypeValueFromInt64( const object_t* o, unsigned tid, void* dst ) { return getObjectValue(o->u.i64,tid,dst,o->type->label); }
rc_t _objTypeValueFromUInt64( const object_t* o, unsigned tid, void* dst ) { return getObjectValue(o->u.u64,tid,dst,o->type->label); }
rc_t _objTypeValueFromFloat( const object_t* o, unsigned tid, void* dst ) { return getObjectValue(o->u.f,tid, dst,o->type->label); }
rc_t _objTypeValueFromDouble( const object_t* o, unsigned tid, void* dst ) { return getObjectValue(o->u.d,tid, dst,o->type->label); }
rc_t _objTypeValueFromBool( const object_t* o, unsigned tid, void* dst ) { return getObjectValue(o->u.b,tid,dst,o->type->label); }
rc_t _objTypeValueFromNonValue( const object_t* o, unsigned tid, void* dst )
{
switch(tid)
{
case kCStringTId:
*(const char**)dst = nullptr;
return kOkRC;
case kStringTId:
*(char**)dst = nullptr;
return kOkRC;
}
return cwLogError(kInvalidArgRC, "There is no conversion from '%s' to '%s'.", _objTypeIdToLabel(tid), o->type->label);
}
rc_t _objTypeValueFromCString( const object_t* o, unsigned tid, void* dst )
{
if( tid == kCStringTId )
{
*(const char**)dst = o->u.str;
return kOkRC;
}
return _objTypeValueFromNonValue(o,tid,dst);
}
2019-12-19 03:24:12 +00:00
rc_t _objTypeValueFromString( const object_t* o, unsigned tid, void* dst )
{
// When objects are parsed all strings are non-const. therefore when a string is retrieved
// from an object the string will always be non-const - but the type of the variable
// to receive the value may be const - we detect this here and redirect to the 'const char*'
// version of the function.
if( tid == kCStringTId )
return _objTypeValueFromCString(o,tid,dst);
if( tid == kStringTId )
{
*(char**)dst = o->u.str;
return kOkRC;
}
if( tid == kNullTId )
{
*(char**)dst = nullptr;
return kOkRC;
}
return _objTypeValueFromNonValue(o,tid,dst);
}
2019-12-19 03:24:12 +00:00
rc_t _objTypeValueFromVect( const object_t* o, unsigned tid, void* dst )
{ return _objTypeValueFromNonValue(o,tid,dst); }
void _objTypePrintIndent( const char* text, unsigned indent, const char* indentStr=" " )
{
for(unsigned i=0; i<indent; ++i)
cwLogPrint("%s",indentStr);
cwLogPrint("%s",text);
2019-12-19 03:24:12 +00:00
}
void _objTypePrintChild( const object_t* o, print_ctx_t& c, const char* eolStr=",\n", const char* indentStr=" " )
{
_objTypePrintIndent(" ",c.indent,indentStr);
o->type->print(o,c);
cwLogPrint("%s",eolStr);
}
void _objTypePrintNull( const object_t* o, print_ctx_t& c ) { cwLogPrint("NULL "); }
void _objTypePrintError( const object_t* o, print_ctx_t& c ) { cwLogPrint("Error "); }
void _objTypePrintChar( const object_t* o, print_ctx_t& c ) { cwLogPrint("%c",o->u.c); }
void _objTypePrintInt8( const object_t* o, print_ctx_t& c ) { cwLogPrint("%i",o->u.i8); }
void _objTypePrintUInt8( const object_t* o, print_ctx_t& c ) { cwLogPrint("%i",o->u.u8); }
void _objTypePrintInt16( const object_t* o, print_ctx_t& c ) { cwLogPrint("%i",o->u.i16); }
void _objTypePrintUInt16( const object_t* o, print_ctx_t& c ) { cwLogPrint("%i",o->u.u16); }
void _objTypePrintInt32( const object_t* o, print_ctx_t& c ) { cwLogPrint("%i",o->u.i32); }
void _objTypePrintUInt32( const object_t* o, print_ctx_t& c ) { cwLogPrint("%i",o->u.u32); }
void _objTypePrintInt64( const object_t* o, print_ctx_t& c ) { cwLogPrint("%lli", o->u.i64); }
void _objTypePrintUInt64( const object_t* o, print_ctx_t& c ) { cwLogPrint("%lli", o->u.u64); }
void _objTypePrintBool( const object_t* o, print_ctx_t& c ) { cwLogPrint("%s",o->u.b ? "true" : "false"); }
void _objTypePrintFloat( const object_t* o, print_ctx_t& c ) { cwLogPrint("%f",o->u.f); }
void _objTypePrintDouble( const object_t* o, print_ctx_t& c ) { cwLogPrint("%f",o->u.d); }
void _objTypePrintString( const object_t* o, print_ctx_t& c ) { cwLogPrint("%s",o->u.str); }
void _objTypePrintVect( const object_t* o, print_ctx_t& c ) { cwLogPrint("<vect>"); }
2019-12-19 03:24:12 +00:00
void _objTypePrintPair( const object_t* o, print_ctx_t& c )
{
o->u.children->type->print(o->u.children,c);
cwLogPrint(": ");
2019-12-19 03:24:12 +00:00
o->u.children->sibling->type->print(o->u.children->sibling,c);
}
void _objTypePrintList( const object_t* o, print_ctx_t& c )
{
const char* indentStr = c.listOnOneLineFl ? "" : " ";
char bracketStr[] = { '[','\0','\0' };
char eoValStr[] = { ',','\0','\0' };
if(!c.listOnOneLineFl)
{
bracketStr[1] = '\n';
eoValStr[1] = '\n';
}
_objTypePrintIndent(bracketStr,0);
c.indent += 2;
for(const object_t* ch=o->u.children; ch!=nullptr; ch=ch->sibling)
{
if( ch->sibling == nullptr )
eoValStr[0] = ' ';
_objTypePrintChild(ch,c,eoValStr,indentStr);
}
c.indent -= 2;
_objTypePrintIndent("]",c.listOnOneLineFl ? 0 : c.indent);
}
void _objTypePrintDict( const object_t* o, print_ctx_t& c )
{
_objTypePrintIndent("{\n",0);
c.indent += 2;
for(const object_t* ch=o->u.children; ch!=nullptr; ch=ch->sibling)
_objTypePrintChild(ch,c);
c.indent -= 2;
_objTypePrintIndent("}",c.indent);
}
void _objTypePrintRoot( const object_t* o, print_ctx_t& c )
{
_objTypePrintDict(o,c);
}
unsigned _objTypeToStringNull( const object_t* o, char* buf, unsigned n ) { return snprintf(buf,n,"NULL "); }
unsigned _objTypeToStringError( const object_t* o, char* buf, unsigned n ) { return snprintf(buf,n,"Error "); }
unsigned _objTypeToStringChar( const object_t* o, char* buf, unsigned n ) { return toText(buf,n,o->u.c); }
unsigned _objTypeToStringInt8( const object_t* o, char* buf, unsigned n ) { return toText(buf,n,o->u.i8); }
unsigned _objTypeToStringUInt8( const object_t* o, char* buf, unsigned n ) { return toText(buf,n,o->u.u8); }
unsigned _objTypeToStringInt16( const object_t* o, char* buf, unsigned n ) { return toText(buf,n,o->u.i16); }
unsigned _objTypeToStringUInt16( const object_t* o, char* buf, unsigned n ) { return toText(buf,n,o->u.u16); }
unsigned _objTypeToStringInt32( const object_t* o, char* buf, unsigned n ) { return toText(buf,n,o->u.i32); }
unsigned _objTypeToStringUInt32( const object_t* o, char* buf, unsigned n ) { return toText(buf,n,o->u.u32); }
unsigned _objTypeToStringInt64( const object_t* o, char* buf, unsigned n ) { return toText(buf,n,o->u.i64); }
unsigned _objTypeToStringUInt64( const object_t* o, char* buf, unsigned n ) { return toText(buf,n,o->u.u64); }
unsigned _objTypeToStringBool( const object_t* o, char* buf, unsigned n ) { return toText(buf,n,o->u.b); }
unsigned _objTypeToStringFloat( const object_t* o, char* buf, unsigned n ) { return toText(buf,n,o->u.f); }
unsigned _objTypeToStringDouble( const object_t* o, char* buf, unsigned n ) { return toText(buf,n,o->u.d); }
unsigned _objTypeToStringVect( const object_t* o, char* buf, unsigned n ) { return snprintf(buf,n,"<vect>"); }
unsigned _objTypeToStringString( const object_t* o, char* buf, unsigned n )
{
unsigned i = snprintf(buf,n,"\"");
const char* str = o->u.str;
if( str == nullptr )
{
cwLogWarning("Unexpected empty string while generating text output.");
}
i += toText(buf+i,_udiff(n,i),str);
return i + snprintf(buf+i,_udiff(n,i),"\"");
}
unsigned _objTypeToStringPair( const object_t* o, char* buf, unsigned n )
{
unsigned i = o->u.children->type->to_string(o->u.children,buf,n);
i += snprintf(buf+i,_udiff(n,i)," : ");
return i + o->u.children->sibling->type->to_string(o->u.children->sibling,buf+i,_udiff(n,i));
}
unsigned _objTypeToStringList( const object_t* o, char* buf, unsigned n )
{
unsigned i = snprintf(buf,n," [ ");
for(const object_t* ch=o->u.children; ch!=nullptr; ch=ch->sibling)
{
i += ch->type->to_string(ch,buf+i,_udiff(n,i));
if( ch->sibling != nullptr )
i += snprintf(buf+i,_udiff(n,i),", ");
}
i += snprintf(buf+i,_udiff(n,i)," ] ");
return i;
}
unsigned _objTypeToStringDict( const object_t* o, char* buf, unsigned n )
{
unsigned i = snprintf(buf,n," { " );
2019-12-19 03:24:12 +00:00
for(const object_t* ch=o->u.children; ch!=nullptr; ch=ch->sibling)
{
i += ch->type->to_string(ch,buf+i,_udiff(n,i));
if( ch->sibling != nullptr )
i += snprintf(buf+i,_udiff(n,i),", ");
}
i += snprintf(buf+i,_udiff(n,i)," } ");
return i;
}
unsigned _objTypeToStringRoot( const object_t* o, char* buf, unsigned n )
{
return _objTypeToStringDict(o,buf,n);
}
object_t* _objTypeDuplContainer( const struct object_str* src, struct object_str* parent )
{
object_t* o = _objAppendRightMostNode( parent, _objAllocate( src->type->id, parent ));
for(object_t* ch=src->u.children; ch!=nullptr; ch=ch->sibling)
ch->type->duplicate(ch,o);
return o;
}
object_t* _objTypeDuplNull( const struct object_str* src, struct object_str* parent ) { return _objAllocate( src->type->id, parent); }
object_t* _objTypeDuplError( const struct object_str* src, struct object_str* parent ) { return _objAllocate( src->type->id, parent); }
object_t* _objTypeDuplChar( const struct object_str* src, struct object_str* parent ) { return _objCreateValueNode<char>(parent,src->u.c); }
object_t* _objTypeDuplInt8( const struct object_str* src, struct object_str* parent ) { return _objCreateValueNode<int8_t>(parent,src->u.i8); }
object_t* _objTypeDuplUInt8( const struct object_str* src, struct object_str* parent ) { return _objCreateValueNode<uint8_t>(parent,src->u.u8); }
object_t* _objTypeDuplInt16( const struct object_str* src, struct object_str* parent ) { return _objCreateValueNode<int16_t>(parent,src->u.i16); }
object_t* _objTypeDuplUInt16( const struct object_str* src, struct object_str* parent ) { return _objCreateValueNode<int16_t>(parent,src->u.u16); }
object_t* _objTypeDuplInt32( const struct object_str* src, struct object_str* parent ) { return _objCreateValueNode<int32_t>(parent,src->u.i32); }
object_t* _objTypeDuplUInt32( const struct object_str* src, struct object_str* parent ) { return _objCreateValueNode<int32_t>(parent,src->u.u32); }
object_t* _objTypeDuplInt64( const struct object_str* src, struct object_str* parent ) { return _objCreateValueNode<int64_t>(parent,src->u.i64); }
object_t* _objTypeDuplUInt64( const struct object_str* src, struct object_str* parent ) { return _objCreateValueNode<uint64_t>(parent,src->u.u64); }
object_t* _objTypeDuplBool( const struct object_str* src, struct object_str* parent ) { return _objCreateValueNode<bool >(parent,src->u.b ); }
object_t* _objTypeDuplFloat( const struct object_str* src, struct object_str* parent ) { return _objCreateValueNode<float >(parent,src->u.f ); }
object_t* _objTypeDuplDouble( const struct object_str* src, struct object_str* parent ) { return _objCreateValueNode<double >(parent,src->u.d ); }
object_t* _objTypeDuplString( const struct object_str* src, struct object_str* parent ) { return _objCreateValueNode<char* >(parent,src->u.str); }
object_t* _objTypeDuplCString(const struct object_str* src, struct object_str* parent ) { return _objCreateValueNode<const char*>(parent,src->u.str);}
object_t* _objTypeDuplVect( const struct object_str* src, struct object_str* parent ) { assert(0); return nullptr; }
object_t* _objTypeDuplPair( const struct object_str* src, struct object_str* parent ) { return _objTypeDuplContainer(src,parent); }
object_t* _objTypeDuplList( const struct object_str* src, struct object_str* parent ) { return _objTypeDuplContainer(src,parent); }
object_t* _objTypeDuplDict( const struct object_str* src, struct object_str* parent ) { return _objTypeDuplContainer(src,parent); }
object_t* _objTypeDuplRoot( const struct object_str* src, struct object_str* parent ) { return _objTypeDuplContainer(src,parent); }
2019-12-19 03:24:12 +00:00
objType_t _objTypeArray[] =
{
{ kNullTId, "null", 0, _objTypeFree, _objTypeFreeValue, _objTypeValueFromNonValue, _objTypePrintNull, _objTypeToStringNull, _objTypeDuplNull },
{ kErrorTId, "error", 0, _objTypeFree, _objTypeFreeValue, _objTypeValueFromNonValue, _objTypePrintError, _objTypeToStringError, _objTypeDuplError },
{ kCharTId, "char", 0, _objTypeFree, _objTypeFreeValue, _objTypeValueFromChar, _objTypePrintChar, _objTypeToStringChar, _objTypeDuplChar },
{ kInt8TId, "int8", 0, _objTypeFree, _objTypeFreeValue, _objTypeValueFromInt8, _objTypePrintInt8, _objTypeToStringInt8, _objTypeDuplInt8 },
{ kUInt8TId, "uint8", 0, _objTypeFree, _objTypeFreeValue, _objTypeValueFromUInt8, _objTypePrintUInt8, _objTypeToStringUInt8, _objTypeDuplUInt8 },
{ kInt16TId, "int16", 0, _objTypeFree, _objTypeFreeValue, _objTypeValueFromInt16, _objTypePrintInt16, _objTypeToStringInt16, _objTypeDuplInt16 },
{ kUInt16TId, "uint16", 0, _objTypeFree, _objTypeFreeValue, _objTypeValueFromUInt16, _objTypePrintUInt16, _objTypeToStringUInt16, _objTypeDuplUInt16 },
{ kInt32TId, "int32", 0, _objTypeFree, _objTypeFreeValue, _objTypeValueFromInt32, _objTypePrintInt32, _objTypeToStringInt32, _objTypeDuplInt32 },
{ kUInt32TId, "uint32", 0, _objTypeFree, _objTypeFreeValue, _objTypeValueFromUInt32, _objTypePrintUInt32, _objTypeToStringUInt32, _objTypeDuplUInt32 },
{ kInt64TId, "int64", 0, _objTypeFree, _objTypeFreeValue, _objTypeValueFromInt64, _objTypePrintInt64, _objTypeToStringInt64, _objTypeDuplInt64 },
{ kUInt64TId, "uint64", 0, _objTypeFree, _objTypeFreeValue, _objTypeValueFromUInt64, _objTypePrintUInt64, _objTypeToStringUInt64, _objTypeDuplUInt64 },
{ kBoolTId, "bool", 0, _objTypeFree, _objTypeFreeValue, _objTypeValueFromBool, _objTypePrintBool, _objTypeToStringBool, _objTypeDuplBool },
{ kFloatTId, "float", 0, _objTypeFree, _objTypeFreeValue, _objTypeValueFromFloat, _objTypePrintFloat, _objTypeToStringFloat, _objTypeDuplFloat },
{ kDoubleTId, "double", 0, _objTypeFree, _objTypeFreeValue, _objTypeValueFromDouble, _objTypePrintDouble, _objTypeToStringDouble, _objTypeDuplDouble },
{ kStringTId, "string", 0, _objTypeFree, _objTypeFreeValueString, _objTypeValueFromString, _objTypePrintString, _objTypeToStringString, _objTypeDuplString },
{ kCStringTId, "cstring", 0, _objTypeFree, _objTypeFreeValueString, _objTypeValueFromCString, _objTypePrintString, _objTypeToStringString, _objTypeDuplCString },
{ kVectTId, "vect", 0, _objTypeFree, _objTypeFreeValue, _objTypeValueFromVect, _objTypePrintVect, _objTypeToStringVect, _objTypeDuplVect },
{ kPairTId, "pair", kContainerFl | kValueContainerFl, _objTypeFree, _objTypeFreeValue, _objTypeValueFromNonValue, _objTypePrintPair, _objTypeToStringPair, _objTypeDuplPair },
{ kListTId, "list", kContainerFl | kValueContainerFl, _objTypeFree, _objTypeFreeValue, _objTypeValueFromNonValue, _objTypePrintList, _objTypeToStringList, _objTypeDuplList },
{ kDictTId, "dict", kContainerFl, _objTypeFree, _objTypeFreeValue, _objTypeValueFromNonValue, _objTypePrintDict, _objTypeToStringDict, _objTypeDuplDict },
{ kRootTId, "root", kContainerFl | kValueContainerFl, _objTypeFree, _objTypeFreeValue, _objTypeValueFromNonValue, _objTypePrintRoot, _objTypeToStringRoot, _objTypeDuplRoot },
{ kInvalidTId, "<invalid>", 0, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr }
2019-12-19 03:24:12 +00:00
};
objType_t* _objIdToType( objTypeId_t tid )
{
unsigned i;
for(i=0; _objTypeArray[i].id != kInvalidTId; ++i)
if( _objTypeArray[i].id == tid )
return _objTypeArray + i;
cwLogError(kInvalidIdRC,"The object type id %i is not valid.",tid);
return nullptr;
}
const char* _objTypeIdToLabel( objTypeId_t tid )
{
const objType_t* type;
if((type = _objIdToType(tid)) == nullptr )
return "<invalid>";
return type->label;
}
object_t* _objAllocate( objTypeId_t tid, object_t* parent )
{
objType_t* type = nullptr;
if( tid != kInvalidTId )
{
if((type = _objIdToType(tid)) == nullptr )
{
cwLogError(kObjAllocFailRC,"Object allocation failed.");
return nullptr;
}
}
object_t* o = mem::allocZ<object_t>();
2019-12-19 03:24:12 +00:00
o->type = type;
o->parent = parent;
2019-12-19 03:24:12 +00:00
return o;
}
rc_t _objSyntaxError( lex::handle_t lexH, const char* fmt, ... )
2019-12-19 03:24:12 +00:00
{
va_list vl;
va_start(vl,fmt);
cwLogVError( kSyntaxErrorRC, fmt, vl );
cwLogError( kSyntaxErrorRC, "Error on line: %i.", lex::currentLineNumber(lexH));
2019-12-19 03:24:12 +00:00
va_end(vl);
return kSyntaxErrorRC;
}
rc_t _objVerifyParentIsValueContainer( lex::handle_t lexH, const object_t* parent, const char* msg )
2019-12-19 03:24:12 +00:00
{
if( parent == nullptr )
return _objSyntaxError(lexH,"The parent node must always be valid.");
// it is legal for a parent of a value to be null when the value is the root element.
if( !(parent->is_value_container()))
return _objSyntaxError(lexH,"Value nodes of type '%s' must be contained by 'root', 'pair' or 'array' node.",msg);
return kOkRC;
}
object_t* _objAppendRightMostNode( object_t* parent, object_t* newNode )
2019-12-19 03:24:12 +00:00
{
if( newNode == nullptr )
return nullptr;
if( parent != nullptr )
2019-12-19 03:24:12 +00:00
{
assert( parent->is_container() );
object_t* child = parent->u.children;
2019-12-19 03:24:12 +00:00
if( parent->u.children == nullptr )
parent->u.children = newNode;
else
{
while( child->sibling != nullptr )
child = child->sibling;
2019-12-19 03:24:12 +00:00
child->sibling = newNode;
}
}
2019-12-19 03:24:12 +00:00
newNode->parent = parent;
return newNode;
}
object_t* _objCreateConainerNode( lex::handle_t lexH, object_t* parent, objTypeId_t tid )
2019-12-19 03:24:12 +00:00
{
if( _objVerifyParentIsValueContainer(lexH,parent,_objTypeIdToLabel(tid)) == kOkRC )
return _objAppendRightMostNode( parent, _objAllocate( tid, parent ));
2019-12-19 03:24:12 +00:00
return nullptr;
}
template< typename T >
rc_t _objSetValue( object_t* obj, const T& v )
{
cw::rc_t rc = kOkRC;
if(_objCallSetLeafValue( obj, v ) == nullptr)
rc = cwLogError(kOpFailRC,"Object value assignment failed.");
return rc;
}
2019-12-19 03:24:12 +00:00
}
void cw::object_t::unlink()
{
// if this node has no parent then there it is not part of a tree
// and therefore cannot be unlinked
if( parent == nullptr )
return;
object_t* c0 = nullptr;
object_t* c = parent->u.children;
for(; c!=nullptr; c=c->sibling)
{
if( c == this )
{
if( c0 == nullptr )
parent->u.children = c->sibling;
else
c0->sibling = c->sibling;
c->parent = nullptr;
c->sibling = nullptr;
return;
}
c0 = c;
}
// if a child has a parent then it must be in that parent's child list
cwAssert(0);
}
2019-12-19 03:24:12 +00:00
void cw::object_t::free()
{
unlink();
2019-12-19 03:24:12 +00:00
if( is_container() )
{
object_t* o1 = nullptr;
for(object_t* o = u.children; o != nullptr; o=o1 )
{
o1 = o->sibling;
o->free();
}
}
type->free(this);
}
cw::rc_t cw::object_t::append_child( struct object_str* child )
{
if( !is_container() )
return cwLogError(kInvalidDataTypeRC,"The parent of a child object node must be a 'container'.");
_objAppendRightMostNode( this, child );
return kOkRC;
}
2019-12-19 03:24:12 +00:00
unsigned cw::object_t::child_count() const
{
unsigned n = 0;
if( is_container() && u.children != nullptr)
{
object_t* o = u.children;
for(n=1; o->sibling != nullptr; o=o->sibling)
++n;
}
return n;
}
cw::rc_t cw::object_t::value( void* dst, unsigned dstTypeId ) { return type->value(this,dstTypeId,dst); }
cw::rc_t cw::object_t::value( char& v ) const { return type->value(this,kCharTId,&v); }
cw::rc_t cw::object_t::value( int8_t& v ) const { return type->value(this,kInt8TId,&v); }
cw::rc_t cw::object_t::value( uint8_t& v ) const { return type->value(this,kUInt8TId,&v); }
cw::rc_t cw::object_t::value( int16_t& v ) const { return type->value(this,kInt16TId,&v); }
cw::rc_t cw::object_t::value( uint16_t& v ) const { return type->value(this,kUInt16TId,&v); }
cw::rc_t cw::object_t::value( int32_t& v ) const { return type->value(this,kInt32TId,&v); }
cw::rc_t cw::object_t::value( uint32_t& v ) const { return type->value(this,kUInt32TId,&v); }
cw::rc_t cw::object_t::value( int64_t& v ) const { return type->value(this,kInt64TId,&v); }
cw::rc_t cw::object_t::value( uint64_t& v ) const { return type->value(this,kUInt64TId,&v); }
cw::rc_t cw::object_t::value( float& v ) const { return type->value(this,kFloatTId,&v); }
cw::rc_t cw::object_t::value( double& v ) const { return type->value(this,kDoubleTId,&v); }
cw::rc_t cw::object_t::value( bool& v ) const { return type->value(this,kBoolTId,&v); }
2019-12-19 03:24:12 +00:00
cw::rc_t cw::object_t::value( char*& v ) const { return type->value(this,kStringTId,&v); }
cw::rc_t cw::object_t::value( const char*& v ) const { return type->value(this,kCStringTId,&v); }
2019-12-19 03:24:12 +00:00
cw::rc_t cw::object_t::set_value( char v ) { return _objSetValue(this,v); }
cw::rc_t cw::object_t::set_value( int8_t v ) { return _objSetValue(this,v); }
cw::rc_t cw::object_t::set_value( uint8_t v ) { return _objSetValue(this,v); }
cw::rc_t cw::object_t::set_value( int16_t v ) { return _objSetValue(this,v); }
cw::rc_t cw::object_t::set_value( uint16_t v ) { return _objSetValue(this,v); }
cw::rc_t cw::object_t::set_value( int32_t v ) { return _objSetValue(this,v); }
cw::rc_t cw::object_t::set_value( uint32_t v ) { return _objSetValue(this,v); }
cw::rc_t cw::object_t::set_value( int64_t v ) { return _objSetValue(this,v); }
cw::rc_t cw::object_t::set_value( uint64_t v ) { return _objSetValue(this,v); }
cw::rc_t cw::object_t::set_value( float v ) { return _objSetValue(this,v); }
cw::rc_t cw::object_t::set_value( double v ) { return _objSetValue(this,v); }
cw::rc_t cw::object_t::set_value( bool v ) { return _objSetValue(this,v); }
cw::rc_t cw::object_t::set_value( char* v ) { return _objSetValue(this,v); }
cw::rc_t cw::object_t::set_value( const char* v ) { return _objSetValue(this,v); }
2019-12-19 03:24:12 +00:00
const char* cw::object_t::pair_label() const
{
cwAssert( is_pair() );
if( is_pair() )
return u.children->u.str;
return nullptr;
}
const struct cw::object_str* cw::object_t::pair_value() const
{
cwAssert( is_pair() );
if( is_pair() )
return u.children->sibling;
return nullptr;
}
struct cw::object_str* cw::object_t::pair_value()
{
cwAssert( is_pair() );
if( is_pair() )
return u.children->sibling;
return nullptr;
}
const struct cw::object_str* cw::object_t::find( const char* label, unsigned flags ) const
2019-12-19 03:24:12 +00:00
{
if( is_container() )
{
for(object_t* o=u.children; o!=nullptr; o=o->sibling)
{
if( o->is_pair() && textCompare(o->pair_label(),label) == 0 )
return o->pair_value();
const object_t* ch;
if( cwIsFlag(flags,kRecurseFl) )
if((ch = o->find(label)) != nullptr )
return ch;
2019-12-19 03:24:12 +00:00
}
2020-01-27 22:48:17 +00:00
}
return nullptr;
}
struct cw::object_str* cw::object_t::find( const char* label, unsigned flags )
{
return const_cast<struct object_str*>(((const object_t*)this)->find(label,flags));
}
const struct cw::object_str* cw::object_t::child_ele( unsigned idx ) const
2020-01-27 22:48:17 +00:00
{
if( is_container() )
2020-01-27 22:48:17 +00:00
{
unsigned i = 0;
for(object_t* o=u.children; o!=nullptr; o=o->sibling,++i)
if( i == idx )
return o;
2019-12-19 03:24:12 +00:00
}
return nullptr;
}
struct cw::object_str* cw::object_t::child_ele( unsigned idx )
{
return const_cast<struct object_str*>(((const object_t*)this)->child_ele(idx));
}
const struct cw::object_str* cw::object_t::next_child_ele( const struct object_str* ele ) const
{
if( is_container() )
{
if( ele == nullptr )
return u.children;
return ele->sibling;
}
return nullptr;
}
struct cw::object_str* cw::object_t::next_child_ele( struct object_str* ele )
{
return const_cast<struct object_str*>(((const object_t*)this)->next_child_ele(ele));
}
unsigned cw::object_t::to_string( char* buf, unsigned bufByteN ) const
{
return type->to_string(this,buf,bufByteN );
}
2020-01-27 22:48:17 +00:00
char* cw::object_t::to_string() const
{
unsigned n = 1024;
char* buf = mem::alloc<char>(n);
do
{
unsigned actual_bytes = to_string(buf,n);
if( actual_bytes < n )
break;
n *= 2;
buf = mem::resize(buf,n);
}while(1);
return buf;
}
2019-12-19 03:24:12 +00:00
void cw::object_t::print(const print_ctx_t* c) const
{
print_ctx_t ctx;
if( c != nullptr )
ctx = *c;
type->print(this,ctx);
}
cw::object_t* cw::object_t::duplicate() const
{ return type->duplicate(this,nullptr); }
cw::object_t* cw::newObject( std::uint8_t v, object_t* parent)
{ return _objCreateValueNode<uint8_t>( parent, v ); }
cw::object_t* cw::newObject( std::int8_t v, object_t* parent)
{ return _objCreateValueNode<int8_t>( parent, v ); }
2019-12-19 03:24:12 +00:00
cw::object_t* cw::newObject( std::uint16_t v, object_t* parent)
{ return _objCreateValueNode<uint16_t>( parent, v ); }
cw::object_t* cw::newObject( std::int16_t v, object_t* parent)
{ return _objCreateValueNode<int16_t>( parent, v ); }
cw::object_t* cw::newObject( std::uint32_t v, object_t* parent)
{ return _objCreateValueNode<uint32_t>( parent, v ); }
cw::object_t* cw::newObject( std::int32_t v, object_t* parent)
{ return _objCreateValueNode<int32_t>( parent, v ); }
cw::object_t* cw::newObject( std::uint64_t v, object_t* parent)
{ return _objCreateValueNode<uint64_t>( parent, v ); }
cw::object_t* cw::newObject( std::int64_t v, object_t* parent)
{ return _objCreateValueNode<uint64_t>( parent, v ); }
cw::object_t* cw::newObject( bool v, object_t* parent)
{ return _objCreateValueNode<bool>( parent, v ); }
cw::object_t* cw::newObject( float v, object_t* parent)
{ return _objCreateValueNode<float>( parent, v ); }
cw::object_t* cw::newObject( double v, object_t* parent)
{ return _objCreateValueNode<double>( parent, v ); }
cw::object_t* cw::newObject( char* v, object_t* parent)
{ return _objCreateValueNode<const char*>( parent, v ); }
cw::object_t* cw::newObject( const char* v, object_t* parent)
{ return _objCreateValueNode<const char*>( parent, v ); }
2019-12-19 03:24:12 +00:00
cw::object_t* cw::newDictObject( object_t* parent )
{ return _objAppendRightMostNode(parent, _objAllocate( kDictTId, parent) ); }
cw::object_t* cw::newListObject( object_t* parent )
{ return _objAppendRightMostNode(parent, _objAllocate( kListTId, parent) ); }
cw::object_t* cw::newPairObject( const char* label, object_t* value, object_t* parent)
{
object_t* pair = _objAppendRightMostNode(parent, _objAllocate( kPairTId, parent) );
_objCreateValueNode<const char*>( pair, label );
pair->append_child(value);
return value;
}
cw::object_t* cw::newPairObject( const char* label, std::uint8_t v, object_t* parent)
{ return _objCreatePairNode<uint8_t>( parent, label, v ); }
cw::object_t* cw::newPairObject( const char* label, std::int8_t v, object_t* parent)
{ return _objCreatePairNode<int8_t>( parent, label, v ); }
cw::object_t* cw::newPairObject( const char* label, std::uint16_t v, object_t* parent)
{ return _objCreatePairNode<uint16_t>( parent, label, v ); }
cw::object_t* cw::newPairObject( const char* label, std::int16_t v, object_t* parent)
{ return _objCreatePairNode<int16_t>( parent, label, v ); }
cw::object_t* cw::newPairObject( const char* label, std::uint32_t v, object_t* parent)
{ return _objCreatePairNode<uint32_t>( parent, label, v ); }
cw::object_t* cw::newPairObject( const char* label, std::int32_t v, object_t* parent)
{ return _objCreatePairNode<int32_t>( parent, label, v ); }
cw::object_t* cw::newPairObject( const char* label, std::uint64_t v, object_t* parent)
{ return _objCreatePairNode<uint64_t>( parent, label, v ); }
cw::object_t* cw::newPairObject( const char* label, std::int64_t v, object_t* parent)
{ return _objCreatePairNode<uint64_t>( parent, label, v ); }
cw::object_t* cw::newPairObject( const char* label, bool v, object_t* parent)
{ return _objCreatePairNode<bool>( parent, label, v ); }
cw::object_t* cw::newPairObject( const char* label, float v, object_t* parent)
{ return _objCreatePairNode<float>( parent, label, v ); }
cw::object_t* cw::newPairObject( const char* label, double v, object_t* parent)
{ return _objCreatePairNode<double>( parent, label, v ); }
cw::object_t* cw::newPairObject( const char* label, char* v, object_t* parent)
{ return _objCreatePairNode<const char*>( parent, label, v ); }
cw::object_t* cw::newPairObject( const char* label, const char* v, object_t* parent)
{ return _objCreatePairNode<const char*>( parent, label, v ); }
2019-12-19 03:24:12 +00:00
cw::rc_t cw::objectFromString( const char* s, object_t*& objRef )
{
lex::handle_t lexH;
rc_t rc;
unsigned lexFlags = 0;
unsigned lexId = lex::kErrorLexTId;
object_t* cnp = _objAllocate(kRootTId,nullptr);
object_t* root = cnp;
2019-12-19 03:24:12 +00:00
objRef = nullptr;
if((rc = lex::create(lexH,s,textLength(s), lexFlags )) != kOkRC )
2019-12-19 03:24:12 +00:00
return rc;
// setup the lexer with additional tokens
for(unsigned i=0; _objTokenArray[i].id != lex::kErrorLexTId; ++i)
if((rc = lex::registerToken( lexH, _objTokenArray[i].id, _objTokenArray[i].label )) != kOkRC )
2019-12-19 03:24:12 +00:00
{
rc = cwLogError(rc,"Object lexer token registration failed on token id:%i : '%s'",_objTokenArray[i].id, _objTokenArray[i].label);
goto errLabel;
}
// register the segmented token matcher
if((rc = lex::registerMatcher( lexH, kSegmentedIdLexTId, _lexSegmentedIdMatcher)) != kOkRC )
{
rc = cwLogError(rc,"Object lexer token matcher registration failed");
goto errLabel;
}
2019-12-19 03:24:12 +00:00
// main parser loop
while((lexId = lex::getNextToken(lexH)) != lex::kErrorLexTId && (lexId != lex::kEofLexTId) && (rc == kOkRC))
2019-12-19 03:24:12 +00:00
{
switch( lexId )
{
case kLCurlyLexTId:
cnp = _objCreateConainerNode( lexH, cnp, kDictTId );
break;
case kRCurlyLexTId:
if( cnp == nullptr )
_objSyntaxError(lexH,"An end of 'object' was encountered without an associated 'object' start.");
else
cnp = cnp->parent;
break;
case kLHardLexTId:
cnp = _objCreateConainerNode( lexH, cnp, kListTId );
break;
case kRHardLexTId:
if( cnp == nullptr )
rc = _objSyntaxError(lexH,"An end of 'array' was encountered without an associated 'array' start.");
else
cnp = cnp->parent;
break;
case kColonLexTId:
if( cnp == nullptr || !cnp->is_pair() )
rc = _objSyntaxError(lexH,"A colon was encountered outside a 'pair' node.");
break;
case kCommaLexTId:
if( cnp == nullptr || (!cnp->is_list() && !cnp->is_dict()) )
rc = _objSyntaxError(lexH,"Unexpected comma outside of 'array' or 'object'.");
break;
case lex::kRealLexTId:
if( tokenIsSinglePrecision(lexH) )
_objCreateValueNode( cnp, lex::tokenFloat(lexH),"float" );
else
_objCreateValueNode( cnp, lex::tokenDouble(lexH), "double" );
2019-12-19 03:24:12 +00:00
break;
case lex::kIntLexTId:
if( tokenIsUnsigned(lexH) )
_objCreateValueNode( cnp, lex::tokenUInt(lexH), "uint" );
else
_objCreateValueNode( cnp, lex::tokenInt(lexH), "int" );
2019-12-19 03:24:12 +00:00
break;
case lex::kHexLexTId:
_objCreateValueNode( cnp, lex::tokenInt(lexH), "int", kHexFl );
2019-12-19 03:24:12 +00:00
break;
case kTrueLexTId:
_objCreateValueNode( cnp, true, "true" );
break;
case kFalseLexTId:
_objCreateValueNode( cnp, false, "false" );
break;
case kNullLexTId:
_objAppendRightMostNode( cnp, _objAllocate( kNullTId, cnp ));
2019-12-19 03:24:12 +00:00
break;
case kSegmentedIdLexTId:
case lex::kIdentLexTId:
case lex::kQStrLexTId:
2019-12-19 03:24:12 +00:00
{
// if the parent is an object then this string must be a pair label
if( cnp->is_dict() )
cnp = _objAppendRightMostNode( cnp, _objAllocate( kPairTId, cnp ));
unsigned n = lex::tokenCharCount(lexH);
char s[ n + 1 ];
memcpy(s,lex::tokenText(lexH),n);
s[n] = 0;
2019-12-19 03:24:12 +00:00
//char* v = mem::duplStr(lex::tokenText(lexH),lex::tokenCharCount(lexH));
unsigned identFl = lexId != lex::kQStrLexTId ? kIdentFl : 0;
2019-12-19 03:24:12 +00:00
_objCreateValueNode<char*>( cnp, s, "string", identFl );
2019-12-19 03:24:12 +00:00
}
break;
case lex::kEofLexTId:
2019-12-19 03:24:12 +00:00
break;
default:
_objSyntaxError(lexH,"Unknown token type (%i) in text.", int(lexId) );
}
if( cnp == nullptr )
{
rc = _objSyntaxError( lexH, "Node parse failed." );
goto errLabel;
}
2019-12-19 03:24:12 +00:00
// if this is a pair node and it now has both values
// then make the parent 'object' the current node
if( cnp->is_pair() && cnp->child_count()==2 )
cnp = cnp->parent;
}
if( lexId == lex::kErrorLexTId )
{
rc = cwLogError(kSyntaxErrorRC,"A lexical element was not recognized.");
goto errLabel;
}
// if the root has only one child then make the child the root
if( root != nullptr && root->child_count() == 1 )
{
cnp = root->u.children;
cnp->unlink();
root->free();
root = cnp;
}
2019-12-19 03:24:12 +00:00
objRef = root;
errLabel:
rc_t rc0 = lex::destroy(lexH);
2019-12-19 03:24:12 +00:00
return rc != kOkRC ? rc : rc0;
}
cw::rc_t cw::objectFromFile( const char* fn, object_t*& objRef )
{
rc_t rc = kOkRC;
unsigned bufByteCnt = 0;
char* buf = NULL;
objRef = nullptr;
2019-12-19 03:24:12 +00:00
if(( buf = file::fnToStr(fn, &bufByteCnt)) == NULL )
rc = cwLogError(kOpFailRC,"File to text buffer conversion failed on '%s'.",cwStringNullGuard(fn));
else
2019-12-19 03:24:12 +00:00
{
rc = objectFromString( buf, objRef );
mem::release(buf);
2019-12-19 03:24:12 +00:00
}
return rc;
}
void cw::objectPrintTypes( object_t* o0 )
{
if( o0->is_container() )
for(object_t* o = o0->u.children; o!=nullptr; o=o->sibling)
objectPrintTypes(o);
printf("%s ",o0->type->label);
}
cw::rc_t cw::objectToFile( const char* fn, const object_t* obj )
{
rc_t rc = kOkRC;
char* buf;
if((buf = obj->to_string()) == nullptr )
{
rc = cwLogError(kOpFailRC,"Unable to convert object to string.");
goto errLabel;
}
if((rc = file::fnWrite(fn,buf,textLength(buf))) != kOkRC )
{
rc = cwLogError(rc,"Object write failed on write to '%s'.",cwStringNullGuard(fn));
goto errLabel;
}
errLabel:
mem::release(buf);
return rc;
}
2019-12-19 03:24:12 +00:00
namespace cw
{
rc_t _object_test_basic( const test::test_args_t& args )
{
rc_t rc = kOkRC;
cw::object_t* o = nullptr;
const char s [] = "{ a:1, b:2, c:[ 1.23, 4.56 ], d:true, e:false, f:true }";
int a = 0;
int b = 0;
const cw::object_t* c = nullptr;
bool d,e,f;
const unsigned bufN = 128;
char buf[bufN];
unsigned i = 0;
cw::object_t* oo = nullptr;
if((rc = cw::objectFromString(s,o)) != kOkRC )
goto errLabel;
int v;
if((rc = o->get("b",v)) != kOkRC )
goto errLabel;
cwLogPrint("value:%i\n",v);
o->print();
if((rc = o->getv("a",a,"b",b)) != kOkRC )
goto errLabel;
cwLogPrint("G: %i %i\n",a,b);
if((rc = o->readv("a",0,a,
"b",0,b,
"c",cw::kOptFl | cw::kListTId,c,
"d",0,d,
"e",0,e,
"f",0,f)) != kOkRC )
{
goto errLabel;
}
cwLogPrint("R: %i %i : %i %i %i\n",a,b,d,e,f);
i = o->to_string(buf,bufN);
cwLogPrint("%i : %s\n",i, buf);
oo = o->duplicate();
oo->print();
oo->free();
o->free();
errLabel:
return rc;
}
rc_t _object_test_to_json( const test::test_args_t& args )
{
double v0[] = {1.23,2.34,3.45};
unsigned v0N = sizeof(v0)/sizeof(v0[0]);
int v1[] = {-1,0,1,2,3,4};
unsigned v1N = sizeof(v1)/sizeof(v1[0]);
cw::object_t* d = cw::newDictObject();
d->putv("A","Abc","B",1.234);
d->put_numeric_list("v0",v0,v0N);
d->put_numeric_list("v1",v1,v1N);
char* s = d->to_string();
cwLogPrint("%s\n",s);
cw::mem::release(s);
d->free();
return kOkRC;
}
}
cw::rc_t cw::object_test( const test::test_args_t& args )
{
rc_t rc = kOkRC;
if( textIsEqual(args.test_label,"basic") )
{
rc = _object_test_basic(args);
goto errLabel;
}
if( textIsEqual(args.test_label,"to_json") )
{
rc = _object_test_to_json(args);
goto errLabel;
}
rc = cwLogError(kInvalidArgRC,"Unknown test case module:%s test:%s.",args.module_label,args.test_label);
errLabel:
return rc;
}
2019-12-19 03:24:12 +00:00