diff --git a/src/ScriptResolving/AngelScript/AngelScriptMetadata.hpp b/src/ScriptResolving/AngelScript/AngelScriptMetadata.hpp new file mode 100644 index 0000000..71e898a --- /dev/null +++ b/src/ScriptResolving/AngelScript/AngelScriptMetadata.hpp @@ -0,0 +1,70 @@ +#ifndef PKMNLIB_ANGELSCRIPTMETADATA_HPP +#define PKMNLIB_ANGELSCRIPTMETADATA_HPP + +class AngelscriptMetadata { +public: + explicit AngelscriptMetadata(const std::string& metadataString) { Parse(metadataString); } + + inline const ArbUt::StringView& GetIdentifier() const noexcept { return _identifier; } + inline const std::string& GetParameter(const ArbUt::StringView& name) const noexcept { + return _parameters.Get(name); + } + +private: + ArbUt::StringView _identifier; + ArbUt::Dictionary _parameters; + + size_t ReadUpToOrEnd(const std::string& s, const char symbol, size_t start) { + for (size_t i = start; i < s.length(); ++i) { + if (s.at(i) == symbol) { + return i; + } + } + return s.length(); + } + + size_t FindNextNonSpace(const std::string& s, size_t start) { + for (size_t i = start; i < s.length(); ++i) { + if (s.at(i) != ' ') { + return i; + } + } + return s.length(); + } + + void Parse(const std::string& metadataString) { + auto idEnd = ReadUpToOrEnd(metadataString, ' ', 0); + _identifier = ArbUt::StringView(metadataString.substr(0, idEnd).c_str(), idEnd); + auto current = idEnd; + while (true) { + current = FindNextNonSpace(metadataString, current); + if (current >= metadataString.length()){ + break; + } + ParseParameter(metadataString, current); + } + } + + void ParseParameter(const std::string& metadataString, size_t& current) { + auto start = current; + current = ReadUpToOrEnd(metadataString, '=', current); + auto name = ArbUt::StringView(metadataString.substr(start, current - start).c_str(), current - start); + current++; + start = current; + auto isQuoted = metadataString[current] == '"'; + if (isQuoted) { + current++; + start++; + current = ReadUpToOrEnd(metadataString, '"', current); + } else { + current = ReadUpToOrEnd(metadataString, ' ', current); + } + auto value = metadataString.substr(start, current - start); + _parameters.Insert(name, value); + if (isQuoted){ + current += 1; + } + } +}; + +#endif // PKMNLIB_ANGELSCRIPTMETADATA_HPP diff --git a/src/ScriptResolving/AngelScript/AngelScriptResolver.cpp b/src/ScriptResolving/AngelScript/AngelScriptResolver.cpp index 36680f6..c2daf30 100644 --- a/src/ScriptResolving/AngelScript/AngelScriptResolver.cpp +++ b/src/ScriptResolving/AngelScript/AngelScriptResolver.cpp @@ -8,6 +8,7 @@ #include "../../../extern/angelscript_addons/scriptstdstring/scriptstdstring.h" #include "../../Battling/PkmnScriptCategory.hpp" #include "../../Battling/Pokemon/Pokemon.hpp" +#include "AngelScriptMetadata.hpp" #include "ByteCodeHandling/FileByteCodeStream.hpp" #include "ByteCodeHandling/MemoryByteCodeStream.hpp" #include "TypeRegistry/BasicScriptClass.hpp" @@ -238,66 +239,23 @@ void AngelScriptResolver::FinalizeModule() { auto typeInfo = _mainModule->GetObjectTypeByIndex(n); if (typeInfo->DerivesFrom(pkmnScriptType)) { auto metadata = _builder.GetMetadataForType(typeInfo->GetTypeId()); - for (size_t m = 0; m < metadata.size(); m++) { - auto data = metadata[m]; - if (std::regex_match(data, base_match, metadataMatcher)) { - auto mt = base_match[1].str(); - auto metadataKind = ArbUt::StringView(mt.c_str(), mt.length()); - auto metadataVariables = base_match[2].str(); - if (!std::regex_match(metadataVariables, base_match, variableMatcher)) { - continue; - } - ArbUt::StringView effectName; - for (size_t variableIndex = 1; variableIndex < base_match.size(); variableIndex += 2) { - if (ArbUt::StringView::CalculateHash(base_match[variableIndex].str().c_str()) == "effect"_cnc) { - auto val = base_match[variableIndex + 1].str(); - effectName = ArbUt::StringView(val.c_str(), val.length()); - } - } - RegisterScriptType(typeInfo, metadataKind, effectName); - } + for (auto& m : metadata) { + auto data = AngelscriptMetadata(m); + RegisterScriptType(typeInfo, data.GetIdentifier(), + ArbUt::StringView(data.GetParameter("effect"_cnc).c_str())); } } else if (typeInfo->DerivesFrom(itemUseScriptType)) { auto metadata = _builder.GetMetadataForType(typeInfo->GetTypeId()); - for (size_t m = 0; m < metadata.size(); m++) { - auto data = metadata[m]; - if (std::regex_match(data, base_match, metadataMatcher)) { - auto mt = base_match[1].str(); - auto metadataKind = ArbUt::StringView(mt.c_str(), mt.length()); - auto metadataVariables = base_match[2].str(); - if (!std::regex_match(metadataVariables, base_match, variableMatcher)) { - continue; - } - ArbUt::StringView effectName; - for (size_t variableIndex = 1; variableIndex < base_match.size(); variableIndex += 2) { - if (ArbUt::StringView::CalculateHash(base_match[variableIndex].str().c_str()) == "effect"_cnc) { - auto val = base_match[variableIndex + 1].str(); - effectName = ArbUt::StringView(val.c_str(), val.length()); - } - } - _itemUseTypes.Insert(effectName, typeInfo); - } + for (auto& m : metadata) { + auto data = AngelscriptMetadata(m); + _itemUseTypes.Insert(ArbUt::StringView(data.GetParameter("effect"_cnc).c_str()), typeInfo); } + } else if (typeInfo->DerivesFrom(evolutionScriptType)) { auto metadata = _builder.GetMetadataForType(typeInfo->GetTypeId()); - for (size_t m = 0; m < metadata.size(); m++) { - auto data = metadata[m]; - if (std::regex_match(data, base_match, metadataMatcher)) { - auto mt = base_match[1].str(); - auto metadataKind = ArbUt::StringView(mt.c_str(), mt.length()); - auto metadataVariables = base_match[2].str(); - if (!std::regex_match(metadataVariables, base_match, variableMatcher)) { - continue; - } - ArbUt::StringView effectName; - for (size_t variableIndex = 1; variableIndex < base_match.size(); variableIndex += 2) { - if (ArbUt::StringView::CalculateHash(base_match[variableIndex].str().c_str()) == "effect"_cnc) { - auto val = base_match[variableIndex + 1].str(); - effectName = ArbUt::StringView(val.c_str(), val.length()); - } - } - _evolutionTypes.Insert(effectName, typeInfo); - } + for (auto& m : metadata) { + auto data = AngelscriptMetadata(m); + _evolutionTypes.Insert(ArbUt::StringView(data.GetParameter("effect"_cnc).c_str()), typeInfo); } } } diff --git a/tests/ScriptTests/MetadataTests.cpp b/tests/ScriptTests/MetadataTests.cpp new file mode 100644 index 0000000..11129ff --- /dev/null +++ b/tests/ScriptTests/MetadataTests.cpp @@ -0,0 +1,67 @@ +#ifdef TESTS_BUILD +#include "../../extern/doctest.hpp" +#include "../../src/ScriptResolving/AngelScript/AngelScriptMetadata.hpp" + +TEST_CASE("Metadata without parameters") { + auto m = AngelscriptMetadata("Foo"); + REQUIRE_EQ(m.GetIdentifier(), "Foo"_cnc); +} + +TEST_CASE("Metadata with single parameter") { + auto m = AngelscriptMetadata("Foo bar=zet"); + REQUIRE_EQ(m.GetIdentifier(), "Foo"_cnc); + REQUIRE_EQ(m.GetParameter("bar"_cnc), "zet"); +} + +TEST_CASE("Metadata with single parameter with trailing space") { + auto m = AngelscriptMetadata("Foo bar=zet "); + REQUIRE_EQ(m.GetIdentifier(), "Foo"_cnc); + REQUIRE_EQ(m.GetParameter("bar"_cnc), "zet"); +} + +TEST_CASE("Metadata with single parameter with two spaces") { + auto m = AngelscriptMetadata("Foo bar=zet"); + REQUIRE_EQ(m.GetIdentifier(), "Foo"_cnc); + REQUIRE_EQ(m.GetParameter("bar"_cnc), "zet"); +} + +TEST_CASE("Metadata with two parameters") { + auto m = AngelscriptMetadata("Foo bar=zet met=blabla"); + REQUIRE_EQ(m.GetIdentifier(), "Foo"_cnc); + REQUIRE_EQ(m.GetParameter("bar"_cnc), "zet"); + REQUIRE_EQ(m.GetParameter("met"_cnc), "blabla"); +} + +TEST_CASE("Metadata with two parameters and two spaces") { + auto m = AngelscriptMetadata("Foo bar=zet met=blabla"); + REQUIRE_EQ(m.GetIdentifier(), "Foo"_cnc); + REQUIRE_EQ(m.GetParameter("bar"_cnc), "zet"); + REQUIRE_EQ(m.GetParameter("met"_cnc), "blabla"); +} + +TEST_CASE("Metadata with single parameter in quotes") { + auto m = AngelscriptMetadata("Foo bar=\"this is a test string\""); + REQUIRE_EQ(m.GetIdentifier(), "Foo"_cnc); + REQUIRE_EQ(m.GetParameter("bar"_cnc), "this is a test string"); +} + +TEST_CASE("Metadata with single parameter in quotes with trailing space") { + auto m = AngelscriptMetadata("Foo bar=\"this is a test string\" "); + REQUIRE_EQ(m.GetIdentifier(), "Foo"_cnc); + REQUIRE_EQ(m.GetParameter("bar"_cnc), "this is a test string"); +} + +TEST_CASE("Metadata with single parameter in quotes without closing quote") { + auto m = AngelscriptMetadata("Foo bar=\"this is a test string"); + REQUIRE_EQ(m.GetIdentifier(), "Foo"_cnc); + REQUIRE_EQ(m.GetParameter("bar"_cnc), "this is a test string"); +} + +TEST_CASE("Metadata with two parameters in quotes") { + auto m = AngelscriptMetadata("Foo bar=\"this is a test string\" parameter2=\"xoxo another string\""); + REQUIRE_EQ(m.GetIdentifier(), "Foo"_cnc); + REQUIRE_EQ(m.GetParameter("bar"_cnc), "this is a test string"); + REQUIRE_EQ(m.GetParameter("parameter2"_cnc), "xoxo another string"); +} + +#endif \ No newline at end of file