"); //-->
当得到token数组之后,我们对语法进行分析,并得到最终产物抽象语法树(不懂的请自己百度,这是编译原理中的概念).语法解析的过程是递归向下的,定义在Generate_函数中.
struct TokenNode {
int32_t num_index = -1;
std::shared_ptr<TokenNode> left = nullptr;
std::shared_ptr<TokenNode> right = nullptr;
TokenNode(int32_t num_index, std::shared_ptr<TokenNode> left, std::shared_ptr<TokenNode> right);
TokenNode() = default;
};
抽象语法树由一个二叉树组成,其中存储它的左子节点和右子节点以及对应的操作编号num_index. num_index为正, 则表明是输入的编号,例如@0,@1中的num_index依次为1和2. 如果num_index为负数则表明当前的节点是mul或者add等operator.
std::shared_ptr<TokenNode> ExpressionParser::Generate_(int32_t &index) {
CHECK(index < this->tokens_.size());
const auto current_token = this->tokens_.at(index);
CHECK(current_token.token_type == TokenType::TokenInputNumber
|| current_token.token_type == TokenType::TokenAdd || current_token.token_type == TokenType::TokenMul);
因为是一个递归函数,所以index指向token数组中的当前处理位置.current_token表示当前处理的token,它作为当前递归层的第一个Token, 必须是以下类型的一种.
TokenInputNumber = 0,
TokenAdd = 2,
TokenMul = 3,
如果当前token类型是输入数字类型, 则直接返回一个操作数token作为一个叶子节点,不再向下递归, 也就是在add(@0,@1)中的@0和@1,它们在前面的词法分析中被归类为TokenInputNumber类型.
if (current_token.token_type == TokenType::TokenInputNumber) {
uint32_t start_pos = current_token.start_pos + 1;
uint32_t end_pos = current_token.end_pos;
CHECK(end_pos > start_pos);
CHECK(end_pos <= this->statement_.length());
const std::string &str_number =
std::string(this->statement_.begin() + start_pos, this->statement_.begin() + end_pos);
return std::make_shared<TokenNode>(std::stoi(str_number), nullptr, nullptr);
}
else if (current_token.token_type == TokenType::TokenMul || current_token.token_type == TokenType::TokenAdd) {
std::shared_ptr<TokenNode> current_node = std::make_shared<TokenNode>();
current_node->num_index = -int(current_token.token_type);
index += 1;
CHECK(index < this->tokens_.size());
// 判断add之后是否有( left bracket
CHECK(this->tokens_.at(index).token_type == TokenType::TokenLeftBracket);
index += 1;
CHECK(index < this->tokens_.size());
const auto left_token = this->tokens_.at(index);
// 判断当前需要处理的left token是不是合法类型
if (left_token.token_type == TokenType::TokenInputNumber
|| left_token.token_type == TokenType::TokenAdd || left_token.token_type == TokenType::TokenMul) {
// (之后进行向下递归得到@0
current_node->left = Generate_(index);
} else {
LOG(FATAL) << "Unknown token type: " << int(left_token.token_type);
}
}
如果当前Token类型是mul或者add. 那么我们需要向下递归构建对应的左子节点和右子节点.
例如对于add(@1,@2),再遇到add之后,我们需要先判断是否存在left bracket, 然后再向下递归得到@1, 但是@1所代表的 数字类型,不会再继续向下递归.
当左子树构建完毕之后,我们将左子树连接到current_node的left指针中,随后我们开始构建右子树.此处描绘的过程体现在current_node->left = Generate_(index);中.
index += 1;
// 当前的index指向add(@1,@2)中的逗号
CHECK(index < this->tokens_.size());
// 判断是否是逗号
CHECK(this->tokens_.at(index).token_type == TokenType::TokenComma);
index += 1;
CHECK(index < this->tokens_.size());
// current_node->right = Generate_(index);构建右子树
const auto right_token = this->tokens_.at(index);
if (right_token.token_type == TokenType::TokenInputNumber
|| right_token.token_type == TokenType::TokenAdd || right_token.token_type == TokenType::TokenMul) {
current_node->right = Generate_(index);
} else {
LOG(FATAL) << "Unknown token type: " << int(left_token.token_type);
}
index += 1;
CHECK(index < this->tokens_.size());
CHECK(this->tokens_.at(index).token_type == TokenType::TokenRightBracket);
return current_node;
例如对于add(@1,@2),index当前指向逗号的位置,所以我们需要先判断是否存在comma, 随后开始构建右子树.右子树中的向下递归分析中得到了@2. 当右子树构建完毕后,我们将它(Generate_返回的节点,此处返回的是一个叶子节点,其中的数据是@2) 放到current_node的right指针中.
串联起来的例子简单来说,我们复盘一下add(@0,@1)这个例子.输入到Generate_函数中, 是一个token数组.
Generate_数组首先检查第一个输入是否为add,mul或者是input number中的一种.
CHECK(current_token.token_type == TokenType::TokenInputNumber||
current_token.token_type == TokenType::TokenAdd || current_token.token_type == TokenType::TokenMul);
第一个输入add,所以我们需要判断其后是否是left bracket来判断合法性, 如果合法则构建左子树.
else if (current_token.token_type == TokenType::TokenMul || current_token.token_type == TokenType::TokenAdd) {
std::shared_ptr<TokenNode> current_node = std::make_shared<TokenNode>();
current_node->num_index = -int(current_token.token_type);
index += 1;
CHECK(index < this->tokens_.size());
CHECK(this->tokens_.at(index).token_type == TokenType::TokenLeftBracket);
index += 1;
CHECK(index < this->tokens_.size());
const auto left_token = this->tokens_.at(index);
if (left_token.token_type == TokenType::TokenInputNumber
|| left_token.token_type == TokenType::TokenAdd || left_token.token_type == TokenType::TokenMul) {
current_node->left = Generate_(index);
}
处理下一个token, 构建左子树.
if (current_token.token_type == TokenType::TokenInputNumber) {
uint32_t start_pos = current_token.start_pos + 1;
uint32_t end_pos = current_token.end_pos;
CHECK(end_pos > start_pos);
CHECK(end_pos <= this->statement_.length());
const std::string &str_number =
std::string(this->statement_.begin() + start_pos, this->statement_.begin() + end_pos);
return std::make_shared<TokenNode>(std::stoi(str_number), nullptr, nullptr);
}
递归进入左子树后,判断是TokenType::TokenInputNumber则返回一个新的TokenNode到add token成为左子树.
检查下一个token是否为逗号,也就是在add(@0,@1)的@0是否为,
CHECK(this->tokens_.at(index).token_type == TokenType::TokenComma);
index += 1;
CHECK(index < this->tokens_.size());
下一步是构建add token的右子树
index += 1;
CHECK(index < this->tokens_.size());
const auto right_token = this->tokens_.at(index);
if (right_token.token_type == TokenType::TokenInputNumber
|| right_token.token_type == TokenType::TokenAdd || right_token.token_type == TokenType::TokenMul) {
current_node->right = Generate_(index);
} else {
LOG(FATAL) << "Unknown token type: " << int(left_token.token_type);
}
index += 1;
CHECK(index < this->tokens_.size());
CHECK(this->tokens_.at(index).token_type == TokenType::TokenRightBracket);
return current_node;
current_node->right = Generate_(index); /// 构建add(@0,@1)中的右子树
Generate_(index)递归进入后遇到的token是@1 token,因为是Input Number类型所在构造TokenNode后返回.
if (current_token.token_type == TokenType::TokenInputNumber) {
uint32_t start_pos = current_token.start_pos + 1;
uint32_t end_pos = current_token.end_pos;
CHECK(end_pos > start_pos);
CHECK(end_pos <= this->statement_.length());
const std::string &str_number =
std::string(this->statement_.begin() + start_pos, this->statement_.begin() + end_pos);
return std::make_shared<TokenNode>(std::stoi(str_number), nullptr, nullptr);
}
至此, add语句的抽象语法树构建完成.
struct TokenNode {
int32_t num_index = -1;
std::shared_ptr<TokenNode> left = nullptr;
std::shared_ptr<TokenNode> right = nullptr;
TokenNode(int32_t num_index, std::shared_ptr<TokenNode> left, std::shared_ptr<TokenNode> right);
TokenNode() = default;
};
在上述结构中, left存放的是@0表示的节点, right存放的是@1表示的节点.
专栏文章内容及配图由作者撰写发布,仅供工程师学习之用,如有侵权或者其他违规问题,请联系本站处理。 联系我们
相关推荐
EEPW2018年3月刊(工业物联网)
基于VisitionX制造智能眼镜
研华 COMPUTEX 首度整合全球伙伴大会 强化全球边缘 AI 生态系统联结
GPU:面临工作负载转变的高吞吐架构
AI热潮引发多层陶瓷电容MLCC供应短缺
电子元件培训教材
爱立信携手 Net Feasa 布局海事网络 融合公网级通信与智能体 AI 赋能航运
人工智能是如何帮助阻止造假者的?
基于Microchip MCU的AI/ML培训教程2
EEPW2018年6月刊(5G)
紧凑型集成连接器模块抑制噪声 为人工智能应用实现以太网供电
尼吉康的事业介绍
基于Microchip MCU的AI/ML培训教程3
AI 驱动估值飙升:光通信半导体企业市值暴涨
基于Ai-WB2-12F与Rd-04的雷达检测系统
赋能边缘端对话式人工智能
释说芯语16:硬科技:构建企业未来之路(附PPT)
继上次海联达Ai-ap100拆机之电源改造
AI竞争进入下半场:从“卷参数”到“卷单价”
Nigel AI赋能LabVIEW,NI用AI重塑测试新边界
瑞萨电子AI单元解决方案成功提高GE医疗(日本)日野工厂的生产力
基于Microchip MCU的AI/ML培训教程1
PowiGaN for AI Data Centers: Unmatched Power Density and Reliability
WTC-AI太阳能热水器电路图
万家乐JSYZ5-AI燃气热水器电路图
英伟达CFO:我们早就知道内存大涨价要来了
海联达(Aigale)Ai-HD1 无线全高清套件拆解
WTC-AI型太阳能热水器电路图
CSR8670CSR8675智能语音Alexa蓝牙方案开发
iCAN-4017 AI功能模块