tars2node是用来将 tars协议文件
转换为Nodejs客户端/服务端代码文件的工具,仓库地址:https://github.com/tars-node/tars2node
目录结构
下面是主要内容的目录结构和各目录下代码的功能1
.
├── bin # 编译好的各平台可执行文件
├── src # 入口&代码生成器,根据AST生成目标代码
└── third_partly
├── tarsgrammar # tars协议文件的词法、文法定义(基于flex、bison)
├── tarsparse # tars协议的parser,解析、转换tars协议文件,得到AST
└── util # 工具代码
工作流程
parse阶段和 generate阶段的的逻辑是重点。
parse阶段
parse阶段的代码在 third_partly
目录中,主要工作是做tars协议词法/文法解析,并转换得到 AST。1
third_partly/
├── tarsgrammar
│ ├── tars.lex.cpp
│ ├── tars.tab.cpp
│ └── tars.tab.hpp
├── tarsparse
│ ├── element.cpp
│ ├── element.h
│ ├── parse.cpp
│ └── parse.h
└── util
tarsparse/element.h
中定义了 tars 协议的文法类型,包括:
- context —— 文件
- namespace —— 协议中的namespace
- interface —— 协议中的 interface
- operation —— interface 中的方法
- 各种数据结构 —— struct、map、vector、enum、各基本类型等
tarsparse/parser.h
为解析器入口,它调用 tarsgrammar
中的词法、文法解析器,得到 tarsparse/element.h
中定义的文法对象,即一个 AST 对象。
下面是tarsparse/parser.h
中定义的具体的解析结果,其中有 context → namespace → interface/strcut/enum/const
这样的包含关系。
1 | protected: |
下面是 parse阶段 流程的示意图。
generate阶段
generate阶段的代码在 src
目录中,主要工作是根据parse阶段得到的AST,生成不同的目标代码。1
src/
├── code_generator.cpp
├── code_generator.h # 代码生成器
├── file_util.cpp
├── gen_js.cpp # 生成js目标代码(struct + enum + const)
├── gen_js_dts.cpp # 生成js目标时,可选生成dts声明文件
├── gen_proxy.cpp # 生成js目标代码(client proxy)
├── gen_proxy_dts.cpp # 生成js目标代码(client proxy)时,可选生成dts声明文件
├── gen_proxy_ts.cpp # 生成ts目标代码(client proxy)
├── gen_server.cpp # 生成js目标代码(server imp)
├── gen_server_dts.cpp # 生成js目标代码(server imp)时,可选生成dts声明文件
├── gen_server_imp.cpp # 生成js目标代码(server imp 用户实现的imp业务逻辑文件)
├── gen_server_ts.cpp # 生成ts目标代码(server imp)
├── gen_server_ts_imp.cpp # 生成ts目标代码(server imp 用户实现的imp业务逻辑文件)
├── gen_ts.cpp # 生成ts目标代码(struct + enum + const)
├── idl_scan.cpp # 工具函数 idl扫描,缓存一些信息
├── idl_util.cpp # 工具函数
└── main.cpp # 整个程序入口
在开始生成代码之前,会根据输入选项从两个维度区分生成代码 —— 语言维度可以选择生成ts/js
,逻辑维度可以选择生成 主体成员/client/server
。其中主体成员是指协议中 namespace 包裹下,除了 interface 之外的部分, 包括 struct、enum、const
。
实际上 ts/js
的生成逻辑大同小异,而 主体成员/client/server
的生成路径略有差异,我们只需要挑一种进行分析,如分析js语言的生成过程。
主体成员
生成工作,只生成struct、enum、const
,不关注 interface 的实现。例如XXX.tars
,则会生成XXXTars.js
。client
生成工作,会包含主体成员
生成工作,并且加上 interface 中所有函数的client proxy调用函数
生成。例如XXX.tars
,则会生成XXXProxy.js
。server
生成工作,会包含主体成员
生成工作,并且加上 interface 中所有函数的server imp处理函数
生成,以及需要用户实现的imp 业务逻辑文件
。例如XXX.tars
,则会生成XXX.js
和XXXImp.js
。
1 | if (!_bClient && !_bServer) |
下面是 整个 generate 流程的示意图。
本文链接:https://www.zoucz.com/blog/2022/05/05/7d20b6c0-cc62-11ec-9fe7-534bbf9f369d/