blob: 2024a595c59a764da99732dd66868f04167302a3 [file]
// Copyright 2023 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef MPACT_SIM_DECODER_PROTO_FORMAT_VISITOR_H_
#define MPACT_SIM_DECODER_PROTO_FORMAT_VISITOR_H_
#include <deque>
#include <functional>
#include <list>
#include <memory>
#include <string>
#include <string_view>
#include <utility>
#include <vector>
#include "absl/container/flat_hash_map.h"
#include "absl/container/flat_hash_set.h"
#include "absl/status/status.h"
#include "absl/status/statusor.h"
#include "antlr4-runtime/ParserRuleContext.h"
#include "mpact/sim/decoder/ProtoFormatLexer.h"
#include "mpact/sim/decoder/ProtoFormatParser.h"
#include "mpact/sim/decoder/antlr_parser_wrapper.h"
#include "mpact/sim/decoder/decoder_error_listener.h"
#include "mpact/sim/decoder/proto_constraint_expression.h"
#include "mpact/sim/decoder/proto_encoding_info.h"
#include "mpact/sim/decoder/proto_format_contexts.h"
#include "mpact/sim/decoder/proto_instruction_group.h"
#include "src/google/protobuf/descriptor.h"
#include "util/regexp/re2/re2.h"
// This file defines the visitor class used to generate C++ code for decoding
// instructions encoded in protobufs.
namespace mpact {
namespace sim {
namespace decoder {
namespace proto_fmt {
class ProtoInstructionEncoding;
using ::mpact::sim::decoder::proto_fmt::generated::ProtoFormatLexer;
using ::mpact::sim::decoder::proto_fmt::generated::ProtoFormatParser;
using ProtoFmtAntlrParserWrapper =
decoder::AntlrParserWrapper<ProtoFormatParser, ProtoFormatLexer>;
class ProtoFormatVisitor {
public:
ProtoFormatVisitor() = default;
~ProtoFormatVisitor() = default;
// This is the main function. It parses the .proto_fmt file and generates
// the required C++ code.
// Parameters:
// file_names: vector of source file names.
// decoder_name: the decoder for which to generate code.
// prefix: if non-empty, the prefix used in the generated file names.
// include_roots: vector of directories from which to resolve include files.
// proto_dirs: vector of directories from which to resolve proto files.
// proto_files: vector of .proto files to import.
// directory: target directory to generate the C++ files in.
absl::Status Process(const std::vector<std::string>& file_names,
const std::string& decoder_name, std::string_view prefix,
const std::vector<std::string>& include_roots,
const std::vector<std::string>& proto_dirs,
const std::vector<std::string>& proto_files,
std::string_view directory);
// Accessors for the error listener.
decoder::DecoderErrorListener* error_listener() const {
return error_listener_.get();
}
void set_error_listener(
std::unique_ptr<decoder::DecoderErrorListener> listener) {
error_listener_ = std::move(listener);
}
private:
// This struct holds information about a range assignment in an instruction
// generator.
struct RangeAssignmentInfo {
std::vector<std::string> range_names;
std::list<RE2> range_regexes;
std::vector<std::vector<std::string>> range_values;
};
// Expand the given name according to any using statements that may apply.
std::string Expand(std::string_view name) const;
// Get the field descriptor for the named field from the message type while
// building up a vector of one_of_fields that are on the path from the top
// level message to the field.
const google::protobuf::FieldDescriptor* GetField(
const std::string& field_name,
const google::protobuf::Descriptor* message_type,
std::vector<const google::protobuf::FieldDescriptor*>& one_of_fields)
const;
// Get the descriptor for the named enumeration member.
const google::protobuf::EnumValueDescriptor* GetEnumValueDescriptor(
const std::string& full_name) const;
// Get the numeric value of the named enumeration member.
absl::StatusOr<int> GetEnumValue(const std::string& enum_name) const;
// Helpful template function used to find a descriptor by name.
template <typename T>
const T* FindByName(const std::string& message_name, const std::string& name,
std::function<const T*(const std::string&)> finder) const;
// Visit the parse tree to catalog the different declarations.
void PreProcessDeclarations(const std::vector<DeclarationCtx*>& declarations);
// Process an include file declaration.
void VisitIncludeFile(IncludeFileCtx* ctx);
// Parse an included file.
void ParseIncludeFile(antlr4::ParserRuleContext* ctx,
const std::string& file_name,
const std::vector<std::string>& dirs);
// Creates the top level data structures and visits the declarations that
// are necessary to generate the instruction decoder.
std::unique_ptr<ProtoEncodingInfo> ProcessTopLevel(
const std::string& decoder_name);
// The following methods visit specific nodes in the parse tree.
ProtoInstructionGroup* VisitInstructionGroupDef(
InstructionGroupDefCtx* ctx, ProtoEncodingInfo* encoding_info);
void VisitInstructionDef(InstructionDefCtx* ctx,
ProtoInstructionGroup* inst_group,
ProtoEncodingInfo* encoding_info);
void VisitFieldConstraint(FieldConstraintCtx* ctx,
ProtoInstructionEncoding* inst_encoding,
const ProtoInstructionGroup* inst_group);
ProtoConstraintExpression* VisitConstraintExpression(
ConstraintExprCtx* ctx,
const google::protobuf::FieldDescriptor* field_desc,
const ProtoInstructionGroup* inst_group);
ProtoConstraintExpression* VisitValue(ValueCtx* ctx);
ProtoConstraintExpression* VisitNumber(NumberCtx* ctx);
ProtoConstraintExpression* VisitQualifiedIdent(
QualifiedIdentCtx* ctx,
const google::protobuf::FieldDescriptor* field_desc,
const ProtoInstructionGroup* inst_group);
void VisitSetterGroupDef(SetterGroupDefCtx* ctx,
ProtoInstructionGroup* inst_group,
ProtoEncodingInfo* encoding_info);
void VisitSetterDef(SetterDefCtx* ctx,
ProtoInstructionEncoding* inst_encoding,
ProtoInstructionGroup* inst_group,
ProtoEncodingInfo* encoding_info);
void VisitSetterRef(SetterRefCtx* ctx,
ProtoInstructionEncoding* inst_encoding,
ProtoInstructionGroup* inst_group,
ProtoEncodingInfo* encoding_info);
std::unique_ptr<ProtoEncodingInfo> VisitDecoderDef(DecoderDefCtx* ctx);
// Process and generate instruction definitions from a generate statement.
void ProcessInstructionDefGenerator(InstructionDefCtx* ctx,
ProtoInstructionGroup* inst_group,
ProtoEncodingInfo* encoding_info);
std::string GenerateInstructionDefList(
const std::vector<RangeAssignmentInfo*>& range_info_vec, int index,
const std::string& template_str_in) const;
// Process instruction groups.
void ProcessSingleGroup(DecoderAttributeCtx* attr_ctx,
ProtoEncodingInfo* encoding_info,
absl::flat_hash_set<std::string>& group_name_set);
void ProcessParentGroup(DecoderAttributeCtx* attr_ctx,
ProtoEncodingInfo* encoding_info,
absl::flat_hash_set<std::string>& group_name_set);
// Called to generate and emit code for the decoder according to the parsed
// input file.
StringPair EmitCode(ProtoEncodingInfo* encoding_info);
// Finders used to find specific object types from the proto2 pool.
using FieldFinder = std::function<const google::protobuf::FieldDescriptor*(
const std::string&)>;
using MessageFinder =
std::function<const google::protobuf::Descriptor*(const std::string&)>;
using EnumTypeFinder = std::function<const google::protobuf::EnumDescriptor*(
const std::string&)>;
using EnumValueFinder =
std::function<const google::protobuf::EnumValueDescriptor*(
const std::string&)>;
FieldFinder field_finder_;
MessageFinder message_finder_;
EnumTypeFinder enum_type_finder_;
EnumValueFinder enum_value_finder_;
// This stores a vector of include file root directories.
std::vector<std::string> include_dir_vec_;
// Keep track of files that are included in case there is recursive includes.
std::deque<std::string> include_file_stack_;
// Error listening object passed to the parser.
std::unique_ptr<decoder::DecoderErrorListener> error_listener_ = nullptr;
std::string decoder_name_;
// Descriptor pool.
const google::protobuf::DescriptorPool* descriptor_pool_;
absl::flat_hash_map<std::string, const google::protobuf::FileDescriptor*>
file_descriptor_map_;
// Using decl map.
absl::flat_hash_map<std::string, std::string> using_decl_map_;
// Maps from identifiers to declaration contexts.
absl::flat_hash_map<std::string, InstructionGroupDefCtx*> group_decl_map_;
absl::flat_hash_map<std::string, DecoderDefCtx*> decoder_decl_map_;
// AntlrParserWrapper vector.
std::vector<ProtoFmtAntlrParserWrapper*> antlr_parser_wrappers_;
};
} // namespace proto_fmt
} // namespace decoder
} // namespace sim
} // namespace mpact
#endif // MPACT_SIM_DECODER_PROTO_FORMAT_VISITOR_H_