ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

自己动手写RISC-V的C编译器-02语法描述方法和递归下降解析

2022-08-08 22:00:09  阅读:350  来源: 互联网

标签:02 node return nd RISC primary tok 编译器 mul


本节增加对*/+-()运算的支持

使用生成规则表示运算符优先级

expr = mul("+" mul | "-" mul)*
mul  = num("*" num | "/" num)*

上面的表达式可以很容易的推导出对于对于运算1*2+3的语法树

expr开始推导乘除法一定会在加减法的更下一层,所以很自然的得出乘法优先级大于加减法优先级。接下来我们对表达式进行拓展,使编译器能解析()、*、/

expr    = mul("+" mul | "-"mul)*
mul     = primary("*" primary | "/" primary)*
primary = num | "(" expr ")"

运算1*(2+3)的语法树如下:

数据结构设计

​ 通过上面的解释,我们发现将一串暂时无意义的字符串变成一个有规则的树形结构可以更好的表示算式的优先级,上面的树形结构就是所谓AST(抽象语法树) 所以解析算式我们采用AST

节点数据结构设计

和上一篇类似,我们依然采用enum类型来自动生成每个节点的类型编号。

typedef enum {
    nd_num,
    nd_add,
    nd_sub,
    nd_mul,
    nd_div,
} node_kind;

AST的节点就是一个二叉树的子节点,所以他应该包括三部分节点类型、左子树、右子树、如果是数字节点那还应该包括数字的值。

typedef struct node node;
struct node {
    node_kind kind;
    node *lch;
    node *rch;
    int val;
};

使用三个函数来构建二叉树的基本结构

node *new_node(node_kind kind) {  // 用于构建一个子节点并返回该节点的指针
    node *nd = calloc(1, sizeof(node));
    nd->kind = kind;
    return nd;
}

node *new_binary(node_kind kind, node *lch, node *rch) {  // 用域构建一个二叉树
    node *nd = new_node(kind);
    nd->lch = lch;
    nd->rch = rch;
    return nd;
}

node *new_num(int val) {  // 构建一个数字子节点
    node *nd = new_node(nd_num);
    nd->val = val;
    return nd;
}

递归下降解析

构建AST

有了上面的运算规则就可以着手编写程序对算式进行解析了,首先先构建语法树。构建语法树需要用到三个函数exprmulprimary分别用于解析算式乘法算式数字或括号表达式

node *expr(token **rest, token *tok);
node *mul(token **rest, token *tok);
node *primary(token **rest, token *tok);

下面通过代码具体看看如何构建出一颗AST

// expr    = mul("+" mul | "-"mul)*
// mul     = primary("*" primary | "/" primary)*
// primary = num | "(" expr ")"

node *expr(token **rest, token *tok){
    node *nd = mul(&tok, tok);
    
    while (true) {
        if (equal(tok, "+")) {
            nd = new_binary(nd_add, nd, mul(&tok, tok->next));
            continue;
        }
        
        if (equal(tok, "-")) {
            nd = new_binary(nd_sub, nd, mul(&tok, tok->next));
            continue;
        }
        
        *rest = tok;
        return nd;
    }
}

node *mul(token **rest, token *tok) {
    node *nd = primary(&tok, tok);
    
    while (true) {
        if (equal(tok, "*")) {
            nd = new_binary(nd_mul, nd, primary(&tok, tok->next));
            continue;
        }
        
        if (equal(tok, "/")) {
            nd = new_binary(nd_div, nd, primary(&tok, tok->next));
            continue;
        }
        
        *rest = tok;
        return nd;
    }
}

node *primary(token **rest, token *tok) {
    if (tok->kind == num) {
        node *nd = new_num(tok->val);
        *rest = tok->next;
        return nd;
    }
    
    if (equal(tok, "(")) {
        node *nd = expr(&tok, tok->next);
        *rest = skip(tok, ")");
        return nd;
    }
    
    error_at(tok->loc, "expexted an expression");
    return NULL;
}
通过遍历AST生成汇编代码

以以下AST为例

​ 先遍历到最右子节点,将3压入栈。之后遍历到节点2,并放入a0中,将3从栈中弹出并放入a1中。根据23的父节点类型选择相应的汇编代码,最后完成汇编代码的生成。

int depth;

void push() {
    printf("    addi sp, sp, -8\n");
    printf("    sd a0, 0(sp)\n");
    depth ++;
}

void pop(char *reg) {
    printf("    ld %s, 0(sp)\n", reg);
    printf("    addi sp, sp, 8\n");
    depth --;
}

void gen_expr(node *nd) {
    if (nd->kind == nd_num) {
        printf("    li a0, %d\n", nd->val);
        return;
    }

    gen_expr(nd->rch);
    push();
    gen_expr(nd->lch);
    pop("a1");

    switch (nd->kind)
    {
    case nd_add:
        printf("    add a0, a0, a1\n");
        return;
    case nd_sub:
        printf("    sub a0, a0, a1\n");
        return;
    case nd_mul:
        printf("    mul a0, a0, a1\n");
        return;
    case nd_div:
        printf("    div a0, a0, a1\n");
        return;
    default:
        break;
    }

}

标签:02,node,return,nd,RISC,primary,tok,编译器,mul
来源: https://www.cnblogs.com/hanyj-home/p/16564139.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有