libcw/cwObject.cpp

560 lines
20 KiB
C++
Raw Normal View History

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 "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
};
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
};
void _objTypeFree( object_t* o )
{ mem::release(o); }
2019-12-19 03:24:12 +00:00
void _objTypeFreeString( object_t* o )
{
mem::release( o->u.str );
2019-12-19 03:24:12 +00:00
_objTypeFree(o);
}
const char* _objTypeIdToLabel( objTypeId_t tid );
void _objTypeNullToString( object_t* o, char* buf, unsigned bufN ) { snprintf(buf,bufN,"%s","NULL"); }
void _objTypeCharToString( object_t* o, char* buf, unsigned bufN ) { number_to_string<char>(o->u.c,"%c",buf,bufN); }
void _objTypeInt8ToString( object_t* o, char* buf, unsigned bufN ) { number_to_string<int8_t>(o->u.i8,"%i",buf,bufN); }
void _objTypeUInt8ToString( object_t* o, char* buf, unsigned bufN ) { number_to_string<uint8_t>(o->u.u8,"%i",buf,bufN); }
void _objTypeInt16ToString( object_t* o, char* buf, unsigned bufN ) { number_to_string<int16_t>(o->u.i16,"%i",buf,bufN); }
void _objTypeUInt16ToString( object_t* o, char* buf, unsigned bufN ) { number_to_string<uint16_t>(o->u.u16,"%i",buf,bufN); }
void _objTypeInt32ToString( object_t* o, char* buf, unsigned bufN ) { number_to_string<int32_t>(o->u.i32,"%i",buf,bufN); }
void _objTypeUInt32ToString( object_t* o, char* buf, unsigned bufN ) { number_to_string<uint32_t>(o->u.u32,"%i",buf,bufN); }
void _objTypeInt64ToString( object_t* o, char* buf, unsigned bufN ) { number_to_string<int64_t>(o->u.i64,"%i",buf,bufN); }
void _objTypeUInt64ToString( object_t* o, char* buf, unsigned bufN ) { number_to_string<uint64_t>(o->u.u64,"%i",buf,bufN); }
void _objTypeBoolToString( object_t* o, char* buf, unsigned bufN ) { number_to_string<bool>(o->u.b,"%i",buf,bufN); }
void _objTypeFloatToString( object_t* o, char* buf, unsigned bufN ) { number_to_string<float>(o->u.f,"%f",buf,bufN); }
void _objTypeDoubleToString( object_t* o, char* buf, unsigned bufN ) { number_to_string<double>(o->u.d,"%f",buf,bufN); }
void _objTypeStringToString( object_t* o, char* buf, unsigned bufN ) { snprintf(buf,bufN,"%s",o->u.str); }
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 )
{ return cwLogError(kInvalidArgRC, "There is no conversion from '%s' to '%s'.", _objTypeIdToLabel(tid), o->type->label); }
rc_t _objTypeValueFromString( const object_t* o, unsigned tid, void* dst )
{ return _objTypeValueFromNonValue(o,tid,dst); }
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)
printf(indentStr);
printf("%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);
printf(eolStr);
}
void _objTypePrintNull( const object_t* o, print_ctx_t& c ) { printf("NULL "); }
void _objTypePrintError( const object_t* o, print_ctx_t& c ) { printf("Error "); }
void _objTypePrintChar( const object_t* o, print_ctx_t& c ) { printf("%c",o->u.c); }
void _objTypePrintInt8( const object_t* o, print_ctx_t& c ) { printf("%i",o->u.i8); }
void _objTypePrintUInt8( const object_t* o, print_ctx_t& c ) { printf("%i",o->u.u8); }
void _objTypePrintInt16( const object_t* o, print_ctx_t& c ) { printf("%i",o->u.i16); }
void _objTypePrintUInt16( const object_t* o, print_ctx_t& c ) { printf("%i",o->u.u16); }
void _objTypePrintInt32( const object_t* o, print_ctx_t& c ) { printf("%i",o->u.i32); }
void _objTypePrintUInt32( const object_t* o, print_ctx_t& c ) { printf("%i",o->u.u32); }
void _objTypePrintInt64( const object_t* o, print_ctx_t& c ) { printf("%li",o->u.i64); }
void _objTypePrintUInt64( const object_t* o, print_ctx_t& c ) { printf("%li",o->u.u64); }
void _objTypePrintBool( const object_t* o, print_ctx_t& c ) { printf("%s",o->u.b ? "true" : "false"); }
void _objTypePrintFloat( const object_t* o, print_ctx_t& c ) { printf("%f",o->u.f); }
void _objTypePrintDouble( const object_t* o, print_ctx_t& c ) { printf("%f",o->u.d); }
void _objTypePrintString( const object_t* o, print_ctx_t& c ) { printf("%s",o->u.str); }
void _objTypePrintVect( const object_t* o, print_ctx_t& c ) { printf("<vect>"); }
void _objTypePrintPair( const object_t* o, print_ctx_t& c )
{
o->u.children->type->print(o->u.children,c);
printf(": ");
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);
}
objType_t _objTypeArray[] =
{
{ kNullTId, "null", 0, _objTypeFree, _objTypeValueFromNonValue, _objTypePrintNull },
{ kErrorTId, "error", 0, _objTypeFree, _objTypeValueFromNonValue, _objTypePrintError },
{ kCharTId, "char", 0, _objTypeFree, _objTypeValueFromChar, _objTypePrintChar },
{ kInt8TId, "int8", 0, _objTypeFree, _objTypeValueFromInt8, _objTypePrintInt8 },
{ kUInt8TId, "uint8", 0, _objTypeFree, _objTypeValueFromUInt8, _objTypePrintUInt8 },
{ kInt16TId, "int16", 0, _objTypeFree, _objTypeValueFromInt16, _objTypePrintInt16 },
{ kUInt16TId, "uint16", 0, _objTypeFree, _objTypeValueFromUInt16, _objTypePrintUInt16 },
{ kInt32TId, "int32", 0, _objTypeFree, _objTypeValueFromInt32, _objTypePrintInt32 },
{ kUInt32TId, "uint32", 0, _objTypeFree, _objTypeValueFromUInt32, _objTypePrintUInt32 },
{ kInt64TId, "int64", 0, _objTypeFree, _objTypeValueFromInt64, _objTypePrintInt64 },
{ kUInt64TId, "uint64", 0, _objTypeFree, _objTypeValueFromUInt64, _objTypePrintUInt64 },
{ kBoolTId, "bool", 0, _objTypeFree, _objTypeValueFromBool, _objTypePrintBool },
{ kFloatTId, "float", 0, _objTypeFree, _objTypeValueFromFloat, _objTypePrintFloat },
{ kDoubleTId, "double", 0, _objTypeFree, _objTypeValueFromDouble, _objTypePrintDouble },
{ kStringTId, "string", 0, _objTypeFreeString, _objTypeValueFromString, _objTypePrintString },
{ kVectTId, "vect", 0, _objTypeFree, _objTypeValueFromVect, _objTypePrintVect },
{ kPairTId, "pair", kContainerFl | kValueContainerFl, _objTypeFree, _objTypeValueFromNonValue, _objTypePrintPair },
{ kListTId, "list", kContainerFl | kValueContainerFl, _objTypeFree, _objTypeValueFromNonValue, _objTypePrintList },
{ kDictTId, "dict", kContainerFl, _objTypeFree, _objTypeValueFromNonValue, _objTypePrintDict },
{ kRootTId, "root", kContainerFl | kValueContainerFl, _objTypeFree, _objTypeValueFromNonValue, _objTypePrintRoot },
{ kInvalidTId, "<invalid>", 0, nullptr }
};
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* _objAppendLeftMostNode( object_t* parent, object_t* newNode )
{
if( newNode == nullptr )
return nullptr;
object_t* child = parent->u.children;
if( parent->u.children == nullptr )
parent->u.children = newNode;
else
{
while( child->sibling != nullptr )
child = child->sibling;
child->sibling = newNode;
}
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 _objAppendLeftMostNode( parent, _objAllocate( tid, parent ));
return nullptr;
}
}
void cw::object_t::free()
{
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);
}
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( char*& v ) const { return type->value(this,kStringTId,&v); }
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;
}
const struct cw::object_str* cw::object_t::find( const char* label ) const
{
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((ch = o->find(label)) != nullptr )
return ch;
}
2020-01-27 22:48:17 +00:00
}
return nullptr;
}
const struct cw::object_str* cw::object_t::list_ele( unsigned idx ) const
{
if( is_list() )
{
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;
}
2020-01-27 22:48:17 +00:00
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::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;
}
// main parser loop
while((lexId = lex::getNextToken(lexH)) != lex::kErrorLexTId && (lexId != lex::kEofLexTId) && (rc == kOkRC))
2019-12-19 03:24:12 +00:00
{
//printf("Lex:%s\n",lexIdToLabel(lexH,lexId));
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:
_objCreateValueNode( cnp, lex::tokenDouble(lexH), "real" );
2019-12-19 03:24:12 +00:00
break;
case lex::kIntLexTId:
_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:
_objAppendLeftMostNode( cnp, _objAllocate( kNullTId, cnp ));
break;
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 = _objAppendLeftMostNode( cnp, _objAllocate( kPairTId, cnp ));
char* v = mem::duplStr(lex::tokenText(lexH),lex::tokenCharCount(lexH));
unsigned identFl = lexId == lex::kIdentLexTId ? kIdentFl : 0;
2019-12-19 03:24:12 +00:00
_objCreateValueNode<char*>( cnp, v, "string", identFl );
}
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 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;
}
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;
if(( buf = file::fnToStr(fn, &bufByteCnt)) != NULL )
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);
}