Update some AS stuff

This commit is contained in:
Aang23 2025-08-12 19:30:15 +02:00
parent 4f600e3d6f
commit 26d514baea
8 changed files with 598 additions and 112 deletions

View file

@ -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>", "T &opIndex(uint index)", asMETHODPR(CScriptArray, At, (asUINT), void*), asCALL_THISCALL); assert( r >= 0 );
r = engine->RegisterObjectMethod("array<T>", "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<T>", "uint opForBegin() const", asFUNCTIONPR(CScriptArray_opForBegin, (const CScriptArray *), asUINT), asCALL_CDECL_OBJLAST); assert(r >= 0);
r = engine->RegisterObjectMethod("array<T>", "bool opForEnd(uint) const", asFUNCTIONPR(CScriptArray_opForEnd, (asUINT, const CScriptArray*), bool), asCALL_CDECL_OBJLAST); assert(r >= 0);
r = engine->RegisterObjectMethod("array<T>", "uint opForNext(uint) const", asFUNCTIONPR(CScriptArray_opForNext, (asUINT, const CScriptArray*), asUINT), asCALL_CDECL_OBJLAST); assert(r >= 0);
r = engine->RegisterObjectMethod("array<T>", "const T &opForValue0(uint index) const", asMETHODPR(CScriptArray, At, (asUINT) const, const void*), asCALL_THISCALL); assert(r >= 0);
r = engine->RegisterObjectMethod("array<T>", "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<T>", "array<T> &opAssign(const array<T>&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<asUINT*>(gen->GetAddressOfReturnLocation()) = CScriptArray_opForBegin(reinterpret_cast<CScriptArray*>(gen->GetObject()));
}
static void ScriptArray_opForEnd_Generic(asIScriptGeneric* gen)
{
*reinterpret_cast<bool*>(gen->GetAddressOfReturnLocation()) = CScriptArray_opForEnd(gen->GetArgDWord(0), reinterpret_cast<CScriptArray*>(gen->GetObject()));
}
static void ScriptArray_opForNext_Generic(asIScriptGeneric* gen)
{
*reinterpret_cast<asUINT*>(gen->GetAddressOfReturnLocation()) = CScriptArray_opForNext(gen->GetArgDWord(0), reinterpret_cast<CScriptArray*>(gen->GetObject()));
}
static void ScriptArray_opForValue1_Generic(asIScriptGeneric* gen)
{
*reinterpret_cast<asUINT*>(gen->GetAddressOfReturnLocation()) = CScriptArray_opForValue1(gen->GetArgDWord(0), reinterpret_cast<CScriptArray*>(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<T>", asBEHAVE_RELEASE, "void f()", asFUNCTION(ScriptArrayRelease_Generic), asCALL_GENERIC); assert( r >= 0 );
r = engine->RegisterObjectMethod("array<T>", "T &opIndex(uint index)", asFUNCTION(ScriptArrayAt_Generic), asCALL_GENERIC); assert( r >= 0 );
r = engine->RegisterObjectMethod("array<T>", "const T &opIndex(uint index) const", asFUNCTION(ScriptArrayAt_Generic), asCALL_GENERIC); assert( r >= 0 );
r = engine->RegisterObjectMethod("array<T>", "array<T> &opAssign(const array<T>&in)", asFUNCTION(ScriptArrayAssignment_Generic), asCALL_GENERIC); assert( r >= 0 );
r = engine->RegisterObjectMethod("array<T>", "uint opForBegin() const", asFUNCTION(ScriptArray_opForBegin_Generic), asCALL_GENERIC); assert(r >= 0);
r = engine->RegisterObjectMethod("array<T>", "bool opForEnd(uint) const", asFUNCTION(ScriptArray_opForEnd_Generic), asCALL_GENERIC); assert(r >= 0);
r = engine->RegisterObjectMethod("array<T>", "uint opForNext(uint) const", asFUNCTION(ScriptArray_opForNext_Generic), asCALL_GENERIC); assert(r >= 0);
r = engine->RegisterObjectMethod("array<T>", "const T &opForValue0(uint index) const", asFUNCTION(ScriptArrayAt_Generic), asCALL_GENERIC); assert(r >= 0);
r = engine->RegisterObjectMethod("array<T>", "uint opForValue1(uint index) const", asFUNCTION(ScriptArray_opForValue1_Generic), asCALL_GENERIC); assert(r >= 0);
r = engine->RegisterObjectMethod("array<T>", "array<T> &opAssign(const array<T>&in)", asFUNCTION(ScriptArrayAssignment_Generic), asCALL_GENERIC); assert(r >= 0);
r = engine->RegisterObjectMethod("array<T>", "void insertAt(uint index, const T&in value)", asFUNCTION(ScriptArrayInsertAt_Generic), asCALL_GENERIC); assert( r >= 0 );
r = engine->RegisterObjectMethod("array<T>", "void insertAt(uint index, const array<T>& arr)", asFUNCTION(ScriptArrayInsertAtArray_Generic), asCALL_GENERIC); assert(r >= 0);

View file

@ -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

View file

@ -163,8 +163,9 @@ protected:
std::string nameSpace;
};
std::vector<SMetadataDecl> foundDeclarations;
std::string currentClass;
std::string currentNamespace;
std::string currentClass;
std::string currentNamespace;
std::vector<int> currentNamespaceStack;
// Storage of metadata for global declarations
std::map<int, std::vector<std::string> > typeMetadataMap;

View file

@ -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<CScriptDictIter*>(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<const CScriptDictValue**>(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<const dictKey_t**>(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

View file

@ -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

View file

@ -25,6 +25,7 @@ namespace satdump
{
RegisterStdString(engine);
RegisterScriptArray(engine, true);
RegisterStdStringUtils(engine);
RegisterScriptDictionary(engine);
registerLogger(engine);

View file

@ -7,6 +7,8 @@
#ifndef __psp2__
#include <locale.h> // setlocale()
#endif
#include <regex>
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);

View file

@ -12,7 +12,7 @@ BEGIN_AS_NAMESPACE
// for a specified delimiter. Example:
//
// string str = "A|B||D";
// array<string>@ array = str.split("|");
// array<string>@ 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<string> array = {"A", "B", "", "D"};
// string str = join(array, "|");
// array<string> arr = {"A", "B", "", "D"};
// string str = join(arr, "|");
//
// The resulting string is:
//
// "A|B||D"
//
// AngelScript signature:
// string join(const array<string> &in array, const string &in delim)
// string join(const array<string> &in arr, const string &in delim)
static string StringJoin(const CScriptArray &array, const string &delim)
{
// Create the new string