编译原理Lab4-使用LightIR框架自动产生cminus-f语言的LLVM IR

  • [[#实验框架|实验框架]]
  • [[#实验过程|实验过程]]
    • [[#实验过程#全局变量的设计|全局变量的设计]]
    • [[#实验过程#1ASTProgram|1ASTProgram]]
    • [[#实验过程#2ASTNum|2ASTNum]]
    • [[#实验过程#3ASTVarDeclaration|3ASTVarDeclaration]]
    • [[#实验过程#4ASTFunDeclaration|4ASTFunDeclaration]]
    • [[#实验过程#5ASTParam|5ASTParam]]
    • [[#实验过程#6ASTCompoundStmt|6ASTCompoundStmt]]
    • [[#实验过程#7ASTExpressionStmt|7ASTExpressionStmt]]
    • [[#实验过程#8ASTSelectionStmt|8ASTSelectionStmt]]
    • [[#实验过程#9ASTIterationStmt|9ASTIterationStmt]]
    • [[#实验过程#10ASTReturnStmt|10ASTReturnStmt]]
    • [[#实验过程#11ASTVar|11ASTVar]]
    • [[#实验过程#12ASTAssignExpression|12ASTAssignExpression]]
    • [[#实验过程#13ASTSimpleExpression|13ASTSimpleExpression]]
    • [[#实验过程#14ASTAdditiveExpression|14ASTAdditiveExpression]]
    • [[#实验过程#15ASTTerm|15ASTTerm]]
    • [[#实验过程#16ASTCall|16ASTCall]]
  • [[#编译|编译]]
    • [[#编译#结果验证|结果验证]]
  • [[#实验总结|实验总结]]
  • [[#参考|参考]]

实验要求

需要使用 LightIR 框架自动产生 cminus-f 语言的LLVM IR。
要编写的代码是:修改 src/cminusfc/cminusf_builder.cpp ,其内含各个 visit 函数,我们需要补全这些 visit 的函数。来实现自动 IR 产生的算法,使得它能正确编译任何合法的 cminus-f 程序
要掌握 logging 工具,仔细看 Light IR 文档(主要看 C++ APIs 部分),仔细看cminus-f 的语法与语义

实验难点

  • 函数没有返回值,都是void类型,对于一些有计算结果的代码不好处理。但是实验有规定不能修改实验代码,用全局变量来存储返回值也是一个不错的选择,关键在于很正确的调用这些函数并且逻辑不出现错误,这样才能正确的传递信息
  • 在生成代码的时候需要对不符合要求的数值进行数据转换。

实验框架

本次实验使用了由C++编写的 LightIR 来生成 LLVM IR。为了便于大家进行实验,该框架自动完成了语法树到 C++ 上的抽象语法树的转换。 我们可以使用访问者模式来设计抽象语法树中的算法。大家可以参考打印抽象语法树的算法, 以及运行 test_ast 来理解访问者模式下算法的执行流程。

在include/cminusf_builder.hpp中,我还定义了一个用于存储作用域的类Scope。它的作用是辅助我们在遍历语法树时,管理不同作用域中的变量。它提供了以下接口:

// 进入一个新的作用域
void enter();
// 退出一个作用域
void exit();
// 往当前作用域插入新的名字->值映射
bool push(std::string name, Value *val);
// 根据名字,寻找到值
Value* find(std::string name);
// 判断当前是否在全局作用域内
bool in_global();

你们需要根据语义合理调用enter与exit,并且在变量声明和使用时正确调用push与find。在类CminusfBuilder中,有一个Scope类型的成员变量scope,它在初始化时已经将input、output等函数加入了作用域中。因此,你们在进行名字查找时不需要顾虑是否需要对特殊函数进行特殊操作。

实验过程

将实验仓库克隆到本地
打开本地的工作目录,在命令行中输入
git clone https://gitee.com/你的gitee用户名/cminus_compiler-2022-fall.git

全局变量的设计

#include "cminusf_builder.hpp"
#include "logging.hpp"
// use these macros to get constant value
// 得到常数值的表示,方便后面多次用到
#define CONST_INT(num) \ConstantInt::get(num, module.get())#define CONST_FP(num) \ConstantFP::get(num, module.get())// You can define global variables here
// to store state
Value *ret;               //存储返回值
std::vector<Type *> Ints; //储存参数的类型,以确定函数的类型
int return_flag = 0;       //全局变量标识当前模块是否已经有return语句

1ASTProgram

ASTProgram 语义规则为program -> declaration-list,遍历program下层的declaration-list中的声明即可

void CminusfBuilder::visit(ASTProgram &node) //遍历program下层的declaration-list中的声明
//program -> declaration-list
{for (auto decl : node.declarations){decl->accept(*this);}
}

2ASTNum

ASTNum判断node的类型(整型或浮点型),将将相应的value值存入,并将结果赋给ret,这里的ret是一个 全局变量。

void CminusfBuilder::visit(ASTNum &node) //判断node的类型(整型或浮点型),将将相应的value值存入,并将地址赋给ret{if(return_flag)return;if (node.type == TYPE_INT){ret = CONST_INT(node.i_val);}else if (node.type == TYPE_FLOAT){ret = CONST_FP(node.f_val);}}

3ASTVarDeclaration

语义规则为var-declaration -> type-specifier ID ; ∣ type-specifier ID [INTEGER ],变量要么是整型或浮点型,要么是数组。 首先使用scope.in_global判断是否为全局变量,然后根据node.num是否为空判断是否为数组变量 (若不为空则为数组变量),接着根据node.type判断是整型还是浮点型,最后将变量名和变量值 压入scope中。

void CminusfBuilder::visit(ASTVarDeclaration &node) //变量声明
//var-declaration -> type-specifier ID ; ∣ type-specifier ID [INTEGER ]
{if(return_flag)return;auto TyInt32 = Type::get_int32_type(module.get());auto TyFloat = Type::get_float_type(module.get());if (!scope.in_global()) //局部{if (node.num != nullptr) //数组(指针非空{if(!node.num->i_val){Value * call_error = scope.find("neg_idx_except");//数组定义是大小为零时,打印报错信息builder->create_call(call_error,{});}if (node.type == TYPE_INT) //整型数组{auto *arrayType = ArrayType::get(TyInt32, node.num->i_val);auto Local_IntArrayAlloca = builder->create_alloca(arrayType); //为数组分配空间scope.push(node.id, Local_IntArrayAlloca);}else if (node.type == TYPE_FLOAT) //浮点型数组{auto *arrayType = ArrayType::get(TyFloat, node.num->i_val);auto Local_FloatArrayAlloca = builder->create_alloca(arrayType); //为数组分配空间scope.push(node.id, Local_FloatArrayAlloca);}}else //变量{if (node.type == TYPE_INT) //整型变量{auto Local_IntAlloca = builder->create_alloca(TyInt32); //为变量分配空间scope.push(node.id, Local_IntAlloca);}else if (node.type == TYPE_FLOAT) //浮点型变量{auto Local_FloatAlloca = builder->create_alloca(TyFloat); //为变量分配空间scope.push(node.id, Local_FloatAlloca);}}}else //全局{if (node.num != nullptr ) //数组(指针非空{if(!node.num->i_val){Value * call_error = scope.find("neg_idx_except");builder->create_call(call_error,{});}if (node.type == TYPE_INT) //整型数组{auto *arrayType = ArrayType::get(TyInt32, node.num->i_val);auto initializer = ConstantZero::get(arrayType, module.get());auto Globle_IntArrayAlloca = GlobalVariable::create(node.id, module.get(), arrayType, false, initializer); //为数组分配空间scope.push(node.id, Globle_IntArrayAlloca);}else if (node.type == TYPE_FLOAT) //浮点型数组{auto *arrayType = ArrayType::get(TyFloat, node.num->i_val);auto initializer = ConstantZero::get(arrayType, module.get()); //初始值赋为零auto Globle_FloatArrayAlloca = GlobalVariable::create(node.id, module.get(), arrayType, false, initializer); //为数组分配空间scope.push(node.id, Globle_FloatArrayAlloca);}}else //变量{if (node.type == TYPE_INT) //整型变量{auto initializer = ConstantZero::get(TyInt32, module.get());auto Globle_IntAlloca = GlobalVariable::create(node.id, module.get(), TyInt32, false, initializer); //为变量分配空间scope.push(node.id, Globle_IntAlloca);}else if (node.type == TYPE_FLOAT) //浮点型变量{auto initializer = ConstantZero::get(TyFloat, module.get());auto Globle_FloatAlloca = GlobalVariable::create(node.id, module.get(), TyFloat, false, initializer); //为变量分配空间scope.push(node.id, Globle_FloatAlloca);}}}
}

4ASTFunDeclaration

语义规则为fun-declaration → type-specifier ID ( params ) compound-stmt 首先要用create构造函数,那么就要得到函数的类型和参数列表,根据node.type得到函数类型, 然后遍历node.params,对于每一个param,得到其参数类型。若param为数组,则开辟数组空间,若param为整型或浮点型变量,则开辟单个变量的空间,然后把参数store下来。最后处理 compound_stmt。

void CminusfBuilder::visit(ASTFunDeclaration &node) //函数声明//fun-declaration → type-specifier ID ( params ) compound-stmt{scope.enter(); //进入函数的作用域Type *TYPE32 = Type::get_int32_type(module.get());Type *TyFloat = Type::get_float_type(module.get());Type *TYPEV = Type::get_void_type(module.get());Type *TYPEARRAY_32 = PointerType::get_int32_ptr_type(module.get());Type *funType;Type *TYPEARRAY_INT_32 = PointerType::get_int32_ptr_type(module.get());Type *TYPEARRAY_FLOAT_32 = PointerType::get_float_ptr_type(module.get());//判断新声明的function的返回值类型if (node.type == TYPE_FLOAT)funType = TyFloat;else if(node.type == TYPE_VOID){funType = TYPEV;}else{funType = TYPE32;}// 函数参数的vectorstd::vector<Value *> args;if (node.params.size() > 0) //参数列表非空{for (auto param : node.params) //遍历Fundeclaration下的param节点,得到参数类型{param->accept(*this); //得到参数类型}auto fun = Function::create(FunctionType::get(funType, Ints), node.id, module.get()); //由函数类型定义函数auto bb = BasicBlock::create(module.get(), "entry", fun); // BB的名字在生成中无所谓,但是可以方便阅读builder->set_insert_point(bb);scope.exit(); //先退出当前作用域scope.push(node.id, fun); //函数名放进作用域scope.enter(); //再次进入函数的作用域for (auto param : node.params) //遍历Fundeclaration下的param节点,根据参数的类型分配空间{if (param->isarray) //数组{if (param->type == TYPE_INT) //整型数组{auto pAlloca = builder->create_alloca(TYPEARRAY_INT_32); //在内存中分配空间scope.push(param->id, pAlloca);}else if (param->type == TYPE_FLOAT)  //浮点型数组{auto pAlloca = builder->create_alloca(TYPEARRAY_FLOAT_32);scope.push(param->id, pAlloca);}}else if (param->type == TYPE_INT) //整型{auto pAlloca = builder->create_alloca(TYPE32);scope.push(param->id, pAlloca);}else if (param->type == TYPE_FLOAT) //浮点型{auto pAlloca = builder->create_alloca(TyFloat);scope.push(param->id, pAlloca);}}for (auto arg = fun->arg_begin(); arg != fun->arg_end(); arg++) //获取形参{args.push_back(*arg); // * 号运算符是从迭代器中取出迭代器当前指向的元素}int i = 0;for (auto param : node.params) //将参数store下来{auto pAlloca = scope.find(param->id);if (pAlloca == nullptr)exit(0);elsebuilder->create_store(args[i++], pAlloca);Ints.pop_back(); //清空向量}}else //参数列表为空{auto fun = Function::create(FunctionType::get(funType, Ints), node.id, module.get()); //由函数类型定义函数auto bb = BasicBlock::create(module.get(), "entry", fun);// BB的名字在生成中无所谓,但是可以方便阅读builder->set_insert_point(bb);scope.exit(); //先退出当前作用域scope.push(node.id, fun); //函数名放进作用域scope.enter(); //再次进入函数的作用域}node.compound_stmt->accept(*this); //执行compound-stmtif(return_flag == 0) //当前模块没有return{auto return_type = builder->get_insert_block()->get_parent()->get_return_type();if(return_type->is_void_type())builder->create_void_ret();else if(return_type->is_integer_type())builder->create_ret(CONST_INT(0));elsebuilder->create_ret(CONST_FP(0));}return_flag = 0;scope.exit();}

5ASTParam

语义规则为param -> type-specifier ID | type-specifier ID [],对于每一个传进来的参数node,将 其类型存入到全局变量Ints中。 若为数组类型则将数组类型push进Ints(参数列表)中,若为变量类型则将变量类型push进Ints (参数列表)中。

void CminusfBuilder::visit(ASTParam &node) //对于每一个传进来的参数node,将其类型存入到全局变量Ints中 //参数//param -> type-specifier ID | type-specifier ID []
{Type *TYPE32 = Type::get_int32_type(module.get());Type *TyFloat = Type::get_float_type(module.get());Type *TYPEARRAY_INT_32 = PointerType::get_int32_ptr_type(module.get());Type *TYPEARRAY_FLOAT_32 = PointerType::get_float_ptr_type(module.get());//返回参数类型并分配空间if (node.isarray) //数组参数{if (node.type == TYPE_INT){Ints.push_back(TYPEARRAY_INT_32);}else if (node.type == TYPE_FLOAT){Ints.push_back(TYPEARRAY_FLOAT_32);}}else if (node.type == TYPE_INT) //整型{Ints.push_back(TYPE32);}else if (node.type == TYPE_FLOAT) //浮点型{Ints.push_back(TyFloat);}return;
}

6ASTCompoundStmt

语义规则为compound-stmt -> { local-declarations statement-list }。 处理每个局部声明和每个语句,然后退出作用域

void CminusfBuilder::visit(ASTCompoundStmt &node) //复合语句
// compound-stmt -> { local-declarations statement-list }
{if(return_flag)return;scope.enter(); //先进入到新的作用域,可能有局部变量for (auto loc_decl : node.local_declarations) //遍历所有local_declarations{loc_decl->accept(*this);}for (auto stmt : node.statement_list) //遍历所有statement-list{stmt->accept(*this);}scope.exit();
}

7ASTExpressionStmt

语义规则为expression-stmt→expression ; ∣ ;expression→assign-expression ∣ simple expression 若expression存在(即不为空),则处理。

void CminusfBuilder::visit(ASTExpressionStmt &node) //对expression结点调用accept //表达式语句//expression-stmt→expression ; ∣ ;//expression→assign-expression ∣ simple-expression{if(return_flag)return;if (node.expression != nullptr){node.expression->accept(*this);}}

8ASTSelectionStmt

首先accpet判断语句,若为float则用fcmp与0比较,若为int则用icmp与0比较,若为指针则将地址 中的值load出来赋给ret后再进行比较。 然后判断是否存在else语句,若存在则创建true块和false块,若在块中不存在return,则都去到 next块,若不存在则只创建true块和next块,如果true块中没有return则去到next块。

void CminusfBuilder::visit(ASTSelectionStmt &node) //if
//selection-stmt→ ​if ( expression ) statement∣ if ( expression ) statement else statement​
{if(return_flag)return;Type *TYPE32 = Type::get_int32_type(module.get());node.expression->accept(*this);if (ret->get_type()->is_pointer_type()) //指针ret = builder->create_load(ret);if (ret->get_type()->is_float_type())ret = builder->create_fcmp_ne(ret, CONST_FP(0));else if (ret->get_type() == TYPE32)ret = builder->create_icmp_ne(ret, CONST_INT(0));//currentFunctionauto currentFunc = builder->get_insert_block()->get_parent();auto trueBB = BasicBlock::create(module.get(), "", currentFunc); //创建true分支BasicBlock *falseBB;BasicBlock *nextBB;BranchInst *br;int insertedflag = 0;if (node.else_statement != nullptr) //有else{falseBB = BasicBlock::create(module.get(), "", currentFunc);br = builder->create_cond_br(ret, trueBB, falseBB);//falseBBbuilder->set_insert_point(falseBB);node.else_statement->accept(*this);if (builder->get_insert_block()->get_terminator() == nullptr){ // no return inside the blockinsertedflag = 1;nextBB = BasicBlock::create(module.get(), "", currentFunc);builder->create_br(nextBB);}return_flag = 0;//tureBBbuilder->set_insert_point(trueBB);node.if_statement->accept(*this);if (builder->get_insert_block()->get_terminator() == nullptr){ // no return inside the blockif (insertedflag == 0){insertedflag = 1;nextBB = BasicBlock::create(module.get(), "", currentFunc);}builder->create_br(nextBB);}return_flag = !insertedflag;//nextBBif (insertedflag == 1){builder->set_insert_point(nextBB);}}else //无else{//tureBBnextBB = BasicBlock::create(module.get(), "", currentFunc);br = builder->create_cond_br(ret, trueBB, nextBB);builder->set_insert_point(trueBB);node.if_statement->accept(*this);if (return_flag == 0){builder->create_br(nextBB);}return_flag = 0;//nextBBbuilder->set_insert_point(nextBB);}
}

9ASTIterationStmt

语义规则为iteration-stmt→while ( expression ) statement
首先创建loopJudge, loopBody和out块, 然后进入loopJudge块,accept判断语句,获得判断语句 的结果,若判断结果为int,则用icmp与0比较,若判断结果为float,则用fcmp与0比较,若为真, 则进入loopBody块,如果loopBody块中没有return,就再次进入loopJudge块;若为假,就进入 out块。

void CminusfBuilder::visit(ASTIterationStmt &node)//迭代语句 while
//iteration-stmt→while ( expression ) statement
{if(return_flag)return;Type *TYPE32 = Type::get_int32_type(module.get());//currentFunctionauto currentFunc = builder->get_insert_block()->get_parent();auto loopJudge = BasicBlock::create(module.get(), "", currentFunc);auto loopBody = BasicBlock::create(module.get(), "", currentFunc);auto out = BasicBlock::create(module.get(), "", currentFunc);if (builder->get_insert_block()->get_terminator() == nullptr)builder->create_br(loopJudge);//loopJudge BBbuilder->set_insert_point(loopJudge);node.expression->accept(*this);if (ret->get_type()->is_pointer_type())ret = builder->create_load(ret);if (ret->get_type()->is_float_type())ret = builder->create_fcmp_ne(ret, CONST_FP(0));else if (ret->get_type() == TYPE32)ret = builder->create_icmp_ne(ret, CONST_INT(0));auto br = builder->create_cond_br(ret, loopBody, out);//loopBody BBbuilder->set_insert_point(loopBody);node.statement->accept(*this);if (builder->get_insert_block()->get_terminator() == nullptr)builder->create_br(loopJudge);return_flag = 0;//outloop BBbuilder->set_insert_point(out);
}

10ASTReturnStmt

语义规则为return-stmt→return ; ∣ return expression ;
首先得到返回值类型,若为void则创建void返回,若为指针则将地址中的值load出来然后返回,若 为整型或浮点型则做类型匹配,最后用create_ret对返回值进行返回。

void CminusfBuilder::visit(ASTReturnStmt &node) //return语句 //强制转换//return-stmt→return ; ∣ return expression ;{if(return_flag)return;Type *TYPE32 = Type::get_int32_type(module.get());Type *TYPE1 = Type::get_int1_type(module.get());Type *TyFloat = Type::get_float_type(module.get());auto return_type = builder->get_insert_block()->get_parent()->get_return_type();if (node.expression == nullptr) //空指针{if (!return_type->is_void_type())printf("return_type is not void, but expression is empty\n");builder->create_void_ret();}else{node.expression->accept(*this);if (return_type->is_void_type()) //void型{printf("return_type is void, but expression is not empty\n");builder->create_void_ret();return;}if (ret->get_type()->is_pointer_type()) //ret为指针型ret = builder->create_load(ret);if (return_type == TYPE32) //转化为int型{if (ret->get_type() == TYPE1) //布尔型ret = builder->create_zext(ret, TYPE32);else if (ret->get_type() == TyFloat)ret = builder->create_fptosi(ret, TYPE32);}if (return_type == TyFloat) //转化为float型{if (ret->get_type()->is_integer_type())ret = builder->create_sitofp(ret, TyFloat);}builder->create_ret(ret);}return_flag = 1;}

11ASTVar

语义规则为var→ID ∣ ID [ expression] var
在cminus-f中比较简单,既可以是整型也可以是取了下标后的数组。

void CminusfBuilder::visit(ASTVar &node) //变量//var→ID ∣ ID [ expression]{if(return_flag)return;Type *FloatPtrType = Type::get_float_ptr_type(module.get());Type *Int32PtrType = Type::get_int32_ptr_type(module.get());Type *TYPE32 = Type::get_int32_type(module.get());Type *TYPE1 = Type::get_int1_type(module.get());//currentFunctionauto currentFunc = builder->get_insert_block()->get_parent();auto var = scope.find(node.id);if (var){if (node.expression != nullptr) //数组{node.expression->accept(*this);Value *num = ret;//转化为intif (num->get_type()->is_pointer_type())num = builder->create_load(num);if (num->get_type() == TYPE1)num = builder->create_zext(num, TYPE32);else if (num->get_type()->is_float_type())num = builder->create_fptosi(num, TYPE32);//if num < 0; enter exphandBBauto exphandBB = BasicBlock::create(module.get(), "", currentFunc);auto normalBB = BasicBlock::create(module.get(), "", currentFunc);auto outBB = BasicBlock::create(module.get(), "", currentFunc);auto flagnum = builder->create_icmp_ge(num, CONST_INT(0));auto br = builder->create_cond_br(flagnum, normalBB, exphandBB);//normalBBbuilder->set_insert_point(normalBB);if (var->get_type()->get_pointer_element_type()->is_pointer_type()){//var is an array that sub func get from main funcauto var_load = builder->create_load(var);var = builder->create_gep(var_load, {num});}else if (var->get_type()->get_pointer_element_type()->is_array_type()){//var is an id of array,get address of id[num]var = builder->create_gep(var, {CONST_INT(0), num});}else{printf("id is a float or int, but expression is not empty\n");}ret = var;builder->create_br(outBB);//exphandBBbuilder->set_insert_point(exphandBB);Value * call_error = scope.find("neg_idx_except");builder->create_call(call_error, {});builder->create_br(outBB);//outBBbuilder->set_insert_point(outBB);}else{if (!(var->get_type()->get_pointer_element_type()->is_float_type()) && !(var->get_type()->get_pointer_element_type()->is_integer_type()) && !(var->get_type()->get_pointer_element_type()->is_array_type())){var = builder->create_load(var);}ret = var;}}else{printf("cannot find the var\n");return;}}

12ASTAssignExpression

语义规则为assign-expression→var = expression
首先accept拿到表达式左边var的地址,然后accept拿到表达式右边expression的值,匹配左值和 右值的类型,把左值store到右值的地址中。

void CminusfBuilder::visit(ASTAssignExpression &node) //赋值 //强制转换//assign-expression→var = expression{if(return_flag)return;Type *TYPE32 = Type::get_int32_type(module.get());Type *TYPE1 = Type::get_int1_type(module.get());Type *TYPEFLOAT = Type::get_float_type(module.get());node.var.get()->accept(*this);Value *var = ret;node.expression.get()->accept(*this);if (var->get_type()->get_pointer_element_type()->is_float_type()) //指向的是float型{if (ret->get_type()->is_pointer_type())ret = builder->create_load(ret);if (ret->get_type()->is_integer_type())ret = builder->create_sitofp(ret, TYPEFLOAT);builder->create_store(ret, var);}else //指向的是int型{if (ret->get_type()->is_pointer_type())ret = builder->create_load(ret);if (ret->get_type() == TYPE1)ret = builder->create_zext(ret, TYPE32);else if (ret->get_type()->is_float_type())ret = builder->create_fptosi(ret, TYPE32);builder->create_store(ret, var);}}

13ASTSimpleExpression

语义规则为simple-expression -> additive-expression relop additive- expression | additive expression

首先accept左边,再accept右边,然后判断左右两边的类型,若至少有一个为float,则把两个都转 成float,再判断op类型,左边和右边进行比较,将结果作为整数保存。

void CminusfBuilder::visit(ASTSimpleExpression &node) //简单表达式//simple-expression -> additive-expression relop additive- expression | additive-expression{if(return_flag)return;Type *Int32Type = Type::get_int32_type(module.get());Type *FloatType = Type::get_float_type(module.get());Type *Int1Type = Type::get_int1_type(module.get());//simple-expression -> additive-expressionif (!node.additive_expression_r) //简单加法表达式,通过accept调用下一层级{node.additive_expression_l->accept(*this);}//simple-expression -> additive-expression relop additive- expressionelse //关系表达式,运算结果为整型1 或者 0{//获取左值和右值Value *AdditiveLoad_l;Value *AdditiveLoad_r;Value *icmp;node.additive_expression_l->accept(*this);if (ret->get_type()->is_pointer_type())AdditiveLoad_l = builder->create_load(ret);elseAdditiveLoad_l = ret;node.additive_expression_r->accept(*this);if (ret->get_type()->is_pointer_type())AdditiveLoad_r = builder->create_load(ret);elseAdditiveLoad_r = ret;int flag = 0; //标志是否为浮点数if (AdditiveLoad_l->get_type()->is_float_type()) //l是浮点数{flag = 1;if (AdditiveLoad_r->get_type()->is_integer_type())AdditiveLoad_r = builder->create_sitofp(AdditiveLoad_r, FloatType);}else{if (AdditiveLoad_r->get_type()->is_float_type()) //r是浮点数{flag = 1;AdditiveLoad_l = builder->create_sitofp(AdditiveLoad_l, FloatType);}else{flag = 0;if (AdditiveLoad_l->get_type() == Int1Type)AdditiveLoad_l = builder->create_zext(AdditiveLoad_l, Int32Type);if (AdditiveLoad_r->get_type() == Int1Type)AdditiveLoad_r = builder->create_zext(AdditiveLoad_r, Int32Type);}}if (flag == 1) //float型{switch (node.op){case OP_GE:icmp = builder->create_fcmp_ge(AdditiveLoad_l, AdditiveLoad_r);break;case OP_GT:icmp = builder->create_fcmp_gt(AdditiveLoad_l, AdditiveLoad_r);break;case OP_LE:icmp = builder->create_fcmp_le(AdditiveLoad_l, AdditiveLoad_r);break;case OP_LT:icmp = builder->create_fcmp_lt(AdditiveLoad_l, AdditiveLoad_r);break;case OP_EQ:icmp = builder->create_fcmp_eq(AdditiveLoad_l, AdditiveLoad_r);break;case OP_NEQ:icmp = builder->create_fcmp_ne(AdditiveLoad_l, AdditiveLoad_r);break;default:break;}}else //int型{switch (node.op){case OP_GE:icmp = builder->create_icmp_ge(AdditiveLoad_l, AdditiveLoad_r);break;case OP_GT:icmp = builder->create_icmp_gt(AdditiveLoad_l, AdditiveLoad_r);break;case OP_LE:icmp = builder->create_icmp_le(AdditiveLoad_l, AdditiveLoad_r);break;case OP_LT:icmp = builder->create_icmp_lt(AdditiveLoad_l, AdditiveLoad_r);break;case OP_EQ:icmp = builder->create_icmp_eq(AdditiveLoad_l, AdditiveLoad_r);break;case OP_NEQ:icmp = builder->create_icmp_ne(AdditiveLoad_l, AdditiveLoad_r);break;default:break;}}ret = icmp;}}

14ASTAdditiveExpression

语义规则为additive-expression -> additive-expression addop term | term
首先accept additive_expression和term,然后判断二者类型,若至少有一个为float,则将两个都 转成float,然后进行计算

void CminusfBuilder::visit(ASTAdditiveExpression &node)//additive-expression -> additive-expression addop term | term{if(return_flag)return;Type *Int32Type = Type::get_int32_type(module.get());Type *Int1Type = Type::get_int1_type(module.get());Type *FloatType = Type::get_float_type(module.get());Value *AdditiveExpression;Value *Term;Value *icmp;//additive-expression -> termif (node.additive_expression == nullptr) //如果只是简单的项,转到下一层{node.term->accept(*this);}//additive-expression -> additive-expression addop termelse{node.additive_expression->accept(*this);if (ret->get_type()->is_pointer_type())AdditiveExpression = builder->create_load(ret);elseAdditiveExpression = ret;node.term->accept(*this);if (ret->get_type()->is_pointer_type())Term = builder->create_load(ret);elseTerm = ret;int flag = 0;if (AdditiveExpression->get_type()->is_float_type()) //如果是浮点数相加{flag = 1;if (Term->get_type()->is_integer_type())Term = builder->create_sitofp(Term, FloatType);}else{if (Term->get_type()->is_float_type()) //term为float型{flag = 1;AdditiveExpression = builder->create_sitofp(AdditiveExpression, FloatType);}else{flag = 0;if (AdditiveExpression->get_type() == Int1Type)AdditiveExpression = builder->create_zext(AdditiveExpression, Int32Type);if (Term->get_type() == Int1Type)Term = builder->create_zext(Term, Int32Type);}}if (flag == 1) //float型{if (node.op == OP_PLUS){icmp = builder->create_fadd(AdditiveExpression, Term);}else{icmp = builder->create_fsub(AdditiveExpression, Term);}}else //int型{if (node.op == OP_PLUS){icmp = builder->create_iadd(AdditiveExpression, Term);}else{icmp = builder->create_isub(AdditiveExpression, Term);}}ret = icmp;}}

15ASTTerm

语义规则为term -> term mulop factor | factor
大致和ASTAdditiveExpression差不多。

void CminusfBuilder::visit(ASTTerm &node)//term -> term mulop factor | factor{if(return_flag)return;Type *Int32Type = Type::get_int32_type(module.get());Type *Int1Type = Type::get_int1_type(module.get());Type *FloatType = Type::get_float_type(module.get());Value *Term;Value *Factor;Value *icmp;//term -> factorif (!node.term){node.factor->accept(*this);}//term -> term mulop factorelse{node.term->accept(*this);if (ret->get_type()->is_pointer_type())Term = builder->create_load(ret);elseTerm = ret;node.factor->accept(*this);if (ret->get_type()->is_pointer_type())Factor = builder->create_load(ret);elseFactor = ret;int flag = 0;if (Term->get_type()->is_float_type()){flag = 1;if (Factor->get_type()->is_integer_type())Factor = builder->create_sitofp(Factor, FloatType);}else{if (Factor->get_type()->is_float_type()){flag = 1;Term = builder->create_sitofp(Term, FloatType);}else{flag = 0;if (Factor->get_type() == Int1Type)Factor = builder->create_zext(Factor, Int32Type);if (Term->get_type() == Int1Type)Term = builder->create_zext(Term, Int32Type);}}if (flag == 1){if (node.op == OP_MUL){icmp = builder->create_fmul(Term, Factor);}else{icmp = builder->create_fdiv(Term, Factor);}}else{if (node.op == OP_MUL){icmp = builder->create_imul(Term, Factor);}else{icmp = builder->create_isdiv(Term, Factor);}}ret = icmp;}}

16ASTCall

首先scope.find找到对应的函数,get_type获取函数类型,然后遍历node.args,检查参数类型是 否匹配,若形参为int或float且形参与实参类型不匹配,则将实参转换为形参类型,压入function (参数列表)。

void CminusfBuilder::visit(ASTCall &node){//根据名字寻找到对应的值if(return_flag)return; Value *value;value = scope.find(node.id); //调用scope.find()找ID对应的值if (value == nullptr) //是不是函数名{printf("cannot find the fun\n");return;}auto fun = value->get_type();if (!fun->is_function_type()) //检查函数类型return;auto callfun = static_cast<FunctionType *>(fun);Value *value_args;int i = 0;std::vector<Value *> function;Type *Int32Type = Type::get_int32_type(module.get());Type *FloatType = Type::get_float_type(module.get());Type *Int32PtrType = Type::get_int32_ptr_type(module.get());Type *Int1Type = Type::get_int1_type(module.get());for (auto Args : node.args) //检查参数类型是否匹配{auto arg_type = callfun->get_param_type(i);i++;Args->accept(*this);if (ret->get_type() == Int1Type) //如果ret是布尔型,ret先转换成32位整型{ret = builder->create_zext(ret, Int32Type);}if (arg_type == Int32Type) //要求的参数为整型{if (ret->get_type()->is_pointer_type())ret = builder->create_load(ret);else if (ret->get_type() == FloatType)ret = builder->create_fptosi(ret, Int32Type);value_args = ret;}else if (arg_type == FloatType) //要求的参数为浮点数{if (ret->get_type()->is_pointer_type())ret = builder->create_load(ret);else if (ret->get_type() == Int32Type)ret = builder->create_sitofp(ret, FloatType);value_args = ret;}else //要求的参数为指针{if (ret->get_type() == Int32Type || ret->get_type() == FloatType)return;value_args = ret;}function.push_back(value_args);}if (i != callfun->get_num_of_args()){printf("\t the num of arg error\n");return;}//call,get into sub funcret = builder->create_call(value, function);}

编译

mkdir build
cd build
cmake … -DLLVM_DIR=/path/to/your/llvm/install/lib/cmake/llvm/

sunny2004@sunny2004-VirtualBox:~/lab1/cminus_compiler-2023-fall$ mkdir build
sunny2004@sunny2004-VirtualBox:~/lab1/cminus_compiler-2023-fall$ cd build
sunny2004@sunny2004-VirtualBox:~/lab1/cminus_compiler-2023-fall/build$ cmake .. -DLLVM_DIR=/path/to/your/llvm/install/lib/cmake/llvm/
-- The C compiler identification is GNU 5.4.0
-- The CXX compiler identification is GNU 5.4.0
-- Check for working C compiler: /usr/bin/cc
-- Check for working C compiler: /usr/bin/cc -- works
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Detecting C compile features
-- Detecting C compile features - done
-- Check for working CXX compiler: /usr/bin/c++
-- Check for working CXX compiler: /usr/bin/c++ -- works
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Detecting CXX compile features
-- Detecting CXX compile features - done
-- Found FLEX: /usr/bin/flex (found version "2.6.4") 
-- Found BISON: /usr/bin/bison (found version "3.5.1") 
-- Found LLVM 10.0.0
-- Using LLVMConfig.cmake in: /usr/lib/llvm-10/cmake
-- Configuring done
-- Generating done
-- Build files have been written to: /home/sunny2004/lab1/cminus_compiler-2023-fall/build

make -j

sunny2004@sunny2004-VirtualBox:~/lab1/cminus_compiler-2023-fall/build$ make -j
[  1%] [FLEX][lex] Building scanner with flex 2.6.4
Scanning dependencies of target cminus_io
Scanning dependencies of target OP_lib
Scanning dependencies of target common
[  3%] [BISON][syntax] Building parser with bison 3.5.1
Scanning dependencies of target IR_lib
[  5%] [FLEX][lex] Building scanner with flex 2.6.4
lexical_analyzer.l:60: warning, 无法匹配规则
syntax_analyzer.y[  7%] Building C object src/io/CMakeFiles/cminus_io.dir/io.c.o
lexical_analyzer.l:53: warning, 无法匹配规则
: 警告: 1 项偏移/归约冲突 [-Wconflicts-sr]
[ 18%] Building CXX object src/optimization/CMakeFiles/OP_lib.dir/ConstPropagation.cpp.o
[  9%] Building C object src/common/CMakeFiles/common.dir/syntax_tree.c.o
[ 11%] Building CXX object src/common/CMakeFiles/common.dir/logging.cpp.o
[ 13%] Building CXX object src/optimization/CMakeFiles/OP_lib.dir/Dominators.cpp.o
Scanning dependencies of target flex
[ 15%] Building CXX object src/lightir/CMakeFiles/IR_lib.dir/Value.cpp.o
[ 16%] Building CXX object src/lightir/CMakeFiles/IR_lib.dir/BasicBlock.cpp.o
[ 20%] Building CXX object src/lightir/CMakeFiles/IR_lib.dir/Constant.cpp.o
[ 22%] Building CXX object src/common/CMakeFiles/common.dir/ast.cpp.o
[ 24%] Building CXX object src/lightir/CMakeFiles/IR_lib.dir/Function.cpp.o
[ 26%] Building CXX object src/lightir/CMakeFiles/IR_lib.dir/Type.cpp.o
[ 28%] Building CXX object src/optimization/CMakeFiles/OP_lib.dir/Mem2Reg.cpp.o
[ 30%] Building CXX object src/lightir/CMakeFiles/IR_lib.dir/User.cpp.o
[ 32%] Building CXX object src/optimization/CMakeFiles/OP_lib.dir/LoopSearch.cpp.o
[ 33%] Building CXX object src/optimization/CMakeFiles/OP_lib.dir/ActiveVars.cpp.o
[ 35%] Building CXX object src/optimization/CMakeFiles/OP_lib.dir/LoopInvHoist.cpp.o
[ 37%] Building CXX object src/lightir/CMakeFiles/IR_lib.dir/GlobalVariable.cpp.o
[ 39%] Building CXX object src/lightir/CMakeFiles/IR_lib.dir/Module.cpp.o
[ 41%] Building CXX object src/lightir/CMakeFiles/IR_lib.dir/Instruction.cpp.o
[ 43%] Building CXX object src/lightir/CMakeFiles/IR_lib.dir/IRprinter.cpp.o
[ 45%] Building C object src/lexer/CMakeFiles/flex.dir/lex.yy.c.o
[ 47%] Linking C static library ../../libcminus_io.a
lexical_analyzer.l: In function ‘analyzer’:
lexical_analyzer.l:92:5: warning: suggest parentheses around assignment used as truth value [-Wparentheses]
lexical_analyzer.l: At top level:
/home/sunny2004/lab1/cminus_compiler-2023-fall/build/src/lexer/lex.yy.c:1320:17: warning: ‘yyunput’ defined but not used [-Wunused-function]static void yyunput (int c, char * yy_bp )^
/home/sunny2004/lab1/cminus_compiler-2023-fall/build/src/lexer/lex.yy.c:1363:16: warning: ‘input’ defined but not used [-Wunused-function]static int input  (void)^
Scanning dependencies of target syntax
[ 47%] Built target cminus_io
[ 49%] Building C object src/parser/CMakeFiles/syntax.dir/syntax_analyzer.c.o
[ 50%] Building C object src/parser/CMakeFiles/syntax.dir/lexical_analyzer.c.o
[ 52%] Linking C static library ../../libflex.a
/home/sunny2004/lab1/cminus_compiler-2023-fall/build/src/parser/lexical_analyzer.c:1301:17: warning: ‘yyunput’ defined but not used [-Wunused-function]static void yyunput (int c, char * yy_bp )^
syntax_analyzer.y: In function ‘parse’:
syntax_analyzer.y:180:5: warning: implicit declaration of function ‘yyrestart’ [-Wimplicit-function-declaration]
/home/sunny2004/lab1/cminus_compiler-2023-fall/build/src/parser/lexical_analyzer.c:1344:16: warning: ‘input’ defined but not used [-Wunused-function]static int input  (void)^
[ 52%] Built target flex
Scanning dependencies of target lexer
[ 54%] Linking C static library ../../libsyntax.a
[ 56%] Building C object tests/lab1/CMakeFiles/lexer.dir/main.c.o
[ 56%] Built target syntax
[ 58%] Linking C executable ../../lexer
[ 58%] Built target lexer
[ 60%] Linking CXX static library ../../libIR_lib.a
[ 62%] Linking CXX static library ../../libcommon.a
[ 62%] Built target IR_lib
Scanning dependencies of target stu_while_generator
Scanning dependencies of target stu_if_generator
Scanning dependencies of target stu_assign_generator
Scanning dependencies of target stu_fun_generator
Scanning dependencies of target gcd_array_generator
[ 64%] Building CXX object tests/lab3/CMakeFiles/stu_while_generator.dir/stu_cpp/while_generator.cpp.o
[ 66%] Building CXX object tests/lab3/CMakeFiles/stu_if_generator.dir/stu_cpp/if_generator.cpp.o
[ 69%] Building CXX object tests/lab3/CMakeFiles/stu_fun_generator.dir/stu_cpp/fun_generator.cpp.o
[ 71%] Building CXX object tests/lab3/CMakeFiles/gcd_array_generator.dir/ta_gcd/gcd_array_generator.cpp.o
[ 67%] Building CXX object tests/lab3/CMakeFiles/stu_assign_generator.dir/stu_cpp/assign_generator.cpp.o
[ 71%] Built target common
Scanning dependencies of target test_logging
Scanning dependencies of target test_ast
Scanning dependencies of target parser
[ 73%] Building CXX object tests/CMakeFiles/test_ast.dir/test_ast.cpp.o
[ 75%] Building CXX object tests/CMakeFiles/test_logging.dir/test_logging.cpp.o
[ 77%] Building C object tests/lab2/CMakeFiles/parser.dir/main.c.o
[ 79%] Linking CXX executable ../../parser
[ 79%] Built target parser
[ 81%] Linking CXX executable ../test_logging
[ 81%] Built target test_logging
[ 83%] Linking CXX executable ../test_ast
[ 84%] Linking CXX executable ../../stu_while_generator
[ 86%] Linking CXX executable ../../stu_if_generator
[ 88%] Linking CXX executable ../../stu_assign_generator
[ 88%] Built target test_ast
[ 90%] Linking CXX executable ../../stu_fun_generator
[ 92%] Linking CXX executable ../../gcd_array_generator
[ 92%] Built target stu_if_generator
[ 94%] Linking CXX static library ../../libOP_lib.a
[ 94%] Built target stu_while_generator
[ 94%] Built target stu_assign_generator
[ 94%] Built target stu_fun_generator
[ 94%] Built target gcd_array_generator
[ 94%] Built target OP_lib
Scanning dependencies of target cminusfc
[ 96%] Building CXX object src/cminusfc/CMakeFiles/cminusfc.dir/cminusfc.cpp.o
[ 98%] Building CXX object src/cminusfc/CMakeFiles/cminusfc.dir/cminusf_builder.cpp.o
[100%] Linking CXX executable ../../cminusfc
[100%] Built target cminusfc
sunny2004@sunny2004-VirtualBox:~/lab1/cminus_compiler-2023-fall/build$ 

make install(需要管理员权限)

root@sunny2004-VirtualBox:/home/sunny2004/lab1/cminus_compiler-2023-fall/build# make install
[  5%] Built target flex
[ 15%] Built target syntax
[ 18%] Built target cminus_io
[ 26%] Built target common
[ 47%] Built target IR_lib
[ 60%] Built target OP_lib
[ 66%] Built target cminusfc
[ 69%] Built target test_logging
[ 73%] Built target test_ast
[ 77%] Built target lexer
[ 81%] Built target parser
[ 84%] Built target stu_while_generator
[ 88%] Built target stu_if_generator
[ 92%] Built target stu_assign_generator
[ 96%] Built target stu_fun_generator
[100%] Built target gcd_array_generator
Install the project...
-- Install configuration: "Debug"
-- Installing: /usr/local/lib/libcminus_io.a
-- Installing: /usr/local/bin/cminusfc
root@sunny2004-VirtualBox:/home/sunny2004/lab1/cminus_compiler-2023-fall/build#

结果验证

运行 python lab4_test.py

sunny2004@sunny2004-VirtualBox:~/lab1/cminus_compiler-2023-fall/tests/lab4$ python lab4_test.py
===========TEST START===========
Case 01:	Success
Case 02:	Success
Case 03:	Success
Case 04:	Success
Case 05:	Success
Case 06:	Success
Case 07:	Success
Case 08:	Success
Case 09:	Success
Case 10:	Success
Case 11:	Success
Case 12:	Success
============TEST END============

实验总结

本次实验主要的难点在于读懂实验需求,以及对代码的特殊细节进行处理,刚开始做的时候非常困惑,查阅了非常多的资料,包括实验本身给的文档、各类头文件,也查阅了别人的代码和注释,参照了lab3 中给出的gcd_generator,才勉强写出代码。大量时间用在阅读和理解上。 在debug的过程中尝试了多种思路,包括使用log等等,但还是很难debug,参照别人的代码时也发现了 自己考虑问题不全面,接口不熟悉等一些问题,希望以后这样的实验还是能与同学多交流吧

参考

编译原理:cminus_compiler-2021-fall Lab4_编译原理实验 lab4-CSDN博客
还有一个:岳麓山大小姐的
https://blog.csdn.net/qq_45795586/article/details/122593206

上面
这个很管用

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/603993.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

LeetCode每日一题 | 383. 赎金信

文章目录 LeetCode-383. 赎金信题目描述问题分析程序代码&#xff08;Golang 版本&#xff09; LeetCode-383. 赎金信 题目描述 原题链接 给你两个字符串&#xff1a;ransomNote 和 magazine &#xff0c;判断 ransomNote 能不能由 magazine 里面的字符构成。 如果可以&#…

【代码片段】Linux C++打印当前函数调用堆栈

在开发大型项目时&#xff0c;尤其是多线程情况下&#xff0c;一般无法使用断点调试&#xff0c;这时候将当前函数的调用堆栈打印出来是非常有必要和有效的问题排查手段。 这里记录一段Linux环境下&#xff0c;打印函数堆栈的代码。 void get_native_callstack(std::string &a…

【ThreeJS入门——】WEB 3D可视化技术——threejs

简介 网页上已经可以做出很多复杂的动画&#xff0c;精美的效果。下图就是通过WebGL在网页中绘制高性能的3D图形。 threejs是一个让用户通过javascript入手进入搭建webgl项目的类库。 1、搭建第一个场景和物体 三维的物体要渲染在二维的屏幕上。首先要创建一个场景来放置物体…

c++学习第八讲---类和对象---继承

继承&#xff1a; 使子类&#xff08;派生类&#xff09;拥有与父类&#xff08;基类&#xff09;相同的成员&#xff0c;以节约代码量。 1.继承的基本语法&#xff1a; class 子类名&#xff1a;继承方式 父类名{} &#xff1b; 例&#xff1a; class father { public:in…

计算机毕业设计 | SpringBoot+vue移动端音乐网站 音乐播放器(附源码)

1&#xff0c;项目背景 随着计算机技术的发展&#xff0c;网络技术对我们生活和工作显得越来越重要&#xff0c;特别是现在信息高度发达的今天&#xff0c;人们对最新信息的需求和发布迫切的需要及时性。为了满足不同人们对网络需求&#xff0c;各种特色&#xff0c;各种主题的…

Plantuml之nwdiag网络图语法介绍(二十九)

简介&#xff1a; CSDN博客专家&#xff0c;专注Android/Linux系统&#xff0c;分享多mic语音方案、音视频、编解码等技术&#xff0c;与大家一起成长&#xff01; 优质专栏&#xff1a;Audio工程师进阶系列【原创干货持续更新中……】&#x1f680; 优质专栏&#xff1a;多媒…

【JUC】Volatile关键字+CPU/JVM底层原理

Volatile关键字 volatile内存语义 1.当写一个volatile变量时&#xff0c;JMM会把该线程对应的本地内存中的共享变量值立即刷新回主内存中。 2.当读一个volatile变量时&#xff0c;JMM会把该线程对应的本地内存设置为无效&#xff0c;直接从主内存中读取共享变量 所以volatile…

ARTrack 阅读记录

目录 环境配置与脚本编写 前向传播过程 网络结构 环境配置与脚本编写 按照官网执行并没有顺利完成&#xff0c;将yaml文件中的 pip 项 手动安装的 conda create -n artrack python3.9 # 启动该环境&#xff0c;并跳转到项目主目录路径下 astor0.8.1 configparser5.2.0 data…

C++学习笔记——友元及重载运算符

目录 一、友元 1.1声明友元函数 1.2声明友元类 二、运算符重载 2.1重载加号运算符 2.2重载流插入运算符 三、一个简单的银行管理系统 四、 详细的介绍 一、友元 在 C 中&#xff0c;友元是一个函数或类&#xff0c;它可以访问另一个类的私有成员或保护成员。通常情况下…

uView Alert 提示

用于页面中展示重要的提示信息。 基础用法# Alert 组件不属于浮层元素&#xff0c;不会自动消失或关闭。 Alert 组件提供四种类型&#xff0c;由 type 属性指定&#xff0c;默认值为 info。 success alert info alert warning alert error alert 主题# Alert 组件提供了…

HTML小白入门基础(概述,结构与基本常用标签)

目录 一、什么是HTML 二、HTML的基本结构&#xff1a; 三、结构与属性&#xff1a; 四、常见标签&#xff1a; 一、什么是HTML HTML&#xff1a;超文本标记语言(HyperText Markup Language) 超文本&#xff1a;指的是网页中可以显示的内容(图片&#x…

【Python机器学习】基于随机森林全球经济危机预测

一、引言 全球经济危机是一个复杂的问题,受到多种因素的影响,如金融市场、政策环境、地缘政治等。预测经济危机对于政策制定者、投资者和企业来说至关重要,因为它可以帮助他们提前做出应对措施,减少潜在的损失。然而,准确预测经济危机是一项具有挑战性的任务,因为涉及到…

【LeetCode739】每日温度

1、题目描述 【题目链接】 给定一个整数数组 temperatures &#xff0c;表示每天的温度&#xff0c;返回一个数组 answer &#xff0c;其中 answer[i] 是指对于第 i 天&#xff0c;下一个更高温度出现在几天后。如果气温在这之后都不会升高&#xff0c;请在该位置用 0 来代替。…

ROS+moveit+jakaminicob仿真运动

先浅浅的放一个官方的c文档&#xff1a; Motion Planning API — moveit_tutorials Melodic documentation 目录 一、实现运动到目标点的程序 二、在rviz里面新建扫描平台 一、实现运动到目标点的程序 &#xff08;等我得空了补一个c运行环境部署说明&#xff09; #inclu…

【Linux】CentOS 7重装保留数据的方法

我们需要重装CentOS 7系统&#xff0c;但是又想保留原来的数据。这篇文章将会从多个方面详细介绍如何重装CentOS 7系统&#xff0c;同时又能保留原有的数据。 一、备份重要数据 在重装CentOS 7系统之前&#xff0c;我们需要备份我们的重要数据。这可以通过多种方式实现&#…

React16源码: React中创建更新的方式及ReactDOM.render的源码实现

React当中创建更新的主要方式 ReactDOM.render || hydrate 这两个API都是我们要把整个应用第一次进行渲染到我们的页面上面能够展现出来我们整个应用的样子的一个过程这是初次渲染 setState 后续更新应用 forceUpdate 后续更新应用 replaceState 在后续被舍弃 关于 ReactDOM…

Qt undefined reference to `vtable for xxx‘

一、问题背景 在编译QT代码时&#xff0c;出现 undefined reference to xxx::entered()&#xff0c;通过鼠标双击QtCreator“问题栏”中的该行&#xff0c;则会跳转到发送信号的代码所在行。与上述代码一同出现在“问题栏”的还有 undefined reference to vtable for xxx’。 …

Git常用命令diff和mv

Git常用命令diff和mv 1、diff # 查看工作区和暂存区所有文件的对比 # 该命令可以显示尚未添加到stage的文件的变更 $ git diff# 查看工作区和暂存区单个文件的对比 $ git diff file# 显示暂存区和上一个commit的差异 # 查看暂存区与指定提交版本的不同,版本可缺省为HEAD $ gi…

力扣(leetcode)第412题Fizz Buzz(Python)

412.Fizz Buzz 题目链接&#xff1a;412.Fizz Buzz 给你一个整数 n &#xff0c;找出从 1 到 n 各个整数的 Fizz Buzz 表示&#xff0c;并用字符串数组 answer&#xff08;下标从 1 开始&#xff09;返回结果&#xff0c;其中&#xff1a; answer[i] “FizzBuzz” 如果 i 同…

Linux-文件系统管理实验2

1、将bin目录下的所有文件列表放到bin.txt文档中&#xff0c;并将一共有多少个命令的结果信息保存到该文件的最后一行。统计出文件中以b开头的所有命令有多少个&#xff0c;并将这些命令保存到b.txt文档中。将文档中以p结尾的所有命令保存到p.txt文件中&#xff0c;并统计有多少…