diff options
author | Devtools Arcadia <arcadia-devtools@yandex-team.ru> | 2022-02-07 18:08:42 +0300 |
---|---|---|
committer | Devtools Arcadia <arcadia-devtools@mous.vla.yp-c.yandex.net> | 2022-02-07 18:08:42 +0300 |
commit | 1110808a9d39d4b808aef724c861a2e1a38d2a69 (patch) | |
tree | e26c9fed0de5d9873cce7e00bc214573dc2195b7 /tools/enum_parser | |
download | ydb-1110808a9d39d4b808aef724c861a2e1a38d2a69.tar.gz |
intermediate changes
ref:cde9a383711a11544ce7e107a78147fb96cc4029
Diffstat (limited to 'tools/enum_parser')
22 files changed, 2045 insertions, 0 deletions
diff --git a/tools/enum_parser/enum_parser/bin/ya.make b/tools/enum_parser/enum_parser/bin/ya.make new file mode 100644 index 0000000000..09e47ee1c0 --- /dev/null +++ b/tools/enum_parser/enum_parser/bin/ya.make @@ -0,0 +1,23 @@ +OWNER( + g:util + mvel +) + +PROGRAM(enum_parser) + +SRCDIR( + tools/enum_parser/enum_parser +) + +SRCS( + main.cpp +) + +INCLUDE(${ARCADIA_ROOT}/build/prebuilt/tools/enum_parser/enum_parser/ya.make.induced_deps) + +PEERDIR( + library/cpp/getopt/small + tools/enum_parser/parse_enum +) + +END() diff --git a/tools/enum_parser/enum_parser/main.cpp b/tools/enum_parser/enum_parser/main.cpp new file mode 100644 index 0000000000..0943c69c1d --- /dev/null +++ b/tools/enum_parser/enum_parser/main.cpp @@ -0,0 +1,471 @@ +#include <library/cpp/json/writer/json_value.h> +#include <library/cpp/json/writer/json.h> +#include <library/cpp/getopt/small/last_getopt.h> + +#include <tools/enum_parser/parse_enum/parse_enum.h> + +#include <util/stream/file.h> +#include <util/stream/output.h> +#include <util/stream/input.h> +#include <util/stream/mem.h> + +#include <util/charset/wide.h> +#include <util/string/builder.h> +#include <util/string/strip.h> +#include <util/string/cast.h> +#include <util/string/join.h> +#include <util/string/subst.h> +#include <util/generic/map.h> +#include <util/generic/string.h> +#include <util/generic/vector.h> +#include <util/generic/ptr.h> +#include <util/generic/yexception.h> +#include <util/generic/maybe.h> +#include <util/system/fs.h> +#include <util/folder/path.h> + +void WriteHeader(const TString& headerName, IOutputStream& out, IOutputStream* headerOutPtr = nullptr) { + out << "// This file was auto-generated. Do not edit!!!\n"; + out << "#include " << headerName << "\n"; + out << "#include <tools/enum_parser/enum_serialization_runtime/enum_runtime.h>\n\n"; + out << "#include <tools/enum_parser/enum_parser/stdlib_deps.h>\n\n"; + out << "#include <util/generic/typetraits.h>\n"; + out << "#include <util/generic/singleton.h>\n"; + out << "#include <util/generic/string.h>\n"; + out << "#include <util/generic/vector.h>\n"; + out << "#include <util/generic/map.h>\n"; + out << "#include <util/generic/serialized_enum.h>\n"; + out << "#include <util/string/cast.h>\n"; + out << "#include <util/stream/output.h>\n\n"; + + if (headerOutPtr) { + auto& outHeader = *headerOutPtr; + outHeader << "// This file was auto-generated. Do not edit!!!\n"; + outHeader << "#pragma once\n\n"; + outHeader << "#include <util/generic/serialized_enum.h>\n"; + outHeader << "#include " << headerName << "\n"; + } +} + +static inline void JsonEscape(TString& s) { + SubstGlobal(s, "\\", "\\\\"); + SubstGlobal(s, "\"", "\\\""); + SubstGlobal(s, "\r", "\\r"); + SubstGlobal(s, "\n", "\\n"); + SubstGlobal(s, "\t", "\\t"); +} + +static inline TString JsonQuote(const TString& s) { + TString quoted = s; + JsonEscape(quoted); + return "\"" + quoted + "\""; // do not use .Quote() here, it performs escaping! +} + + +/// Simplifed JSON map encoder for generic types +template<typename T> +void OutKey(IOutputStream& out, const TString& key, const T& value, bool escape = true) { + TString quoted = ToString(value); + if (escape) { + quoted = JsonQuote(quoted); + } + out << "\"" << key << "\": " << quoted << ",\n"; +} + +/// Simplifed JSON map encoder for TMaybe +void OutKey(IOutputStream& out, const TString& key, const TMaybe<TString>& value) { + TString quoted; + if (value) { + quoted = JsonQuote(ToString(*value)); + } else { + quoted = "null"; + } + out << "\"" << key << "\": " << quoted << ",\n"; +} + + +/// Simplifed JSON map encoder for bool values +void OutKey(IOutputStream& out, const TString& key, const bool& value) { + out << "\"" << key << "\": " << (value ? "true" : "false") << ",\n"; +} + + +/// Simplifed JSON map encoder for array items +template<typename T> +void OutItem(IOutputStream& out, const T& value, bool escape = true) { + TString quoted = ToString(value); + if (escape) { + quoted = JsonQuote(quoted); + } + out << quoted << ",\n"; +} + +/// Cut trailing ",\n" or "," +static inline void FinishItems(TStringStream& out) { + TString& s = out.Str(); + if (s.EndsWith(",\n")) { + s.remove(s.size() - 2, 2); + } + if (s.EndsWith(",")) { + s.pop_back(); + } +} + + +static inline void OpenMap(TStringStream& out) { + out << "{\n"; +} + +static inline void CloseMap(TStringStream& out) { + out << "}\n"; +} + +static inline void OpenArray(TStringStream& out) { + out << "[\n"; +} + +static inline void CloseArray(TStringStream& out) { + out << "]\n"; +} + +static TString WrapStringBuf(const TStringBuf str) { + return TString::Join("TStringBuf(\"", str, "\")"); +} + +void GenerateEnum( + const TEnumParser::TEnum& en, + IOutputStream& out, + IOutputStream* jsonEnumOut = nullptr, + IOutputStream* headerOutPtr = nullptr +) { + TStringStream jEnum; + OpenMap(jEnum); + + size_t count = en.Items.size(); + OutKey(jEnum, "count", count); + const TString name = TEnumParser::ScopeStr(en.Scope) + en.CppName; + OutKey(jEnum, "full_name", name); + OutKey(jEnum, "cpp_name", en.CppName); + TStringStream scopeJson; + OpenArray(scopeJson); + for (const auto& scopeItem : en.Scope) { + OutItem(scopeJson, scopeItem); + } + FinishItems(scopeJson); + CloseArray(scopeJson); + + OutKey(jEnum, "scope", scopeJson.Str(), false); + OutKey(jEnum, "enum_class", en.EnumClass); + + TEnumParser::TScope outerScope = en.Scope; + if (en.EnumClass) { + outerScope.push_back(en.CppName); + } + + TString outerScopeStr = TEnumParser::ScopeStr(outerScope); + + TString cName = name; + SubstGlobal(cName, "::", ""); + + out << "// I/O for " << name << "\n"; + + TString nsName = "N" + cName + "Private"; + + out << "namespace { namespace " << nsName << " {\n"; + + TVector<TString> nameInitializerPairs; + TVector<TString> valueInitializerPairs; + TVector<TString> cppNamesInitializer; + + TStringStream jItems; + OpenArray(jItems); + + for (const auto& it : en.Items) { + TStringStream jEnumItem; + OpenMap(jEnumItem); + + OutKey(jEnumItem, "cpp_name", it.CppName); + OutKey(jEnumItem, "value", it.Value); + OutKey(jEnumItem, "comment_text", it.CommentText); + + TStringStream jAliases; + OpenArray(jAliases); + + TString strValue = it.CppName; + if (it.Aliases) { + // first alias is main + strValue = it.Aliases[0]; + OutKey(jEnumItem, "str_value", strValue); + } + nameInitializerPairs.push_back("TNameBufs::EnumStringPair(" + outerScopeStr + it.CppName + ", " + WrapStringBuf(strValue) + ")"); + cppNamesInitializer.push_back(WrapStringBuf(it.CppName)); + + for (const auto& alias : it.Aliases) { + valueInitializerPairs.push_back("TNameBufs::EnumStringPair(" + outerScopeStr + it.CppName + ", " + WrapStringBuf(alias) + ")"); + OutItem(jAliases, alias); + } + FinishItems(jAliases); + CloseArray(jAliases); + + if (!it.Aliases) { + valueInitializerPairs.push_back("TNameBufs::EnumStringPair(" + outerScopeStr + it.CppName + ", " + WrapStringBuf(it.CppName) + ")"); + } + OutKey(jEnumItem, "aliases", jAliases.Str(), false); + + FinishItems(jEnumItem); + CloseMap(jEnumItem); + + OutItem(jItems, jEnumItem.Str(), false); + } + FinishItems(jItems); + CloseArray(jItems); + OutKey(jEnum, "items", jItems.Str(), false); + + auto defineConstArray = [&out, payloadCache = TMap<std::pair<TString, TVector<TString>>, TString>()](const TStringBuf indent, const TStringBuf elementType, const TStringBuf name, const TVector<TString>& items) mutable { + if (items.empty()) { // ISO C++ forbids zero-size array + out << indent << "static constexpr const TArrayRef<const " << elementType << "> " << name << ";\n"; + } else { + // try to reuse one of the previous payload arrays + const auto inserted = payloadCache.emplace(std::make_pair(elementType, items), ToString(name) + "_PAYLOAD"); + const TString& payloadStorageName = inserted.first->second; + if (inserted.second) { // new array content or type + out << indent << "static constexpr const " << elementType << " " << payloadStorageName << "[" << items.size() << "]{\n"; + for (const auto& it : items) { + out << indent << " " << it << ",\n"; + } + out << indent << "};\n"; + } + out << indent << "static constexpr const TArrayRef<const " << elementType << "> " << name << "{" << payloadStorageName << "};\n"; + } + out << "\n"; + }; + + out << " class TNameBufs : public ::NEnumSerializationRuntime::TEnumDescription<" << name << "> {\n"; + out << " public:\n"; + out << " using TBase = ::NEnumSerializationRuntime::TEnumDescription<" << name << ">;\n\n"; + out << " inline TNameBufs();\n\n"; + + // Instance + out << " static inline const TNameBufs& Instance() {\n"; + out << " return *SingletonWithPriority<TNameBufs, 0>();\n"; // destroy enum serializers last, because it may be used from destructor of another global object + out << " }\n"; + out << " };\n\n"; + + // Initialization data + defineConstArray(" ", "TNameBufs::TBase::TEnumStringPair", "NAMES_INITIALIZATION_PAIRS", nameInitializerPairs); + defineConstArray(" ", "TNameBufs::TBase::TEnumStringPair", "VALUES_INITIALIZATION_PAIRS", valueInitializerPairs); + defineConstArray(" ", "TStringBuf", "CPP_NAMES_INITIALIZATION_ARRAY", cppNamesInitializer); + + out << " static constexpr const TNameBufs::TInitializationData ENUM_INITIALIZATION_DATA{\n"; + out << " NAMES_INITIALIZATION_PAIRS,\n"; + out << " VALUES_INITIALIZATION_PAIRS,\n"; + out << " CPP_NAMES_INITIALIZATION_ARRAY,\n"; + out << " " << WrapStringBuf(outerScopeStr) << ",\n"; + out << " " << WrapStringBuf(name) << "\n"; + out << " };\n\n"; + + // Constructor + out << " inline TNameBufs::TNameBufs()\n"; + out << " : TBase(ENUM_INITIALIZATION_DATA)\n"; + out << " {\n"; + out << " }\n\n"; + + out << "}}\n\n"; + + if (headerOutPtr) { + (*headerOutPtr) << "// I/O for " << name << "\n"; + } + + // outer ToString + if (headerOutPtr) { + (*headerOutPtr) << "const TString& ToString(" << name << ");\n"; + } + out << "const TString& ToString(" << name << " x) {\n"; + out << " const " << nsName << "::TNameBufs& names = " << nsName << "::TNameBufs::Instance();\n"; + out << " return names.ToString(x);\n"; + out << "}\n\n"; + + // outer FromString + if (headerOutPtr) { + (*headerOutPtr) << "bool FromString(const TString& name, " << name << "& ret);\n"; + } + out << "bool FromString(const TString& name, " << name << "& ret) {\n"; + out << " const " << nsName << "::TNameBufs& names = " << nsName << "::TNameBufs::Instance();\n"; + out << " return names.FromString(name, ret);\n"; + out << "}\n\n"; + + // outer FromString + if (headerOutPtr) { + (*headerOutPtr) << "bool FromString(const TStringBuf& name, " << name << "& ret);\n"; + } + out << "bool FromString(const TStringBuf& name, " << name << "& ret) {\n"; + out << " const " << nsName << "::TNameBufs& names = " << nsName << "::TNameBufs::Instance();\n"; + out << " return names.FromString(name, ret);\n"; + out << "}\n\n"; + + // specialization for internal FromStringImpl + out << "template<>\n"; + out << name << " FromStringImpl<" << name << ">(const char* data, size_t len) {\n"; + out << " const " << nsName << "::TNameBufs& names = " << nsName << "::TNameBufs::Instance();\n"; + out << " return names.FromString(TStringBuf(data, len));\n"; + out << "}\n\n"; + + // specialization for internal TryFromStringImpl + out << "template<>\n"; + out << "bool TryFromStringImpl<" << name << ">(const char* data, size_t len, " << name << "& result) {\n"; + out << " return FromString(TStringBuf(data, len), result);\n"; + out << "}\n\n"; + + // outer Out + out << "template<>\n"; + out << "void Out<" << name << ">(IOutputStream& os, TTypeTraits<" << name << ">::TFuncParam n) {\n"; + out << " const " << nsName << "::TNameBufs& names = " << nsName << "::TNameBufs::Instance();\n"; + out << " return names.Out(&os, n);\n"; + out << "}\n\n"; + + // specializations for NEnumSerializationRuntime function family + out << "namespace NEnumSerializationRuntime {\n"; + // template<> GetEnumAllValues + out << " template<>\n"; + out << " TMappedArrayView<" << name <<"> GetEnumAllValuesImpl<" << name << ">() {\n"; + out << " const " << nsName << "::TNameBufs& names = " << nsName << "::TNameBufs::Instance();\n"; + out << " return names.AllEnumValues();\n"; + out << " }\n\n"; + + // template<> GetEnumAllNames + out << " template<>\n"; + out << " const TString& GetEnumAllNamesImpl<" << name << ">() {\n"; + out << " const " << nsName << "::TNameBufs& names = " << nsName << "::TNameBufs::Instance();\n"; + out << " return names.AllEnumNames();\n"; + out << " }\n\n"; + + // template<> GetEnumNames<EnumType> + out << " template<>\n"; + out << " TMappedDictView<" << name << ", TString> GetEnumNamesImpl<" << name << ">() {\n"; + out << " const " << nsName << "::TNameBufs& names = " << nsName << "::TNameBufs::Instance();\n"; + out << " return names.EnumNames();\n"; + out << " }\n\n"; + + // template<> GetEnumAllCppNames, see IGNIETFERRO-534 + out << " template<>\n"; + out << " const TVector<TString>& GetEnumAllCppNamesImpl<" << name << ">() {\n"; + out << " const " << nsName << "::TNameBufs& names = " << nsName << "::TNameBufs::Instance();\n"; + out << " return names.AllEnumCppNames();\n"; + out << " }\n"; + + out << "}\n\n"; + + if (headerOutPtr) { + // <EnumType>Count + auto& outHeader = *headerOutPtr; + outHeader << "template <>\n"; + outHeader << "constexpr size_t GetEnumItemsCount<" << name << ">() {\n"; + outHeader << " return " << en.Items.size() << ";\n"; + outHeader << "}\n"; + } + + FinishItems(jEnum); + jEnum << "}\n"; + + if (jsonEnumOut) { + *jsonEnumOut << jEnum.Str(); + } +} + +int main(int argc, char** argv) { + try { + using namespace NLastGetopt; + TOpts opts = NLastGetopt::TOpts::Default(); + opts.AddHelpOption(); + + TString outputFileName; + TString outputHeaderFileName; + TString outputJsonFileName; + TString includePath; + opts.AddLongOption('o', "output").OptionalArgument("<output-file>").StoreResult(&outputFileName) + .Help( + "Output generated code to specified file.\n" + "When not set, standard output is used." + ); + opts.AddLongOption('h', "header").OptionalArgument("<output-header>").StoreResult(&outputHeaderFileName) + .Help( + "Generate appropriate header to specified file.\n" + "Works only if output file specified." + ); + opts.AddLongOption("include-path").OptionalArgument("<header-path>").StoreResult(&includePath) + .Help( + "Include input header using this path in angle brackets.\n" + "When not set, header basename is used in double quotes." + ); + + opts.AddLongOption('j', "json-output").OptionalArgument("<json-output>").StoreResult(&outputJsonFileName) + .Help( + "Generate enum data in JSON format." + ); + + opts.SetFreeArgsNum(1); + opts.SetFreeArgTitle(0, "<input-file>", "Input header file with enum declarations"); + + TOptsParseResult res(&opts, argc, argv); + + TVector<TString> freeArgs = res.GetFreeArgs(); + TString inputFileName = freeArgs[0]; + + THolder<IOutputStream> hOut; + IOutputStream* out = &Cout; + + THolder<IOutputStream> headerOut; + + THolder<IOutputStream> jsonOut; + + + if (outputFileName) { + NFs::Remove(outputFileName); + hOut.Reset(new TFileOutput(outputFileName)); + out = hOut.Get(); + + if (outputHeaderFileName) { + headerOut.Reset(new TFileOutput(outputHeaderFileName)); + } + + if (outputJsonFileName) { + jsonOut.Reset(new TFileOutput(outputJsonFileName)); + } + } + + if (!includePath) { + includePath = TString() + '"' + TFsPath(inputFileName).Basename() + '"'; + } else { + includePath = TString() + '<' + includePath + '>'; + } + + TEnumParser parser(inputFileName); + WriteHeader(includePath, *out, headerOut.Get()); + + TStringStream jEnums; + OpenArray(jEnums); + + for (const auto& en : parser.Enums) { + if (!en.CppName) { + // skip unnamed enum declarations + continue; + } + + TStringStream jEnum; + GenerateEnum(en, *out, &jEnum, headerOut.Get()); + OutItem(jEnums, jEnum.Str(), false); + } + FinishItems(jEnums); + CloseArray(jEnums); + + if (jsonOut) { + *jsonOut << jEnums.Str() << Endl; + } + + return 0; + } catch (...) { + Cerr << CurrentExceptionMessage() << Endl; + } + + return 1; +} diff --git a/tools/enum_parser/enum_parser/stdlib_deps.h b/tools/enum_parser/enum_parser/stdlib_deps.h new file mode 100644 index 0000000000..a28e2bf927 --- /dev/null +++ b/tools/enum_parser/enum_parser/stdlib_deps.h @@ -0,0 +1,5 @@ +#pragma once + +#include <array> +#include <initializer_list> +#include <utility> diff --git a/tools/enum_parser/enum_parser/ya.make b/tools/enum_parser/enum_parser/ya.make new file mode 100644 index 0000000000..82bac4e0f7 --- /dev/null +++ b/tools/enum_parser/enum_parser/ya.make @@ -0,0 +1,16 @@ +OWNER( + g:util + mvel +) + +IF (USE_PREBUILT_TOOLS) + INCLUDE(${ARCADIA_ROOT}/build/prebuilt/tools/enum_parser/enum_parser/ya.make.prebuilt) +ENDIF() + +IF (NOT PREBUILT) + INCLUDE(${ARCADIA_ROOT}/tools/enum_parser/enum_parser/bin/ya.make) +ENDIF() + +RECURSE( + bin +) diff --git a/tools/enum_parser/enum_serialization_runtime/README.md b/tools/enum_parser/enum_serialization_runtime/README.md new file mode 100644 index 0000000000..7d2cbae4ce --- /dev/null +++ b/tools/enum_parser/enum_serialization_runtime/README.md @@ -0,0 +1,2 @@ +This library should not be used or referred directly. +Use `GENERATE_ENUM_SERIALIZATION_WITH_HEADER` and `GENERATE_ENUM_SERIALIZATION` macros instead. diff --git a/tools/enum_parser/enum_serialization_runtime/enum_runtime.cpp b/tools/enum_parser/enum_serialization_runtime/enum_runtime.cpp new file mode 100644 index 0000000000..73d38b396a --- /dev/null +++ b/tools/enum_parser/enum_serialization_runtime/enum_runtime.cpp @@ -0,0 +1,100 @@ +#include "enum_runtime.h" + +#include <util/generic/map.h> +#include <util/generic/yexception.h> +#include <util/stream/output.h> + +namespace NEnumSerializationRuntime { + template <typename TEnumRepresentationType> + [[noreturn]] static void ThrowUndefinedValueException(const TEnumRepresentationType key, const TStringBuf className) { + throw yexception() << "Undefined value " << key << " in " << className << ". "; + } + + template <typename TEnumRepresentationType> + const TString& TEnumDescriptionBase<TEnumRepresentationType>::ToString(TRepresentationType key) const { + const auto it = Names.find(key); + if (Y_LIKELY(it != Names.end())) { + return it->second; + } + ThrowUndefinedValueException(key, ClassName); + } + + template <typename TEnumRepresentationType> + std::pair<bool, TEnumRepresentationType> TEnumDescriptionBase<TEnumRepresentationType>::TryFromString(const TStringBuf name) const { + const auto it = Values.find(name); + if (it != Values.end()) { + return {true, it->second}; + } + return {false, TRepresentationType()}; + } + + [[noreturn]] static void ThrowUndefinedNameException(const TStringBuf name, const TStringBuf className, const TStringBuf allEnumNames) { + ythrow yexception() << "Key '" << name << "' not found in enum " << className << ". Valid options are: " << allEnumNames << ". "; + } + + template <typename TEnumRepresentationType> + auto TEnumDescriptionBase<TEnumRepresentationType>::FromString(const TStringBuf name) const -> TRepresentationType { + const auto findResult = TryFromString(name); + if (Y_LIKELY(findResult.first)) { + return findResult.second; + } + ThrowUndefinedNameException(name, ClassName, AllEnumNames()); + } + + template <typename TEnumRepresentationType> + void TEnumDescriptionBase<TEnumRepresentationType>::Out(IOutputStream* os, const TRepresentationType key) const { + (*os) << this->ToString(key); + } + + template <typename TEnumRepresentationType> + TEnumDescriptionBase<TEnumRepresentationType>::TEnumDescriptionBase(const TInitializationData& enumInitData) + : ClassName(enumInitData.ClassName) + { + const TArrayRef<const TEnumStringPair>& namesInitializer = enumInitData.NamesInitializer; + const TArrayRef<const TEnumStringPair>& valuesInitializer = enumInitData.ValuesInitializer; + const TArrayRef<const TStringBuf>& cppNamesInitializer = enumInitData.CppNamesInitializer; + + TMap<TRepresentationType, TString> mapValueToName; + TMap<TString, TRepresentationType> mapNameToValue; + const bool bijectiveHint = (namesInitializer.data() == valuesInitializer.data() && namesInitializer.size() == valuesInitializer.size()); + if (bijectiveHint) { + for (const TEnumStringPair& it : namesInitializer) { + TString name{it.Name}; + mapValueToName.emplace(it.Key, name); + mapNameToValue.emplace(std::move(name), it.Key); + } + } else { + for (const TEnumStringPair& it : namesInitializer) { + mapValueToName.emplace(it.Key, TString(it.Name)); + } + for (const TEnumStringPair& it : valuesInitializer) { + mapNameToValue.emplace(TString(it.Name), it.Key); + } + } + Names = std::move(mapValueToName); + Values = std::move(mapNameToValue); + + AllValues.reserve(Names.size()); + for (const auto& it : Names) { + if (!AllNames.empty()) { + AllNames += ", "; + } + AllNames += TString::Join('\'', it.second, '\''); + AllValues.push_back(it.first); + } + + AllCppNames.reserve(cppNamesInitializer.size()); + for (const auto& cn : cppNamesInitializer) { + AllCppNames.push_back(TString::Join(enumInitData.CppNamesPrefix, cn)); + } + } + + template <typename TEnumRepresentationType> + TEnumDescriptionBase<TEnumRepresentationType>::~TEnumDescriptionBase() = default; + + // explicit instantiation + template class TEnumDescriptionBase<int>; + template class TEnumDescriptionBase<unsigned>; + template class TEnumDescriptionBase<long long>; + template class TEnumDescriptionBase<unsigned long long>; +} diff --git a/tools/enum_parser/enum_serialization_runtime/enum_runtime.h b/tools/enum_parser/enum_serialization_runtime/enum_runtime.h new file mode 100644 index 0000000000..f157d43109 --- /dev/null +++ b/tools/enum_parser/enum_serialization_runtime/enum_runtime.h @@ -0,0 +1,116 @@ +#pragma once + +#include <util/generic/array_ref.h> +#include <util/generic/strbuf.h> +#include <util/generic/string.h> +#include <util/generic/vector.h> +#include <util/generic/serialized_enum.h> + +#include <utility> + +class IOutputStream; + +namespace NEnumSerializationRuntime { + /// Stores all information about enumeration except its real type + template <typename TEnumRepresentationType> + class TEnumDescriptionBase { + public: + using TRepresentationType = TEnumRepresentationType; + + struct TEnumStringPair { + const TRepresentationType Key; + const TStringBuf Name; + }; + + /// Refers initialization data stored in constexpr-friendly format + struct TInitializationData { + const TArrayRef<const TEnumStringPair> NamesInitializer; + const TArrayRef<const TEnumStringPair> ValuesInitializer; + const TArrayRef<const TStringBuf> CppNamesInitializer; + const TStringBuf CppNamesPrefix; + const TStringBuf ClassName; + }; + + public: + TEnumDescriptionBase(const TInitializationData& enumInitData); + ~TEnumDescriptionBase(); + + const TString& ToString(TRepresentationType key) const; + std::pair<bool, TRepresentationType> TryFromString(const TStringBuf name) const; + TRepresentationType FromString(const TStringBuf name) const; + void Out(IOutputStream* os, const TRepresentationType key) const; + + const TString& AllEnumNames() const noexcept { + return AllNames; + } + + const TVector<TString>& AllEnumCppNames() const noexcept { + return AllCppNames; + } + + const TMap<TRepresentationType, TString>& TypelessEnumNames() const noexcept { + return Names; + } + + const TVector<TRepresentationType>& TypelessEnumValues() const noexcept { + return AllValues; + } + + private: + TMap<TRepresentationType, TString> Names; + TMap<TString, TRepresentationType> Values; + TString AllNames; + TVector<TString> AllCppNames; + TString ClassName; + TVector<TRepresentationType> AllValues; + }; + + /// Wraps TEnumDescriptionBase and performs on-demand casts + template <typename EEnum, typename TEnumRepresentationType = typename NDetail::TSelectEnumRepresentationType<EEnum>::TType> + class TEnumDescription: public NDetail::TMappedViewBase<EEnum, TEnumRepresentationType>, private TEnumDescriptionBase<TEnumRepresentationType> { + public: + using TBase = TEnumDescriptionBase<TEnumRepresentationType>; + using TCast = NDetail::TMappedViewBase<EEnum, TEnumRepresentationType>; + using TBase::AllEnumCppNames; + using TBase::AllEnumNames; + using typename TBase::TEnumStringPair; + using typename TBase::TRepresentationType; + using typename TBase::TInitializationData; + + public: + using TBase::TBase; + + const TString& ToString(const EEnum key) const { + return TBase::ToString(TCast::CastToRepresentationType(key)); + } + + bool FromString(const TStringBuf name, EEnum& ret) const { + const auto findResult = TBase::TryFromString(name); + if (findResult.first) { + ret = TCast::CastFromRepresentationType(findResult.second); + return true; + } + return false; + } + + EEnum FromString(const TStringBuf name) const { + return TCast::CastFromRepresentationType(TBase::FromString(name)); + } + + TMappedDictView<EEnum, TString> EnumNames() const noexcept { + return {TBase::TypelessEnumNames()}; + } + + TMappedArrayView<EEnum> AllEnumValues() const noexcept { + return {TBase::TypelessEnumValues()}; + } + + void Out(IOutputStream* os, const EEnum key) const { + TBase::Out(os, TCast::CastToRepresentationType(key)); + } + + static constexpr TEnumStringPair EnumStringPair(const EEnum key, const TStringBuf name) noexcept { + return {TCast::CastToRepresentationType(key), name}; + } + }; +} diff --git a/tools/enum_parser/enum_serialization_runtime/ya.make b/tools/enum_parser/enum_serialization_runtime/ya.make new file mode 100644 index 0000000000..c18e4aee10 --- /dev/null +++ b/tools/enum_parser/enum_serialization_runtime/ya.make @@ -0,0 +1,13 @@ +LIBRARY() + +OWNER( + g:util + swarmer +) + +SRCS( + enum_runtime.cpp +) + + +END() diff --git a/tools/enum_parser/parse_enum/parse_enum.cpp b/tools/enum_parser/parse_enum/parse_enum.cpp new file mode 100644 index 0000000000..3db0d7a4d9 --- /dev/null +++ b/tools/enum_parser/parse_enum/parse_enum.cpp @@ -0,0 +1,422 @@ +#include "parse_enum.h" + +#include <library/cpp/cppparser/parser.h> + +#include <util/stream/file.h> +#include <util/stream/output.h> +#include <util/stream/input.h> +#include <util/stream/mem.h> + +#include <util/charset/wide.h> +#include <util/string/strip.h> +#include <util/string/cast.h> +#include <util/generic/map.h> +#include <util/generic/string.h> +#include <util/generic/vector.h> +#include <util/generic/ptr.h> +#include <util/generic/yexception.h> + +/** + * Parse C-style strings inside multiline comments + **/ +class TValuesContext: public TCppFullSax { +public: + void DoString(const TText& text) override { + Values.push_back(text.Data); + } + + ~TValuesContext() override { + } + + TVector<TString> Values; +}; + +static TVector<TString> ParseEnumValues(const TString& strValues) { + TVector<TString> result; + + TValuesContext ctx; + TCppSaxParser parser(&ctx); + TMemoryInput in(strValues.data(), strValues.size()); + TransferData(static_cast<IInputStream*>(&in), &parser); + parser.Finish(); + for (const auto& value : ctx.Values) { + Y_ENSURE(value.size() >= 2, "Invalid C-style string. "); + TString dequoted = value.substr(1, value.size() - 2); + // TODO: support C-unescaping + result.push_back(dequoted); + } + return result; +} + +/** + * Parse C++ fragment with one enum + **/ +class TEnumContext: public TCppFullSax { +public: + typedef TEnumParser::TItem TItem; + typedef TEnumParser::TEnum TEnum; + + TEnumContext(TEnum& currentEnum) + : CurrentEnum(currentEnum) + { + } + + ~TEnumContext() override { + } + + void AddEnumItem() { + if (!CurrentItem.CppName) { + // uninitialized element should have no value too + Y_ASSERT(!CurrentItem.Value.Defined()); + return; + } + + // enum item C++ name should not be empty + Y_ASSERT(CurrentItem.CppName); + CurrentItem.NormalizeValue(); + CurrentEnum.Items.push_back(CurrentItem); + CurrentItem.Clear(); + InEnumState = Begin; + } + + template<class T> + void AppendValue(const T& text) { + // by pg@ advice, do not parse enum value + // leave it to C++ compiler to parse/interpret + + if (!CurrentItem.Value) + CurrentItem.Value = TString(); + + *CurrentItem.Value += text; + } + + void DoEnd() override { + AddEnumItem(); + } + + void DoWhiteSpace(const TText& text) override { + if (InValue == InEnumState || InValueCall == InEnumState) { + AppendValue(text.Data); + } + } + + void DoSyntax(const TText& text) override { + // For some reason, parser sometimes passes chunks like '{};' here, + // so we handle each symbol separately. + for (const char& sym : text.Data) { + if ('{' == sym && InValue != InEnumState && InValueCall != InEnumState) { + BodyDetected = true; + continue; + } else if ('=' == sym && InValueCall != InEnumState) { + InEnumState = InValue; + continue; + } else if (('(' == sym || '{' == sym) && (InValue == InEnumState || InValueCall == InEnumState)) { + // there may be constexpr function / constructor / macro call in value part, + // handle them appropriately + InEnumState = InValueCall; + ++BracesBalance; + AppendValue(sym); + continue; + } else if ((')' == sym || '}' == sym) && InValueCall == InEnumState) { + if (!--BracesBalance) { + InEnumState = InValue; + } + AppendValue(sym); + continue; + } else if ((',' == sym || '}' == sym) && InValueCall != InEnumState) { + AddEnumItem(); + continue; + } else if (InValue == InEnumState || InValueCall == InEnumState) { + AppendValue(sym); + } + } + } + + void DoName(const TText& text) override { + if (!BodyDetected) { + return; + } + + if (InValue == InEnumState || InValueCall == InEnumState) { + AppendValue(text.Data); + return; + } + + CurrentItem.CppName = text.Data; + InEnumState = AfterCppName; + } + + void DoMultiLineComment(const TText& text) override { + Y_ENSURE(text.Data.size() >= 4, "Invalid multiline comment " << text.Data.Quote() << ". "); + TString commentText = text.Data.substr(2, text.Data.size() - 4); + commentText = StripString(commentText); + CurrentItem.CommentText = commentText; + CurrentItem.Aliases = ParseEnumValues(commentText); + + if (CurrentItem.Aliases && !CurrentItem.CppName) { + // this means we process multiline comment when item name was not set yet. + ythrow yexception() << "Are you hit with https://clubs.at.yandex-team.ru/stackoverflow/2603 typo? "; + } + } + + bool BodyDetected = false; + enum EInEnumState { + Begin, + AfterCppName, + InValue, + InValueCall, + End, + }; + EInEnumState InEnumState = Begin; + + TEnum& CurrentEnum; + TItem CurrentItem; + + size_t BracesBalance = 0; +}; + +/** + * Parse C++ file + **/ +class TCppContext: public TCppFullSax { +public: + typedef TEnumParser::TScope TScope; + typedef TEnumParser::TItem TItem; + typedef TEnumParser::TEnum TEnum; + typedef TEnumParser::TEnums TEnums; + + const TString NAMESPACE = "<namespace>"; + const TString CLASS = "<class>"; + const TString STRUCT = "<struct>"; + const TString ENUM = "<enum>"; + const TString BLOCK = "<block>"; + + TCppContext(const char* data, const TString& sourceFileName = TString()) + : Data(data) + , SourceFileName(sourceFileName) + { + } + + ~TCppContext() override { + } + + void DoSyntax(const TText& text) override { + // For some reason, parser sometimes passes chunks like '{};' here, + // so we handle each symbol separately. + const TString& syn = text.Data; + if (syn == "::" && InCompositeNamespace) { + LastScope += syn; + InCompositeNamespace = false; + ScopeDeclaration = true; + return; + } + for (size_t i = 0; i < syn.size(); ++i) { + if ('{' == syn[i]) { + OnEnterScope(text.Offset + i); + if (InEnum) { + CurrentEnum.BodyDetected = true; + } + } else if ('}' == syn[i]) { + OnLeaveScope(text.Offset + i); + } else if (';' == syn[i]) { + // Handle SEARCH-1392 + if (InEnum && !CurrentEnum.BodyDetected) { + CurrentEnum.ForwardDeclaration = true; + InEnum = false; + } + } + } + } + + void DoKeyword(const TText& text) override { + if (text.Data == "enum") { + Y_ENSURE(!InEnum, "Enums cannot be nested. "); + InEnum = true; + EnumPos = text.Offset; + CurrentEnum.Clear(); + CurrentEnum.Scope = Scope; + ScopeDeclaration = true; + NextScopeName = ENUM; + //PrintScope(); + } else if (text.Data == "class") { + if (InEnum) { + CurrentEnum.EnumClass = true; + return; + } + NextScopeName = CLASS; + ScopeDeclaration = true; + //PrintScope(); + } else if (text.Data == "struct") { + if (InEnum) { + CurrentEnum.EnumClass = true; + return; + } + NextScopeName = STRUCT; + ScopeDeclaration = true; + //PrintScope(); + } else if (text.Data == "namespace") { + NextScopeName = NAMESPACE; + LastScope.clear(); + ScopeDeclaration = true; + //PrintScope(); + } + } + + void DoName(const TText& text) override { + if (!ScopeDeclaration) { + return; + } + if (InEnum) { + CurrentEnum.CppName = text.Data; + } else { + if (NextScopeName == NAMESPACE) { + InCompositeNamespace = true; + LastScope += text.Data; + } else { + LastScope = text.Data; + } + } + ScopeDeclaration = false; + } + + void OnEnterScope(size_t /* offset */) { + if (ScopeDeclaration) { + // unnamed declaration or typedef + ScopeDeclaration = false; + } + InCompositeNamespace = false; + Scope.push_back(LastScope); + LastScope.clear(); + //PrintScope(); + } + + /// @param offset: terminating curly brace position + void OnLeaveScope(size_t offset) { + if (!Scope) { + size_t contextOffsetBegin = (offset >= 256) ? offset - 256 : 0; + TString codeContext = TString(Data + contextOffsetBegin, offset - contextOffsetBegin + 1); + ythrow yexception() << "C++ source parse failed: unbalanced scope. Did you miss a closing '}' bracket? " + "Context: enum " << CurrentEnum.CppName.Quote() << + " in scope " << TEnumParser::ScopeStr(CurrentEnum.Scope).Quote() << ". Code context:\n... " << + codeContext << " ..."; + } + Scope.pop_back(); + + if (InEnum) { + Y_ASSERT(offset > EnumPos); + InEnum = false; + try { + ParseEnum(Data + EnumPos, offset - EnumPos + 1); + } catch (...) { + TString ofFile; + if (SourceFileName) { + ofFile += " of file "; + ofFile += SourceFileName.Quote(); + } + ythrow yexception() << "Failed to parse enum " << CurrentEnum.CppName << + " in scope " << TEnumParser::ScopeStr(CurrentEnum.Scope) << ofFile << + "\n<C++ parser error message>: " << CurrentExceptionMessage(); + } + } + //PrintScope(); + } + + void ParseEnum(const char* data, size_t length) { + TEnumContext enumContext(CurrentEnum); + TMemoryInput in(data, length); + TCppSaxParser parser(&enumContext); + TransferData(&in, &parser); + parser.Finish(); + //PrintEnum(CurrentEnum); + Enums.push_back(CurrentEnum); + } + + // Some debug stuff goes here + static void PrintScope(const TScope& scope) { + Cerr << "Current scope: " << TEnumParser::ScopeStr(scope) << Endl; + } + + void PrintScope() { + PrintScope(Scope); + } + + void PrintEnum(const TEnum& en) { + Cerr << "Enum within scope " << TEnumParser::ScopeStr(en.Scope).Quote() << Endl; + for (const auto& item : en.Items) { + Cerr << " " << item.CppName; + if (item.Value) + Cerr << " = " << *item.Value; + Cerr << Endl; + for (const auto& value : item.Aliases) { + Cerr << " " << value << Endl; + } + } + } + + void PrintEnums() { + for (const auto& en : Enums) + PrintEnum(en); + } + +public: + TScope Scope; + TEnums Enums; +private: + const char* const Data; + TString SourceFileName; + + bool InEnum = false; + bool ScopeDeclaration = false; + bool InCompositeNamespace = false; + TString NextScopeName = BLOCK; + TString LastScope; + size_t EnumPos = 0; + TEnum CurrentEnum; +}; + + +TEnumParser::TEnumParser(const TString& fileName) { + THolder<IInputStream> hIn; + IInputStream* in = nullptr; + if (fileName != "-") { + SourceFileName = fileName; + hIn.Reset(new TFileInput(fileName)); + in = hIn.Get(); + } else { + in = &Cin; + } + TString contents = in->ReadAll(); + Parse(contents.data(), contents.size()); +} + +TEnumParser::TEnumParser(const char* data, size_t length) { + Parse(data, length); +} + +TEnumParser::TEnumParser(IInputStream& in) { + TString contents = in.ReadAll(); + Parse(contents.data(), contents.size()); +} + +void TEnumParser::Parse(const char* data, size_t length) { + const TStringBuf span(data, length); + const bool hasPragmaOnce = span.Contains("#pragma once"); + const bool isProtobufHeader = span.Contains("// Generated by the protocol buffer compiler"); + const bool isFlatbuffersHeader = span.Contains("// automatically generated by the FlatBuffers compiler"); + Y_ENSURE( + hasPragmaOnce || isProtobufHeader || isFlatbuffersHeader, + "Serialization functions can be generated only for enums in header files, see SEARCH-975. " + ); + TCppContext cppContext(data, SourceFileName); + TMemoryInput in(data, length); + TCppSaxParser parser(&cppContext); + TransferData(&in, &parser); + parser.Finish(); + //cppContext.PrintEnums(); + // obtain result + Enums = cppContext.Enums; + if (cppContext.Scope) { + cppContext.PrintScope(); + ythrow yexception() << "Unbalanced scope, something is wrong with enum parser. "; + } +} diff --git a/tools/enum_parser/parse_enum/parse_enum.h b/tools/enum_parser/parse_enum/parse_enum.h new file mode 100644 index 0000000000..ef8b512ae4 --- /dev/null +++ b/tools/enum_parser/parse_enum/parse_enum.h @@ -0,0 +1,78 @@ +#pragma once + +#include <util/stream/output.h> +#include <util/stream/input.h> +#include <util/stream/mem.h> +#include <util/string/strip.h> +#include <util/generic/maybe.h> +#include <util/generic/string.h> +#include <util/generic/vector.h> + +class TEnumParser { +public: + + struct TItem { + TMaybe<TString> Value; + TString CppName; + TVector<TString> Aliases; + TString CommentText; + + void Clear() { + *this = TItem(); + } + + void NormalizeValue() { + if (!Value) + return; + StripInPlace(*Value); + } + + }; + + // vector is to preserve declaration order + typedef TVector<TItem> TItems; + + typedef TVector<TString> TScope; + + struct TEnum { + TItems Items; + TString CppName; + TScope Scope; + // enum or enum class + bool EnumClass = false; + bool BodyDetected = false; + bool ForwardDeclaration = false; + + void Clear() { + *this = TEnum(); + } + }; + + typedef TVector<TEnum> TEnums; + + /// Parse results stored here + TEnums Enums; + + /// Parse enums from file containing C++ code + TEnumParser(const TString& fileName); + + /// Parse enums from memory buffer containing C++ code + TEnumParser(const char* data, size_t length); + + /// Parse enums from input stream + TEnumParser(IInputStream& in); + + static TString ScopeStr(const TScope& scope) { + TString result; + for (const TString& name : scope) { + result += name; + result += "::"; + } + return result; + } + +private: + void Parse(const char* data, size_t length); +protected: + TString SourceFileName; +}; diff --git a/tools/enum_parser/parse_enum/parse_enum_ut.cpp b/tools/enum_parser/parse_enum/parse_enum_ut.cpp new file mode 100644 index 0000000000..21ed6a2fc4 --- /dev/null +++ b/tools/enum_parser/parse_enum/parse_enum_ut.cpp @@ -0,0 +1,315 @@ +#include <library/cpp/resource/resource.h> +#include <library/cpp/testing/unittest/registar.h> + +#include <tools/enum_parser/parse_enum/parse_enum.h> + +typedef TEnumParser::TEnum TEnum; +typedef TEnumParser::TEnums TEnums; +typedef TEnumParser::TItems TItems; + +Y_UNIT_TEST_SUITE(TEnumParserTest) { + + Y_UNIT_TEST(MainTest) { + TString text = NResource::Find("/enums"); + TMemoryInput input(text.data(), text.size()); + TEnumParser parser(input); + const TEnums& enums = parser.Enums; + + UNIT_ASSERT_VALUES_EQUAL(enums.size(), 16u); + + // check ESimple + { + const TEnum& e = enums[0]; + UNIT_ASSERT_VALUES_EQUAL(e.Scope.size(), 0u); + UNIT_ASSERT_VALUES_EQUAL(e.CppName, "ESimple"); + const TItems& it = e.Items; + UNIT_ASSERT_VALUES_EQUAL(it.size(), 3u); + + UNIT_ASSERT_VALUES_EQUAL(it[0].CppName, "Http"); + UNIT_ASSERT_VALUES_EQUAL(it[0].Aliases.size(), 0u); + UNIT_ASSERT(!it[0].Value.Defined()); + + UNIT_ASSERT_VALUES_EQUAL(it[1].CppName, "Https"); + UNIT_ASSERT_VALUES_EQUAL(it[1].Aliases.size(), 0u); + UNIT_ASSERT(!it[1].Value.Defined()); + + UNIT_ASSERT_VALUES_EQUAL(it[2].CppName, "ItemCount"); + UNIT_ASSERT_VALUES_EQUAL(it[2].Aliases.size(), 0u); + UNIT_ASSERT(!it[2].Value.Defined()); + } + + // ESimpleWithComma + { + const TEnum& e = enums[1]; + UNIT_ASSERT_VALUES_EQUAL(e.Scope.size(), 0u); + UNIT_ASSERT_VALUES_EQUAL(e.CppName, "ESimpleWithComma"); + const TItems& it = e.Items; + UNIT_ASSERT_VALUES_EQUAL(it.size(), 4u); + + UNIT_ASSERT_VALUES_EQUAL(it[0].CppName, "Http"); + UNIT_ASSERT(it[0].Value.Defined()); + UNIT_ASSERT_VALUES_EQUAL(*it[0].Value, "3"); + UNIT_ASSERT_VALUES_EQUAL(it[0].Aliases.size(), 0u); + + UNIT_ASSERT_VALUES_EQUAL(it[1].CppName, "Http2"); + UNIT_ASSERT(it[1].Value.Defined()); + UNIT_ASSERT_VALUES_EQUAL(it[1].Aliases.size(), 0u); + UNIT_ASSERT_VALUES_EQUAL(*it[1].Value, "Http"); + + UNIT_ASSERT_VALUES_EQUAL(it[2].CppName, "Https"); + UNIT_ASSERT_VALUES_EQUAL(it[2].Aliases.size(), 0u); + UNIT_ASSERT(!it[2].Value.Defined()); + + UNIT_ASSERT_VALUES_EQUAL(it[3].CppName, "ItemCount"); + UNIT_ASSERT_VALUES_EQUAL(it[3].Aliases.size(), 0u); + UNIT_ASSERT(!it[3].Value.Defined()); + } + + // check ECustomAliases + { + const TEnum& e = enums[2]; + UNIT_ASSERT_VALUES_EQUAL(e.Scope.size(), 0u); + UNIT_ASSERT_VALUES_EQUAL(e.CppName, "ECustomAliases"); + const TItems& it = e.Items; + UNIT_ASSERT_VALUES_EQUAL(it.size(), 3u); + UNIT_ASSERT_VALUES_EQUAL(it[0].CppName, "CAHttp"); + UNIT_ASSERT(it[0].Value.Defined()); + UNIT_ASSERT_VALUES_EQUAL(*it[0].Value, "3"); + UNIT_ASSERT_VALUES_EQUAL(it[0].Aliases.size(), 1u); + UNIT_ASSERT_VALUES_EQUAL(it[0].Aliases[0], "http"); + + UNIT_ASSERT(!it[1].Value.Defined()); + UNIT_ASSERT_VALUES_EQUAL(it[1].CppName, "CAHttps"); + UNIT_ASSERT_VALUES_EQUAL(it[1].Aliases.size(), 1u); + UNIT_ASSERT_VALUES_EQUAL(it[1].Aliases[0], "https"); + + UNIT_ASSERT_VALUES_EQUAL(it[2].CppName, "CAItemCount"); + UNIT_ASSERT_VALUES_EQUAL(it[2].Aliases.size(), 0u); + } + + // check EMultipleAliases + { + const TEnum& e = enums[3]; + UNIT_ASSERT_VALUES_EQUAL(e.Scope.size(), 0u); + UNIT_ASSERT_VALUES_EQUAL(e.CppName, "EMultipleAliases"); + const TItems& it = e.Items; + UNIT_ASSERT_VALUES_EQUAL(it.size(), 3u); + UNIT_ASSERT_VALUES_EQUAL(it[0].CppName, "MAHttp"); + UNIT_ASSERT(it[0].Value.Defined()); + UNIT_ASSERT_VALUES_EQUAL(*it[0].Value, "9"); + UNIT_ASSERT_VALUES_EQUAL(it[0].Aliases.size(), 3u); + UNIT_ASSERT_VALUES_EQUAL(it[0].Aliases[0], "http://"); + UNIT_ASSERT_VALUES_EQUAL(it[0].Aliases[1], "secondary"); + // yes, quoted values are NOT decoded, it is a known (minor) bug + UNIT_ASSERT_VALUES_EQUAL(it[0].Aliases[2], "old\\nvalue"); + + UNIT_ASSERT_VALUES_EQUAL(it[1].CppName, "MAHttps"); + UNIT_ASSERT(it[1].Value.Defined()); + UNIT_ASSERT_VALUES_EQUAL(*it[1].Value, "1"); + UNIT_ASSERT_VALUES_EQUAL(it[1].Aliases.size(), 1u); + UNIT_ASSERT_VALUES_EQUAL(it[1].Aliases[0], "https://"); + + UNIT_ASSERT_VALUES_EQUAL(it[2].CppName, "MAItemCount"); + UNIT_ASSERT(!it[2].Value.Defined()); + UNIT_ASSERT_VALUES_EQUAL(it[2].Aliases.size(), 0u); + } + + // check NEnumNamespace::EInNamespace + { + const TEnum& e = enums[4]; + UNIT_ASSERT_VALUES_EQUAL(e.Scope.size(), 1u); + UNIT_ASSERT_VALUES_EQUAL(e.Scope[0], "NEnumNamespace"); + UNIT_ASSERT_VALUES_EQUAL(e.CppName, "EInNamespace"); + const TItems& it = e.Items; + UNIT_ASSERT_VALUES_EQUAL(it.size(), 3u); + UNIT_ASSERT_VALUES_EQUAL(it[0].CppName, "Http"); + UNIT_ASSERT(it[0].Value.Defined()); + } + + // check NEnumNamespace::TEnumClass::EInClass + { + const TEnum& e = enums[5]; + UNIT_ASSERT_VALUES_EQUAL(e.Scope.size(), 2u); + UNIT_ASSERT_VALUES_EQUAL(e.Scope[0], "NEnumNamespace"); + UNIT_ASSERT_VALUES_EQUAL(e.Scope[1], "TEnumClass"); + UNIT_ASSERT_VALUES_EQUAL(e.CppName, "EInClass"); + const TItems& it = e.Items; + UNIT_ASSERT_VALUES_EQUAL(it.size(), 3u); + UNIT_ASSERT_VALUES_EQUAL(it[0].CppName, "Http"); + UNIT_ASSERT(it[0].Value.Defined()); + UNIT_ASSERT_VALUES_EQUAL(*it[0].Value, "9"); + + UNIT_ASSERT(it[1].Value.Defined()); + UNIT_ASSERT_VALUES_EQUAL(*it[1].Value, "NEnumNamespace::Https"); + + UNIT_ASSERT_VALUES_EQUAL(it[2].CppName, "Https3"); + UNIT_ASSERT(it[2].Value.Defined()); + UNIT_ASSERT_VALUES_EQUAL(*it[2].Value, "1 + 2"); + } + + // check unnamed enum (no code should be generated for it) + { + const TEnum& e = enums[6]; + UNIT_ASSERT_VALUES_EQUAL(e.Scope.size(), 0u); + UNIT_ASSERT_VALUES_EQUAL(e.CppName, ""); + const TItems& it = e.Items; + UNIT_ASSERT_VALUES_EQUAL(it.size(), 3u); + } + + // TEXT_WEIGHT + { + const TEnum& e = enums[7]; + UNIT_ASSERT_VALUES_EQUAL(e.Scope.size(), 0u); + UNIT_ASSERT_VALUES_EQUAL(e.CppName, "TEXT_WEIGHT"); + const TItems& it = e.Items; + UNIT_ASSERT_VALUES_EQUAL(it.size(), 5u); + + UNIT_ASSERT_VALUES_EQUAL(it[0].CppName, "WEIGHT_ZERO"); + UNIT_ASSERT(it[0].Value.Defined()); + UNIT_ASSERT_VALUES_EQUAL(*it[0].Value, "-1"); + UNIT_ASSERT_VALUES_EQUAL(it[0].Aliases.size(), 0u); + + UNIT_ASSERT_VALUES_EQUAL(it[1].CppName, "WEIGHT_LOW"); + UNIT_ASSERT_VALUES_EQUAL(it[1].Aliases.size(), 0u); + UNIT_ASSERT(!it[1].Value.Defined()); + + UNIT_ASSERT_VALUES_EQUAL(it[2].CppName, "WEIGHT_NORMAL"); + UNIT_ASSERT_VALUES_EQUAL(it[2].Aliases.size(), 0u); + UNIT_ASSERT(!it[2].Value.Defined()); + } + + // EDuplicateKeys + { + const TEnum& e = enums[8]; + UNIT_ASSERT_VALUES_EQUAL(e.Scope.size(), 0u); + UNIT_ASSERT_VALUES_EQUAL(e.CppName, "EDuplicateKeys"); + const TItems& it = e.Items; + UNIT_ASSERT_VALUES_EQUAL(it.size(), 5u); + + UNIT_ASSERT_VALUES_EQUAL(it[0].CppName, "Key0"); + UNIT_ASSERT(it[0].Value.Defined()); + UNIT_ASSERT_VALUES_EQUAL(*it[0].Value, "0"); + UNIT_ASSERT_VALUES_EQUAL(it[0].Aliases.size(), 0u); + + UNIT_ASSERT_VALUES_EQUAL(it[1].CppName, "Key0Second"); + UNIT_ASSERT(it[1].Value.Defined()); + UNIT_ASSERT_VALUES_EQUAL(*it[1].Value, "Key0"); + UNIT_ASSERT_VALUES_EQUAL(it[1].Aliases.size(), 0u); + } + + // EEmpty + { + const TEnum& e = enums[10]; + const TItems& it = e.Items; + UNIT_ASSERT_VALUES_EQUAL(it.size(), 0u); + } + + // NComposite::NInner::EInCompositeNamespaceSimple + { + const TEnum& e = enums[11]; + UNIT_ASSERT_VALUES_EQUAL(e.Scope.size(), 1u); + UNIT_ASSERT_VALUES_EQUAL(e.Scope[0], "NComposite::NInner"); + UNIT_ASSERT_VALUES_EQUAL(e.CppName, "EInCompositeNamespaceSimple"); + const TItems& it = e.Items; + UNIT_ASSERT_VALUES_EQUAL(it.size(), 3u); + UNIT_ASSERT_VALUES_EQUAL(it[0].CppName, "one"); + UNIT_ASSERT_VALUES_EQUAL(*it[1].Value, "2") ; + } + + // NOuterSimple::NComposite::NMiddle::NInner::NInnerSimple::TEnumClass::EVeryDeep + { + const TEnum& e = enums[12]; + UNIT_ASSERT_VALUES_EQUAL(e.Scope.size(), 4u); + UNIT_ASSERT_VALUES_EQUAL(e.Scope[0], "NOuterSimple"); + UNIT_ASSERT_VALUES_EQUAL(e.Scope[1], "NComposite::NMiddle::NInner"); + UNIT_ASSERT_VALUES_EQUAL(e.Scope[2], "NInnerSimple"); + UNIT_ASSERT_VALUES_EQUAL(e.Scope[3], "TEnumClass"); + UNIT_ASSERT_VALUES_EQUAL(e.CppName, "EVeryDeep"); + const TItems& it = e.Items; + UNIT_ASSERT_VALUES_EQUAL(it.size(), 2u); + UNIT_ASSERT_VALUES_EQUAL(it[0].CppName, "Key0"); + UNIT_ASSERT_VALUES_EQUAL(it[1].CppName, "Key1"); + UNIT_ASSERT_VALUES_EQUAL(*it[1].Value, "1"); + } + + // ENonLiteralValues + { + const TEnum& e = enums[13]; + UNIT_ASSERT_VALUES_EQUAL(e.Scope.size(), 0u); + UNIT_ASSERT_VALUES_EQUAL(e.CppName, "ENonLiteralValues"); + const TItems& it = e.Items; + UNIT_ASSERT_VALUES_EQUAL(it.size(), 5u); + UNIT_ASSERT_VALUES_EQUAL(it[0].CppName, "one"); + UNIT_ASSERT_VALUES_EQUAL(*it[0].Value, "MACRO(1, 2)"); + UNIT_ASSERT_VALUES_EQUAL(it[1].CppName, "two"); + UNIT_ASSERT_VALUES_EQUAL(*it[1].Value, "2"); + UNIT_ASSERT_VALUES_EQUAL(it[2].CppName, "three"); + UNIT_ASSERT_VALUES_EQUAL(*it[2].Value, "func(3)"); + UNIT_ASSERT_VALUES_EQUAL(it[3].CppName, "four"); + UNIT_ASSERT_VALUES_EQUAL(it[3].Value.Defined(), false); + UNIT_ASSERT_VALUES_EQUAL(it[4].CppName, "five"); + UNIT_ASSERT_VALUES_EQUAL(it[4].Value, "MACRO(MACRO(1, 2), 2)"); + } + + // NotifyingStatus + { + const TEnum& e = enums[15]; + UNIT_ASSERT_VALUES_EQUAL(e.Scope.size(), 0u); + UNIT_ASSERT_VALUES_EQUAL(e.CppName, "NotifyingStatus"); + const TItems& it = e.Items; + UNIT_ASSERT_VALUES_EQUAL(it.size(), 4u); + UNIT_ASSERT_VALUES_EQUAL(it[0].CppName, "NEW"); + UNIT_ASSERT_VALUES_EQUAL(*it[0].Value, "0"); + UNIT_ASSERT_VALUES_EQUAL(it[1].CppName, "FAILED_WILL_RETRY"); + UNIT_ASSERT_VALUES_EQUAL(*it[1].Value, "1"); + UNIT_ASSERT_VALUES_EQUAL(it[2].CppName, "FAILED_NO_MORE_TRIALS"); + UNIT_ASSERT_VALUES_EQUAL(*it[2].Value, "2"); + UNIT_ASSERT_VALUES_EQUAL(it[3].CppName, "SENT"); + UNIT_ASSERT_VALUES_EQUAL(*it[3].Value, "3"); + } + } + + Y_UNIT_TEST(BadCodeParseTest) { + TString text = NResource::Find("/badcode"); + TMemoryInput input(text.data(), text.size()); + TEnumParser parser(input); + const TEnums& enums = parser.Enums; + + UNIT_ASSERT_VALUES_EQUAL(enums.size(), 1u); + + // check <anonymous namespace>::ETest correct parsing + { + const TEnum& e = enums[0]; + UNIT_ASSERT_VALUES_EQUAL(e.Scope.size(), 1u); + UNIT_ASSERT_VALUES_EQUAL(e.CppName, "ETest"); + const TItems& it = e.Items; + UNIT_ASSERT_VALUES_EQUAL(it.size(), 3u); + UNIT_ASSERT_VALUES_EQUAL(it[0].CppName, "Http"); + UNIT_ASSERT(it[0].Value.Defined()); + } + + } + + Y_UNIT_TEST(UnbalancedCodeParseTest) { + // Thanks gotmanov@ for providing this example + TString text = NResource::Find("/unbalanced"); + TMemoryInput input(text.data(), text.size()); + try { + TEnumParser parser(input); + UNIT_ASSERT(false); + } catch(...) { + UNIT_ASSERT(CurrentExceptionMessage().Contains("unbalanced scope. Did you miss a closing")); + } + } + + Y_UNIT_TEST(AliasBeforeNameTest) { + TString text = NResource::Find("/alias_before_name"); + TMemoryInput input(text.data(), text.size()); + try { + TEnumParser parser(input); + UNIT_ASSERT(false); + } catch(...) { + UNIT_ASSERT(CurrentExceptionMessage().Contains("https://clubs.at.yandex-team.ru/stackoverflow/2603")); + } + } +} diff --git a/tools/enum_parser/parse_enum/ut/alias_before_name.h b/tools/enum_parser/parse_enum/ut/alias_before_name.h new file mode 100644 index 0000000000..64015c1db6 --- /dev/null +++ b/tools/enum_parser/parse_enum/ut/alias_before_name.h @@ -0,0 +1,7 @@ +#pragma once + +// https://clubs.at.yandex-team.ru/stackoverflow/2603 bad example +enum EStrange { + One, /* "one" */ + Two, /* "two" */ +}; diff --git a/tools/enum_parser/parse_enum/ut/badcode.h b/tools/enum_parser/parse_enum/ut/badcode.h new file mode 100644 index 0000000000..88448c8eae --- /dev/null +++ b/tools/enum_parser/parse_enum/ut/badcode.h @@ -0,0 +1,10 @@ +#pragma once + +// Anonymous namespaces are meaningless, but should not break our parser +namespace { + enum ETest { + Http = 9 /* "http://" "secondary" "old\nvalue" */, + Https = 1 /* "https://" */, + ETestItemCount, + }; +} diff --git a/tools/enum_parser/parse_enum/ut/enums.cpp b/tools/enum_parser/parse_enum/ut/enums.cpp new file mode 100644 index 0000000000..a03045855e --- /dev/null +++ b/tools/enum_parser/parse_enum/ut/enums.cpp @@ -0,0 +1,195 @@ +#include "enums.h" +#include "enums_with_header.h" +#include <tools/enum_parser/parse_enum/ut/enums_with_header.h_serialized.h> + +#include "including_header.h" + +// just to test that generated stuff works +#include <util/generic/serialized_enum.h> +#include <library/cpp/testing/unittest/registar.h> + +#include <util/generic/ptr.h> +#include <util/generic/singleton.h> + + +void FunctionUsingEFwdEnum(EFwdEnum) { +} + +class TEnumSerializationInitializer { +public: + TEnumSerializationInitializer() { + UNIT_ASSERT_VALUES_EQUAL(ToString(EDestructionPriorityTest::first), "first"); + } + ~TEnumSerializationInitializer() { + UNIT_ASSERT_VALUES_EQUAL(ToString(EDestructionPriorityTest::second), "second"); + } +}; + +class TEnumSerializationInitializerHolder { +public: + TEnumSerializationInitializerHolder() { + } + + ~TEnumSerializationInitializerHolder() { + } + + void Init() { Ptr.Reset(new TEnumSerializationInitializer); } +private: + THolder<TEnumSerializationInitializer> Ptr; +}; + + +Y_UNIT_TEST_SUITE(TEnumGeneratorTest) { + + template<typename T> + void CheckToString(const T& value, const TString& strValue) { + UNIT_ASSERT_VALUES_EQUAL(ToString(value), strValue); + } + + Y_UNIT_TEST(ToStringTest) { + // ESimple + CheckToString(Http, "Http"); + CheckToString(Https, "Https"); + CheckToString(ItemCount, "ItemCount"); + + // ESimpleWithComma + CheckToString(ESimpleWithComma::Http, "Http"); + CheckToString(ESimpleWithComma::Https, "Https"); + CheckToString(ESimpleWithComma::Http2, "Http"); // Http2 is an alias for Http + CheckToString(ESimpleWithComma::ItemCount, "ItemCount"); + + // ECustomAliases + CheckToString(CAHttp, "http"); + CheckToString(CAHttps, "https"); + CheckToString(CAItemCount, "CAItemCount"); + + // EMultipleAliases + CheckToString(MAHttp, "http://"); + CheckToString(MAHttps, "https://"); + CheckToString(MAItemCount, "MAItemCount"); + + // EDuplicateKeys + CheckToString(Key0, "Key0"); + CheckToString(Key0Second, "Key0"); // obtain FIRST encountered value with such integer key + CheckToString(Key1, "Key1"); + CheckToString(Key2, "k2"); + CheckToString(Key3, "k2"); // we CANNOT obtain "k3" here (as Key3 == Key2) + } + + template<typename T> + void CheckFromString(const TString& strValue, const T& value) { + UNIT_ASSERT_VALUES_EQUAL(static_cast<int>(FromString<T>(TStringBuf(strValue))), static_cast<int>(value)); + } + + template<typename T> + void CheckFromStringFail(const TString& strValue) { + UNIT_ASSERT_EXCEPTION(FromString<T>(TStringBuf(strValue)), yexception); + } + + template<typename T> + void CheckTryFromString(const TString& strValue, const T& value) { + T x; + UNIT_ASSERT_VALUES_EQUAL(TryFromString(TStringBuf(strValue), x), true); + UNIT_ASSERT_VALUES_EQUAL(x, value); + } + + template<typename T> + void CheckTryFromStringFail(const TString& strValue) { + T x = T(-666); + UNIT_ASSERT_VALUES_EQUAL(TryFromString(TStringBuf(strValue), x), false); + UNIT_ASSERT_VALUES_EQUAL(int(x), -666); + } + + Y_UNIT_TEST(TryFromStringTest) { + // ESimple + CheckFromString("Http", Http); + CheckFromString("Https", Https); + CheckFromString("ItemCount", ItemCount); + CheckFromStringFail<ESimple>("ItemC0unt"); + + CheckTryFromString("Http", Http); + CheckTryFromString("Https", Https); + CheckTryFromString("ItemCount", ItemCount); + CheckTryFromStringFail<ESimple>("ItemC0unt"); + + // ESimpleWithComma + CheckTryFromString("Http", ESimpleWithComma::Http); + CheckTryFromString("Https", ESimpleWithComma::Https); + CheckTryFromString("ItemCount", ESimpleWithComma::ItemCount); + CheckTryFromStringFail<ESimpleWithComma>(""); + + // ECustomAliases + CheckTryFromString("http", CAHttp); + CheckTryFromString("https", CAHttps); + CheckTryFromString("CAItemCount", CAItemCount); + + // EDuplicateKeys + CheckTryFromString("Key0", Key0); + CheckTryFromString("Key0Second", Key0Second); + CheckTryFromString("Key1", Key1); + CheckTryFromString("k2", Key2); + CheckTryFromString("k2.1", Key2); + CheckTryFromString("k3", Key3); + } + + Y_UNIT_TEST(AllNamesValuesTest) { + { + auto allNames = GetEnumAllCppNames<EDuplicateKeys>(); + UNIT_ASSERT(!!allNames); + UNIT_ASSERT_VALUES_EQUAL(allNames.size(), 5u); + UNIT_ASSERT_VALUES_EQUAL(allNames[4], "Key3"); + } + { + auto allNames = GetEnumAllCppNames<ESimpleWithComma>(); + UNIT_ASSERT(!!allNames); + UNIT_ASSERT_VALUES_EQUAL(allNames.size(), 4u); + UNIT_ASSERT_VALUES_EQUAL(allNames[1], "ESimpleWithComma::Http2"); + } + } + + Y_UNIT_TEST(EnumWithHeaderTest) { + UNIT_ASSERT_VALUES_EQUAL(GetEnumItemsCount<EWithHeader>(), 3); + } + + Y_UNIT_TEST(AllNamesValuesWithHeaderTest) { + { + auto allNames = GetEnumAllCppNames<EWithHeader>(); + UNIT_ASSERT_VALUES_EQUAL(allNames.size(), 3u); + UNIT_ASSERT_VALUES_EQUAL(allNames.at(2), "HThree"); + } + { + UNIT_ASSERT_VALUES_EQUAL(GetEnumAllNames<EWithHeader>(), "'one', 'HTwo', 'HThree'"); + } + } + + Y_UNIT_TEST(AllValuesTest) { + const auto& allNames = GetEnumNames<EWithHeader>(); + const auto& allValues = GetEnumAllValues<EWithHeader>(); + UNIT_ASSERT_VALUES_EQUAL(allValues.size(), 3u); + UNIT_ASSERT_VALUES_EQUAL(allValues[2], HThree); + size_t size = 0; + for (const EWithHeader value : GetEnumAllValues<EWithHeader>()) { + size += 1; + UNIT_ASSERT_VALUES_EQUAL(allNames.contains(value), true); + } + UNIT_ASSERT_VALUES_EQUAL(size, 3u); + } + + Y_UNIT_TEST(EnumNamesTest) { + const auto& names = GetEnumNames<EWithHeader>(); + UNIT_ASSERT_VALUES_EQUAL(names.size(), 3u); + + UNIT_ASSERT(names.contains(HOne)); + UNIT_ASSERT_VALUES_EQUAL(names.at(HOne), "one"); + + UNIT_ASSERT(names.contains(HTwo)); + UNIT_ASSERT_VALUES_EQUAL(names.at(HTwo), "HTwo"); + + UNIT_ASSERT(names.contains(HThree)); + UNIT_ASSERT_VALUES_EQUAL(names.at(HThree), "HThree"); + } + + Y_UNIT_TEST(EnumSerializerDestructionPriority) { + Singleton<TEnumSerializationInitializerHolder>()->Init(); + } +}; diff --git a/tools/enum_parser/parse_enum/ut/enums.h b/tools/enum_parser/parse_enum/ut/enums.h new file mode 100644 index 0000000000..93d835c78d --- /dev/null +++ b/tools/enum_parser/parse_enum/ut/enums.h @@ -0,0 +1,195 @@ +#pragma once +// Sample file for parse_enum unittests + +#include <util/generic/fwd.h> +#include <util/system/compiler.h> + +// Test template declarations +template<class T> +void Func(T&); + +template<> +void Func(struct ENonDeclared&); + +template<class TClass> class TFwdDecl; + +// Test in-function class declarations +void InexistentFunction(struct TFwdStructDecl); +void InexistentFunction2(struct TFwdStructDecl, class TMegaClass); + + +static inline void Func() { + class TLocal { + int M; + public: + void F() { + // to shut up clang + Y_UNUSED(M); + } + }; + + { + // unnamed block + } +} + +// Test forward declarations, pt 2 +namespace NTestContainer { + struct TStruct; +} + +// Enums +enum ESimple { + Http, + Https, + ItemCount +}; + +enum class ESimpleWithComma { + Http = 3, + Http2 = Http, + Https, // 4 + ItemCount, // 5 +}; + +enum ECustomAliases { + CAHttp = 3 /* "http" */, + CAHttps /* "https" */, + CAItemCount, +}; + +enum EMultipleAliases { + MAHttp = 9 /* "http://" "secondary" "old\nvalue" */, + MAHttps = 1 /* "https://" */, + MAItemCount, +}; + +namespace NEnumNamespace { + enum EInNamespace { + Http = 9 /* "http://" "secondary" "old\nvalue" */, + Https = 1 /* "https://" */, + ItemCount /* "real value" */, + }; +}; + +struct TStruct { + int M; +}; + +namespace NEnumNamespace { + class TEnumClass: public TStruct { + public: + enum EInClass { + Http = 9 /* "http://" "secondary" "old\nvalue" */, + Https1 = NEnumNamespace::Https /* "https://" */, + // holy crap, this will work too: + Https3 = 1 /* "https://" */ + 2, + }; + }; +} + +enum { + One, + Two, + Three, +}; + +struct { + int M; +} SomeStruct; + +static inline void f() { + (void)(SomeStruct); + (void)(f); +} + +// buggy case taken from library/cpp/html/face/parstypes.h +enum TEXT_WEIGHT { + WEIGHT_ZERO=-1,// NOINDEX_RELEV + WEIGHT_LOW, // LOW_RELEV + WEIGHT_NORMAL, // NORMAL_RELEV + WEIGHT_HIGH, // HIGH_RELEV (H1,H2,H3,ADDRESS,CAPTION) + WEIGHT_BEST // BEST_RELEV (TITLE) +}; + +// enum with duplicate keys +enum EDuplicateKeys { + Key0 = 0, + Key0Second = Key0, + Key1, + Key2 = 3 /* "k2" "k2.1" */, + Key3 = 3 /* "k3" */, +}; + +enum class EFwdEnum; +void FunctionUsingEFwdEnum(EFwdEnum); +enum class EFwdEnum { + One, + Two +}; + +// empty enum (bug found by sankear@) +enum EEmpty { +}; + +namespace NComposite::NInner { + enum EInCompositeNamespaceSimple { + one, + two = 2, + three, + }; +} + +namespace NOuterSimple { + namespace NComposite::NMiddle::NInner { + namespace NInnerSimple { + class TEnumClass { + public: + enum EVeryDeep { + Key0 = 0, + Key1 = 1, + }; + }; + } + } +} + + +constexpr int func(int value) { + return value; +} + +#define MACRO(x, y) x + +// enum with nonliteral values +enum ENonLiteralValues { + one = MACRO(1, 2), + two = 2, + three = func(3), + four, + five = MACRO(MACRO(1, 2), 2), +}; + +#undef MACRO + + +enum EDestructionPriorityTest { + first, + second +}; + + +enum class NotifyingStatus +{ + NEW = 0, + FAILED_WILL_RETRY = 1, + FAILED_NO_MORE_TRIALS = 2, + SENT = 3 +}; + +/* + * Still unsupported features: + * + * a) Anonymous namespaces (it is parsed correctly, though) + * b) Enums inside template classes (impossible by design) + **/ diff --git a/tools/enum_parser/parse_enum/ut/enums_with_header.h b/tools/enum_parser/parse_enum/ut/enums_with_header.h new file mode 100644 index 0000000000..26fe5565a9 --- /dev/null +++ b/tools/enum_parser/parse_enum/ut/enums_with_header.h @@ -0,0 +1,8 @@ +#pragma once + +enum EWithHeader { + HOne /* "one" */, + HTwo, + HThree, +}; + diff --git a/tools/enum_parser/parse_enum/ut/including_header.h b/tools/enum_parser/parse_enum/ut/including_header.h new file mode 100644 index 0000000000..b3b2a2129f --- /dev/null +++ b/tools/enum_parser/parse_enum/ut/including_header.h @@ -0,0 +1,9 @@ +#pragma once + +#include <util/generic/serialized_enum.h> +#include <tools/enum_parser/parse_enum/ut/enums_with_header.h_serialized.h> + +int TestEnumWithHeader() { + return GetEnumItemsCount<EWithHeader>(); +} + diff --git a/tools/enum_parser/parse_enum/ut/stringlist.cpp b/tools/enum_parser/parse_enum/ut/stringlist.cpp new file mode 100644 index 0000000000..f69d0fc08d --- /dev/null +++ b/tools/enum_parser/parse_enum/ut/stringlist.cpp @@ -0,0 +1 @@ +"qqqq\nqqqqqq", "test:\\string" diff --git a/tools/enum_parser/parse_enum/ut/unbalanced.h b/tools/enum_parser/parse_enum/ut/unbalanced.h new file mode 100644 index 0000000000..9caf54044c --- /dev/null +++ b/tools/enum_parser/parse_enum/ut/unbalanced.h @@ -0,0 +1,4 @@ +#pragma once + +} + diff --git a/tools/enum_parser/parse_enum/ut/ya.make b/tools/enum_parser/parse_enum/ut/ya.make new file mode 100644 index 0000000000..03ace866d4 --- /dev/null +++ b/tools/enum_parser/parse_enum/ut/ya.make @@ -0,0 +1,33 @@ +UNITTEST() + +OWNER( + g:util + mvel +) + +PEERDIR( + ADDINCL tools/enum_parser/parse_enum + library/cpp/resource +) + +SRCDIR(tools/enum_parser/parse_enum) + +RESOURCE( + enums.h /enums + badcode.h /badcode + unbalanced.h /unbalanced + alias_before_name.h /alias_before_name +) + +# self-test +GENERATE_ENUM_SERIALIZATION(enums.h) + +# test GENERATE_ENUM_SERIALIZATION_WITH_HEADER macro +GENERATE_ENUM_SERIALIZATION_WITH_HEADER(enums_with_header.h) + +SRCS( + parse_enum_ut.cpp + enums.cpp +) + +END() diff --git a/tools/enum_parser/parse_enum/ya.make b/tools/enum_parser/parse_enum/ya.make new file mode 100644 index 0000000000..b8d07c66d2 --- /dev/null +++ b/tools/enum_parser/parse_enum/ya.make @@ -0,0 +1,16 @@ +LIBRARY() + +OWNER( + g:util + mvel +) + +SRCS( + parse_enum.cpp +) + +PEERDIR( + library/cpp/cppparser +) + +END() diff --git a/tools/enum_parser/ya.make b/tools/enum_parser/ya.make new file mode 100644 index 0000000000..762b282b3e --- /dev/null +++ b/tools/enum_parser/ya.make @@ -0,0 +1,6 @@ +RECURSE( + parse_enum + parse_enum/ut + enum_parser + enum_serialization_runtime +) |