diff options
author | vitalyisaev <vitalyisaev@ydb.tech> | 2023-11-30 13:26:22 +0300 |
---|---|---|
committer | vitalyisaev <vitalyisaev@ydb.tech> | 2023-11-30 15:44:45 +0300 |
commit | 0a98fece5a9b54f16afeb3a94b3eb3105e9c3962 (patch) | |
tree | 291d72dbd7e9865399f668c84d11ed86fb190bbf /tools | |
parent | cb2c8d75065e5b3c47094067cb4aa407d4813298 (diff) | |
download | ydb-0a98fece5a9b54f16afeb3a94b3eb3105e9c3962.tar.gz |
YQ Connector:Use docker-compose in integrational tests
Diffstat (limited to 'tools')
-rw-r--r-- | tools/event2cpp/bin/ya.make | 21 | ||||
-rw-r--r-- | tools/event2cpp/proto_events.cpp | 893 | ||||
-rw-r--r-- | tools/event2cpp/proto_events.h | 20 | ||||
-rw-r--r-- | tools/event2cpp/ya.make | 13 | ||||
-rw-r--r-- | tools/struct2fieldcalc/parsestruct.rl | 669 | ||||
-rw-r--r-- | tools/struct2fieldcalc/ya.make | 16 |
6 files changed, 1632 insertions, 0 deletions
diff --git a/tools/event2cpp/bin/ya.make b/tools/event2cpp/bin/ya.make new file mode 100644 index 0000000000..c56449821b --- /dev/null +++ b/tools/event2cpp/bin/ya.make @@ -0,0 +1,21 @@ +SET(IDE_FOLDER "_Builders") + +PROGRAM(event2cpp) + +PEERDIR( + ADDINCL contrib/libs/protobuf + contrib/libs/protoc + library/cpp/eventlog/proto +) + +SRCDIR( + tools/event2cpp +) + +SRCS( + proto_events.cpp +) + +INCLUDE(${ARCADIA_ROOT}/build/prebuilt/tools/event2cpp/ya.make.induced_deps) + +END() diff --git a/tools/event2cpp/proto_events.cpp b/tools/event2cpp/proto_events.cpp new file mode 100644 index 0000000000..66e9296d2c --- /dev/null +++ b/tools/event2cpp/proto_events.cpp @@ -0,0 +1,893 @@ +#include <google/protobuf/compiler/cpp/cpp_helpers.h> +#include <google/protobuf/io/zero_copy_stream.h> +#include <google/protobuf/io/printer.h> +#include <google/protobuf/stubs/strutil.h> +#include <google/protobuf/stubs/common.h> +#include <google/protobuf/descriptor.h> +#include <google/protobuf/descriptor.pb.h> + +#include <util/string/cast.h> +#include <util/generic/singleton.h> +#include <util/generic/yexception.h> + +#include <library/cpp/eventlog/proto/events_extension.pb.h> + +#include "proto_events.h" + +namespace NProtoBuf::NCompiler::NPlugins { + +namespace NInternal { + using namespace google::protobuf; + using namespace google::protobuf::compiler; + using namespace google::protobuf::compiler::cpp; + + typedef std::map<TProtoStringType, TProtoStringType> TVariables; + + void CheckMessageId(size_t id, const TProtoStringType& name) { + typedef std::map<size_t, TProtoStringType> TMessageIds; + TMessageIds* ids = Singleton<TMessageIds>(); + TMessageIds::const_iterator it = ids->find(id); + + if (it != ids->end()) { + throw yexception() << "Duplicate message_id = " << id + << " in messages " << name + << " and " << it->second << Endl; + } + + (*ids)[id] = name; + } + + void SetCommonFieldVariables(const FieldDescriptor* descriptor, TVariables* variables) { + (*variables)["rname"] = descriptor->name(); + (*variables)["name"] = FieldName(descriptor); + } + + TProtoStringType HeaderFileName(const FileDescriptor* file) { + TProtoStringType basename = cpp::StripProto(file->name()); + + return basename.append(".pb.h"); + } + + TProtoStringType SourceFileName(const FileDescriptor* file) { + TProtoStringType basename = cpp::StripProto(file->name()); + + return basename.append(".pb.cc"); + } + + void GeneratePrintingCycle(TVariables vars, TProtoStringType printTemplate, io::Printer* printer) { + printer->Print("\n{\n"); + printer->Indent(); + printer->Print(vars, + "NProtoBuf::$repeated_field_type$< $type$ >::const_iterator b = $name$().begin();\n" + "NProtoBuf::$repeated_field_type$< $type$ >::const_iterator e = $name$().end();\n\n"); + printer->Print("output << \"[\";\n"); + printer->Print("if (b != e) {\n"); + vars["obj"] = "(*b++)"; + printer->Print(vars, printTemplate.c_str()); + printer->Print(";\n"); + printer->Print(vars, + "for (NProtoBuf::$repeated_field_type$< $type$ >::const_iterator it = b; it != e; ++it) {\n"); + printer->Indent(); + printer->Print("output << \",\";\n"); + vars["obj"] = "(*it)"; + printer->Print(vars, printTemplate.c_str()); + printer->Print(";\n"); + printer->Outdent(); + printer->Print("}\n}\n"); + printer->Print("output << \"]\";\n"); + printer->Outdent(); + printer->Print("}\n"); + } + + class TFieldExtGenerator { + public: + TFieldExtGenerator(const FieldDescriptor* field) + : Descriptor_(field) + { + SetCommonFieldVariables(Descriptor_, &Variables_); + } + + virtual ~TFieldExtGenerator() { + } + + virtual bool NeedProtobufMessageFieldPrinter() const { + return false; + } + + virtual void GenerateCtorArgument(io::Printer* printer) = 0; + virtual void GenerateInitializer(io::Printer* printer, const TString& prefix) = 0; + virtual void GeneratePrintingCode(io::Printer* printer) = 0; + protected: + const FieldDescriptor* Descriptor_; + TVariables Variables_; + }; + + class TMessageFieldExtGenerator: public TFieldExtGenerator { + public: + TMessageFieldExtGenerator(const FieldDescriptor* field) + : TFieldExtGenerator(field) + { + Variables_["type"] = ClassName(Descriptor_->message_type(), true); + Variables_["has_print_function"] = Descriptor_->message_type()->options().HasExtension(message_id) ? "true" : "false"; + } + + bool NeedProtobufMessageFieldPrinter() const override { + return true; + } + + void GenerateCtorArgument(io::Printer* printer) override { + printer->Print(Variables_, + "const $type$& arg_$name$"); + } + + void GenerateInitializer(io::Printer* printer, const TString& prefix) override { + Variables_["prefix"] = prefix; + printer->Print(Variables_, + "$prefix$mutable_$name$()->CopyFrom(arg_$name$);\n"); + } + + void GeneratePrintingCode(io::Printer* printer) override { + printer->Print("output << \"{\";\n"); + printer->Print(Variables_, + "protobufMessageFieldPrinter.PrintProtobufMessageFieldToOutput<$type$, $has_print_function$>($name$(), escapedOutput);\n"); + printer->Print("output << \"}\";\n"); + } + }; + + class TMapFieldExtGenerator: public TFieldExtGenerator { + public: + TMapFieldExtGenerator(const FieldDescriptor* field) + : TFieldExtGenerator(field) + { + } + + void GenerateCtorArgument(io::Printer* /* printer */) override { + } + + void GenerateInitializer(io::Printer* /* printer */, const TString& /* prefix */) override { + } + + void GeneratePrintingCode(io::Printer* /* printer */) override { + } + }; + + class TRepeatedMessageFieldExtGenerator: public TFieldExtGenerator { + public: + TRepeatedMessageFieldExtGenerator(const FieldDescriptor* field) + : TFieldExtGenerator(field) + { + Variables_["type"] = ClassName(Descriptor_->message_type(), true); + Variables_["repeated_field_type"] = "RepeatedPtrField"; + Variables_["has_print_function"] = Descriptor_->message_type()->options().HasExtension(message_id) ? "true" : "false"; + } + + bool NeedProtobufMessageFieldPrinter() const override { + return true; + } + + void GenerateCtorArgument(io::Printer* printer) override { + printer->Print(Variables_, + "const $type$& arg_$name$"); + } + + void GenerateInitializer(io::Printer* printer, const TString& prefix) override { + Variables_["prefix"] = prefix; + printer->Print(Variables_, + "$prefix$add_$name$()->CopyFrom(arg_$name$);\n"); + } + void GeneratePrintingCode(io::Printer* printer) override { + GeneratePrintingCycle(Variables_, "protobufMessageFieldPrinter.PrintProtobufMessageFieldToOutput<$type$, $has_print_function$>($obj$, escapedOutput)", printer); + } + }; + + class TStringFieldExtGenerator: public TFieldExtGenerator { + public: + TStringFieldExtGenerator(const FieldDescriptor* field) + : TFieldExtGenerator(field) + { + Variables_["pointer_type"] = Descriptor_->type() == FieldDescriptor::TYPE_BYTES ? "void" : "char"; + Variables_["type"] = "TProtoStringType"; + } + + void GenerateCtorArgument(io::Printer* printer) override { + printer->Print(Variables_, + (Descriptor_->type() == FieldDescriptor::TYPE_BYTES ? + "const $pointer_type$* arg_$name$, size_t arg_$name$_size" : "const $type$& arg_$name$") + ); + } + + void GenerateInitializer(io::Printer* printer, const TString& prefix) override { + Variables_["prefix"] = prefix; + printer->Print( + Variables_, + Descriptor_->type() == FieldDescriptor::TYPE_BYTES ? + "$prefix$set_$name$(arg_$name$, arg_$name$_size);\n" : + "$prefix$set_$name$(arg_$name$);\n" + ); + } + + void GeneratePrintingCode(io::Printer* printer) override { + Repr::ReprType fmt = Repr::none; + + if (Descriptor_->options().HasExtension(repr)) { + fmt = Descriptor_->options().GetExtension(repr); + } + + switch (fmt) { + case Repr::as_base64: + printer->Print(Variables_, "NProtoBuf::PrintAsBase64($name$(), output);\n"); + break; + + case Repr::none: + /* TODO: proper error handling?*/ + default: + printer->Print(Variables_, "escapedOutput << $name$();\n"); + break; + } + } + }; + + class TRepeatedStringFieldExtGenerator: public TFieldExtGenerator { + public: + TRepeatedStringFieldExtGenerator(const FieldDescriptor* field) + : TFieldExtGenerator(field) + { + Variables_["pointer_type"] = Descriptor_->type() == FieldDescriptor::TYPE_BYTES ? "void" : "char"; + Variables_["type"] = "TProtoStringType"; + Variables_["repeated_field_type"] = "RepeatedPtrField"; + } + + void GenerateCtorArgument(io::Printer* printer) override { + printer->Print(Variables_, + (Descriptor_->type() == FieldDescriptor::TYPE_BYTES ? + "const $pointer_type$* arg_$name$, size_t arg_$name$_size": "const $type$& arg_$name$") + ); + } + + void GenerateInitializer(io::Printer* printer, const TString& prefix) override { + Variables_["prefix"] = prefix; + printer->Print( + Variables_, + Descriptor_->type() == FieldDescriptor::TYPE_BYTES ? + "$prefix$add_$name$(arg_$name$, arg_$name$_size);\n" : + "$prefix$add_$name$(arg_$name$);\n" + ); + } + void GeneratePrintingCode(io::Printer* printer) override { + GeneratePrintingCycle(Variables_, "output << \"\\\"\" << $obj$ << \"\\\"\"", printer); + } + }; + + class TEnumFieldExtGenerator: public TFieldExtGenerator { + public: + TEnumFieldExtGenerator(const FieldDescriptor* field) + : TFieldExtGenerator(field) + { + Variables_["type"] = ClassName(Descriptor_->enum_type(), true); + } + + void GenerateCtorArgument(io::Printer* printer) override { + printer->Print(Variables_, + "$type$ arg_$name$"); + } + + void GenerateInitializer(io::Printer* printer, const TString& prefix) override { + Variables_["prefix"] = prefix; + printer->Print(Variables_, + "$prefix$set_$name$(arg_$name$);\n"); + } + + void GeneratePrintingCode(io::Printer* printer) override { + printer->Print(Variables_, + "output << $type$_Name($name$());\n"); + } + }; + + class TRepeatedEnumFieldExtGenerator: public TFieldExtGenerator { + public: + TRepeatedEnumFieldExtGenerator(const FieldDescriptor* field) + : TFieldExtGenerator(field) + { + Variables_["type"] = ClassName(Descriptor_->enum_type(), true); + Variables_["repeated_field_type"] = "RepeatedField"; + } + + void GenerateCtorArgument(io::Printer* printer) override { + printer->Print(Variables_, + "$type$ arg_$name$"); + } + + void GenerateInitializer(io::Printer* printer, const TString& prefix) override { + Variables_["prefix"] = prefix; + printer->Print(Variables_, + "$prefix$add_$name$(arg_$name$);\n"); + } + + void GeneratePrintingCode(io::Printer* printer) override { + TStringStream pattern; + + TProtoStringType type = Variables_["type"]; + pattern << "output << " << type << "_Name(" << type << "($obj$))"; + Variables_["type"] = "int"; + GeneratePrintingCycle(Variables_, pattern.Str(), printer); + Variables_["type"] = type; + } + }; + + class TPrimitiveFieldExtGenerator: public TFieldExtGenerator { + public: + TPrimitiveFieldExtGenerator(const FieldDescriptor* field) + : TFieldExtGenerator(field) + { + Variables_["type"] = PrimitiveTypeName(Descriptor_->cpp_type()); + } + + void GenerateCtorArgument(io::Printer* printer) override { + printer->Print(Variables_, + "$type$ arg_$name$"); + } + + void GenerateInitializer(io::Printer* printer, const TString& prefix) override { + Variables_["prefix"] = prefix; + printer->Print(Variables_, + "$prefix$set_$name$(arg_$name$);\n"); + } + + void GeneratePrintingCode(io::Printer* printer) override { + Repr::ReprType fmt = Repr::none; + + if (Descriptor_->options().HasExtension(repr)) { + fmt = Descriptor_->options().GetExtension(repr); + } + + switch (fmt) { + case Repr::as_bytes: + printer->Print(Variables_, "NProtoBuf::PrintAsBytes($name$(), output);\n"); + break; + + case Repr::as_hex: + printer->Print(Variables_, "NProtoBuf::PrintAsHex($name$(), output);\n"); + break; + + case Repr::none: + /* TODO: proper error handling? */ + default: + printer->Print(Variables_, "output << $name$();\n"); + break; + } + } + }; + + class TRepeatedPrimitiveFieldExtGenerator: public TFieldExtGenerator { + public: + TRepeatedPrimitiveFieldExtGenerator(const FieldDescriptor* field) + : TFieldExtGenerator(field) + { + Variables_["type"] = PrimitiveTypeName(Descriptor_->cpp_type()); + Variables_["repeated_field_type"] = "RepeatedField"; + } + + void GenerateCtorArgument(io::Printer* printer) override { + printer->Print(Variables_, + "$type$ arg_$name$"); + } + + void GenerateInitializer(io::Printer* printer, const TString& prefix) override { + Variables_["prefix"] = prefix; + printer->Print(Variables_, + "$prefix$add_$name$(arg_$name$);\n"); + } + + void GeneratePrintingCode(io::Printer* printer) override { + GeneratePrintingCycle(Variables_, "output << $obj$", printer); + } + }; + + std::unique_ptr<TFieldExtGenerator> MakeGenerator(const FieldDescriptor* field) { + if (field->is_map()) { + return std::make_unique<TMapFieldExtGenerator>(field); + } else if (field->is_repeated()) { + switch (field->cpp_type()) { + case FieldDescriptor::CPPTYPE_MESSAGE: + return std::make_unique<TRepeatedMessageFieldExtGenerator>(field); + case FieldDescriptor::CPPTYPE_STRING: + switch (field->options().ctype()) { + default: // RepeatedStringFieldExtGenerator handles unknown ctypes. + case FieldOptions::STRING: + return std::make_unique<TRepeatedStringFieldExtGenerator>(field); + } + case FieldDescriptor::CPPTYPE_ENUM: + return std::make_unique<TRepeatedEnumFieldExtGenerator>(field); + default: + return std::make_unique<TRepeatedPrimitiveFieldExtGenerator>(field); + } + } else { + switch (field->cpp_type()) { + case FieldDescriptor::CPPTYPE_MESSAGE: + return std::make_unique<TMessageFieldExtGenerator>(field); + case FieldDescriptor::CPPTYPE_STRING: + switch (field->options().ctype()) { + default: // StringFieldGenerator handles unknown ctypes. + case FieldOptions::STRING: + return std::make_unique<TStringFieldExtGenerator>(field); + } + case FieldDescriptor::CPPTYPE_ENUM: + return std::make_unique<TEnumFieldExtGenerator>(field); + default: + return std::make_unique<TPrimitiveFieldExtGenerator>(field); + } + } + } + + class TMessageExtGenerator { + public: + TMessageExtGenerator(const Descriptor* descriptor, OutputDirectory* outputDirectory) + : Descriptor_(descriptor) + , HasMessageId_(Descriptor_->options().HasExtension(message_id)) + , ClassName_(ClassName(Descriptor_, false)) + , OutputDirectory_(outputDirectory) + , HasGeneratorWithProtobufMessageFieldPrinter_(false) + , CanGenerateSpecialConstructor_(false) + { + NestedGenerators_.reserve(descriptor->nested_type_count()); + for (int i = 0; i < descriptor->nested_type_count(); i++) { + NestedGenerators_.emplace_back(descriptor->nested_type(i), OutputDirectory_); + } + + if (HasMessageId_) { + FieldGenerators_.reserve(descriptor->field_count()); + for (int i = 0; i < descriptor->field_count(); i++) { + FieldGenerators_.emplace_back(MakeGenerator(descriptor->field(i))); + HasGeneratorWithProtobufMessageFieldPrinter_ |= FieldGenerators_.back()->NeedProtobufMessageFieldPrinter(); + } + } + + { + size_t intFieldCount = 0; + size_t mapFieldCount = 0; + size_t nonMapFieldCount = 0; + for (int i = 0; i < Descriptor_->field_count(); ++i) { + const FieldDescriptor* field = Descriptor_->field(i); + if (field->is_map()) { + ++mapFieldCount; + } else { + ++nonMapFieldCount; + } + switch (field->cpp_type()) { + case FieldDescriptor::CPPTYPE_INT32: + case FieldDescriptor::CPPTYPE_INT64: + case FieldDescriptor::CPPTYPE_UINT32: + case FieldDescriptor::CPPTYPE_UINT64: + ++intFieldCount; + break; + default: + break; + } + } + + CanGenerateSpecialConstructor_ = ( + // Certain field combinations would result in ambiguous constructor generation. + // Do not generate special contructor for such combinations. + (intFieldCount != nonMapFieldCount || nonMapFieldCount != 2) && + + // Generate special contructor only if there is at least one non-map Field. + nonMapFieldCount > 0 + ); + } + + } + + void GenerateClassDefinitionExtension() { + if (Descriptor_->options().HasExtension(realm_name) || Descriptor_->options().HasExtension(message_id)) { + GeneratePseudonim(); + } + + if (!HasMessageId_) { + return; + } + + CheckMessageId(Descriptor_->options().GetExtension(message_id), ClassName_); + + TProtoStringType fileName = HeaderFileName(Descriptor_->file()); + TProtoStringType scope = "class_scope:" + Descriptor_->full_name(); + std::unique_ptr<io::ZeroCopyOutputStream> output( + OutputDirectory_->OpenForInsert(fileName, scope)); + io::Printer printer(output.get(), '$'); + + printer.Print("//Yandex events extension.\n"); + GenerateHeaderImpl(&printer); + + for (auto& nestedGenerator: NestedGenerators_) { + nestedGenerator.GenerateClassDefinitionExtension(); + } + } + + bool GenerateClassExtension() { + TProtoStringType fileName = SourceFileName(Descriptor_->file()); + std::unique_ptr<io::ZeroCopyOutputStream> output( + OutputDirectory_->OpenForInsert(fileName, "namespace_scope")); + io::Printer printer(output.get(), '$'); + + bool hasEventExtension = GenerateSourceImpl(&printer); + + for (auto& nestedGenerator: NestedGenerators_) { + hasEventExtension |= nestedGenerator.GenerateSourceImpl(&printer); + } + + return hasEventExtension; + } + + void GenerateRegistration(io::Printer* printer) { + if (!HasMessageId_) { + return; + } + + TVariables vars; + vars["classname"] = ClassName_; + + printer->Print(vars, "NProtoBuf::TEventFactory::Instance()->RegisterEvent($classname$::descriptor()->options().GetExtension(message_id), factory->GetPrototype($classname$::descriptor()), $classname$::Print);\n"); + } + + private: + void GenerateHeaderImpl(io::Printer* printer) { + TVariables vars; + TProtoStringType mId(ToString(Descriptor_->options().GetExtension(message_id))); + vars["classname"] = ClassName_; + vars["messageid"] = mId.data(); + vars["superclass"] = SuperClassName(Descriptor_, Options{}); + + printer->Print(vars, + "enum {ID = $messageid$};\n\n"); + + { + /* + * Unconditionally generate FromFields() factory method, + * so it could be used in template code + */ + printer->Print(vars, "static $classname$ FromFields(\n"); + GenerateCtorArgs(printer); + printer->Print(");\n"); + } + + if (CanGenerateSpecialConstructor_) { + printer->Print(vars, "$classname$(\n"); + GenerateCtorArgs(printer); + printer->Print(");\n"); + } + + { + printer->Print("void Print(IOutputStream& output, EFieldOutputFlags outputFlags = {}) const;\n"); + printer->Print("static void Print(const google::protobuf::Message* ev, IOutputStream& output, EFieldOutputFlags outputFlags = {});\n"); + } + } + + void GeneratePseudonim() { + TProtoStringType fileName = HeaderFileName(Descriptor_->file()); + std::unique_ptr<io::ZeroCopyOutputStream> output( + OutputDirectory_->OpenForInsert(fileName, "namespace_scope")); + io::Printer printer(output.get(), '$'); + + std::vector<TProtoStringType> realm_parts; + + if (Descriptor_->options().HasExtension(realm_name)) { + SplitStringUsing(Descriptor_->options().GetExtension(realm_name), ".", &realm_parts); + } + + if (realm_parts.size() > 0) printer.Print("\n"); + + for (size_t i = 0; i < realm_parts.size(); ++i) { + printer.Print("namespace $part$ {\n", + "part", realm_parts[i]); + } + + printer.Print("typedef $fullclassname$ T$classname$;\n", + "fullclassname", FullClassName(Descriptor_), + "classname", ClassName_); + + for (size_t i = realm_parts.size(); i > 0; --i) { + printer.Print("} // namespace $part$\n", + "part", realm_parts[i - 1]); + } + } + + TProtoStringType FullClassName(const Descriptor* descriptor) { + TProtoStringType result; + std::vector<TProtoStringType> parts; + + SplitStringUsing(descriptor->file()->package(), ".", &parts); + for (size_t i = 0; i < parts.size(); ++i) { + result += "::" + parts[i]; + } + + result += "::" + ClassName(descriptor, false); + + return result; + } + + bool GenerateSourceImpl(io::Printer* printer) { + if (!HasMessageId_) { + return false; + } + + TVariables vars; + vars["classname"] = ClassName_; + + { + // Generate static $classname$::FromFields impl. + printer->Print(vars, "$classname$ $classname$::FromFields(\n"); + GenerateCtorArgs(printer); + printer->Print(")\n"); + + printer->Print("{\n"); + + printer->Indent(); + printer->Print(vars, "$classname$ result;\n"); + GenerateFieldInitializers(printer, /* prefix = */ "result."); + printer->Print("return result;\n"); + printer->Outdent(); + + printer->Print("}\n\n"); + } + + if (CanGenerateSpecialConstructor_) { + // Generate special constructor impl. + printer->Print(vars, "$classname$::$classname$(\n"); + GenerateCtorArgs(printer); + printer->Print(")\n"); + + printer->Print("{\n"); + + printer->Indent(); + printer->Print("SharedCtor();\n"); + GenerateFieldInitializers(printer, /* prefix = */ ""); + printer->Outdent(); + + printer->Print("}\n\n"); + } + + { + // Generate $classname$::Print impl. + const size_t fieldCount = Descriptor_->field_count(); + if (fieldCount > 0) { + printer->Print(vars, + "void $classname$::Print(IOutputStream& output, EFieldOutputFlags outputFlags) const {\n"); + printer->Indent(); + printer->Print( + "TEventFieldOutput escapedOutput{output, outputFlags};\n" + "Y_UNUSED(escapedOutput);\n"); + + if (HasGeneratorWithProtobufMessageFieldPrinter_) { + printer->Print( + "TEventProtobufMessageFieldPrinter protobufMessageFieldPrinter(EProtobufMessageFieldPrintMode::DEFAULT);\n"); + } + } else { + printer->Print(vars, + "void $classname$::Print(IOutputStream& output, EFieldOutputFlags) const {\n"); + printer->Indent(); + } + + printer->Print(vars, + "output << \"$classname$\";\n"); + + for (size_t i = 0; i < fieldCount; ++i) { + printer->Print("output << \"\\t\";\n"); + FieldGenerators_[i]->GeneratePrintingCode(printer); + } + + printer->Outdent(); + printer->Print("}\n\n"); + } + + { + // Generate static $classname$::Print impl. + printer->Print(vars, + "void $classname$::Print(const google::protobuf::Message* ev, IOutputStream& output, EFieldOutputFlags outputFlags) {\n"); + printer->Indent(); + printer->Print(vars, + "const $classname$* This(CheckedCast<const $classname$*>(ev));\n"); + printer->Print( + "This->Print(output, outputFlags);\n"); + printer->Outdent(); + printer->Print("}\n\n"); + } + + return true; + } + + void GenerateCtorArgs(io::Printer* printer) { + printer->Indent(); + const size_t fieldCount = Descriptor_->field_count(); + bool isFirst = true; + for (size_t i = 0; i < fieldCount; ++i) { + if (Descriptor_->field(i)->is_map()) { + continue; + } + const char* delimiter = isFirst ? "" : ", "; + isFirst = false; + printer->Print(delimiter); + FieldGenerators_[i]->GenerateCtorArgument(printer); + } + printer->Outdent(); + } + + void GenerateFieldInitializers(io::Printer* printer, const TString& prefix) { + for (auto& fieldGeneratorHolder: FieldGenerators_) { + fieldGeneratorHolder->GenerateInitializer(printer, prefix); + } + } + + private: + const Descriptor* Descriptor_; + const bool HasMessageId_; + TProtoStringType ClassName_; + OutputDirectory* OutputDirectory_; + bool HasGeneratorWithProtobufMessageFieldPrinter_; + bool CanGenerateSpecialConstructor_; + std::vector<std::unique_ptr<TFieldExtGenerator>> FieldGenerators_; + std::vector<TMessageExtGenerator> NestedGenerators_; + }; + + class TFileExtGenerator { + public: + TFileExtGenerator(const FileDescriptor* file, OutputDirectory* output_directory) + : OutputDirectory_(output_directory) + , File_(file) + { + MessageGenerators_.reserve(file->message_type_count()); + for (int i = 0; i < file->message_type_count(); i++) { + MessageGenerators_.emplace_back(file->message_type(i), OutputDirectory_); + } + } + + void GenerateHeaderExtensions() { + TProtoStringType fileName = HeaderFileName(File_); + + std::unique_ptr<io::ZeroCopyOutputStream> output( + OutputDirectory_->OpenForInsert(fileName, "includes")); + io::Printer printer(output.get(), '$'); + + printer.Print("#include <library/cpp/eventlog/event_field_output.h>\n"); + printer.Print("#include <library/cpp/eventlog/event_field_printer.h>\n"); + + for (auto& messageGenerator: MessageGenerators_) { + messageGenerator.GenerateClassDefinitionExtension(); + } + } + + void GenerateSourceExtensions() { + TProtoStringType fileName = SourceFileName(File_); + + std::unique_ptr<io::ZeroCopyOutputStream> output( + OutputDirectory_->OpenForInsert(fileName, "includes")); + io::Printer printer(output.get(), '$'); + printer.Print("#include <google/protobuf/io/printer.h>\n"); + printer.Print("#include <google/protobuf/io/zero_copy_stream_impl_lite.h>\n"); + printer.Print("#include <google/protobuf/stubs/strutil.h>\n"); + printer.Print("#include <library/cpp/eventlog/events_extension.h>\n"); + printer.Print("#include <util/generic/cast.h>\n"); + printer.Print("#include <util/stream/output.h>\n"); + + bool hasEventExtension = false; + + for (auto& messageGenerator: MessageGenerators_) { + hasEventExtension |= messageGenerator.GenerateClassExtension(); + } + + if (hasEventExtension) { + GenerateEventRegistrations(); + } + } + + void GenerateEventRegistrations() { + TVariables vars; + TProtoStringType fileId = FilenameIdentifier(File_->name()); + vars["regfunction"] = "regevent_" + fileId; + vars["regclassname"] = "TRegister_" + fileId; + vars["regvarname"] = "registrator_" + fileId ; + vars["filename"] = File_->name(); + + { + TProtoStringType fileName = SourceFileName(File_); + std::unique_ptr<io::ZeroCopyOutputStream> output( + OutputDirectory_->OpenForInsert(fileName, "namespace_scope")); + io::Printer printer(output.get(), '$'); + + GenerateRegistrationFunction(vars, printer); + GenerateRegistratorDefinition(vars, printer); + } + + { + + TProtoStringType fileName = HeaderFileName(File_); + std::unique_ptr<io::ZeroCopyOutputStream> output( + OutputDirectory_->OpenForInsert(fileName, "namespace_scope")); + io::Printer printer(output.get(), '$'); + GenerateRegistratorDeclaration(vars, printer); + } + } + + void GenerateRegistrationFunction(const TVariables& vars, io::Printer& printer) { + printer.Print(vars, + "void $regfunction$() {\n"); + printer.Indent(); + + printer.Print("google::protobuf::MessageFactory* factory = google::protobuf::MessageFactory::generated_factory();\n\n"); + for (auto& messageGenerator: MessageGenerators_) { + messageGenerator.GenerateRegistration(&printer); + } + printer.Outdent(); + printer.Print("}\n\n"); + } + + void GenerateRegistratorDeclaration(const TVariables& vars, io::Printer& printer) { + printer.Print(vars, "\nclass $regclassname$ {\n"); + printer.Print("public:\n"); + printer.Indent(); + printer.Print(vars, "$regclassname$();\n"); + printer.Outdent(); + printer.Print("private:\n"); + printer.Indent(); + printer.Print("static bool Registered;\n"); + printer.Outdent(); + printer.Print(vars, "};\n"); + printer.Print(vars, "static $regclassname$ $regvarname$;\n\n"); + } + + void GenerateRegistratorDefinition(const TVariables& vars, io::Printer& printer) { + printer.Print(vars, "$regclassname$::$regclassname$() {\n"); + printer.Indent(); + printer.Print("if (!Registered) {\n"); + printer.Indent(); + printer.Print(vars, "NProtoBuf::TEventFactory::Instance()->ScheduleRegistration(&$regfunction$);\n"); + printer.Print("Registered = true;\n"); + printer.Outdent(); + printer.Print("}\n"); + printer.Outdent(); + printer.Print("}\n\n"); + printer.Print(vars, "bool $regclassname$::Registered;\n\n"); + } + private: + OutputDirectory* OutputDirectory_; + const FileDescriptor* File_; + std::vector<TMessageExtGenerator> MessageGenerators_; + }; +} + + bool TProtoEventExtensionGenerator::Generate(const google::protobuf::FileDescriptor* file, + const TProtoStringType& parameter, + google::protobuf::compiler::OutputDirectory* outputDirectory, + TProtoStringType* error) const { + Y_UNUSED(parameter); + Y_UNUSED(error); + + NInternal::TFileExtGenerator fileGenerator(file, outputDirectory); + + // Generate header. + fileGenerator.GenerateHeaderExtensions(); + + // Generate cc file. + fileGenerator.GenerateSourceExtensions(); + + return true; + } + +} // namespace NProtoBuf::NCompiler::NPlugins + +int main(int argc, char* argv[]) { +#ifdef _MSC_VER + // Don't print a silly message or stick a modal dialog box in my face, + // please. + _set_abort_behavior(0u, ~0u); +#endif // !_MSC_VER + + try { + NProtoBuf::NCompiler::NPlugins::TProtoEventExtensionGenerator generator; + return google::protobuf::compiler::PluginMain(argc, argv, &generator); + } catch (yexception& e) { + Cerr << e.what() << Endl; + } catch (...) { + Cerr << "Unknown error in TProtoEventExtensionGenerator" << Endl; + } + + return 1; +} diff --git a/tools/event2cpp/proto_events.h b/tools/event2cpp/proto_events.h new file mode 100644 index 0000000000..628b4856af --- /dev/null +++ b/tools/event2cpp/proto_events.h @@ -0,0 +1,20 @@ +#pragma once + +#include <google/protobuf/compiler/plugin.h> +#include <google/protobuf/compiler/code_generator.h> +#include <google/protobuf/stubs/common.h> + +namespace NProtoBuf::NCompiler::NPlugins { + +class TProtoEventExtensionGenerator : public google::protobuf::compiler::CodeGenerator { + public: + TProtoEventExtensionGenerator() {} + ~TProtoEventExtensionGenerator() override {} + + bool Generate(const google::protobuf::FileDescriptor* file, + const TProtoStringType& parameter, + google::protobuf::compiler::OutputDirectory* output_directory, + TProtoStringType* error) const override; +}; + +} // namespace NProtoBuf::NCompiler::NPlugins diff --git a/tools/event2cpp/ya.make b/tools/event2cpp/ya.make new file mode 100644 index 0000000000..9a89909346 --- /dev/null +++ b/tools/event2cpp/ya.make @@ -0,0 +1,13 @@ +SET(IDE_FOLDER "_Builders") + +IF (USE_PREBUILT_TOOLS) + INCLUDE(${ARCADIA_ROOT}/build/prebuilt/tools/event2cpp/ya.make.prebuilt) +ENDIF() + +IF (NOT PREBUILT) + INCLUDE(${ARCADIA_ROOT}/tools/event2cpp/bin/ya.make) +ENDIF() + +RECURSE( + bin +) diff --git a/tools/struct2fieldcalc/parsestruct.rl b/tools/struct2fieldcalc/parsestruct.rl new file mode 100644 index 0000000000..eec9022a40 --- /dev/null +++ b/tools/struct2fieldcalc/parsestruct.rl @@ -0,0 +1,669 @@ +#include <library/cpp/getopt/small/opt2.h> + +#include <library/cpp/deprecated/fgood/fgood.h> +#include <util/generic/hash.h> +#include <util/string/split.h> +#include <util/string/printf.h> + +#include <map> +#include <set> +#include <stdarg.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <vector> + +#include <fcntl.h> + +// For ragel +static char *tokstart, *tokend; +static int act, cs; + +static int curly_level = 0; +static int wanted_curly_level = -1; +static int wanted_curly_level2 = -1; // for nested anonymous union / struct + +static char *dmp_name_start = 0; +static char *dmp_name_end = 0; +static char *dmp_from_start = 0; +static char *dmp_from_end = 0; +static char *dmp_to_start = 0; +static char *dmp_to_end = 0; +static char *dmp_mode_start = 0; + +// Parser state +static TString current_struct; +static TString current_enum; +static TString last_expression; + +int line; +int last_entry_line; +TString current_file; + +// List of all emitted callbacks +typedef std::vector<std::pair<TString, int> > callback_list; +static callback_list getvals; +static callback_list prints; + +// Enum -> vector of enumerators name map +typedef std::vector<TString> enumerators_list; +typedef std::map<TString, enumerators_list> enums_map; +static enums_map enums; + +struct dump_items_list : public std::vector<std::pair<TString, TString> > { + std::set<TString> wanted_enums; + std::vector<TString> efunctions; +}; +typedef std::map<TString, dump_items_list> structs_map; +static structs_map structs; + +// Token types for parser +enum TokenType { + TT_IDENTIFIER, + TT_ENUM, + TT_STRUCT, + TT_UNION, + TT_CONST, + TT_PUBLIC, + TT_DECIMAL, + TT_COMMA, + TT_EQUALS, + TT_SEMICOLON, + TT_COLON, + TT_OPENSQUARE, + TT_CLOSESQUARE, + TT_OPENPAREN, + TT_CLOSEPAREN, + TT_OPENCURLY, + TT_CLOSECURLY, + TT_PIPE, + TT_COMPLEXTYPE, + + TT_OTHER, +}; + +// Structure for single token found by ragel +struct Token { + Token(int t, char *begin, int length): type(t), string(begin, length) {} + + bool check(int t) { + return t == type; + } + + int type; + TString string; +}; + +// Stack of currently accumilated tokens +typedef std::vector<Token> TokenStack; +TokenStack token_stack; + +// Parser state +static enum { + DEFAULT, + ENUM, + STRUCT, +} state = DEFAULT; + +void errexit(const char *fmt, ...) { + fprintf(stderr, "%s:%d: ", current_file.data(), line); + + va_list ap; + va_start(ap, fmt); + vfprintf(stderr, fmt, ap); + va_end(ap); + + fprintf(stderr, "\n"); + + exit(1); +} + +void emit_entry(const TString &name, bool nosave, const char *fmt, ...) { + char buffer[1024]; + + TString expression; + + va_list ap; + va_start(ap, fmt); + if (vsnprintf(buffer, sizeof(buffer) - 1, fmt, ap) >= (int)sizeof(buffer) - 1) + errexit("expression buffer overflow"); + va_end(ap); + + expression = buffer; + + if (!nosave) + last_expression = expression; + + last_entry_line = line; + structs[current_struct].push_back(std::make_pair(name, expression)); +} + +void emit_message(const char *fmt, ...) { + char buffer[1024]; + + va_list ap; + va_start(ap, fmt); + if (vsnprintf(buffer, sizeof(buffer) - 1, fmt, ap) >= (int)sizeof(buffer) - 1) + errexit("expression buffer overflow"); + va_end(ap); + + structs[current_struct].push_back(std::make_pair(TString(), buffer)); +} + +// Checks token stack to match specific length and token type pattern +bool check_token_stack(int count, ...) { + int len = token_stack.size(); + if (len < count) + return false; + + va_list ap; + va_start(ap, count); + + for (int i = 0; i < count; i++) { + if (!token_stack[i+len-count].check(va_arg(ap, int))) { + va_end(ap); + return false; + } + } + + va_end(ap); + return true; +} + +// Test all available patterns when parsing enums +bool check_enum_patterns() { + int len = token_stack.size(); + + /* enumerator: IDENTIFIER = DECIMAL */ + if (check_token_stack(3, TT_IDENTIFIER, TT_EQUALS, TT_DECIMAL)) { + enums[current_enum].push_back(token_stack[len - 3].string); + return true; + } + + /* enumerator: IDENTIFIER = IDENTIFIER [|] IDENTIFIER [[|] IDENTIFIER ...]*/ + if (check_token_stack(4, TT_IDENTIFIER, TT_PIPE, TT_IDENTIFIER, TT_COMMA)) { + int i = token_stack.size() - 3; + for (; i > 0; i--) + if (!token_stack[i].check(TT_IDENTIFIER) && !token_stack[i].check(TT_PIPE)) + break; + if (i > 0 && token_stack[i].check(TT_EQUALS) && token_stack[i - 1].check(TT_IDENTIFIER)) { + enums[current_enum].push_back(token_stack[i - 1].string); + return true; + } + } + + return false; +} + +// Test all available patterns when parsing structs +bool check_struct_patterns() { + int len = token_stack.size(); + + /* field: IDENTIFIER [IDENTIFIER ...] IDENTIFIER */ + if (check_token_stack(3, TT_IDENTIFIER, TT_IDENTIFIER, TT_SEMICOLON)) { + emit_entry(token_stack[len - 2].string, false, "&((%s*)0)->%s", current_struct.data(), token_stack[len - 2].string.c_str()); + return true; + } + + /* bitfield: IDENTIFIER [IDENTIFIER ...] IDENTIFIER : DECIMAL ; */ + if (check_token_stack(5, TT_IDENTIFIER, TT_IDENTIFIER, TT_COLON, TT_DECIMAL, TT_SEMICOLON)) { + const TString &type = token_stack[len - 5].string; + const TString &name = token_stack[len - 4].string; + structs[current_struct].efunctions.push_back(Sprintf("%s Get%sFrom%s(const %s *ptr){ return ptr->%s; }", type.data(), name.data(), current_struct.data(), current_struct.data(), name.data())); + emit_entry(token_stack[len - 4].string, false, "dump_item((%s_ext_fn_t)Get%sFrom%s, \"%s\")", type.data(), name.data(), current_struct.data(), name.data()); + //errexit("bitfields currently unsupported"); + return true; + } + + /* array field: IDENTIFIER [IDENTIFIER ...] IDENTIFIER [ DECIMAL ] ; */ + if (check_token_stack(6, TT_IDENTIFIER, TT_IDENTIFIER, TT_OPENSQUARE, TT_DECIMAL, TT_CLOSESQUARE, TT_SEMICOLON)) { + emit_entry(token_stack[len - 5].string, false, "dump_item(&((%s*)0)->%s[0], %s)", current_struct.data(), token_stack[len - 5].string.c_str(), token_stack[len - 3].string.c_str()); + return true; + } + + /* array field: char [IDENTIFIER ...] IDENTIFIER [ IDENTIFIER */ + if (check_token_stack(4, TT_IDENTIFIER, TT_IDENTIFIER, TT_OPENSQUARE, TT_IDENTIFIER) && token_stack[len - 4].string == "char") { + emit_entry(token_stack[len - 3].string, false, "dump_item(&((%s*)0)->%s[0])", current_struct.data(), token_stack[len - 3].string.c_str()); + return true; + } + + /* simple function: IDENTIFIER [IDENTIFIER ...] IDENTIFIER ( ) const { */ + if (check_token_stack(6, TT_IDENTIFIER, TT_IDENTIFIER, TT_OPENPAREN, TT_CLOSEPAREN, TT_CONST, TT_OPENCURLY)) { + emit_entry(token_stack[len - 5].string, false, "(%s_fn_t)&%s::%s", token_stack[len-6].string.c_str(), current_struct.data(), token_stack[len - 5].string.c_str()); + return true; + } + + /* special function: COMPLEXTYPE IDENTIFIER ( ) const { */ + if (check_token_stack(6, TT_COMPLEXTYPE, TT_IDENTIFIER, TT_OPENPAREN, TT_CLOSEPAREN, TT_CONST, TT_OPENCURLY)) { + emit_entry(token_stack[len - 5].string, false, "(%s_fn_t)&%s::%s", /*token_stack[len-6].string.c_str()*/"str", current_struct.data(), token_stack[len - 5].string.c_str()); + return true; + } + + /* special function: COMPLEXTYPE IDENTIFIER (TString &buf, COMPLEXTYPE IDENTIFIER) const { */ + if (check_token_stack(12, TT_COMPLEXTYPE, TT_IDENTIFIER, TT_OPENPAREN, TT_IDENTIFIER, TT_OTHER, TT_IDENTIFIER, TT_COMMA, TT_COMPLEXTYPE, TT_IDENTIFIER, TT_CLOSEPAREN, TT_CONST, TT_SEMICOLON)) { + emit_entry(token_stack[len - 11].string, false, "(%s_fn_t)&%s::%s", /*token_stack[len-12].string.c_str()*/"strbuf_2", current_struct.data(), token_stack[len - 11].string.c_str()); + return true; + } + + return false; +} + +// Process single token +void process_token(int type) { + // Skip stuff with higher nesting level than we need + if (wanted_curly_level > 0 && curly_level > wanted_curly_level2) + return; + + // Add token to stack + token_stack.push_back(Token(type, tokstart, tokend - tokstart)); + int len = token_stack.size(); + + // Match end of token stack with patterns depending on mode + switch(state) { + case ENUM: + if (check_enum_patterns()) + token_stack.clear(); + return; + + case STRUCT: + if (check_struct_patterns()) + token_stack.clear(); + if (check_token_stack(2, TT_STRUCT, TT_OPENCURLY) || check_token_stack(2, TT_UNION, TT_OPENCURLY)) + wanted_curly_level2 = curly_level + 1; + return; + + default: // state == DEFAULT + { + // enum encountered + if (check_token_stack(3, TT_ENUM, TT_IDENTIFIER, TT_OPENCURLY)) { + state = ENUM; + current_enum = token_stack[len - 2].string; + current_struct = ""; + wanted_curly_level = wanted_curly_level2 = curly_level + 1; + token_stack.clear(); + return; + } + + // struct encountered + int id_pos = 0, base_pos = 0; + if (check_token_stack(3, TT_STRUCT, TT_IDENTIFIER, TT_OPENCURLY)) + id_pos = len - 2; + if (check_token_stack(5, TT_STRUCT, TT_IDENTIFIER, TT_COLON, TT_IDENTIFIER, TT_OPENCURLY)) + id_pos = len - 4, base_pos = len - 2; + if (check_token_stack(6, TT_STRUCT, TT_IDENTIFIER, TT_COLON, TT_PUBLIC, TT_IDENTIFIER, TT_OPENCURLY)) + id_pos = len - 5, base_pos = len - 2; + + if (id_pos) { + state = STRUCT; + current_enum = ""; + current_struct = token_stack[id_pos].string; + + wanted_curly_level = wanted_curly_level2 = curly_level+1; + + if (structs.find(current_struct) != structs.end()) + errexit("struct %s was already met", current_struct.data()); + + if (base_pos) { + TString base_struct = token_stack[base_pos].string; + + // May duplicate dumped enums + structs_map::iterator base = structs.find(token_stack[base_pos].string); + + if (base == structs.end()) + errexit("for struct %s, base struct %s was not found!\n", current_struct.data(), base_struct.data()); + + // Copy members of base struct + structs[current_struct].insert(structs[current_struct].end(), base->second.begin(), base->second.end()); + structs[current_struct].wanted_enums.insert(base->second.wanted_enums.begin(), base->second.wanted_enums.end()); + } + emit_message("// members of struct %s...", current_struct.data()); + token_stack.clear(); + return; + } + current_enum = current_struct = ""; + } + } +} + +%%{ + machine Scanner; + write data nofinal; + + # Floating literals. + fract_const = digit* '.' digit+ | digit+ '.'; + exponent = [eE] [+\-]? digit+; + float_suffix = [flFL]; + identifier = [a-zA-Z_] [a-zA-Z0-9_]*; + chrtype = "const" space+ "char" space* "*"; + + c_comment := + ('\n' %{line++;} | any)* :>> '*/' @{ fgoto main; }; + + main := |* + + # high priority single-char tokens + '{' { + process_token(TT_OPENCURLY); + curly_level++; + }; + '}' { + curly_level--; + process_token(TT_CLOSECURLY); + if (curly_level < wanted_curly_level) { + state = DEFAULT; + wanted_curly_level = wanted_curly_level2 = -1; + } + if (curly_level < wanted_curly_level2) { + wanted_curly_level2 = curly_level; + } + }; + ',' { process_token(TT_COMMA); }; + '=' { process_token(TT_EQUALS); }; + ';' { process_token(TT_SEMICOLON); }; + ':' { process_token(TT_COLON); }; + '[' { process_token(TT_OPENSQUARE); }; + ']' { process_token(TT_CLOSESQUARE); }; + '(' { process_token(TT_OPENPAREN); }; + ')' { process_token(TT_CLOSEPAREN); }; + '|' { process_token(TT_PIPE); }; + + # Single and double literals. + 'L'? "'" ( [^'\\\n] | /\\./ )* "'" { + process_token(TT_OTHER); + }; + 'L'? '"' ( [^"\\\n] | /\\./ )* '"' { + process_token(TT_OTHER); + }; + + # Identifiers & reserved words + "enum" { process_token(TT_ENUM); }; + "struct" { process_token(TT_STRUCT); }; + "union" { process_token(TT_UNION); }; + "const" { process_token(TT_CONST); }; + "public" { process_token(TT_PUBLIC); }; + chrtype { process_token(TT_COMPLEXTYPE); }; + identifier { process_token(TT_IDENTIFIER); }; + + # Floating literals. + fract_const exponent? float_suffix? | digit+ exponent float_suffix? { + process_token(TT_OTHER); + }; + + # Integer decimal. Leading part buffered by float. + ( '0' | [1-9] [0-9]* ) [ulUL]{0,3} { + process_token(TT_DECIMAL); + }; + + # Integer octal. Leading part buffered by float. + '0' [0-9]+ [ulUL]{0,2} { + process_token(TT_OTHER); + }; + + # Integer hex. Leading 0 buffered by float. + '0' ( 'x' [0-9a-fA-F]+ [ulUL]{0,2} ) { + process_token(TT_OTHER); + }; + + # Only buffer the second item, first buffered by symbol. */ + '::' { process_token(TT_OTHER); }; + '==' { process_token(TT_OTHER); }; + '!=' { process_token(TT_OTHER); }; + '&&' { process_token(TT_OTHER); }; + '||' { process_token(TT_OTHER); }; + '*=' { process_token(TT_OTHER); }; + '/=' { process_token(TT_OTHER); }; + '%=' { process_token(TT_OTHER); }; + '+=' { process_token(TT_OTHER); }; + '-=' { process_token(TT_OTHER); }; + '&=' { process_token(TT_OTHER); }; + '^=' { process_token(TT_OTHER); }; + '|=' { process_token(TT_OTHER); }; + '++' { process_token(TT_OTHER); }; + '--' { process_token(TT_OTHER); }; + '->' { process_token(TT_OTHER); }; + '->*' { process_token(TT_OTHER); }; + '.*' { process_token(TT_OTHER); }; + + # Three char compounds, first item already buffered. */ + '...' { process_token(TT_OTHER); }; + + # Single char symbols. + ( punct - [_"'] ) { process_token(TT_OTHER); }; + + # Comments with special meaning + '//' [^\n]* '@nodmp' [^\n]* '\n' { + if (line != last_entry_line) + errexit("cannot find which entry @nodmp belongs to (last entry was at line %d)", last_entry_line); + + structs[current_struct].pop_back(); + line++; + }; + + '//' space* + ( '@dmp' + >{ + dmp_name_start = dmp_from_start = dmp_to_start = + dmp_name_end = dmp_from_end = dmp_to_end = + dmp_mode_start = 0; + } + ) + space* ( ( 'bitmask' | 'val' ) > { dmp_mode_start = p; } ) space* + ( identifier + >{ dmp_name_start = p; } + %{ dmp_name_end = p-1; } + ) + space* 'eff_name' space* 's/^' + ( [a-zA-Z0-9_]* + >{ dmp_from_start = p; } + %{ dmp_from_end = p-1; } + ) + '/' + ( [a-zA-Z0-9_]* + >{ dmp_to_start = p; } + %{ dmp_to_end = p-1; } + ) + '/' [^\n]* '\n' + { + if (line != last_entry_line) + errexit("cannot find which entry @dmp belongs to (last entry was at line %d)", last_entry_line); + + // @dmp processor + TString enum_name = ""; + TString prefix_del = ""; + TString prefix_add = ""; + + // Put enum name for current item to TString + if (dmp_name_start && dmp_name_end && dmp_name_start < dmp_name_end) + enum_name = TString(dmp_name_start, dmp_name_end-dmp_name_start+1); + + // Put prefix for current item to TString + if (dmp_from_start && dmp_from_end && dmp_from_start < dmp_from_end) + prefix_del = TString(dmp_from_start, dmp_from_end-dmp_from_start+1); + + // Put replace prefix for current item to TString + if (dmp_to_start && dmp_to_end && dmp_to_start < dmp_to_end) + prefix_add = TString(dmp_to_start, dmp_to_end-dmp_to_start+1); + + // Find corresponding enum + enums_map::iterator en = enums.find(enum_name); + if (en == enums.end()) + errexit("bad enum name: %s", enum_name.c_str()); + + if (prefix_del == "" && prefix_add == "") + errexit("enum name should be changed, /^// does nothing"); + + if (!dmp_mode_start || (*dmp_mode_start != 'b' && *dmp_mode_start != 'v')) + errexit("unknown enum alias mode"); + + emit_message("// enum-aliases for last field: enum=%s, prefix=%s, replace=%s", + enum_name.c_str(), prefix_del.c_str(), prefix_add.c_str()); + + // Make sure we have enum constants dumped + structs[current_struct].wanted_enums.insert(enum_name); + + // Emit a callback for all enumerators + for (enumerators_list::iterator i = en->second.begin(); i != en->second.end(); ++i) { + TString enumerator_name = *i; + + + if (!enumerator_name.StartsWith(prefix_del)) + errexit("bad prefix %s for enumerator %s", prefix_del.c_str(), enumerator_name.c_str()); + + enumerator_name.replace(0, prefix_del.length(), prefix_add); + + emit_entry(enumerator_name, true, "dump_item(%s, %s, %s)", last_expression.data(), i->data(), *dmp_mode_start == 'b' ? "true" : "false"); + } + emit_message("// end enum-aliases"); + + line++; + }; + + # Comments and whitespace. + '/*' { fgoto c_comment; }; + '//' [^\n]* '\n' { line++; }; + + '\n' { line++; }; + ( any - 33..126 ); + + *|; +}%% + +int main(int argc, char **argv) { + TVector<TString> wanted_structures; + TVector<TString> wanted_enums_v; + TVector<TString> tmp; + + Opt2 opt(argc, argv, "s:e:ESR:"); + for (auto s : opt.MArg('s', "<name1,name2,...> - structs to process")) { + Split(s, ",", tmp); + wanted_structures.insert(wanted_structures.end(), tmp.begin(), tmp.end()); + } + for (auto s : opt.MArg('e', "<name1,name2,...> - enums to process")) { + Split(s, ",", tmp); + wanted_enums_v.insert(wanted_enums_v.end(), tmp.begin(), tmp.end()); + } + TString srcRoot = opt.Arg('R', "<dir> - project source root (to make includes relative to)", 0); + bool dump_all_enums = opt.Has('E', " - dump all enums (don't need -e)"); + bool add_stream_out = opt.Has('S', " - generate Out<>() for IOutputStream output (slow)"); + opt.AutoUsageErr("include.h ..."); + if (wanted_structures.empty() && wanted_enums_v.empty() && !dump_all_enums) + warnx("nothing to dump"); + + if (srcRoot && srcRoot.back() != '/') + srcRoot += '/'; + // Header + printf("// THIS IS GENERATED FILE, DO NOT EDIT!\n"); + printf("// Generated by struct2fieldcalc with command line:"); + for (int i = 0; i < argc; i++) { + TStringBuf arg = argv[i]; + if (srcRoot && arg.StartsWith(srcRoot)) { + arg.Skip(srcRoot.size()); + printf(" ${ARCADIA_ROOT}/%.*s", (int)arg.size(), arg.data()); + } else + printf(" %.*s", (int)arg.size(), arg.data()); + } + printf("\n\n"); + printf("#include <library/cpp/fieldcalc/field_calc_int.h>\n\n"); + + for (size_t arg = 0; arg < opt.Pos.size(); arg++) { + current_file = opt.Pos[arg]; + line = 0; + last_entry_line = 0; + + %% write init; + + // Open input file + TFILEPtr f(opt.Pos[arg], "rb"); + TVector<char> buf(f.length()); + if (f.read(buf.begin(), 1, buf.size()) != buf.size()) + errexit("short read in input file"); + f.close(); + + char *p = buf.begin(); + char *pe = buf.end(); + + TStringBuf arc_path = opt.Pos[arg]; + if (srcRoot && arc_path.StartsWith(srcRoot)) + arc_path.Skip(srcRoot.size()); + + printf("#include <%.*s>\n", (int)arc_path.size(), arc_path.data()); + + line = 1; + + // Parse file, emits functions + %% write exec; + + // Done + if (cs == Scanner_error) + errexit("parse error"); + } + printf("\n"); + + if (wanted_structures.empty()) + for (structs_map::iterator s = structs.begin(); s != structs.end(); ++s) + wanted_structures.push_back(s->first); + + for (TVector<TString>::iterator ws = wanted_structures.begin(); ws != wanted_structures.end(); ++ws) { + structs_map::iterator s = structs.find(*ws); + if (s == structs.end()) + errx(1, "Structure %s not found in input files", ws->data()); + + if (!s->second.efunctions.empty()) { + printf("namespace {\n"); + for (auto f : s->second.efunctions) + printf(" %s\n", f.data()); + printf("}\n"); + } + + printf("template <> std::pair<const named_dump_item*, size_t> get_named_dump_items<%s>() {\n", s->first.c_str()); + printf(" static named_dump_item items[] = {\n"); + + std::set<TString> avail_names; + + // dump structure fields + for (dump_items_list::iterator i = s->second.begin(); i != s->second.end(); ++i) { + if (!i->first) + printf(" %s\n", i->second.data()); + else { + if (avail_names.find(i->first) != avail_names.end()) { + warnx("Identifier %s is already used (while processing struct %s) - NOT DUMPING!", i->first.data(), ws->data()); + continue; + } + avail_names.insert(i->first); + printf(" { \"%s\", %s },\n", i->first.data(), i->second.data()); + } + } + + std::set<TString> &wanted_enums = s->second.wanted_enums; + if (dump_all_enums) + for (enums_map::iterator en = enums.begin(); en != enums.end(); ++en) + wanted_enums.insert(en->first); + else + wanted_enums.insert(wanted_enums_v.begin(), wanted_enums_v.end()); + + // dump enum values for the structure + for (std::set<TString>::iterator wen = wanted_enums.begin(); wen != wanted_enums.end(); ++wen) { + enums_map::iterator en = enums.find(*wen); + if (en == enums.end()) + errx(1, "Unexpected: enum %s not found (while processing struct %s)", wen->data(), ws->data()); + + printf(" // members of enum %s\n", en->first.c_str()); + for (enumerators_list::iterator e = en->second.begin(); e != en->second.end(); ++e) { + if (avail_names.find(*e) != avail_names.end()) + errx(1, "Identifier %s is already used (while processing enum %s for struct %s)", e->data(), wen->data(), ws->data()); + printf(" { \"%s\", (long)%s },\n", e->data(), e->data()); + } + } + printf(" };\n\n"); + printf(" return std::make_pair(items, sizeof(items)/sizeof(*items));\n"); + printf("}\n\n"); + if (add_stream_out) { + printf("template<>" + "void Out<%s>(IOutputStream& os, TTypeTraits<%s>::TFuncParam s) {\n" + " TFieldCalculator<%s>().DumpAll(os, s, \" \");\n" + "}\n\n", s->first.data(), s->first.data(), s->first.data()); + } + } + + return 0; +} diff --git a/tools/struct2fieldcalc/ya.make b/tools/struct2fieldcalc/ya.make new file mode 100644 index 0000000000..a75d06b306 --- /dev/null +++ b/tools/struct2fieldcalc/ya.make @@ -0,0 +1,16 @@ +PROGRAM() + +PEERDIR( + library/cpp/getopt/small + library/cpp/deprecated/fgood +) + +INDUCED_DEPS(h+cpp + ${ARCADIA_ROOT}/library/cpp/fieldcalc/field_calc_int.h +) + +SRCS( + parsestruct.rl +) + +END() |