自己动手写RISC-V的C编译器-01实现加减法

自己动手写RISC-V的C编译器-01实现加减法

本节将完成加减运算,对错误信息进行改进。

数据结构设计
效果

首先先来思考一下加减法的实现效果。当的输入例如"12-34+56"字符串时应该返回如下汇编程序:

	.globl main
main:
	li a0, 12
	addi a0, a0, -34
	addi a0, a0, 56
	ret

​ 由于RISC-V汇编没有sub指令所以用加法来实现减法功能。除此之外编译器还应该能处理像"1 + 2- 3+ 5"这样的字符串去除其中的空格。

数据结构

采用链表的数据结构,链表的结点存储Token,Token的结构如下:

typedef token token;
struct token{
    token_kind kind;  // token类型,例如:+是算数运算符,1是数字类型
    token *next;      // 指向下一节点
    char *loc;        // 该token在原字符串中的位置
    int val;		 // 如果是数值型的token存储其值
    int len;          // token的长度
};

对于字符串"12 - 34 +56 "其链表结构应如下所示:

在这里插入图片描述

所以这就要求我们完成两个任务

  1. 建立Token链表
  2. 遍历链表
解析器实现
Token类型设计

为了实现利用Token生成汇编代码,我们需要设c计Token的类型。主要的Token类型有,数字型、运算符型、字符流的EOF

typedef enum {
    tk_op,   // 运算符类型例如 +, -
    tk_num,  // 数字类型例如 1, 2, ...
    tk_eof,  // 文件或者字符流末尾
} token_kind;
tokenize用于词法分析构建Token列表

在这里插入图片描述

token *new_token(token_kind kind, char *l, char *r) {  // 记录token的长度是为了处理1位以上的数字
    token *tok = calloc(1, sizeof(token));
    tok->kind = kind;
    tok->loc = l;
    tok->len = r - l;
    return tok;
}

token *tokenize() {
    char *P = usr_input;  // 用户输入字符串流"1 + 2 - 3"
    token head = {};
    token *cur = &head;

    while (*P) {
        if (isspace(*P)) { P++; continue; }  // 如果字符流出现空白符,则跳过他指针指向下一个字符
        if (isdigit(*P)) {
            cur->next = new_token(tk_num, P, P);
            cur = cur->next;
            char *old = P;  // 用于保存token起始位置以便算出其长度
            cur->val = strtol(P, &P, 10);
            cur->len = P - old;
            continue;
        }
        if (*P == '+' || *P == '-') {  // 运算符一般为一位不用计算长度
            cur->next = new_token(tk_op, P, P + 1);
            cur = cur->next;
            P++;
            continue;
        }

        error_at(P, "invaild token %c", *P);
    }
    cur->next = new_token(tk_eof, P, P);

    return head.next;
}
main函数

main函数主要用于调用tokenize函数生成Token,并遍历链表

int main(int argc, char *argv[])
{
    if (argc != 2) {
        error("%s: invaild number of arguments\n", argv[0]);
    }

    usr_input = argv[1];
    token *Tok = tokenize();

    printf("    .globl main\n");
    printf("main:\n");
    printf("    li a0, %d\n", get_num(Tok));

    while (Tok->kind != tk_eof) {
        if (equal(Tok, "+")) {
            printf("    addi a0, a0, %d\n", get_num(Tok->next));
        } else if (equal(Tok, "-")) {
            printf("    addi a0, a0, -%d\n", get_num(Tok->next));
        }
        Tok = Tok->next;
    }
    
    printf("    ret\n");

    return 0;
}
整体代码
#include<stdio.h>
#include<stdlib.h>
#include<stdarg.h>
#include<string.h>
#include<stdbool.h>
#include<ctype.h>

typedef enum {
    tk_op,   // ex. +, -
    tk_num,  // 1, 2, ...
    tk_eof,  // the file end
} token_kind;

typedef struct token token;
struct token{
    token_kind kind;
    token *next;
    char *loc;
    int val;
    int len;
};

char *usr_input;

void error(char *fmt, ...) {
    va_list va;
    va_start(va, fmt);
    vfprintf(stderr, fmt, va);
    fprintf(stderr, "\n");
    va_end(va);
    exit(1);
}

void error_at(char *loc, char *fmt, ...) {
    va_list va;
    va_start(va, fmt);
    int pos = loc - usr_input;
    fprintf(stderr, "%s\n", usr_input);
    fprintf(stderr, "%*s", pos, "");
    fprintf(stderr, "^");
    vfprintf(stderr, fmt, va);
    fprintf(stderr, "\n");
    va_end(va);
    exit(1);
}

int get_num(token *tok) {
    if (tok->kind == tk_num) {
        return tok->val;
    } else {
        error_at(tok->loc, "expect a number");
    }
}

bool equal(token *tok, char *str) {
    return memcmp(tok->loc, str, tok->len) == 0 && str[tok->len] == '\0';
}

token *new_token(token_kind kind, char *l, char *r) {
    token *tok = calloc(1, sizeof(token));
    tok->kind = kind;
    tok->loc = l;
    tok->len = r - l;
    return tok;
}

token *tokenize() {
    char *P = usr_input;
    token head = {};
    token *cur = &head;

    while (*P) {
        if (isspace(*P)) { P++; continue; }
        if (isdigit(*P)) {
            cur->next = new_token(tk_num, P, P);
            cur = cur->next;
            char *old = P;
            cur->val = strtol(P, &P, 10);
            cur->len = P - old;
            continue;
        }
        if (*P == '+' || *P == '-') {
            cur->next = new_token(tk_op, P, P + 1);
            cur = cur->next;
            P++;
            continue;
        }

        error_at(P, "invaild token %c", *P);
    }
    cur->next = new_token(tk_eof, P, P);

    return head.next;
}

int main(int argc, char *argv[])
{
    if (argc != 2) {
        error("%s: invaild number of arguments\n", argv[0]);
    }

    usr_input = argv[1];
    token *Tok = tokenize();

    printf("    .globl main\n");
    printf("main:\n");
    printf("    li a0, %d\n", get_num(Tok));

    while (Tok->kind != tk_eof) {
        if (equal(Tok, "+")) {
            printf("    addi a0, a0, %d\n", get_num(Tok->next));
        } else if (equal(Tok, "-")) {
            printf("    addi a0, a0, -%d\n", get_num(Tok->next));
        }
        Tok = Tok->next;
    }
    
    printf("    ret\n");

    return 0;
}
  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值