/** @file * * @par History * - 2005/11/26 Shinigami: changed "strcmp" into "stricmp" to suppress Script Errors * - 2008/02/11 Turley: BStruct::unpack() will accept zero length Structs * - 2009/09/05 Turley: Added struct .? and .- as shortcut for .exists() and .erase() * - 2009/12/21 Turley: ._method() call fix */ #include "bscript/bstruct.h" #include #include #include #include "clib/passert.h" #include "clib/stlutil.h" #include "bscript/barray.h" #include "bscript/bcontiter.h" #include "bscript/berror.h" #include "bscript/blong.h" #include "bscript/bobject.h" #include "bscript/bstring.h" #include "bscript/buninit.h" #include "bscript/executor.h" #include "bscript/objmethods.h" namespace Pol::Bscript { BStruct::BStruct() : BObjectImp( OTStruct ), contents_() {} BStruct::BStruct( BObjectType type ) : BObjectImp( type ), contents_() {} BStruct::BStruct( const BStruct& other, BObjectType type ) : BObjectImp( type ), contents_() { for ( const auto& elem : other.contents_ ) { const std::string& key = elem.first; const BObjectRef& bvalref = elem.second; contents_[key] = BObjectRef( new BObject( bvalref->impref().copy() ) ); } } BStruct::BStruct( std::istream& is, unsigned size, BObjectType type ) : BObjectImp( type ), contents_() { for ( unsigned i = 0; i < size; ++i ) { BObjectImp* keyimp = BObjectImp::unpack( is ); BObjectImp* valimp = BObjectImp::unpack( is ); if ( auto* key = impptrIf( keyimp ); valimp && key ) { contents_[key->value()].set( new BObject( valimp ) ); BObject cleaner( key ); } else { if ( keyimp ) BObject objk( keyimp ); if ( valimp ) BObject objv( valimp ); } } } BObjectImp* BStruct::copy() const { passert( isa( OTStruct ) ); return new BStruct( *this, OTStruct ); } char BStruct::packtype() const { return 't'; } const char* BStruct::typetag() const { return "struct"; } const char* BStruct::typeOf() const { return "Struct"; } u8 BStruct::typeOfInt() const { return OTStruct; } BObjectImp* BStruct::unpack( std::istream& is ) { unsigned size; char colon; if ( !( is >> size >> colon ) ) { return new BError( "Unable to unpack struct elemcount" ); } if ( (int)size < 0 ) { return new BError( "Unable to unpack struct elemcount. Length given must be positive integer!" ); } if ( colon != ':' ) { return new BError( "Unable to unpack struct elemcount. Bad format. Colon not found!" ); } return new BStruct( is, size, OTStruct ); } void BStruct::FormatForStringRep( std::string& rep, const std::string& key, const BObjectRef& bvalref ) const { fmt::format_to( std::back_inserter( rep ), "{} = {}", key, bvalref->impref().getFormattedStringRep() ); } class BStructIterator final : public ContIterator { public: BStructIterator( BStruct* pDict, BObject* pIterVal ); BObject* step() override; private: BObject m_StructObj; BStruct* m_pStruct; BObjectRef m_IterVal; std::string key; bool m_First; }; BStructIterator::BStructIterator( BStruct* pStruct, BObject* pIterVal ) : m_StructObj( pStruct ), m_pStruct( pStruct ), m_IterVal( pIterVal ), key( "" ), m_First( true ) { } BObject* BStructIterator::step() { if ( m_First ) { auto itr = m_pStruct->contents_.begin(); if ( itr == m_pStruct->contents_.end() ) return nullptr; m_First = false; key = ( *itr ).first; m_IterVal->setimp( new String( key ) ); BObjectRef& oref = ( *itr ).second; return oref.get(); } auto itr = m_pStruct->contents_.find( key ); if ( itr == m_pStruct->contents_.end() ) return nullptr; ++itr; if ( itr == m_pStruct->contents_.end() ) return nullptr; key = ( *itr ).first; m_IterVal->setimp( new String( key ) ); BObjectRef& oref = ( *itr ).second; return oref.get(); } ContIterator* BStruct::createIterator( BObject* pIterVal ) { return new BStructIterator( this, pIterVal ); } size_t BStruct::sizeEstimate() const { return sizeof( BStruct ) + Clib::memsize( contents_, []( const auto& v ) { return v.sizeEstimate(); } ); } size_t BStruct::mapcount() const { return contents_.size(); } BObjectRef BStruct::set_member( const char* membername, BObjectImp* value, bool copy ) { BObjectImp* target = copy ? value->copy() : value; auto itr = contents_.find( membername ); if ( itr != contents_.end() ) { BObjectRef& oref = ( *itr ).second; oref->setimp( target ); return oref; } BObjectRef ref( new BObject( target ) ); contents_.emplace( membername, ref ); return ref; } // used programmatically const BObjectImp* BStruct::FindMember( const char* name ) { auto itr = contents_.find( name ); if ( itr != contents_.end() ) return ( *itr ).second->impptr(); return nullptr; } BObjectRef BStruct::get_member( const char* membername ) { auto itr = contents_.find( membername ); if ( itr != contents_.end() ) return ( *itr ).second; return BObjectRef( UninitObject::create() ); } BObjectRef BStruct::OperSubscript( const BObject& obj ) { if ( obj->isa( OTString ) ) { const String* keystr = obj.impptr(); auto itr = contents_.find( keystr->value() ); if ( itr != contents_.end() ) { BObjectRef& oref = ( *itr ).second; return oref; } return BObjectRef( UninitObject::create() ); } return BObjectRef( new BError( "Struct members can only be accessed by name" ) ); } BObjectImp* BStruct::array_assign( BObjectImp* idx, BObjectImp* target, bool copy ) { if ( auto* key = impptrIf( idx ) ) { BObjectImp* new_target = copy ? target->copy() : target; auto itr = contents_.find( key->value() ); if ( itr != contents_.end() ) { BObjectRef& oref = ( *itr ).second; oref->setimp( new_target ); return new_target; } contents_[key->value()].set( new BObject( new_target ) ); return new_target; } return new BError( "Struct members can only be accessed by name" ); } void BStruct::addMember( const char* name, BObjectRef val ) { contents_[name] = std::move( val ); } void BStruct::addMember( const char* name, BObjectImp* imp ) { contents_[name] = BObjectRef( imp ); } BObjectImp* BStruct::call_method_id( const int id, Executor& ex, bool /*forcebuiltin*/ ) { BObject* keyobj; BObject* valobj; switch ( id ) { case MTH_SIZE: if ( ex.numParams() == 0 ) return new BLong( static_cast( contents_.size() ) ); return new BError( "struct.size() doesn't take parameters." ); case MTH_ERASE: if ( ex.numParams() == 1 && ( keyobj = ex.getParamObj( 0 ) ) != nullptr ) { if ( !keyobj->isa( OTString ) ) return new BError( "Struct keys must be strings" ); String* strkey = keyobj->impptr(); int nremove = static_cast( contents_.erase( strkey->value() ) ); return new BLong( nremove ); } return new BError( "struct.erase(key) requires a parameter." ); break; case MTH_INSERT: if ( ex.numParams() == 2 && ( keyobj = ex.getParamObj( 0 ) ) != nullptr && ( valobj = ex.getParamObj( 1 ) ) != nullptr ) { if ( !keyobj->isa( OTString ) ) return new BError( "Struct keys must be strings" ); String* strkey = keyobj->impptr(); contents_[strkey->value()] = BObjectRef( new BObject( valobj->impptr()->copy() ) ); return new BLong( static_cast( contents_.size() ) ); } return new BError( "struct.insert(key,value) requires two parameters." ); break; case MTH_EXISTS: if ( ex.numParams() == 1 && ( keyobj = ex.getParamObj( 0 ) ) != nullptr ) { if ( !keyobj->isa( OTString ) ) return new BError( "Struct keys must be strings" ); String* strkey = keyobj->impptr(); int count = static_cast( contents_.count( strkey->value() ) ); return new BLong( count ); } return new BError( "struct.exists(key) requires a parameter." ); case MTH_KEYS: if ( ex.numParams() == 0 ) { std::unique_ptr arr( new ObjArray ); for ( const auto& content : contents_ ) { arr->addElement( new String( content.first ) ); } return arr.release(); } return new BError( "struct.keys() doesn't take parameters." ); break; default: return nullptr; } } BObjectImp* BStruct::call_method( const char* methodname, Executor& ex ) { ObjMethod* objmethod = getKnownObjMethod( methodname ); if ( objmethod != nullptr ) return this->call_method_id( objmethod->id, ex ); return nullptr; } void BStruct::packonto( std::string& str ) const { using namespace fmt::literals; fmt::format_to( std::back_inserter( str ), "{}{}:"_cf, packtype(), contents_.size() ); for ( const auto& [key, bvalref] : contents_ ) { String::packonto( str, key ); bvalref->impref().packonto( str ); } } std::string BStruct::getStringRep() const { std::string rep = fmt::format( "{}{{ ", typetag() ); bool any = false; for ( const auto& [key, bvalref] : contents_ ) { if ( any ) rep += ", "; else any = true; FormatForStringRep( rep, key, bvalref ); } rep += " }"; return rep; } BObjectRef BStruct::operDotPlus( const char* name ) { if ( contents_.count( name ) == 0 ) { auto pnewobj = new BObject( new UninitObject ); contents_[name] = BObjectRef( pnewobj ); return BObjectRef( pnewobj ); } return BObjectRef( new BError( "Member already exists" ) ); } BObjectRef BStruct::operDotMinus( const char* name ) { contents_.erase( name ); return BObjectRef( new BLong( 1 ) ); } BObjectRef BStruct::operDotQMark( const char* name ) { int count = static_cast( contents_.count( name ) ); return BObjectRef( new BLong( count ) ); } const BStruct::Contents& BStruct::contents() const { return contents_; } } // namespace Pol::Bscript