feature support generate message

This commit is contained in:
tqcq 2023-12-02 00:32:54 +08:00
commit b1a44a46da
19 changed files with 862 additions and 0 deletions

72
.clang-format Normal file
View File

@ -0,0 +1,72 @@
# Generated from CLion C/C++ Code Style settings
BinPackParameters: false
BasedOnStyle: LLVM
AccessModifierOffset: -4
AlignAfterOpenBracket: Align
AlignConsecutiveAssignments: None
AlignOperands: DontAlign
AllowAllArgumentsOnNextLine: false
AllowAllConstructorInitializersOnNextLine: false
AllowAllParametersOfDeclarationOnNextLine: false
AllowShortBlocksOnASingleLine: Always
AllowShortCaseLabelsOnASingleLine: false
AllowShortFunctionsOnASingleLine: All
AllowShortIfStatementsOnASingleLine: true
AllowShortLambdasOnASingleLine: All
AllowShortLoopsOnASingleLine: true
AlwaysBreakTemplateDeclarations: Yes
# 函数和返回类型分两行,方便阅读
AlwaysBreakAfterReturnType: TopLevelDefinitions
BreakBeforeBraces: Custom
BraceWrapping:
AfterCaseLabel: false
AfterClass: false
AfterControlStatement: Never
AfterEnum: false
AfterFunction: true
AfterNamespace: false
AfterUnion: false
BeforeCatch: false
BeforeElse: false
IndentBraces: false
SplitEmptyFunction: false
SplitEmptyRecord: true
BreakBeforeBinaryOperators: NonAssignment
BreakBeforeTernaryOperators: true
BreakConstructorInitializers: BeforeColon
ConstructorInitializerAllOnOneLineOrOnePerLine: true
BreakInheritanceList: BeforeColon
ColumnLimit: 120
CompactNamespaces: false
ContinuationIndentWidth: 4
EmptyLineBeforeAccessModifier: LogicalBlock
SeparateDefinitionBlocks: Always
IndentCaseLabels: false
IndentPPDirectives: None
IndentWidth: 4
KeepEmptyLinesAtTheStartOfBlocks: true
MaxEmptyLinesToKeep: 1
NamespaceIndentation: None
ObjCSpaceAfterProperty: false
ObjCSpaceBeforeProtocolList: false
PointerAlignment: Right
ReflowComments: false
SortIncludes: Never
SpaceAfterCStyleCast: true
SpaceAfterLogicalNot: false
SpaceAfterTemplateKeyword: false
SpaceBeforeAssignmentOperators: true
SpaceBeforeCpp11BracedList: false
SpaceBeforeCtorInitializerColon: true
SpaceBeforeInheritanceColon: true
SpaceBeforeParens: ControlStatements
SpaceBeforeRangeBasedForLoopColon: true
SpaceInEmptyParentheses: false
SpacesBeforeTrailingComments: 0
SpacesInAngles: false
SpacesInCStyleCastParentheses: false
SpacesInContainerLiterals: false
SpacesInParentheses: false
SpacesInSquareBrackets: false
TabWidth: 4
UseTab: Never

4
.gitignore vendored Normal file
View File

@ -0,0 +1,4 @@
.idea/
cmake-build-*
build/
*gen*/

21
CMakeLists.txt Normal file
View File

@ -0,0 +1,21 @@
cmake_minimum_required(VERSION 3.26)
project(crpc)
find_package(antlr4-runtime CONFIG REQUIRED)
set(CMAKE_CXX_STANDARD 17)
FILE(GLOB_RECURSE SRC_LIST ${PROJECT_SOURCE_DIR}/src/*.cpp)
add_executable(crpc
main.cpp
src/item.cpp
src/message.cpp
${SRC_LIST}
src/rpc.cpp
src/service.cpp
src/plugins/c/c_message_plugin.cpp
)
target_link_libraries(crpc PRIVATE antlr4_static)
target_include_directories(crpc PRIVATE /opt/dev/vcpkg/installed/arm64-osx/include/antlr4-runtime)
target_include_directories(crpc PRIVATE src/gen/ src/)

101
main.cpp Normal file
View File

@ -0,0 +1,101 @@
#include <iostream>
#include <ostream>
#include "antlr4-runtime.h"
#include "ProtoParserBaseListener.h"
#include "ProtoParserBaseVisitor.h"
#include "ProtoLexer.h"
#include "ProtoParser.h"
#include "message.h"
#include "service.h"
#include "plugins/c/c_message_plugin.h"
#include <stack>
#include <map>
class Listener : public ProtoParserBaseListener {
public:
void enterMessageStatement(ProtoParser::MessageStatementContext *context) override
{
message_stack.push(std::make_shared<tqcq::Message>(context->ID()->getText()));
}
void exitMessageStatement(ProtoParser::MessageStatementContext *context) override
{
message_stack.top()->SetItems(std::move(items));
assert(items.empty());
if (message_map.find(message_stack.top()->id()) != message_map.end()) {
std::cout << "message " << message_stack.top()->id() << " already exists" << std::endl;
exit(1);
}
message_map.insert(std::make_pair(message_stack.top()->id(), message_stack.top()));
message_stack.pop();
}
void exitItemStatement(ProtoParser::ItemStatementContext *context) override
{
int idx = std::stoi(context->NUMBER()->getText());
tqcq::Item::Ptr item = nullptr;
if (context->INT32()) {
item = std::make_shared<tqcq::Item>(tqcq::Item::Type::kInt32, context->ID()->getText(), idx);
} else if (context->STRING()){
item = std::make_shared<tqcq::Item>(tqcq::Item::Type::kString, context->ID()->getText(), idx);
} else {
assert(false);
}
items.push_back(std::move(item));
}
void exitServiceStatement(ProtoParser::ServiceStatementContext *context) override
{
std::string id = context->ID()->getText();
auto service = std::make_shared<tqcq::Service>(id);
service->SetRPCs(std::move(rpcs));
assert(rpcs.empty());
services.push_back(std::move(service));
}
void exitRpcStatement(ProtoParser::RpcStatementContext *context) override {
std::string id = context->ID(0)->getText();
std::string request_id = context->ID(1)->getText();
std::string response_id = context->ID(2)->getText();
auto rpc = std::make_shared<tqcq::RPC>(id, request_id, response_id);
rpcs.push_back(std::move(rpc));
}
std::map<std::string, tqcq::Message::Ptr> message_map;
std::vector<tqcq::Item::Ptr> items;
std::stack<tqcq::Message::Ptr> message_stack;
std::vector<tqcq::RPC::Ptr> rpcs;
std::vector<tqcq::Service::Ptr> services;
};
int
main(int argc, char *argv[])
{
std::ifstream ifs;
ifs.open(argv[1]);
antlr4::ANTLRInputStream input(ifs);
ProtoLexer lexer(&input);
antlr4::CommonTokenStream tokens(&lexer);
ProtoParser parser(&tokens);
// parser.removeErrorListeners();
// Visitor visitor;
// visitor.visit(parser.program());
Listener listener;
Listener listener2;
antlr4::tree::ParseTreeWalker::DEFAULT.walk(&listener, parser.program());
for (const auto& item : listener.message_map) {
std::cout << item.second->ToString() << std::endl;
}
for (const auto& service : listener.services) {
std::cout << service->ToString() << std::endl;
}
tqcq::CMessagePlugin message_plugin("/tmp/gen");
message_plugin.Generate(listener.message_map);
return 0;
}

27
src/ProtoLexer.g4 Normal file
View File

@ -0,0 +1,27 @@
lexer grammar ProtoLexer;
INT32: 'int32';
STRING: 'string';
VERSION2: 'proto2';
VERSION3: 'proto3';
SYNTAX_TYPE: 'syntax';
MESSAGE_TYPE: 'message';
RPC_TYPE: 'rpc';
RETURNS_TYPE: 'returns';
SERVICE_TYPE: 'service';
EQUAL: '=';
SEMICOLON: ';';
COMMA: ',';
QUOTE: '"';
OPEN_BRACE: '{';
CLOSE_BRACE: '}';
OPEN_PAREN: '(';
CLOSE_PAREN: ')';
NUMBER: [0-9]+;
ID: [a-zA-Z_][a-zA-Z_0-9]*;
WHITESPACE: [ \t\r\n]+ -> skip;

13
src/ProtoParser.g4 Normal file
View File

@ -0,0 +1,13 @@
parser grammar ProtoParser;
options{ tokenVocab='gen/ProtoLexer'; language=Cpp; }
program: syntaxStatement baseStatement* EOF;
syntaxStatement: SYNTAX_TYPE '=' '"' (VERSION2 | VERSION3) '"' ';';
baseStatement: (messageStatement | serviceStatement | ';');
messageStatement: MESSAGE_TYPE ID '{' itemStatement* '}' ';';
serviceStatement: SERVICE_TYPE ID '{' rpcStatement* '}' ';';
itemStatement: (INT32 | STRING) ID '=' NUMBER ';';
rpcStatement: RPC_TYPE ID '(' ID ')' 'returns' '(' ID ')' ';';

50
src/item.cpp Normal file
View File

@ -0,0 +1,50 @@
//
// Created by tqcq on 2023/12/1.
//
#include "item.h"
namespace tqcq {
Item::Item(Item::Type type, std::string id, int idx)
: type_(type),
id_(std::move(id)),
idx_(idx)
{}
Item::Type
Item::type() const
{
return type_;
}
std::string
Item::id() const
{
return id_;
}
int
Item::idx() const
{
return idx_;
}
std::string
Item::ToString()
{
return ToString(type_) + " " + id_ + " = " + std::to_string(idx_)+ ";";
}
std::string
Item::ToString(Item::Type type)
{
switch(type) {
case Item::Type::kInt32:
return "int32";
case Item::Type::kString:
return "string";
default:
return "unknown";
}
}
}// namespace tqcq

40
src/item.h Normal file
View File

@ -0,0 +1,40 @@
//
// Created by tqcq on 2023/12/1.
//
#ifndef CRPC_SRC_ITEM_H_
#define CRPC_SRC_ITEM_H_
#include <string>
#include <memory>
namespace tqcq {
class Item {
public:
enum class Type {
kInt32,
kString
};
static std::string ToString(Type type);
using Ptr = std::shared_ptr<Item>;
Item() = delete;
Item(Type type, std::string id, int idx);
virtual ~Item() = default;
Type type() const;
std::string id() const;
int idx() const;
virtual std::string ToString();
protected:
private:
Type type_;
std::string id_;
int idx_;
};
}// namespace tqcq
#endif//CRPC_SRC_ITEM_H_

53
src/message.cpp Normal file
View File

@ -0,0 +1,53 @@
//
// Created by tqcq on 2023/12/1.
//
#include "message.h"
#include <sstream>
namespace tqcq {
Message::Message(std::string id) : id_(id){}
std::string
Message::id() const
{
return id_;
}
void
Message::AddItem(Item::Ptr item)
{
items_.push_back(std::move(item));
}
std::vector<Item::Ptr>
Message::items() const
{
return items_;
}
void
Message::SetItems(std::vector<Item::Ptr> items)
{
items_ = std::move(items);
}
std::string
Message::ToString()
{
std::stringstream ss;
ss << "message " << id_ << " {";
for (const auto& item : items_) {
ss << std::endl;
ss << " " << item->ToString();
}
if (!items_.empty()) {
ss << std::endl;
}
ss << "};";
return ss.str();
}
}// namespace tqcq

38
src/message.h Normal file
View File

@ -0,0 +1,38 @@
//
// Created by tqcq on 2023/12/1.
//
#ifndef CRPC_SRC_MESSAGE_H_
#define CRPC_SRC_MESSAGE_H_
#include <string>
#include <memory>
#include "item.h"
#include <set>
#include <vector>
namespace tqcq {
class Message {
public:
using Ptr = std::shared_ptr<Message>;
Message(std::string id);
std::string id() const;
void AddItem(Item::Ptr item);
void SetItems(std::vector<Item::Ptr> items);
std::vector<Item::Ptr> items() const;
void AddDependency(std::string dependency);
void SetDependencies(std::set<std::string> dependencies);
std::set<std::string> dependencies() const;
std::string ToString();
private:
std::string id_;
std::vector<Item::Ptr> items_;
std::set<std::string> dependencies_;
};
}// namespace tqcq
#endif//CRPC_SRC_MESSAGE_H_

View File

@ -0,0 +1,170 @@
//
// Created by tqcq on 2023/12/1.
//
#include "c_message_plugin.h"
#include <sstream>
#include <fstream>
#include <iostream>
namespace tqcq {
static std::string GenerateGuardMacro(std::string id, std::string prefix)
{
std::stringstream ss;
ss << prefix << "_" << id << "_H_";
return ss.str();
}
static std::string GenerateHeaderFileName(std::string id)
{
std::stringstream ss;
ss << id << ".h";
return ss.str();
}
std::string GenerateSourceFileName(std::string id)
{
std::stringstream ss;
ss << id << ".c";
return ss.str();
}
CMessagePlugin::CMessagePlugin(std::string generate_path, std::string generate_prefix)
: MessagePlugin(std::move(generate_path), std::move(generate_prefix))
{}
void
CMessagePlugin::GenerateHeader(std::map<std::string, tqcq::Message::Ptr> message_map)
{
for (auto &message : message_map) {
if (!message.second) {
std::cout << "message " << message.first << " is null" << std::endl;
continue;
}
std::stringstream ss;
ss << GenerateIncludeGuardStart(message.second);
ss << GenerateIncludeFile(message.second);
ss << GenerateStructStart(message.second);
ss << GenerateStructItem(message.second);
ss << GenerateStructEnd(message.second);
ss << GenerateFunctionDeclaration(message.second);
ss << GenerateIncludeGuardEnd(message.second);
std::ofstream ofs(generate_path() + "/" + GenerateHeaderFileName(message.second->id()));
ofs << ss.str();
std::cout << ss.str() << std::endl;
}
}
void
CMessagePlugin::GenerateSource(std::map<std::string, tqcq::Message::Ptr> message_map)
{
for (auto &message : message_map) {
std::stringstream ss;
ss << GenerateSourceIncludeFile(message.second);
ss << GenerateFunctionDefinition(message.second);
std::ofstream ofs(generate_path() + "/" + GenerateSourceFileName(message.second->id()));
ofs << ss.str();
}
}
std::string
CMessagePlugin::GenerateIncludeGuardStart(Message::Ptr message)
{
std::stringstream ss;
std::string guard_macro = GenerateGuardMacro(message->id(), generate_prefix());
ss << "#ifndef " << guard_macro << std::endl;
ss << "#define " << guard_macro << std::endl;
ss << std::endl;
return ss.str();
}
std::string
CMessagePlugin::GenerateIncludeGuardEnd(Message::Ptr message)
{
std::stringstream ss;
ss << "#endif //" << GenerateGuardMacro(message->id(), generate_prefix()) << std::endl;
return ss.str();
}
std::string
CMessagePlugin::GenerateIncludeFile(Message::Ptr message)
{
std::stringstream ss;
ss << "#include <stdint.h>" << std::endl;
ss << std::endl;
return ss.str();
}
std::string
CMessagePlugin::GenerateStructStart(Message::Ptr message)
{
std::stringstream ss;
ss << "typedef struct " << " {" << std::endl;
return ss.str();
}
std::string
CMessagePlugin::GenerateStructItem(Message::Ptr message)
{
std::stringstream ss;
for (auto &item : message->items()) {
auto type_name = TranslateTypeToCType(item->type());
ss << " " << type_name << " " << item->id() << ";" << std::endl;
}
return ss.str();
}
std::string
CMessagePlugin::GenerateStructEnd(Message::Ptr message)
{
std::stringstream ss;
ss << "} " << message->id() << ";" << std::endl;
ss << std::endl;
return ss.str();
}
std::string
CMessagePlugin::GenerateFunctionDeclaration(Message::Ptr message)
{
return std::string();
}
std::string
CMessagePlugin::GenerateFunctionDefinition(Message::Ptr message)
{
return std::string();
}
std::string
CMessagePlugin::GenerateSourceIncludeFile(Message::Ptr message)
{
std::stringstream ss;
ss << "#include \"" << GenerateHeaderFileName(message->id()) << "\"" << std::endl;
ss << std::endl;
return ss.str();
}
std::string
CMessagePlugin::TranslateTypeToCType(Item::Type type)
{
switch(type) {
case Item::Type::kInt32:
return "int32_t";
case Item::Type::kString:
return "char*";
}
}
std::string
CMessagePlugin::TranslateTypeToRealType(Item::Type type)
{
switch(type) {
case Item::Type::kInt32:
return "int32_t";
case Item::Type::kString:
return "char*";
}
}
}// namespace tqcq

View File

@ -0,0 +1,39 @@
//
// Created by tqcq on 2023/12/1.
//
#ifndef CRPC_SRC_PLUGINS_C_C_MESSAGE_PLUGIN_H_
#define CRPC_SRC_PLUGINS_C_C_MESSAGE_PLUGIN_H_
#include "plugins/message_plugin.h"
namespace tqcq {
class CMessagePlugin : public MessagePlugin {
public:
CMessagePlugin(std::string generate_path, std::string generate_prefix="CRPC");
~CMessagePlugin() override = default;
void GenerateHeader(std::map<std::string, Message::Ptr> message_map) override;
void GenerateSource(std::map<std::string, Message::Ptr> message_map) override;
private:
std::string GenerateIncludeGuardStart(Message::Ptr message);
std::string GenerateIncludeGuardEnd(Message::Ptr message);
std::string GenerateIncludeFile(Message::Ptr message);
std::string GenerateStructStart(Message::Ptr message);
std::string GenerateStructItem(Message::Ptr message);
std::string GenerateStructEnd(Message::Ptr message);
std::string GenerateFunctionDeclaration(Message::Ptr message);
std::string GenerateFunctionDefinition(Message::Ptr message);
std::string GenerateSourceIncludeFile(Message::Ptr message);
std::string TranslateTypeToCType(Item::Type type);
std::string TranslateTypeToRealType(Item::Type type);
};
}// namespace tqcq
#endif//CRPC_SRC_PLUGINS_C_C_MESSAGE_PLUGIN_H_

View File

@ -0,0 +1,30 @@
//
// Created by tqcq on 2023/12/1.
//
#include "message_plugin.h"
namespace tqcq {
MessagePlugin::MessagePlugin(std::string generate_path, std::string generate_prefix)
: generate_path_(std::move(generate_path)), generate_prefix_(std::move(generate_prefix))
{}
void
MessagePlugin::Generate(std::map<std::string, Message::Ptr> message_map)
{
GenerateHeader(message_map);
GenerateSource(message_map);
}
std::string
MessagePlugin::generate_path() const
{
return generate_path_;
}
std::string
MessagePlugin::generate_prefix() const
{
return generate_prefix_;
}
}// namespace tqcq

View File

@ -0,0 +1,31 @@
//
// Created by tqcq on 2023/12/1.
//
#ifndef CRPC_SRC_PLUGINS_MESSAGE_PLUGIN_H_
#define CRPC_SRC_PLUGINS_MESSAGE_PLUGIN_H_
#include <string>
#include <map>
#include "message.h"
namespace tqcq {
class MessagePlugin {
public:
MessagePlugin(std::string generate_path, std::string generate_prefix="CRPC");
virtual ~MessagePlugin() = default;
virtual void Generate(std::map<std::string, Message::Ptr> message_map);
std::string generate_path() const;
std::string generate_prefix() const;
protected:
virtual void GenerateHeader(std::map<std::string, Message::Ptr> message_map) {};
virtual void GenerateSource(std::map<std::string, Message::Ptr> message_map) {};
private:
std::string generate_path_;
std::string generate_prefix_;
};
}// namespace tqcq
#endif//CRPC_SRC_PLUGINS_MESSAGE_PLUGIN_H_

45
src/rpc.cpp Normal file
View File

@ -0,0 +1,45 @@
//
// Created by tqcq on 2023/12/1.
//
#include "rpc.h"
#include <sstream>
namespace tqcq {
RPC::RPC(std::string id,std::string request_id, std::string response_id)
: id_(std::move(id)),
request_id_(std::move(request_id)),
response_id_(std::move(response_id))
{}
std::string
RPC::id() const
{
return id_;
}
std::string
RPC::request_id() const
{
return request_id_;
}
std::string
RPC::response_id() const
{
return response_id_;
}
std::string
RPC::ToString()
{
std::stringstream ss;
ss << "rpc " << id_ << " (";
ss << request_id_;
ss << ") return (";
ss << response_id_;
ss << ");";
return ss.str();
}
}// namespace tqcq

31
src/rpc.h Normal file
View File

@ -0,0 +1,31 @@
//
// Created by tqcq on 2023/12/1.
//
#ifndef CRPC_SRC_RPC_H_
#define CRPC_SRC_RPC_H_
#include "message.h"
namespace tqcq {
// rpc: RPC_TYPE ID '(' messageStatement ')' 'return' '(' messageStatement ')' ';';
class RPC {
public:
using Ptr = std::shared_ptr<RPC>;
RPC(std::string id, std::string request_id, std::string response_id);
std::string id() const;
std::string request_id() const;
std::string response_id() const;
std::string ToString();
private:
std::string id_;
std::string request_id_;
std::string response_id_;
};
}// namespace tqcq
#endif//CRPC_SRC_RPC_H_

50
src/service.cpp Normal file
View File

@ -0,0 +1,50 @@
//
// Created by tqcq on 2023/12/1.
//
#include "service.h"
#include <sstream>
namespace tqcq {
Service::Service(std::string id) : id_(std::move(id)) {}
std::string
Service::id() const
{
return id_;
}
void
Service::AddRPC(RPC::Ptr rpc)
{
rpcs_.push_back(std::move(rpc));
}
void
Service::SetRPCs(std::vector<RPC::Ptr> rpcs)
{
rpcs_ = std::move(rpcs);
}
std::vector<RPC::Ptr>
Service::rpcs() const
{
return rpcs_;
}
std::string
Service::ToString()
{
std::stringstream ss;
ss << "service " << id_ << " {";
for (auto& rpc : rpcs_) {
ss << std::endl;
ss << " " << rpc->ToString();
}
if (!rpcs_.empty()) {
ss << std::endl;
}
ss << "};";
return ss.str();
}
}// namespace tqcq

31
src/service.h Normal file
View File

@ -0,0 +1,31 @@
//
// Created by tqcq on 2023/12/1.
//
#ifndef CRPC_SRC_SERVICE_H_
#define CRPC_SRC_SERVICE_H_
#include "rpc.h"
namespace tqcq {
// serviceStatement: SERVICE_TYPE ID '{' rpc* '}' ';';
class Service {
public:
using Ptr = std::shared_ptr<Service>;
Service(std::string id);
std::string id() const;
void AddRPC(RPC::Ptr rpc);
void SetRPCs(std::vector<RPC::Ptr> rpcs);
std::vector<RPC::Ptr> rpcs() const;
std::string ToString();
private:
std::string id_;
std::vector<RPC::Ptr> rpcs_;
};
}// namespace tqcq
#endif//CRPC_SRC_SERVICE_H_

16
src/test.proto Normal file
View File

@ -0,0 +1,16 @@
syntax = "proto3";
message TestMessage {
int32 id = 1;
string name = 2;
};
service TestService {
rpc TestMethod(TestMessage) returns (TestMessage);
};
message SearchRequest {
string query = 1;
int32 page_number = 2;
int32 results_per_page = 3;
};