bison flex 实现tiny语言的编译器

news2025/1/16 13:57:22

bison flex 实现tiny语言的编译器

项目地址:tiny-compiler
完成了词法分析,语法分析,中间代码生成,虚拟机执行,没有进行类型检查、错误处理和中间代码优化。

词法分析

%{
#include <iostream>
#include "tiny-parser.tab.h"
using namespace std;
/* lexeme of identifier or reserved word */

extern FILE *fin; /* we read from this file */

extern int current_lineno ;

#undef YY_INPUT
#define YY_INPUT(buf,result,max_size) \
	if ( (result = fread( (char*)buf, sizeof(char), max_size, fin)) < 0) \
		YY_FATAL_ERROR( "read() in flex scanner failed");
%}

digit       [0-9]
number      {digit}+
letter      [a-zA-Z]
identifier  {letter}+
whitespace  [ \t]+

%%

"if"            {return IF_T;}
"then"          {return THEN;}
"else"          {return ELSE;}
"end"           {return END;}
"repeat"        {return REPEAT;}
"until"         {return UNTIL;}
"read"          {return READ;}
"write"         {return WRITE;}
":="            {return ASSIGN;}
"="             {return EQ;}
"<"             {return LT;}
"+"             {return PLUS;}
"-"             {return MINUS;}
"*"             {return TIMES;}
"/"             {return OVER;}
"("             {return LPAREN;}
")"             {return RPAREN;}
";"             {return SEMI;}
{number}        {return NUM;}
{identifier}    {return ID;}
\n             {current_lineno++;}
{whitespace}    {/* skip whitespace */}
\{[^\}]*\}          { 
                    if(yytext[0] == '\n') {
                        current_lineno++;
                    }
                }
"$"             {return EOF;}
"}"             {return ERROR;}
.               {return ERROR;}

%%

这里需要注意两个点,

  1. 定义了current_lineno变量,用于记录当前行号。每次碰到\n都是需要加一的
  2. YY_INPUT 宏,用于指定从哪里读入数据,具体的File * fin在main函数中设置。

语法分析

代码

%{
#include "TreeNode.h"
#define YYSTYPE TreeNode *
#include <string>

using namespace std;

extern int current_lineno ;
extern char *yytext;

int yylex();
int yyerror(const char *s);

static char saved_name[256]; /* for use in assignments */
static int saved_lineNo;  /* ditto */
extern TreeNode * root; /* stores syntax tree for later return */

%}

%token IF_T THEN ELSE END REPEAT UNTIL READ WRITE
%token ID NUM 
%token ASSIGN EQ LT PLUS MINUS TIMES OVER LPAREN RPAREN SEMI
%token ERROR 

%% /* Grammar for TINY */

program     : stmt_seq {root = $1; }
            ;
stmt_seq    : stmt_seq SEMI stmt {$1->children.push_back( $3);
                                $$ = $1;
                                   }
            | stmt  { $$=new_stmt_node();
                     $$->children.push_back( $1);
             }
            ;
stmt        : if_stmt { $$ = $1; }
            | repeat_stmt { $$ = $1; }
            | assign_stmt { $$ = $1; }
            | read_stmt { $$ = $1; }
            | write_stmt { $$ = $1; }
            | error  { $$ = NULL; }
            ;
if_stmt     : IF_T exp THEN stmt_seq END     
                 { 
                  $$ = new_if_stmt(current_lineno, $2, $4);
                 }
            | IF_T exp THEN stmt_seq ELSE stmt_seq END
                 { $$ = new_if_stmt(current_lineno, $2, $4, $6);
                 }
            ;
repeat_stmt : REPEAT stmt_seq UNTIL exp
                 { $$ = new_repeat_stmt(current_lineno, $2, $4);
                 }
            ;
assign_stmt : ID { $1=new_id(current_lineno, yytext);
               }
              ASSIGN exp
                 { 
                    $$ = new_assign_stmt(current_lineno, $1, $4);                
                 }
            ;
read_stmt   : READ ID
                 {
                  $$=new_read_stmt(current_lineno, new_id(current_lineno, yytext));
                 }
            ;
write_stmt  : WRITE exp
                 { 
                  $$=new_write_stmt(current_lineno, $2);
                 }
            ;
exp         : simple_exp LT simple_exp 
                 { 
                  $$ = new_exp(current_lineno, $1,LT,$3);
                 }
            | simple_exp EQ simple_exp
                 { 
                  $$=new_exp(current_lineno, $1,EQ,$3);
                 }
            | simple_exp { $$ =$1; }
            ;
simple_exp  : simple_exp PLUS term 
                 { 
                  $$=new_simple_exp(current_lineno, $1, PLUS, $3);
                 }
            | simple_exp MINUS term
                 { 
                  $$=new_simple_exp(current_lineno, $1, MINUS, $3);
                 } 
            | term { $$ = $1; }
            ;
term        : term TIMES factor 
                 { 
                  $$=new_term(current_lineno, $1, TIMES, $3);
                 }
            | term OVER factor
                 { 
                  $$=new_term(current_lineno, $1, OVER, $3);
                 }
            | factor { $$ = $1; }
            ;
factor      : LPAREN exp RPAREN
                 { $$ = $2; }
            | NUM
                 { 
                  $$=new_num(current_lineno, atoi(yytext));
                 }
            | ID {
                  $$=new_id(current_lineno, yytext);
                 }
            | error { $$ = NULL; }
            ;

%%

int yyerror(const char *s) {
     fprintf(stderr, "Error: %s at line %d near %s\n", s, current_lineno, yytext);
     return 0;
}

tiny语言的表达式还挺难写的

  1. 这里需要先定义token,生成tiny-parser.tab.h文件,然后让tiny-lexer.l包含,这样可以解决重定义token的问题。
  2. 对于每一个表达式的值,如果没有指定的话,就是int型,指定就是你自己定义的类型
    1. 一定要是指针类型,因为局部变量在过了这个函数之后,他就会自动销毁
    2. 不要用c++的智能指针,bison是c语言写的,听说,不支持那么高级的语法
    3. 示例,就是这个宏
#define YYSTYPE TreeNode *
  1. 注意这里是**$4不是$3**,前面的SDT也算是一个$,这个bug特别难找,莫名其妙的空指针错误
assign_stmt : ID { $1=new_id(current_lineno, yytext);
               }
              ASSIGN exp
                 { 
                    $$ = new_assign_stmt(current_lineno, $1, $4);                
                 }
            ;
  1. 要声明yylex()函数,这个是从词法解析里定义的入口函数。

接口

#ifndef TREE_NODE_H
#define TREE_NODE_H
#include <string.h>
#include <vector>
#include "tiny-parser.tab.h"

using namespace std;

extern int lineno ;

enum NodeKind{StmtK=1,ExpK} ;
enum StmtKind{Stmt_K=3,IfK,RepeatK,AssignK,ReadK,WriteK} ;
enum ExpKind {OpK=9,ConstK,IdK} ;
/* ExpType is used for type checking */
enum ExpType{Void_type=9,Integer,Boolean} ;


#define Token_Kind int

class TreeNode{
private:
    void show_info(int depth,bool is_last_child);
public:
    NodeKind kind;
    int lineno;
    union { StmtKind stmt; ExpKind exp;} production_kind;
    union { Token_Kind op;
            int val;   
            char *name; } attr;
    ExpType type; /* for type checking of exps */
    vector<TreeNode *> children;


    void copy_name(const char *name){
        this->attr.name=new char(strlen(name));
        strcpy(this->attr.name,name);
    };
    void show_all_info();


    TreeNode(int line){
        this->lineno = line;
    }

    template<typename... Args>
    TreeNode(int line,TreeNode * first_child, Args... args){
        this->lineno = line;
        this->children.push_back(first_child);
        (this->children.push_back(args),...);
    };
    ~TreeNode(){
        if(this->production_kind.exp==IdK){
            delete this->attr.name;
        }
    }

    virtual void generate_code()=0;
};


TreeNode * new_stmt_node();

TreeNode * new_if_stmt(int line,TreeNode * cond, TreeNode * then_stmt, TreeNode * else_stmt);
TreeNode * new_if_stmt(int line,TreeNode * cond, TreeNode * then_stmt);

TreeNode * new_repeat_stmt(int line, TreeNode * body,TreeNode * cond);

TreeNode * new_assign_stmt(int line,TreeNode * var, TreeNode * exp);

TreeNode * new_read_stmt(int line,TreeNode * var);

TreeNode * new_write_stmt(int line,TreeNode * exp);

TreeNode * new_exp(int line,TreeNode * left, Token_Kind op, TreeNode * right);
TreeNode * new_exp(int line,TreeNode * simple_exp);

TreeNode* new_simple_exp(int line,TreeNode * left, Token_Kind op, TreeNode * right);

TreeNode * new_term(int line,TreeNode * left, Token_Kind op, TreeNode * right);
TreeNode * new_term(int line,TreeNode * factor);


TreeNode * new_num(int line,int val);
TreeNode * new_id(int line,const char *name);

#endif

就是使用TreeNode*节点,记录当前的需要的信息,比方说 表达式的符号,变量的id,当前节点的行号,每次进行到规约的时候,就执行这个,把节点增加到根节点里去。

接口实现

#ifndef TREE_NODE_EXTERN_H
#define TREE_NODE_EXTERN_H
#include <iostream>
#include "TreeNode.h"
using namespace std;

extern FILE *fout;

class StmtNode : public TreeNode {

public:
    StmtNode(int line):TreeNode(line){

    };

    void generate_code() override{
    
        for(auto &child:this->children){
            child->generate_code();
        }
    
    }
};



class IfNode : public TreeNode {
public:
    static int if_count ;
    IfNode(int line, TreeNode* condition, TreeNode* true_stmt, TreeNode* false_stmt):TreeNode(line){};

    IfNode(int line, TreeNode* condition, TreeNode* true_stmt):TreeNode(line,condition,true_stmt){};

    void generate_code() override{
        if_count++;
        this->children[0]->generate_code();
        if(this->children.size()==3){
            //if else
            fprintf(fout,"if_else_%d:\n",if_count);
            this->children[1]->generate_code();
            fprintf(fout,"jmp if_end_%d\n",if_count);

            fprintf(fout,"label if_else_%d\n",if_count);
            this->children[2]->generate_code();

            fprintf(fout,"label if_end_%d\n",if_count);
        }else{
            //if
            fprintf(fout,"if_end_%d:\n",if_count);
            this->children[1]->generate_code();
            fprintf(fout,"label if_end_%d\n",if_count);
        }
          fflush(fout);
    }
};
int IfNode::if_count = 0;

class RepeatNode : public TreeNode {
public:
    static int repeat_count ;
    RepeatNode(int line, TreeNode* condition, TreeNode* stmt):TreeNode(line,condition,stmt){};

    void generate_code() override{
        repeat_count++;
        fprintf(fout,"label repeat_%d\n",repeat_count);
        this->children[0]->generate_code();
        this->children[1]->generate_code();
        fprintf(fout,"repeat_%d\n",repeat_count);
        fflush(fout);
    }
};
int RepeatNode::repeat_count = 0;

class AssignNode : public TreeNode {
public:
    AssignNode(int line, TreeNode* left, TreeNode* right):TreeNode(line,left,right){};

    void generate_code() override{
        this->children[1]->generate_code();
        fprintf(fout,"pop %s\n",this->children[0]->attr.name);
          fflush(fout);
    }
};

class ReadNode : public TreeNode {
public:
    ReadNode(int line, TreeNode* left):TreeNode(line,left){};

    void generate_code() override{
        fprintf(fout,"read %s\n",children[0]->attr.name);
        fflush(fout);
    }
};

class WriteNode : public TreeNode {
public:
    WriteNode(int line, TreeNode* right):TreeNode(line,right){};

    void generate_code() override{
        fprintf(fout,"write %s\n",children[0]->attr.name);
    }
};

class ExpNode : public TreeNode {
public:
    ExpNode(int line, TreeNode* left, TreeNode* right):TreeNode(line,left,right){};
    ExpNode(int line, TreeNode* simple_exp):TreeNode(line,simple_exp){};
    ExpNode(int line):TreeNode(line){};
    void generate_code() override{
        if(this->children.size()==1){
            this->children[0]->generate_code();
        }
        else if(this->children.size()==2){
            //this->show_all_info();
            //cout<<endl;
            this->children[0]->generate_code();
            this->children[1]->generate_code();
            fprintf(fout,"sub\n");
            switch (this->attr.op)
            {
            case EQ:
                fprintf(fout,"jnz ");
                break;
            case LT:
                fprintf(fout,"jeg ");
                break;
            default:
                cerr<<"wrong operator in exp node"<<endl;
                break;
            }
        }
        fflush(fout);
    
    }
};

class SimpleExpNode : public TreeNode {
public:
    SimpleExpNode(int line, TreeNode* left, TreeNode* right,Token_Kind op):TreeNode(line,left,right){
        this->attr.op = op;
        this->kind = ExpK;
        this->type = Integer;
        this->production_kind.exp=OpK;
    };
    SimpleExpNode(int line, TreeNode* factor):TreeNode(line,factor){
        this->kind = ExpK;
        this->type = Integer;
        this->production_kind.exp=OpK;
    };

    void generate_code() override{
        if(this->children.size()==1){
            this->children[0]->generate_code();
        }else{
            switch (this->attr.op)
            {
            case PLUS:
                this->children[0]->generate_code();
                this->children[1]->generate_code();
                fprintf(fout,"add\n");
                break;
            case MINUS:
                this->children[0]->generate_code();
                this->children[1]->generate_code();
                fprintf(fout,"sub\n");
                break;
            default:
                cerr<<"wrong operator in simple exp node"<<endl;
                break;
            }
        }
        fflush(fout);
    }
};

class TermNode : public TreeNode {
public:
    TermNode(int line, TreeNode* left, TreeNode* right):TreeNode(line,left,right){};
    TermNode(int line, TreeNode* factor):TreeNode(line,factor){};
    void generate_code() override{
        if(this->children.size()==0){
            this->children[0]->generate_code();
        }else{
            this->children[0]->generate_code();
            this->children[1]->generate_code();
            switch (this->attr.op)
            {
            case TIMES:
                fprintf(fout,"mul\n");
                break;
            case OVER:
                fprintf(fout,"div\n");
                break;
            default:
                cerr<<"wrong operator in term node"<<endl;
                break;
            }
        }
        fflush(fout);
    }
};

class IdNode : public TreeNode {
public:
    IdNode(int line,const char *name):TreeNode(line){
        this->kind = ExpK;
        this->type = Integer;
        this->attr.name = new char[strlen(name)+1];
        strcpy(this->attr.name,name);
        this->production_kind.exp=IdK;
    }
    void generate_code() override{
        fprintf(fout,"push %s\n",this->attr.name);
        fflush(fout);
    }
};

class NumNode : public TreeNode {
    public:
    NumNode(int line,int value):TreeNode(line){
        this->kind = ExpK;
        this->type = Integer;
        this->attr.val = value;
        this->production_kind.exp=ConstK;
    }
    void generate_code() override{
        fprintf(fout,"push $%d\n",this->attr.val);
        fflush(fout);
    }
};
#endif

对于每一个不同类型的节点,都有子类和他对应,生成不同的汇编代码。

本来应该是像后面的那样,更加简洁一点的,但我懒得改了,哈哈,先做个垃圾出来再说。

抽象语法树的生成

program     : stmt_seq {root = $1; }

这里执行成功了之后,就可以对root调用get_info的接口了,输出所有节点的信息
外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传
这是最后的结果,这个就是树的前序遍历,没啥好说的,最后一行可以不用| 的。


void TreeNode::show_info(int depth,bool is_last_child){
        // 输出当前节点的信息
    for (int i = 0; i < depth; i++) {
        if (i == depth-1) {
            if (is_last_child) {
                cout << "└── ";
            } else {
                cout << "├── ";
            }
        } else {
            cout << "│   ";
        }
    }

    cout<<"line: "<<this->lineno<<", type: "<<getExpTypeName(type);
    cout<<", ";
    if(this->kind==StmtK){
        cout<<"stmt: "<<getStmtKindName(production_kind.stmt)<<endl;
    }else{
        cout<<"exp: "<<getExpKindName(production_kind.exp);
        switch (production_kind.exp)
        {
        case OpK:
            cout<<", op: "<<Token_Kind_to_string(attr.op)<<endl;
            break;
        case ConstK:
            cout<<", val: "<<attr.val<<endl;
            break;
        case IdK:
            cout<<", name: "<<attr.name<<endl;
            break;
        default:
            cout<<endl;
            break;
        }
    }
    
  // 递归输出子节点
    for (size_t i = 0; i < children.size(); i++) {
        if (children[i] != nullptr) {
            children[i]->show_info(depth + 1, (i == (children.size() - 1)));
        } else {
            if (i == children.size() - 1) {
                for (int j = 0; j < depth + 1; j++) {
                    if (j == depth) {
                        cout << "└── ";
                    } else {
                        cout << "│   ";
                    }
                }
            } else {
                for (int j = 0; j < depth + 1; j++) {
                    if (j == depth) {
                        cout << "├── ";
                    } else {
                        cout << "│   ";
                    }
                }
            }
        }
    }
};

虚拟机的实现

见VM文件夹的虚拟机指令,模拟的是一个栈型计算机
外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传
这里用label代替的行号,因为我懒得回填行号了,大不了先遍历一遍,记录所有的label

#include <iostream>
#include <fstream>
#include <stack>
#include <string>
#include <unordered_map>
#include <vector>
#include <functional>

using namespace std;

class VirtualMachine {
public:
    VirtualMachine(const string& filename) {
        // 从文件中读取指令
        ifstream file(filename);
        if (file.is_open()) {
            string line;
            while (getline(file, line)) {
                instructions.push_back(line);
            }
            file.close();
        } else {
            cout << "Unable to open file" << endl;
            exit(1);
        }

        // 初始化指令映射表
        instructionMap.emplace("halt", [this]() { this->halt(); });
        instructionMap.emplace("add", [this]() { this->add(); });
        instructionMap.emplace("sub", [this]() { this->sub(); });
        instructionMap.emplace("mul", [this]() { this->mul(); });
        instructionMap.emplace("div", [this]() { this->div(); });

        instructionMapTwo.emplace("read", [this](string & tokens) { this->read(tokens); });
        instructionMapTwo.emplace("write", [this](string& tokens) { this->write(tokens); });
        instructionMapTwo.emplace("push", [this](string& tokens) { this->push(tokens); });
        instructionMapTwo.emplace("pop", [this](string& tokens) { this->pop(tokens); });
        instructionMapTwo.emplace("label", [this](string& tokens) { this->label(tokens); });
        instructionMapTwo.emplace("jmp", [this](string& tokens) { this->jmp(tokens); });
        instructionMapTwo.emplace("jnz", [this](string &tokens) { this->jnz(tokens); });
        instructionMapTwo.emplace("jeg", [this](string &tokens) { this->jeg(tokens); });
        instructionMapTwo.emplace("jel", [this](string &tokens) { this->jel(tokens); });
    }

    void run() {
        // 初始化label
        for(int i=0;i<instructions.size();i++){
            if(instructions[i].find("label")!=string::npos){
                labels[instructions[i].substr(6)]=i;
            }
        }
        // 运行虚拟机
        while (pc < instructions.size()) {
            
            execute(instructions[pc]);
            pc++;
        }
    }

private:
    stack<int> stack_;
    unordered_map<string, function<void()>> instructionMap;
    unordered_map<string,function<void(string &)>> instructionMapTwo;
    unordered_map<string, int> labels;
    unordered_map<string,int> variables;
    vector<string> instructions;
    int pc = 0; // 程序计数器

    void halt() {
        // 停止运行
        exit(0);
    }

    void read(string & tokens) {
        // 从输入流中读取一个值并存入寄存器
        cout << "Enter a value for variable " << tokens << ": ";
        int value;
        cin >> value;
        variables[tokens] = value;
    }

    void write(string &tokens) {
        // 将寄存器的值输出到输出流
        cout <<tokens << " = " << variables[tokens] << endl;
    }

    void push(string& tokens) {
        // 将寄存器或常数压入栈顶
        if (tokens.front() == '$') {
            int reg = stoi(tokens.substr(1));
            stack_.push(reg);
        } else if(variables.count(tokens)==1){
            stack_.push(variables[tokens]);
        }else{
            cerr<<"Undefined variable: "<<tokens<<endl;
        }
    }

    void pop(string& tokens) {
        // 将栈顶的值弹出并存入寄存器
        int reg = stack_.top();
        variables[tokens] = reg;
        stack_.pop();
    }

    void label(string& tokens) {
        // 啥也不用作,前面记录过了
  
    }

    void jmp(string &tokens) {
        // 跳转到标签
        if(labels.count(tokens)==0){
            cerr << "Undefined label: " << tokens << endl;
            exit(1);
        }
        pc = labels[tokens];
    }

    void jnz(string &tokens) {
        // 如果栈顶的值为0,则跳转到标签
        if (stack_.top() != 0) {
            if(labels.count(tokens)==0){
                cerr << "Undefined label: " << tokens << endl;
                exit(1);
            }
            pc = labels[tokens];
        }
        stack_.pop();
    }

    void jeg(string &tokens){
        // 如果栈顶的值大于等于0,则跳转到标签
        if (stack_.top() >= 0) {
            if(labels.count(tokens)==0){
                cerr << "Undefined label: " << tokens << endl;
            }
            pc=labels[tokens];
        }
        stack_.pop();
    }

    void jel(string &tokens){
        // 如果栈顶的值小于等于0,则跳转到标签
        if (stack_.top() <= 0) {
            if(labels.count(tokens)==0){
                cerr << "Undefined label: " << tokens << endl;
            }
            pc=labels[tokens];
        }
        stack_.pop();
    }

    void add() {
        // 栈顶两个元素相加
        int a = stack_.top();
        stack_.pop();
        int b = stack_.top();
        stack_.pop();
        stack_.push(b + a);
    }

    void sub() {
        // 栈顶两个元素相减
        int a = stack_.top();
        stack_.pop();
        int b = stack_.top();
        stack_.pop();
        stack_.push(b - a);
    }

    void mul() {
        // 栈顶两个元素相乘
        int a = stack_.top();
        stack_.pop();
        int b = stack_.top();
        stack_.pop();
        stack_.push(a * b);
    }

    void div() {
        // 栈顶两个元素相除
        int a = stack_.top();
        stack_.pop();
        int b = stack_.top();
        stack_.pop();
        stack_.push(b / a);
    }

    void execute(string& tokens) {
        if(tokens.front()=='#'){
            return ; // 注释
        }
        // 执行指令
        if (instructionMap.count(tokens)) {
            instructionMap[tokens]();
        } else {
            auto posi= tokens.find(' ');
            if(posi != string::npos){
                string instr= tokens.substr(0,posi);
                string arg= tokens.substr(posi+1);
                if(instructionMapTwo.count(instr)){
                    instructionMapTwo[instr](arg);
                }else{

                    cout << "Invalid instruction: " << tokens << endl;
                }
            }else{
                cout << "Invalid instruction: " << tokens << endl;
            }
        }
    }
};

int main(int argc, char* argv[]) {
    if (argc != 2) {
        cout << "Usage: " << argv[0] << " <input_file>" << endl;
        return 1;
    }

    VirtualMachine vm(argv[1]);
    vm.run();
    return 0;
}

这里写的非常好的地方在于,他把指令放到了一个map里,不用if判断了,耦合很低。

感想

  1. 最难的还是flex和bison的使用,资料和demo太少了,yyerror yylval yytexts啥的宏都要自己查官网文档。
  2. 虚拟机的实现很快,寄存器型的会难一些,需要把if while 这些翻译的逻辑搞懂。

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

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

相关文章

STM32——hal_SPI_(介绍)

SPI&#xff08;串行外围设备接口&#xff09;是一种高速的、全双工、同步的通信协议&#xff0c;通常用于短距离通信&#xff0c;尤其是在嵌入式系统中与各种外围设备进行通信。SPI接口由摩托罗拉公司推出&#xff0c;由于其简单和灵活的特性&#xff0c;它被广泛用于多种应用…

运行软件缺失vcruntime140.dll怎么办?vcruntime140.dll缺失的详细解决方法分享

vcruntime140.dll 是一个动态链接库文件&#xff0c;它是 Microsoft Visual C Redistributable Package 的一部分&#xff0c;为使用 Visual C 编译器开发的应用程序提供必要的运行时环境。该文件包含了大量应用程序运行时需要调用的库函数&#xff0c;这些函数是实现 C 标准库…

基于GFlowNets的蚁群抽样组合优化

本文将基于GFACS论文&#xff0c;探讨其核心思想、技术细节以及在实际应用中的优势。 GFlowNet&#xff1a;摊销MCMC成本的有效工具 GFACS的核心是GFlowNet&#xff0c;它通过训练学习状态转移的概率分布&#xff0c;从而替代传统的MCMC采样方法。GFlowNet的优势在于&#xff1…

真实场景 这周的任意一天,获取上周一到周日的时间范围-作者:【小可耐教你学影刀RPA】

用户场景 我想在这周的任意一天&#xff0c;获取上周一到周日的时间范围&#xff0c;应该怎么做 解决办法1 用指令解决 最简单 解决办法2 自己写逻辑 不过要用到 获取当前日期指令 当前是礼拜几

在 JavaScript 中实现数据加密与解密:Web Cryptography API 与 CryptoJS详解

在 JavaScript 中&#xff0c;可以使用 Web Cryptography API 或第三方库如 crypto-js 来实现加密和解密。本文将介绍如何使用这两种方法在客户端进行数据的加密和解密。 使用 Web Cryptography API Web Cryptography API 是现代浏览器提供的一个强大、原生的加密 API。它允许…

【AI大模型】Transformers大模型库(二):AutoModelForCausalLM

目录​​​​​​​ 一、引言 二、AutoModelForCausalLM 2.1 概述 2.2 主要功能 2.3 代码示例 三、总结 一、引言 这里的Transformers指的是huggingface开发的大模型库&#xff0c;为huggingface上数以万计的预训练大模型提供预测、训练等服务。 &#x1f917; Transfo…

【Web API DOM10】日期(时间)对象

一&#xff1a;实例化 1 获取系统当前时间即创建日期对象 const date new Date() console.log(date) 2024年6月5日周三 2 获取指定的时间 以获取2025年6月29日为例 const date new Date(2025-6-29) console.log(date) 二&#xff1a;日期对象方法 1 使用场景&#xf…

代码随想录算法训练营第二十五天| 216. 组合总和 III、17. 电话号码的字母组合

[LeetCode] 216. 组合总和 III [LeetCode] 216. 组合总和 III 文章解释 [LeetCode] 216. 组合总和 III 视频解释 题目: 找出所有相加之和为 n 的 k 个数的组合&#xff0c;且满足下列条件&#xff1a; 只使用数字1到9每个数字 最多使用一次 返回 所有可能的有效组合的列表 。该…

AI视频教程下载:给初学者的ChatGPT提示词技巧

你是否厌倦了花费数小时在可以通过强大的语言模型自动化的琐碎任务上&#xff1f;你是否准备好利用 ChatGPT——世界上最先进的语言模型——并将你的生产力提升到下一个水平&#xff1f; ChatGPT 是语言处理领域的游戏规则改变者&#xff0c;它能够理解并响应自然语言&#xf…

Vue01-vue的简介

一、Vue是什么&#xff1f; 一套用于构建用户界面的渐进式javaScript框架。 构建用户界面&#xff1a; 渐进式&#xff1a; 目前Vue的地位&#xff1a;生态完善&#xff0c;国内前端工程师必备技能。 二、Vue的特点 一个XXX.vue就是一个组件&#xff0c;封装的概念&#xff0c…

Spring系列-SpringMvc父子容器启动原理解析

1、Spring整合SpringMVC 特性&#xff1a; 说到Spring整合SpringMVC唯一的体现就是父子容器&#xff1a; 通常我们会设置父容器&#xff08;Spring&#xff09;管理Service、Dao层的Bean, 子容器(SpringMVC)管理Controller的Bean .子容器可以访问父容器的Bean, 父容器无法访…

面向长文本处理的键值缓存压缩技术:智能压缩,无损性能,免微调

随着输入长度的增加&#xff0c;大型语言模型&#xff08;LLMs&#xff09;中的键值&#xff08;KV&#xff09;缓存需要存储更多的上下文信息以维持性能&#xff0c;这导致内存消耗和计算时间急剧上升。KV缓存的增长对内存和时间效率的挑战主要表现在两个方面&#xff1a;一是…

【数据库初阶】SQL--DCL

文章目录 DCL1. 基本介绍2. 用户管理2.1 查询用户2.2 创建用户2.3 修改用户密码2.4 删除用户 3. 权限控制3.1 查询权限3.2 授予权限3.3 撤销权限 4. DCL总结 DCL 更多数据库MySQL系统内容就在以下专栏&#xff1a; 专栏链接&#xff1a;数据库MySQL 1. 基本介绍 DCL英文全称是…

推荐系统学习 二

双塔模型的结构 用户的特征&#xff0c;我们知道用户ID还能从用户填写的资料和用户行为中获取很多特征&#xff0c;包括离散特征和连续特征。所有这些特征不能直接输入神经网络&#xff0c;而是要先做一些处理&#xff0c;比如用embedding层把用户ID映射到一个向量 跟之前我们…

【每日刷题】Day56

【每日刷题】Day56 &#x1f955;个人主页&#xff1a;开敲&#x1f349; &#x1f525;所属专栏&#xff1a;每日刷题&#x1f34d; &#x1f33c;文章目录&#x1f33c; 1. 292. Nim 游戏 - 力扣&#xff08;LeetCode&#xff09; 2. 129. 求根节点到叶节点数字之和 - 力扣…

QT案例 记录解决在管理员权限下QFrame控件获取拖拽到控件上的文件路径

参考知乎问答 Qt管理员权限如何支持拖放操作&#xff1f; 的回答和代码示例。 解决在管理员权限运行下&#xff0c;通过窗体的QFrame子控件获取到拖拽的内容。 目录标题 导读解决方案详解示例详细 【管理员权限】在QFrame控件中获取拖拽内容 【管理员权限】继承 IDropTarget 类…

[SaaS] AI+数据,tiktok选品,找达人,看广告数据

TK观察专访丨前阿里“鲁班”创始人用AIGC赋能TikTok获千万融资用AI数据做TikTokhttps://mp.weixin.qq.com/s/xp5UM3ROo48DK4jS9UBMuQ主要还是爬虫做数据的。 商家做内容&#xff1a;1.找达人拍内容&#xff0c;2.商家自己做原生自制内容&#xff0c;3.广告内容。 短视频&…

俞敏洪一句“乱七八糟”,让东方甄选跌了40亿

“虽然直播的特点就是能说会道&#xff0c;但是网上那种买买买的嚎叫&#xff0c;我是完全看不起的”&#xff0c;俞敏洪在2023亚布力论坛上颇为自豪地说。 在他看来&#xff0c;“直播带货”本质上也是教育的一种&#xff0c;对产品进行知识性讲解才是最核心的一环。而知识传…

Centos 7部署NTP

介绍 NTP是Network Time Protocol&#xff08;网络时间协议&#xff09;的简称&#xff0c;它是用来通过互联网或局域网将计算机时钟同步到世界协调时间&#xff08;UTC&#xff09;的协议。 安装 # yum安装 yum install -y ntp# 离线安装 #下载地址&#xff1a;https://mir…

Meta Llama 3 残差结构

Meta Llama 3 残差结构 flyfish 在Transformer架构中&#xff0c;残差结构&#xff08;Residual Connections&#xff09;是一个关键组件&#xff0c;它在模型的性能和训练稳定性上起到了重要作用。残差结构最早由He et al.在ResNet中提出&#xff0c;并被广泛应用于各种深度…