From 26d514baea22b34aeda37121103e552a45a5fa2f Mon Sep 17 00:00:00 2001 From: Aang23 Date: Tue, 12 Aug 2025 19:30:15 +0200 Subject: [PATCH] Update some AS stuff --- .../angelscript/scriptarray/scriptarray.cpp | 60 +++- .../scriptbuilder/scriptbuilder.cpp | 50 ++- .../angelscript/scriptbuilder/scriptbuilder.h | 5 +- .../scriptdictionary/scriptdictionary.cpp | 300 +++++++++++++----- .../scriptdictionary/scriptdictionary.h | 26 ++ .../scriptsatdump/bind_satdump.cpp | 1 + .../scriptstdstring/scriptstdstring.cpp | 260 ++++++++++++++- .../scriptstdstring/scriptstdstring_utils.cpp | 8 +- 8 files changed, 598 insertions(+), 112 deletions(-) diff --git a/src-core/angelscript/scriptarray/scriptarray.cpp b/src-core/angelscript/scriptarray/scriptarray.cpp index 9e9904b9d..b7827a902 100644 --- a/src-core/angelscript/scriptarray/scriptarray.cpp +++ b/src-core/angelscript/scriptarray/scriptarray.cpp @@ -244,6 +244,30 @@ static bool ScriptArrayTemplateCallback(asITypeInfo *ti, bool &dontGarbageCollec return true; } + +asUINT CScriptArray_opForBegin(const CScriptArray*) +{ + return 0; +} + +bool CScriptArray_opForEnd(asUINT iter, const CScriptArray*arr) +{ + if (arr == 0 || arr->GetSize() <= iter) + return true; + + return false; +} + +asUINT CScriptArray_opForNext(asUINT iter, const CScriptArray*) +{ + return iter + 1; +} + +asUINT CScriptArray_opForValue1(asUINT iter, const CScriptArray*) +{ + return iter; +} + // Registers the template array type void RegisterScriptArray(asIScriptEngine *engine, bool defaultArray) { @@ -289,6 +313,13 @@ static void RegisterScriptArray_Native(asIScriptEngine *engine) r = engine->RegisterObjectMethod("array", "T &opIndex(uint index)", asMETHODPR(CScriptArray, At, (asUINT), void*), asCALL_THISCALL); assert( r >= 0 ); r = engine->RegisterObjectMethod("array", "const T &opIndex(uint index) const", asMETHODPR(CScriptArray, At, (asUINT) const, const void*), asCALL_THISCALL); assert( r >= 0 ); + // Support for foreach + r = engine->RegisterObjectMethod("array", "uint opForBegin() const", asFUNCTIONPR(CScriptArray_opForBegin, (const CScriptArray *), asUINT), asCALL_CDECL_OBJLAST); assert(r >= 0); + r = engine->RegisterObjectMethod("array", "bool opForEnd(uint) const", asFUNCTIONPR(CScriptArray_opForEnd, (asUINT, const CScriptArray*), bool), asCALL_CDECL_OBJLAST); assert(r >= 0); + r = engine->RegisterObjectMethod("array", "uint opForNext(uint) const", asFUNCTIONPR(CScriptArray_opForNext, (asUINT, const CScriptArray*), asUINT), asCALL_CDECL_OBJLAST); assert(r >= 0); + r = engine->RegisterObjectMethod("array", "const T &opForValue0(uint index) const", asMETHODPR(CScriptArray, At, (asUINT) const, const void*), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("array", "uint opForValue1(uint index) const", asFUNCTIONPR(CScriptArray_opForValue1, (asUINT, const CScriptArray*), asUINT), asCALL_CDECL_OBJLAST); assert(r >= 0); + // The assignment operator r = engine->RegisterObjectMethod("array", "array &opAssign(const array&in)", asMETHOD(CScriptArray, operator=), asCALL_THISCALL); assert( r >= 0 ); @@ -2203,6 +2234,26 @@ static void ScriptArrayReleaseAllHandles_Generic(asIScriptGeneric *gen) self->ReleaseAllHandles(engine); } +static void ScriptArray_opForBegin_Generic(asIScriptGeneric* gen) +{ + *reinterpret_cast(gen->GetAddressOfReturnLocation()) = CScriptArray_opForBegin(reinterpret_cast(gen->GetObject())); +} + +static void ScriptArray_opForEnd_Generic(asIScriptGeneric* gen) +{ + *reinterpret_cast(gen->GetAddressOfReturnLocation()) = CScriptArray_opForEnd(gen->GetArgDWord(0), reinterpret_cast(gen->GetObject())); +} + +static void ScriptArray_opForNext_Generic(asIScriptGeneric* gen) +{ + *reinterpret_cast(gen->GetAddressOfReturnLocation()) = CScriptArray_opForNext(gen->GetArgDWord(0), reinterpret_cast(gen->GetObject())); +} + +static void ScriptArray_opForValue1_Generic(asIScriptGeneric* gen) +{ + *reinterpret_cast(gen->GetAddressOfReturnLocation()) = CScriptArray_opForValue1(gen->GetArgDWord(0), reinterpret_cast(gen->GetObject())); +} + static void RegisterScriptArray_Generic(asIScriptEngine *engine) { int r = 0; @@ -2221,7 +2272,14 @@ static void RegisterScriptArray_Generic(asIScriptEngine *engine) r = engine->RegisterObjectBehaviour("array", asBEHAVE_RELEASE, "void f()", asFUNCTION(ScriptArrayRelease_Generic), asCALL_GENERIC); assert( r >= 0 ); r = engine->RegisterObjectMethod("array", "T &opIndex(uint index)", asFUNCTION(ScriptArrayAt_Generic), asCALL_GENERIC); assert( r >= 0 ); r = engine->RegisterObjectMethod("array", "const T &opIndex(uint index) const", asFUNCTION(ScriptArrayAt_Generic), asCALL_GENERIC); assert( r >= 0 ); - r = engine->RegisterObjectMethod("array", "array &opAssign(const array&in)", asFUNCTION(ScriptArrayAssignment_Generic), asCALL_GENERIC); assert( r >= 0 ); + + r = engine->RegisterObjectMethod("array", "uint opForBegin() const", asFUNCTION(ScriptArray_opForBegin_Generic), asCALL_GENERIC); assert(r >= 0); + r = engine->RegisterObjectMethod("array", "bool opForEnd(uint) const", asFUNCTION(ScriptArray_opForEnd_Generic), asCALL_GENERIC); assert(r >= 0); + r = engine->RegisterObjectMethod("array", "uint opForNext(uint) const", asFUNCTION(ScriptArray_opForNext_Generic), asCALL_GENERIC); assert(r >= 0); + r = engine->RegisterObjectMethod("array", "const T &opForValue0(uint index) const", asFUNCTION(ScriptArrayAt_Generic), asCALL_GENERIC); assert(r >= 0); + r = engine->RegisterObjectMethod("array", "uint opForValue1(uint index) const", asFUNCTION(ScriptArray_opForValue1_Generic), asCALL_GENERIC); assert(r >= 0); + + r = engine->RegisterObjectMethod("array", "array &opAssign(const array&in)", asFUNCTION(ScriptArrayAssignment_Generic), asCALL_GENERIC); assert(r >= 0); r = engine->RegisterObjectMethod("array", "void insertAt(uint index, const T&in value)", asFUNCTION(ScriptArrayInsertAt_Generic), asCALL_GENERIC); assert( r >= 0 ); r = engine->RegisterObjectMethod("array", "void insertAt(uint index, const array& arr)", asFUNCTION(ScriptArrayInsertAtArray_Generic), asCALL_GENERIC); assert(r >= 0); diff --git a/src-core/angelscript/scriptbuilder/scriptbuilder.cpp b/src-core/angelscript/scriptbuilder/scriptbuilder.cpp index df5c5d747..deff6809f 100644 --- a/src-core/angelscript/scriptbuilder/scriptbuilder.cpp +++ b/src-core/angelscript/scriptbuilder/scriptbuilder.cpp @@ -152,9 +152,11 @@ void CScriptBuilder::ClearAll() currentNamespace = ""; foundDeclarations.clear(); + typeMetadataMap.clear(); funcMetadataMap.clear(); varMetadataMap.clear(); + classMetadataMap.clear(); #endif } @@ -395,16 +397,26 @@ int CScriptBuilder::ProcessScriptSection(const char *script, unsigned int length // Check if namespace so the metadata for members can be gathered if( token == "namespace" ) { - // Get the identifier after "namespace" + // Get the scope after "namespace". It can be composed of multiple nested namespaces, e.g. A::B::C + // Keep track of the number of nested namespace scopes are declared for each block + int nestedNamespaces = 0; do { - pos += len; - t = engine->ParseToken(&modifiedScript[pos], modifiedScript.size() - pos, &len); - } while(t == asTC_COMMENT || t == asTC_WHITESPACE); + do + { + pos += len; + t = engine->ParseToken(&modifiedScript[pos], modifiedScript.size() - pos, &len); + } while (t == asTC_COMMENT || t == asTC_WHITESPACE); - if( currentNamespace != "" ) - currentNamespace += "::"; - currentNamespace += modifiedScript.substr(pos,len); + if (t == asTC_IDENTIFIER) + { + if (currentNamespace != "") + currentNamespace += "::"; + currentNamespace += modifiedScript.substr(pos, len); + nestedNamespaces++; + } + } while (t == asTC_IDENTIFIER || (t == asTC_KEYWORD && modifiedScript.substr(pos, len) == "::")); + currentNamespaceStack.push_back(nestedNamespaces); // Search until first { is encountered while( pos < modifiedScript.length() ) @@ -428,14 +440,20 @@ int CScriptBuilder::ProcessScriptSection(const char *script, unsigned int length // Check if end of namespace if( currentNamespace != "" && token == "}" ) { - size_t found = currentNamespace.rfind( "::" ); - if( found != string::npos ) + assert(currentNamespaceStack.size() > 0); + int nestedNamespaces = currentNamespaceStack[currentNamespaceStack.size()-1]; + currentNamespaceStack.pop_back(); + while (nestedNamespaces-- > 0) { - currentNamespace.erase( found ); - } - else - { - currentNamespace = ""; + size_t found = currentNamespace.rfind("::"); + if (found != string::npos) + { + currentNamespace.erase(found); + } + else + { + currentNamespace = ""; + } } pos += len; continue; @@ -1043,7 +1061,9 @@ int CScriptBuilder::ExtractDeclaration(int pos, string &name, string &declaratio } else if( t == asTC_IDENTIFIER ) { - name = token; + // If a parenthesis is already found then the name is already known so it must not be overwritten + if( !hasParenthesis ) + name = token; } // Skip trailing decorators diff --git a/src-core/angelscript/scriptbuilder/scriptbuilder.h b/src-core/angelscript/scriptbuilder/scriptbuilder.h index 27cabf970..63b28597e 100644 --- a/src-core/angelscript/scriptbuilder/scriptbuilder.h +++ b/src-core/angelscript/scriptbuilder/scriptbuilder.h @@ -163,8 +163,9 @@ protected: std::string nameSpace; }; std::vector foundDeclarations; - std::string currentClass; - std::string currentNamespace; + std::string currentClass; + std::string currentNamespace; + std::vector currentNamespaceStack; // Storage of metadata for global declarations std::map > typeMetadataMap; diff --git a/src-core/angelscript/scriptdictionary/scriptdictionary.cpp b/src-core/angelscript/scriptdictionary/scriptdictionary.cpp index 7cba1998b..f0413dc67 100644 --- a/src-core/angelscript/scriptdictionary/scriptdictionary.cpp +++ b/src-core/angelscript/scriptdictionary/scriptdictionary.cpp @@ -78,6 +78,7 @@ void CScriptDictionary::Init(asIScriptEngine *e) // We start with one reference refCount = 1; gcFlag = false; + iterGuard = 0; // Keep a reference to the engine for as long as we live // We don't increment the reference counter, because the @@ -310,8 +311,11 @@ void CScriptDictionary::Set(const dictKey_t &key, void *value, int typeId) { dictMap_t::iterator it; it = dict.find(key); - if( it == dict.end() ) + if (it == dict.end()) + { it = dict.insert(dictMap_t::value_type(key, CScriptDictValue())).first; + iterGuard++; + } it->second.Set(engine, value, typeId); } @@ -402,6 +406,7 @@ bool CScriptDictionary::Delete(const dictKey_t &key) { it->second.FreeValue(engine); dict.erase(it); + iterGuard++; return true; } @@ -415,6 +420,7 @@ void CScriptDictionary::DeleteAll() it->second.FreeValue(engine); dict.clear(); + iterGuard++; } CScriptArray* CScriptDictionary::GetKeys() const @@ -1055,6 +1061,192 @@ static void CScriptDictValue_FreeValue_Generic(asIScriptGeneric *gen) self->FreeValue(gen->GetEngine()); } +//---------------------------------------------------------------------------- +// Foreach support +CScriptDictionary::CScriptDictIter::CScriptDictIter(const CScriptDictionary* dict) : iter(dict->begin()), refCount(1), iterGuard(dict->iterGuard) {} +CScriptDictionary::CScriptDictIter::~CScriptDictIter() {} + +void CScriptDictionary::CScriptDictIter::AddRef() const +{ + asAtomicInc(refCount); +} + +void CScriptDictionary::CScriptDictIter::Release() const +{ + if (asAtomicDec(refCount) == 0) + { + this->~CScriptDictIter(); + asFreeMem(const_cast(this)); + } +} + +CScriptDictionary::CScriptDictIter* CScriptDictionary::opForBegin() const +{ + // Use the custom memory routine from AngelScript to allow application to better control how much memory is used + CScriptDictionary::CScriptDictIter* iter = (CScriptDictionary::CScriptDictIter*)asAllocMem(sizeof(CScriptDictionary::CScriptDictIter)); + new(iter) CScriptDictionary::CScriptDictIter(this); + return iter; +} + +bool CScriptDictionary::opForEnd(const CScriptDictionary::CScriptDictIter& iter) const +{ + if (iter.iterGuard != iterGuard) + return true; + + if (iter.iter == end()) + return true; + + return false; +} + +CScriptDictionary::CScriptDictIter* CScriptDictionary::opForNext(CScriptDictionary::CScriptDictIter& iter) const +{ + if (iter.iterGuard != iterGuard) + iter.iter = end(); + else + ++iter.iter; + return &iter; +} + +const CScriptDictValue& CScriptDictionary::opForValue0(const CScriptDictionary::CScriptDictIter& iter) const +{ + return iter.iter.m_it->second; +} + +const dictKey_t& CScriptDictionary::opForValue1(const CScriptDictionary::CScriptDictIter& iter) const +{ + return iter.iter.m_it->first; +} + +void ScriptDictIterAddRef_Generic(asIScriptGeneric* gen) +{ + CScriptDictionary::CScriptDictIter* iter = (CScriptDictionary::CScriptDictIter*)gen->GetObject(); + iter->AddRef(); +} + +void ScriptDictIterRelease_Generic(asIScriptGeneric* gen) +{ + CScriptDictionary::CScriptDictIter* iter = (CScriptDictionary::CScriptDictIter*)gen->GetObject(); + iter->Release(); +} + +void ScriptDictionary_opForBegin_Generic(asIScriptGeneric* gen) +{ + CScriptDictionary* dict = (CScriptDictionary*)gen->GetObject(); + *(CScriptDictionary::CScriptDictIter**)gen->GetAddressOfReturnLocation() = dict->opForBegin(); +} + +void ScriptDictionary_opForEnd_Generic(asIScriptGeneric* gen) +{ + CScriptDictionary* dict = (CScriptDictionary*)gen->GetObject(); + CScriptDictionary::CScriptDictIter* iter = *(CScriptDictionary::CScriptDictIter**)gen->GetAddressOfArg(0); + *(bool*)gen->GetAddressOfReturnLocation() = dict->opForEnd(*iter); +} + +void ScriptDictionary_opForNext_Generic(asIScriptGeneric* gen) +{ + CScriptDictionary* dict = (CScriptDictionary*)gen->GetObject(); + CScriptDictionary::CScriptDictIter* iter = *(CScriptDictionary::CScriptDictIter**)gen->GetAddressOfArg(0); + *(CScriptDictionary::CScriptDictIter**)gen->GetAddressOfReturnLocation() = dict->opForNext(*iter); +} + +void ScriptDictionary_opForValue0_Generic(asIScriptGeneric* gen) +{ + CScriptDictionary* dict = (CScriptDictionary*)gen->GetObject(); + CScriptDictionary::CScriptDictIter* iter = *(CScriptDictionary::CScriptDictIter**)gen->GetAddressOfArg(0); + *reinterpret_cast(gen->GetAddressOfReturnLocation()) = &dict->opForValue0(*iter); +} + +void ScriptDictionary_opForValue1_Generic(asIScriptGeneric* gen) +{ + CScriptDictionary* dict = (CScriptDictionary*)gen->GetObject(); + CScriptDictionary::CScriptDictIter* iter = *(CScriptDictionary::CScriptDictIter**)gen->GetAddressOfArg(0); + *reinterpret_cast(gen->GetAddressOfReturnLocation()) = &dict->opForValue1(*iter); +} + +//------------------------------------------------------------------ +// Iterator implementation + +CScriptDictionary::CIterator CScriptDictionary::begin() const +{ + return CIterator(*this, dict.begin()); +} + +CScriptDictionary::CIterator CScriptDictionary::end() const +{ + return CIterator(*this, dict.end()); +} + +CScriptDictionary::CIterator CScriptDictionary::find(const dictKey_t& key) const +{ + return CIterator(*this, dict.find(key)); +} + +CScriptDictionary::CIterator::CIterator( + const CScriptDictionary& dict, + dictMap_t::const_iterator it) + : m_it(it), m_dict(dict) +{ +} + +void CScriptDictionary::CIterator::operator++() +{ + ++m_it; +} + +void CScriptDictionary::CIterator::operator++(int) +{ + ++m_it; + + // Normally the post increment would return a copy of the object with the original state, + // but it is rarely used so we skip this extra copy to avoid unnecessary overhead +} + +CScriptDictionary::CIterator& CScriptDictionary::CIterator::operator*() +{ + return *this; +} + +bool CScriptDictionary::CIterator::operator==(const CIterator& other) const +{ + return m_it == other.m_it; +} + +bool CScriptDictionary::CIterator::operator!=(const CIterator& other) const +{ + return m_it != other.m_it; +} + +const dictKey_t& CScriptDictionary::CIterator::GetKey() const +{ + return m_it->first; +} + +int CScriptDictionary::CIterator::GetTypeId() const +{ + return m_it->second.m_typeId; +} + +bool CScriptDictionary::CIterator::GetValue(asINT64& value) const +{ + return m_it->second.Get(m_dict.engine, &value, asTYPEID_INT64); +} + +bool CScriptDictionary::CIterator::GetValue(double& value) const +{ + return m_it->second.Get(m_dict.engine, &value, asTYPEID_DOUBLE); +} + +bool CScriptDictionary::CIterator::GetValue(void* value, int typeId) const +{ + return m_it->second.Get(m_dict.engine, value, typeId); +} + +const void* CScriptDictionary::CIterator::GetAddressOfValue() const +{ + return m_it->second.GetAddressOfValue(); +} + //-------------------------------------------------------------------------- // Register the type @@ -1130,6 +1322,19 @@ void RegisterScriptDictionary_Native(asIScriptEngine *engine) r = engine->RegisterObjectBehaviour("dictionary", asBEHAVE_ENUMREFS, "void f(int&in)", asMETHOD(CScriptDictionary,EnumReferences), asCALL_THISCALL); assert( r >= 0 ); r = engine->RegisterObjectBehaviour("dictionary", asBEHAVE_RELEASEREFS, "void f(int&in)", asMETHOD(CScriptDictionary,ReleaseAllReferences), asCALL_THISCALL); assert( r >= 0 ); + // Support foreach + r = engine->RegisterObjectType("dictionaryIter", 0, asOBJ_REF); assert(r >= 0); + r = engine->RegisterObjectBehaviour("dictionaryIter", asBEHAVE_ADDREF, "void f()", asMETHOD(CScriptDictionary::CScriptDictIter, AddRef), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectBehaviour("dictionaryIter", asBEHAVE_RELEASE, "void f()", asMETHOD(CScriptDictionary::CScriptDictIter, Release), asCALL_THISCALL); assert(r >= 0); + + r = engine->RegisterObjectMethod("dictionary", "dictionaryIter @opForBegin() const", asMETHODPR(CScriptDictionary, opForBegin, () const, CScriptDictionary::CScriptDictIter *), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("dictionary", "bool opForEnd(dictionaryIter @+) const", asMETHODPR(CScriptDictionary, opForEnd, (const CScriptDictionary::CScriptDictIter&) const, bool), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("dictionary", "dictionaryIter @+ opForNext(dictionaryIter @+) const", asMETHODPR(CScriptDictionary, opForNext, (CScriptDictionary::CScriptDictIter&) const, CScriptDictionary::CScriptDictIter*), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("dictionary", "const dictionaryValue &opForValue0(dictionaryIter @+) const", asMETHODPR(CScriptDictionary, opForValue0, (const CScriptDictionary::CScriptDictIter&) const, const CScriptDictValue &), asCALL_THISCALL); assert(r >= 0); + r = engine->RegisterObjectMethod("dictionary", "const string &opForValue1(dictionaryIter @+) const", asMETHODPR(CScriptDictionary, opForValue1, (const CScriptDictionary::CScriptDictIter&) const, const dictKey_t&), asCALL_THISCALL); assert(r >= 0); + + + #if AS_USE_STLNAMES == 1 // Same as isEmpty r = engine->RegisterObjectMethod("dictionary", "bool empty() const", asMETHOD(CScriptDictionary, IsEmpty), asCALL_THISCALL); assert( r >= 0 ); @@ -1208,92 +1413,21 @@ void RegisterScriptDictionary_Generic(asIScriptEngine *engine) r = engine->RegisterObjectBehaviour("dictionary", asBEHAVE_ENUMREFS, "void f(int&in)", asFUNCTION(ScriptDictionaryEnumReferences_Generic), asCALL_GENERIC); assert( r >= 0 ); r = engine->RegisterObjectBehaviour("dictionary", asBEHAVE_RELEASEREFS, "void f(int&in)", asFUNCTION(ScriptDictionaryReleaseAllReferences_Generic), asCALL_GENERIC); assert( r >= 0 ); + // Support foreach + r = engine->RegisterObjectType("dictionaryIter", 0, asOBJ_REF); assert(r >= 0); + r = engine->RegisterObjectBehaviour("dictionaryIter", asBEHAVE_ADDREF, "void f()", asFUNCTION(ScriptDictIterAddRef_Generic), asCALL_GENERIC); assert(r >= 0); + r = engine->RegisterObjectBehaviour("dictionaryIter", asBEHAVE_RELEASE, "void f()", asFUNCTION(ScriptDictIterRelease_Generic), asCALL_GENERIC); assert(r >= 0); + + r = engine->RegisterObjectMethod("dictionary", "dictionaryIter @opForBegin() const", asFUNCTION(ScriptDictionary_opForBegin_Generic), asCALL_GENERIC); assert(r >= 0); + r = engine->RegisterObjectMethod("dictionary", "bool opForEnd(dictionaryIter @+) const", asFUNCTION(ScriptDictionary_opForEnd_Generic), asCALL_GENERIC); assert(r >= 0); + r = engine->RegisterObjectMethod("dictionary", "dictionaryIter @+ opForNext(dictionaryIter @+) const", asFUNCTION(ScriptDictionary_opForNext_Generic), asCALL_GENERIC); assert(r >= 0); + r = engine->RegisterObjectMethod("dictionary", "const dictionaryValue &opForValue0(dictionaryIter @+) const", asFUNCTION(ScriptDictionary_opForValue0_Generic), asCALL_GENERIC); assert(r >= 0); + r = engine->RegisterObjectMethod("dictionary", "const string &opForValue1(dictionaryIter @+) const", asFUNCTION(ScriptDictionary_opForValue1_Generic), asCALL_GENERIC); assert(r >= 0); + // Cache some things the dictionary will need at runtime SDictionaryCache::Setup(engine); } -//------------------------------------------------------------------ -// Iterator implementation - -CScriptDictionary::CIterator CScriptDictionary::begin() const -{ - return CIterator(*this, dict.begin()); -} - -CScriptDictionary::CIterator CScriptDictionary::end() const -{ - return CIterator(*this, dict.end()); -} - -CScriptDictionary::CIterator CScriptDictionary::find(const dictKey_t &key) const -{ - return CIterator(*this, dict.find(key)); -} - -CScriptDictionary::CIterator::CIterator( - const CScriptDictionary &dict, - dictMap_t::const_iterator it) - : m_it(it), m_dict(dict) -{} - -void CScriptDictionary::CIterator::operator++() -{ - ++m_it; -} - -void CScriptDictionary::CIterator::operator++(int) -{ - ++m_it; - - // Normally the post increment would return a copy of the object with the original state, - // but it is rarely used so we skip this extra copy to avoid unnecessary overhead -} - -CScriptDictionary::CIterator &CScriptDictionary::CIterator::operator*() -{ - return *this; -} - -bool CScriptDictionary::CIterator::operator==(const CIterator &other) const -{ - return m_it == other.m_it; -} - -bool CScriptDictionary::CIterator::operator!=(const CIterator &other) const -{ - return m_it != other.m_it; -} - -const dictKey_t &CScriptDictionary::CIterator::GetKey() const -{ - return m_it->first; -} - -int CScriptDictionary::CIterator::GetTypeId() const -{ - return m_it->second.m_typeId; -} - -bool CScriptDictionary::CIterator::GetValue(asINT64 &value) const -{ - return m_it->second.Get(m_dict.engine, &value, asTYPEID_INT64); -} - -bool CScriptDictionary::CIterator::GetValue(double &value) const -{ - return m_it->second.Get(m_dict.engine, &value, asTYPEID_DOUBLE); -} - -bool CScriptDictionary::CIterator::GetValue(void *value, int typeId) const -{ - return m_it->second.Get(m_dict.engine, value, typeId); -} - -const void *CScriptDictionary::CIterator::GetAddressOfValue() const -{ - return m_it->second.GetAddressOfValue(); -} - END_AS_NAMESPACE diff --git a/src-core/angelscript/scriptdictionary/scriptdictionary.h b/src-core/angelscript/scriptdictionary/scriptdictionary.h index 8adfca589..3661ab67a 100644 --- a/src-core/angelscript/scriptdictionary/scriptdictionary.h +++ b/src-core/angelscript/scriptdictionary/scriptdictionary.h @@ -196,6 +196,31 @@ public: CIterator end() const; CIterator find(const dictKey_t &key) const; + // Iterator to support foreach in script + class CScriptDictIter + { + public: + // Reference counting + void AddRef() const; + void Release() const; + + protected: + friend class CScriptDictionary; + + CIterator iter; + mutable int refCount; + asUINT iterGuard; + + CScriptDictIter(const CScriptDictionary* dict); + ~CScriptDictIter(); + }; + + CScriptDictIter* opForBegin() const; + bool opForEnd(const CScriptDictIter &iter) const; + CScriptDictIter* opForNext(CScriptDictIter& iter) const; + const CScriptDictValue& opForValue0(const CScriptDictIter& iter) const; + const dictKey_t& opForValue1(const CScriptDictIter& iter) const; + // Garbage collections behaviours int GetRefCount(); void SetGCFlag(); @@ -221,6 +246,7 @@ protected: mutable int refCount; mutable bool gcFlag; dictMap_t dict; + asUINT iterGuard; }; // This function will determine the configuration of the engine diff --git a/src-core/angelscript/scriptsatdump/bind_satdump.cpp b/src-core/angelscript/scriptsatdump/bind_satdump.cpp index 48d60eac8..27f4da7d2 100644 --- a/src-core/angelscript/scriptsatdump/bind_satdump.cpp +++ b/src-core/angelscript/scriptsatdump/bind_satdump.cpp @@ -25,6 +25,7 @@ namespace satdump { RegisterStdString(engine); RegisterScriptArray(engine, true); + RegisterStdStringUtils(engine); RegisterScriptDictionary(engine); registerLogger(engine); diff --git a/src-core/angelscript/scriptstdstring/scriptstdstring.cpp b/src-core/angelscript/scriptstdstring/scriptstdstring.cpp index 3fda9dac4..6c53ce5b8 100644 --- a/src-core/angelscript/scriptstdstring/scriptstdstring.cpp +++ b/src-core/angelscript/scriptstdstring/scriptstdstring.cpp @@ -7,6 +7,8 @@ #ifndef __psp2__ #include // setlocale() #endif +#include + using namespace std; @@ -374,6 +376,51 @@ static int StringFindFirst(const string &sub, asUINT start, const string &str) return (int)str.find(sub, (size_t)(start < 0 ? string::npos : start)); } +// This function returns the index of the first position that matches the regular expression +// +// AngelScript signature: +// int string::regexFind(const string &in regex, uint start = 0, uint &out lengthOfMatch = void) +static int StringRegexFind(const string& rex, asUINT start, asUINT& outLengthOfMatch, const string& str) +{ + if (start >= str.length()) + { + outLengthOfMatch = 0; + return -1; + } + + // TODO: If possible add support for matching utf8 characters + // However on with MSVC it doesn't seem that std::regex works with utf8 + // This works with MSVC, but I don't want to have to convert the string to UTF-16 first because the position and length will not work + // https://www.regular-expressions.info/stdregex.html + // + // std::wregex pattern(L"[[:alpha:]]+"); + // bool result = std::regex_match(std::wstring(L"abcdéfg"), pattern); + // + // The solution from stack overflow doesn't work with MSVC + // https://stackoverflow.com/questions/11254232/do-c11-regular-expressions-work-with-utf-8-strings + // + // std::locale old; + // std::locale::global(std::locale("en_US.UTF-8")); + // std::regex pattern("[[:alpha:]]+", std::regex_constants::extended); + // bool result = std::regex_match(std::string(u8"abcdéfg"), pattern); + // + // I've tried setting the manifest to use utf8 code page but it also doesn't work with MSVC + // https://learn.microsoft.com/en-us/windows/apps/design/globalizing/use-utf8-code-page + + std::regex pattern(rex, std::regex_constants::ECMAScript | std::regex_constants::collate); + std::cmatch match; + bool result = std::regex_search(str.c_str() + start, str.c_str()+str.length(), match, pattern); + + if (!result) + { + outLengthOfMatch = 0; + return -1; + } + + outLengthOfMatch = (asUINT)match[0].length(); + return (int)match.prefix().length(); +} + // This function returns the index of the first position where the one of the bytes in substring // exists in the input string. If the characters in the substring doesn't exist in the input // string -1 is returned. @@ -590,6 +637,169 @@ static string formatFloat(double value, const string &options, asUINT width, asU return buf; } +// TODO: variadic: review +static void StringFormat(asIScriptGeneric* gen) +{ + const string& fmt = *(string*)gen->GetArgAddress(0); + string result; + + asUINT defaultArgIdx = 1; // Skip the first argument which is the fmt + for (asUINT i = 0; i < fmt.size(); ++i) + { + char ch = fmt[i]; + if (ch == '{') + { + if (i + 1 >= (asUINT)fmt.size()) + { + asGetActiveContext()->SetException("Invalid format string"); + return; + } + + if (fmt[i + 1] == '{') + { + i += 1; + result += '{'; + } + else + { + // TODO: Parse optional argument index to support for relocating argument + // e.g. "{1} {0}".format("there", "hello") == "hello there" + asUINT argIdx = defaultArgIdx++; + if (argIdx >= (asUINT)gen->GetArgCount()) + { + asGetActiveContext()->SetException("Index out of range"); + return; + } + int typeId = gen->GetArgTypeId(argIdx); + void* ref = gen->GetArgAddress(argIdx); + + switch (typeId) + { + case asTYPEID_BOOL: + result += *(bool*)ref ? "true" : "false"; + break; + +#define AS_STRING_FORMAT_IMPL(tid, type) \ + case tid: result += to_string(*(type*)ref); break + + AS_STRING_FORMAT_IMPL(asTYPEID_INT8, int8_t); + AS_STRING_FORMAT_IMPL(asTYPEID_INT16, int16_t); + AS_STRING_FORMAT_IMPL(asTYPEID_INT32, int32_t); + AS_STRING_FORMAT_IMPL(asTYPEID_INT64, int64_t); + + AS_STRING_FORMAT_IMPL(asTYPEID_UINT8, uint8_t); + AS_STRING_FORMAT_IMPL(asTYPEID_UINT16, uint16_t); + AS_STRING_FORMAT_IMPL(asTYPEID_UINT32, uint32_t); + AS_STRING_FORMAT_IMPL(asTYPEID_UINT64, uint64_t); + + AS_STRING_FORMAT_IMPL(asTYPEID_FLOAT, float); + AS_STRING_FORMAT_IMPL(asTYPEID_DOUBLE, double); + + default: + if (typeId & ~asTYPEID_MASK_SEQNBR) + { + asIScriptEngine* engine = gen->GetEngine(); + int stringTypeId = engine->GetStringFactory(); + if (typeId == stringTypeId) + { + result += *(string*)ref; + } + else + { + // TODO: Better explanation + asGetActiveContext()->SetException("Unformattable"); + return; + } + } + else // enums + { + // TODO: Format enum name + result += to_string(*(int*)ref); + } + } + } + } + else if (ch == '}') + { + if (i + 1 < (asUINT)fmt.size() && fmt[i + 1] == '}') + { + i += 1; + result += '}'; + } + } + else + { + // Ordinary character + result += ch; + } + } + + new(gen->GetAddressOfReturnLocation()) string(std::move(result)); +} + +// TODO: variadic: review +static void StringScan(asIScriptGeneric* gen) +{ + asIScriptEngine* engine = gen->GetEngine(); + + stringstream ss(*(string*)gen->GetArgObject(0)); + asUINT scanned = 0; + + for (asUINT i = 1; i < (asUINT)gen->GetArgCount(); ++i) + { + int typeId = gen->GetArgTypeId(i); + if (!(typeId & ~asTYPEID_MASK_SEQNBR)) + { +#define AS_STRING_SCAN_IMPL(tid, type) \ + case tid:\ + do {\ + type val;\ + ss >> val;\ + if(!ss) goto end_scan;\ + void* ref = gen->GetArgAddress(i); \ + *(type*)ref = val;\ + } while(0); \ + break + + switch (typeId) + { + AS_STRING_SCAN_IMPL(asTYPEID_BOOL, bool); + + AS_STRING_SCAN_IMPL(asTYPEID_INT16, int16_t); + default: // enum + AS_STRING_SCAN_IMPL(asTYPEID_INT32, int32_t); + AS_STRING_SCAN_IMPL(asTYPEID_INT64, int64_t); + + AS_STRING_SCAN_IMPL(asTYPEID_UINT8, uint8_t); + AS_STRING_SCAN_IMPL(asTYPEID_UINT16, uint16_t); + AS_STRING_SCAN_IMPL(asTYPEID_UINT32, uint32_t); + AS_STRING_SCAN_IMPL(asTYPEID_UINT64, uint64_t); + + AS_STRING_SCAN_IMPL(asTYPEID_FLOAT, float); + AS_STRING_SCAN_IMPL(asTYPEID_DOUBLE, double); + } + } + else if (typeId == engine->GetStringFactory()) + { + string val; + ss >> val; + if (!ss) goto end_scan; + + void* ref = gen->GetArgAddress(i); + *(string*)ref = std::move(val); + } + else // Invalid type + { + goto end_scan; + } + + ++scanned; + } + +end_scan: + gen->SetReturnDWord(scanned); +} + // AngelScript signature: // int64 parseInt(const string &in val, uint base = 10, uint &out byteCount = 0) static asINT64 parseInt(const string &val, asUINT base, asUINT *byteCount) @@ -697,21 +907,41 @@ double parseFloat(const string &val, asUINT *byteCount) { char *end; + // Set the locale to C so that we are guaranteed to parse the float value correctly +#if defined(_WIN32) // WinCE doesn't have setlocale. Some quick testing on my current platform // still manages to parse the numbers such as "3.14" even if the decimal for the // locale is ",". -#if !defined(_WIN32_WCE) && !defined(ANDROID) && !defined(__psp2__) - // Set the locale to C so that we are guaranteed to parse the float value correctly - char *tmp = setlocale(LC_NUMERIC, 0); +#if !defined(_WIN32_WCE) + // On Windows setlocale is made threadsafe by turning on thread local setlocale + // ref: https://learn.microsoft.com/en-us/cpp/parallel/multithreading-and-locales?view=msvc-170&redirectedfrom=MSDN + int oldConfig = _configthreadlocale(_ENABLE_PER_THREAD_LOCALE); + char* tmp = setlocale(LC_NUMERIC, 0); string orig = tmp ? tmp : "C"; setlocale(LC_NUMERIC, "C"); +#endif +#else +#if !defined(ANDROID) && !defined(__psp2__) + // On Linux and other similar systems the threadsafe option is uselocale + // ref: https://stackoverflow.com/questions/4057319/is-setlocale-thread-safe-function + locale_t locale = newlocale(LC_NUMERIC_MASK, "C", NULL); + locale_t orig_locale = uselocale(locale); +#endif #endif double res = strtod(val.c_str(), &end); -#if !defined(_WIN32_WCE) && !defined(ANDROID) && !defined(__psp2__) - // Restore the locale + // Restore the original locale +#if defined(_WIN32) +#if !defined(_WIN32_WCE) setlocale(LC_NUMERIC, orig.c_str()); + _configthreadlocale(oldConfig); +#endif +#else +#if !defined(ANDROID) && !defined(__psp2__) +#endif + uselocale(orig_locale); + freelocale(locale); #endif if( byteCount ) @@ -833,8 +1063,10 @@ void RegisterStdString_Native(asIScriptEngine *engine) r = engine->RegisterObjectMethod("string", "int findLastNotOf(const string &in, int start = -1) const", asFUNCTION(StringFindLastNotOf), asCALL_CDECL_OBJLAST); assert(r >= 0); r = engine->RegisterObjectMethod("string", "void insert(uint pos, const string &in other)", asFUNCTION(StringInsert), asCALL_CDECL_OBJLAST); assert(r >= 0); r = engine->RegisterObjectMethod("string", "void erase(uint pos, int count = -1)", asFUNCTION(StringErase), asCALL_CDECL_OBJLAST); assert(r >= 0); + r = engine->RegisterObjectMethod("string", "int regexFind(const string &in regex, uint start = 0, uint &out lengthOfMatch = void) const", asFUNCTION(StringRegexFind), asCALL_CDECL_OBJLAST); assert(r >= 0); - + r = engine->RegisterGlobalFunction("uint scan(const string&in str, ?&out ...)", asFUNCTION(StringScan), asCALL_GENERIC); assert(r >= 0); + r = engine->RegisterGlobalFunction("string format(const string&in fmt, const ?&in ...)", asFUNCTION(StringFormat), asCALL_GENERIC); assert(r >= 0); r = engine->RegisterGlobalFunction("string formatInt(int64 val, const string &in options = \"\", uint width = 0)", asFUNCTION(formatInt), asCALL_CDECL); assert(r >= 0); r = engine->RegisterGlobalFunction("string formatUInt(uint64 val, const string &in options = \"\", uint width = 0)", asFUNCTION(formatUInt), asCALL_CDECL); assert(r >= 0); r = engine->RegisterGlobalFunction("string formatFloat(double val, const string &in options = \"\", uint width = 0, uint precision = 0)", asFUNCTION(formatFloat), asCALL_CDECL); assert(r >= 0); @@ -1281,6 +1513,18 @@ static void StringSubString_Generic(asIScriptGeneric *gen) new(gen->GetAddressOfReturnLocation()) string(StringSubString(start, count, *str)); } +// static int StringRegexFind(const string& rex, asUINT start, asUINT& outLengthOfMatch, const string& str) +static void StringRegexFind_Generic(asIScriptGeneric* gen) +{ + // Get the arguments + string* str = (string*)gen->GetObject(); + string *rex = *(string**)gen->GetAddressOfArg(0); + asUINT start = *(asUINT*)gen->GetAddressOfArg(1); + asUINT* outLen = *(asUINT**)gen->GetAddressOfArg(2); + + *(int*)(gen->GetAddressOfReturnLocation()) = StringRegexFind(*rex, start, *outLen, *str); +} + void RegisterStdString_Generic(asIScriptEngine *engine) { int r = 0; @@ -1354,8 +1598,10 @@ void RegisterStdString_Generic(asIScriptEngine *engine) r = engine->RegisterObjectMethod("string", "int findLastNotOf(const string &in, int start = -1) const", asFUNCTION(StringFindLastNotOf_Generic), asCALL_GENERIC); assert(r >= 0); r = engine->RegisterObjectMethod("string", "void insert(uint pos, const string &in other)", asFUNCTION(StringInsert_Generic), asCALL_GENERIC); assert(r >= 0); r = engine->RegisterObjectMethod("string", "void erase(uint pos, int count = -1)", asFUNCTION(StringErase_Generic), asCALL_GENERIC); assert(r >= 0); + r = engine->RegisterObjectMethod("string", "int regexFind(const string &in regex, uint start = 0, uint &out lengthOfMatch = void) const", asFUNCTION(StringRegexFind_Generic), asCALL_GENERIC); assert(r >= 0); - + r = engine->RegisterGlobalFunction("uint scan(const string&in str, ?&out ...)", asFUNCTION(StringScan), asCALL_GENERIC); assert(r >= 0); + r = engine->RegisterGlobalFunction("string format(const string&in fmt, const ?&in ...)", asFUNCTION(StringFormat), asCALL_GENERIC); assert(r >= 0); r = engine->RegisterGlobalFunction("string formatInt(int64 val, const string &in options = \"\", uint width = 0)", asFUNCTION(formatInt_Generic), asCALL_GENERIC); assert(r >= 0); r = engine->RegisterGlobalFunction("string formatUInt(uint64 val, const string &in options = \"\", uint width = 0)", asFUNCTION(formatUInt_Generic), asCALL_GENERIC); assert(r >= 0); r = engine->RegisterGlobalFunction("string formatFloat(double val, const string &in options = \"\", uint width = 0, uint precision = 0)", asFUNCTION(formatFloat_Generic), asCALL_GENERIC); assert(r >= 0); diff --git a/src-core/angelscript/scriptstdstring/scriptstdstring_utils.cpp b/src-core/angelscript/scriptstdstring/scriptstdstring_utils.cpp index 61ea61b93..d4c992581 100644 --- a/src-core/angelscript/scriptstdstring/scriptstdstring_utils.cpp +++ b/src-core/angelscript/scriptstdstring/scriptstdstring_utils.cpp @@ -12,7 +12,7 @@ BEGIN_AS_NAMESPACE // for a specified delimiter. Example: // // string str = "A|B||D"; -// array@ array = str.split("|"); +// array@ arr = str.split("|"); // // The resulting array has the following elements: // @@ -70,15 +70,15 @@ static void StringSplit_Generic(asIScriptGeneric *gen) // delimiter and concatenates the array elements into one delimited string. // Example: // -// array array = {"A", "B", "", "D"}; -// string str = join(array, "|"); +// array arr = {"A", "B", "", "D"}; +// string str = join(arr, "|"); // // The resulting string is: // // "A|B||D" // // AngelScript signature: -// string join(const array &in array, const string &in delim) +// string join(const array &in arr, const string &in delim) static string StringJoin(const CScriptArray &array, const string &delim) { // Create the new string