编译原理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;

    }

    // 函数参数的vector

    std::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);

            else

                builder->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-stmt

    if(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));

        else

            builder->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));
    //currentFunction
    auto 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);
        //falseBB
        builder->set_insert_point(falseBB);
        node.else_statement->accept(*this);
        if (builder->get_insert_block()->get_terminator() == nullptr)
        { // no return inside the block
            insertedflag = 1;
            nextBB = BasicBlock::create(module.get(), "", currentFunc);
            builder->create_br(nextBB);
        }
        return_flag = 0;
        //tureBB
        builder->set_insert_point(trueBB);
        node.if_statement->accept(*this);
        if (builder->get_insert_block()->get_terminator() == nullptr)
        { // no return inside the block
            if (insertedflag == 0)
            {
                insertedflag = 1;
                nextBB = BasicBlock::create(module.get(), "", currentFunc);
            }
            builder->create_br(nextBB);
        }
        return_flag = !insertedflag;
        //nextBB
        if (insertedflag == 1)
        {
            builder->set_insert_point(nextBB);
        }
    }
    else //无else
    {
        //tureBB
        nextBB = 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;
        //nextBB
        builder->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());
    //currentFunction
    auto 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 BB
    builder->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 BB
    builder->set_insert_point(loopBody);
    node.statement->accept(*this);
    if (builder->get_insert_block()->get_terminator() == nullptr)
        builder->create_br(loopJudge);
    return_flag = 0;
    //outloop BB
    builder->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());

    //currentFunction

    auto 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;

            //转化为int

            if (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 exphandBB

            auto 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);

            //normalBB

            builder->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 func

                auto 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);

            //exphandBB

            builder->set_insert_point(exphandBB);

            Value * call_error = scope.find("neg_idx_except");

            builder->create_call(call_error, {});

            builder->create_br(outBB);

            //outBB

            builder->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-expression

    if (!node.additive_expression_r) //简单加法表达式,通过accept调用下一层级

    {

        node.additive_expression_l->accept(*this);

    }

    //simple-expression -> additive-expression relop additive- expression

    else //关系表达式,运算结果为整型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);

        else

            AdditiveLoad_l = ret;

        node.additive_expression_r->accept(*this);

        if (ret->get_type()->is_pointer_type())

            AdditiveLoad_r = builder->create_load(ret);

        else

            AdditiveLoad_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 -> term

    if (node.additive_expression == nullptr) //如果只是简单的项,转到下一层

    {

        node.term->accept(*this);

    }

    //additive-expression -> additive-expression addop term

    else

    {

        node.additive_expression->accept(*this);

        if (ret->get_type()->is_pointer_type())

            AdditiveExpression = builder->create_load(ret);

        else

            AdditiveExpression = ret;

        node.term->accept(*this);

        if (ret->get_type()->is_pointer_type())

            Term = builder->create_load(ret);

        else

            Term = 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 -> factor

    if (!node.term)

    {

        node.factor->accept(*this);

    }

    //term -> term mulop factor

    else

    {

        node.term->accept(*this);

        if (ret->get_type()->is_pointer_type())

            Term = builder->create_load(ret);

        else

            Term = ret;

        node.factor->accept(*this);

        if (ret->get_type()->is_pointer_type())

            Factor = builder->create_load(ret);

        else

            Factor = 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 func

    ret = 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

上面
这个很管用

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

简析云能耗管理系统在某高校建筑系统平台的设计与应用

叶根胜 安科瑞电气股份有限公司 上海嘉定 201801 摘要&#xff1a;根据本项目&#xff0c;依托某学院电能计量管理系统、供水计量监督系统、供热计量管理系统等基础平台&#xff0c;制定了高校建筑能耗综合管理系统平台应用的总体框架和方案。该系统可以实时监控、统计能耗和…

轻松掌握 Java Faker ,学点真本事,做点“假”数据~

工作中难免遇到需要造点“假”数据的情况&#xff0c;而且数据必须是“真”的&#xff0c;演示效果要好看一些。 一般接到这种要求&#xff0c;大部分的测试都不太知道该怎么去做。今天罗杰老师教你一招&#xff0c;让你做出逼真的“假”数据。 前言 1、什么是 Java Faker 伪…

【c++】list的特性及使用

目录 一、list的介绍 二、list的深度剖析与模拟实现 1、list图解 2、list增删查改模拟实现 三、list与vector的对比 一、list的介绍 STL中的list指的是带头双向循环链表。list是可以在常数范围内任意位置进行插入和删除的序列式容器&#xff0c;并且可以前后双向迭代。lis…

Activiti7官方在线流程设计器下载和部署

文章目录 一、流程设计器下载二、流程设计器简单运行三、流程设计器简单使用四、流程设计器持久化持久化会遇到的常见错误 五、流程设计器汉化说明菜单汉化操作汉化 参考文档 一、流程设计器下载 官网下载地址&#xff1a;https://www.activiti.org/get-started 点击直接获取官…

Flutter 小技巧之升级适配 Xcode15

美好的 2024 从「适配」开始&#xff0c;按照苹果的尿性&#xff0c;2024 春季开始大家将不得使用 Xcode15 来构建 App &#xff0c;另外根据《2024 的 iOS 的隐私清单》 要求&#xff0c;使用 Flutter 的开发者是无法逃避适配 Xcode15 更新的命运。 另外&#xff0c;众所周知…

vue3组件传参

1、props: 2、自定义事件子传父 3、mitt任意组件通讯 4、v-model通讯(v-model绑定在组件上) (1)V2中父子组件的v-model通信&#xff0c;限制了popos接收的属性名必须为value和emit触发的事件名必须为input,所以有时会有冲突; 父组件: 子组件: (2)V3中:限制了popos接收的属性名…

详解Java死锁-检测与解决

第1章&#xff1a;引言 大家好&#xff0c;我是小黑&#xff0c;咱们今天来聊聊死锁。特别是对于咱们这些Java程序员来说&#xff0c;死锁就像是隐藏在暗处的陷阱&#xff0c;稍不注意就会掉进去。但别担心&#xff0c;小黑今天就来带大家一探究竟&#xff0c;看看怎么样才能避…

什么是短视频矩阵系统?效果是怎么样的?

短视频矩阵系统是一种通过将多个短视频连接起来形成一个整体的系统。它的效果是可以提供一种连贯而有序的观看体验&#xff0c;使观众可以连续地观看一系列相关的短视频内容。 短视频矩阵系统的运作方式如下&#xff1a;首先&#xff0c;用户在平台上选择一个短视频开始观看。…

一款开源的MES系统

随着工业4.0的快速发展&#xff0c;制造执行系统&#xff08;MES&#xff09;成为了智能制造的核心。今天&#xff0c;将为大家推荐一款开源的MES系统——iMES工厂管家。 什么是iMES工厂管家 iMES工厂管家是一款专为中小型制造企业打造的开源MES系统。它具备高度的可定制性和灵…

刷了四百道算法题,我在项目里用过哪几道呢?

大家好&#xff0c;我是老三&#xff0c;今天和大家聊一个话题&#xff1a;项目中用到的力扣算法。 不知道从什么时候起&#xff0c;算法已经成为了互联网面试的标配&#xff0c;在十年前&#xff0c;哪怕如日中天的百度&#xff0c;面试也最多考个冒泡排序。后来&#xff0c;…

强化学习的数学原理学习笔记 - 策略梯度(Policy Gradient)

文章目录 概览&#xff1a;RL方法分类策略梯度&#xff08;Policy Gradient&#xff09;Basic Policy Gradient目标函数1&#xff1a;平均状态值目标函数2&#xff1a;平均单步奖励&#x1f7e1;PG梯度计算 &#x1f7e6;REINFORCE 本系列文章介绍强化学习基础知识与经典算法原…

android的求职APP 前端+后端

一 项目名称 基于android的求职APP&#xff0c;包含前台和后台管理系统的&#xff0c;前端主要移动端&#xff0c;应聘者注册账号&#xff0c;然后登陆&#xff0c;完善自己的简历&#xff0c;然后根据自己的需要投递岗位&#xff0c;查看面试邀请&#xff0c;后台主要维护数据…

听GPT 讲Rust源代码--compiler(34)

File: rust/compiler/rustc_middle/src/ty/print/mod.rs 在Rust源代码中&#xff0c;文件rust/compiler/rustc_middle/src/ty/print/mod.rs的作用是定义了打印类型和其他相关信息的功能。 具体来说&#xff0c;该文件中定义了三个trait&#xff0c;分别为Print<tcx>、Pri…

Java_特殊文件

一、属性文件 1.1 特殊文件概述 前面学习了IO流&#xff0c;知道IO流是用来读、写文件中的数据。但是接触到的文件都是普通的文本文件&#xff0c;普通的文本文件里面的数据是没有任何格式规范的&#xff0c;用户可以随意编写&#xff0c;如下图所示。 像这种普通的文本文件…

Selenium教程08:文件的上传+下载的示例练习

1.上传李白.txt文件&#xff0c;这里使用的send_keys方法操作&#xff0c;而不是click点击操作&#xff0c;因为使用点击操作之后&#xff0c;Selenium中没有方法对.exe程序操作&#xff0c;它只能对web网页自动化操作。 # Author : 小红牛 # 微信公众号&#xff1a;WdPython…

web前端开发技术复习问答题

目录 1.简述常见单标签和双标签有哪些&#xff1f; 2.常见块级元素和行级元素有哪些&#xff1f; 3.简述常见的列表有哪些&#xff1f;他们有什么区别&#xff1f; 4.简述超链接的href属性值如何设置&#xff1f;有什么区别 5.CSS基本语法 6. css中常见的引入方式有几种&…

AIGC-无人直播系统技术源头

AIGC-无人直播系统技术&#xff0c;作为当今科技领域的一项重要创新&#xff0c;正在引领着直播行业迈向更高的境界。那么&#xff0c;究竟是什么推动了这项技术的发展呢&#xff1f; 首先&#xff0c;我们不得不提到人工智能&#xff08;AI&#xff09;这一前沿技术的发展。随…

【数据库】CRUD常用函数UNION 和 UNION ALL

文章目录 一、CRUD二、函数2.1 字符函数 (Character Functions):2.2 数字函数 (Numeric Functions):2.3 日期函数 (Date Functions):2.4 流程控制函数:2.5 聚合函数: 三、UNION 和 UNION ALL3.1 UNION&#xff1a;3.2 UNION ALL3.3 注意事项 一、CRUD CRUD 是指数据库操作的四…

Qt/QML编程学习之心得:QProcess进程创建(27)

Qt除了线程Thread,进程也有支持类,那就是QProcess。 可以看出,这个类很大,支持的内容也很多。最简单的使用如: myParam << QString("-param hello") ; bool bRes = QProcess::startDetached("/usr/bin/myApplication", myParam);要启动进程,主…

Vue-4、单向数据绑定与双向数据绑定

1、单向数据绑定 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><title>数据绑定</title><!--引入vue--><script type"text/javascript" src"https://cdn.jsdelivr.net/npm/…