当前位置:网站首页>自己动手写RISC-V的C编译器-01实现加减法
自己动手写RISC-V的C编译器-01实现加减法
2022-08-11 05:29:00 【YOUNIKOJIAO】
自己动手写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 "其链表结构应如下所示:

所以这就要求我们完成两个任务
- 建立Token链表
- 遍历链表
解析器实现
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;
}
边栏推荐
- USB中用NRZI来编码数据
- js学习进阶BOM部分(pink老师笔记)
- 127.0.0.1 已拒绝连接
- Promise.race learning (judging the fastest execution of multiple promise objects)
- Building a data ecology for feature engineering - Embrace the open source ecology, OpenMLDB fully opens up the MLOps ecological tool chain
- SearchGuard configuration
- jdbc接口文档参考,jdbc接口方法逻辑探究
- 智能风控中台设计与落地
- 论文解读:GAN与检测网络多任务/SOD-MTGAN: Small Object Detection via Multi-Task Generative Adversarial Network
- Day 73
猜你喜欢

USB中用NRZI来编码数据

JS事件循环机制

Day 81

Getting Started with JNI

three.js基础学习

论文解读:跨模态/多光谱/多模态检测 Cross-Modality Fusion Transformer for Multispectral Object Detection
![[Meetup]OpenMLDBxDolphinScheduler 链接特征工程与调度环节,打造端到端MLOps工作流](/img/d8/a367c26b51d9dbaf53bf4fe2a13917.png)
[Meetup]OpenMLDBxDolphinScheduler 链接特征工程与调度环节,打造端到端MLOps工作流

Invalid revision: 3.18.1-g262b901-dirty

js 学习进阶(事件高级 pink老师教学笔记)

js learning advanced (event senior pink teacher teaching notes)
随机推荐
哥德巴赫猜想与整数环
USB URB
C语言中switch的嵌套
USB in NRZI to encode the data
Wonderful linkage | OpenMLDB Pulsar Connector principle and practical operation
promise.all 学习(多个promise对象回调)
js写四位随机数能有多少种可能性?并列出所有可能性
[Meetup] OpenMLDBxDolphinScheduler engineering and scheduling link link characteristics, building the end-to-end MLOps workflow
scanf函数在混合接受数据(%d和%c相连接)时候的方式
OpenMLDB官网升级,神秘贡献者地图带你快速进阶
127.0.0.1 已拒绝连接
mysql基础总结
jdbc接口文档参考,jdbc接口方法逻辑探究
USB中用NRZI来编码数据
【无标题】
Day 85
厂商推送平台-华为接入
Matplotlib找不到字体,打印乱码
何凯明新作ViTDET:目标检测领域,颠覆分层backbone理念
系统性能及并发数的一些计算公式