动手实现编译器(五)——实现语句

news/2024/7/8 2:41:05

在上一节,我们已经实现了一条表达式的计算,在这一节中,我们要真正实现语句。
我们来看看SysY语言中对语句的定义:

语句: Stmt → LVal ‘=’ Exp ‘;’ | [Exp] ‘;’ | Block
| ‘if’ '( Cond ‘)’ Stmt [ ‘else’ Stmt ]
| ‘while’ ‘(’ Cond ‘)’ Stmt
| ‘break’ ‘;’ | ‘continue’ ‘;’
| ‘return’ [Exp] ‘;’

为了给明显的输出结果,我们增加一条语法:

打印结果:print → Stmt

输入文件由几个语句组成,它们可以是一个语句,也可以是后面跟有更多语句的语句,每个语句均以关键字开头 print,然后是一个表达式,然后是分号。
首先增加单词类型:

// 单词类型
enum
{
    T_EOF, T_ADD, T_SUB, T_MUL, T_DIV, T_MOD, T_INT, T_SEM, T_PRINT
};

更改词法分析器

在这里,我添加了从SubC编译器搬来的这段代码,它将字母数字字符读入缓冲,直到遇到非字母数字字符。再加上识别关键词的函数,就成为可以识别语句的词法分析器。

#define TEXTLEN		512	// 扫描缓存长度
char Text[TEXTLEN + 1];		// 扫描缓存

// 扫描输入文件中的标识符并将其存储在buf[]中,返回标识符的长度
int scan_ident(int c, char *buf, int lim)
{
    int i = 0;
    // 允许数字、字母和下划线
    while (isalpha(c) || isdigit(c) || '_' == c)
    {
        // 标识符长度超过限制,则出错;否则添加到buf[],并获取下一个字符
        if (lim - 1 == i)
        {
            printf("identifier too long on line %d\n", Line);
            exit(1);
        }
        else if (i < lim - 1)
        {
            buf[i++] = c;
        }
        c = get_nextchr();
    }
    // 遇到分隔符,把它放回去。终止buf[],并返回长度
    put_backchr(c);
    buf[i] = '\0';
    return i;
}

// 给定输入中的一个单词,返回匹配的关键字标记号,
// 如果它不是关键字,则返回0 
int match_keyword(char *s)
{
    switch (*s)
    {
        case 'p':
            if (!strcmp(s, "print"))
                return (T_PRINT);
            break;
    }
    return 0;
}

同时修改scan函数为

// 扫描并返回在输入中找到的下一个单词。
// 如果标记有效则返回 1,如果没有标记则返回 0
int scan(struct token *t)
{
    int c, tokentype;
    c = skip_getchr();
    switch (c)
    {
        case EOF:   t->token = T_EOF;   return 0;
        case '+':   t->token = T_ADD;   break;
        case '-':   t->token = T_SUB;   break;
        case '*':   t->token = T_MUL;   break;
        case '/':   t->token = T_DIV;   break;
        case '%':   t->token = T_MOD;   break;
        case ';':   t->token = T_SEM; break;
        default:    if (isdigit(c)) 
                    {
                        t->intvalue = scan_int(c);
                        t->token = T_INT;
                        break;
                    }
                    else if(isalpha(c) || '_' == c)
                    {
                        scan_ident(c, Text, TEXTLEN);
                        if (tokentype = match_keyword(Text))
                        {
                        	t->token = tokentype;
	                        break;
	                    }
	                    printf("Unrecognised symbol %s on line %d\n", Text, Line);
	                    exit(1);
                    }
                    printf("Unrecognised character %c on line %d\n", c, Line);
                    exit(1);
    }
    return 1;
}

更改语义分析器

到目前为止,我们的语义分析器只能识别一个表达式。而我们的新语法每个表达式都以分号结尾(不是以文件结尾即 T_EOF),因此我们需要在表达式解析器中更改代码以发现T_SEM标记并退出。修改binexpr()函数为

// 返回一个以二元操作符为根的树
struct ASTnode *binexpr(int pretokentype)
{
    struct ASTnode *left, *right;
    int tokentype;

    // 获取左节点的整数,同时获取下一个单词
    left = primary();

    // 如果下一个单词是文件结尾,则返回左节点
    tokentype = Token.token;
    if (tokentype == T_SEM)   return left;

    // 当前单词的优先级高于前一个单词的优先级
    while (op_precedence(tokentype) > pretokentype)
    {
        // 获取下一个单词
        scan(&Token);

        // 根据优先级递归生成右子树
        right = binexpr(OpPrec[tokentype]);

        // 从单词类型得到到节点类型,然后合并左、右子树
        left = mkastnode(token_op(tokentype), left, right, 0);

        // 更新当前单词的详细信息
        tokentype = Token.token;

        // 如果到文件结尾,则返回左节点
        // 此时的左节点已经更新为合并后的树的根节点
        if (tokentype == T_SEM) return left;
    }
    return left;
}

语句分析器

在每个循环中都会先找到"print",然后调用binexpr()解析表达式,最后找到";",如果紧跟着"EOF",就会跳出循环。
在每个表达式树之后,调用代码将树转换为汇编代码,并调用 arm_print_reg()函数以打印出最终值。

// 检查当前单词是否为t,并获取下一个单词
void match(int t, char *what)
{
    if (Token.token == t)
    {
        scan(&Token);
    }
    else
    {
        printf("%s expected on line %d\n", what, Line);
        exit(1);
    }
}

// 检查当前单词是否为";",并获得下一个单词
void semi(void)
{
    match(T_SEM, ";");
}

// 分析语句
void statements()
{
    struct ASTnode *tree;
    int reg;
    while (1)
    {
        // 匹配一个"print"
        match(T_PRINT, "print");
        // 分析表达式并生成汇编代码
        tree = binexpr(0);
        reg = code_generator(tree);
        arm_print_reg(reg);
        arm_freeall_registers();
        // 继续匹配直到遇到"EOF"
        semi();
        if (Token.token == T_EOF)   return;
    }
}

修改main()函数

main() 用statements()函数代替binexpr()函数解析输入文件中的表达式。

// 用法 compiler -o -s outfile infile
int main(int argc, char *argv[])
{
    struct ASTnode *n;
    if(argc != 5)
    {
        fprintf(stderr, "compiler -o -s outfile infile\n");
        exit(1);
    }
    init();
    if ((Infile = fopen(argv[4], "r")) == NULL)
    {
        fprintf(stderr, "Unable to open %s: %s\n", argv[4], strerror(errno));
        exit(1);
    }
    if ((Outfile = fopen(argv[3], "w")) == NULL)
    {
        fprintf(stderr, "Unable to create %s: %s\n", argv[4], strerror(errno));
        exit(1);
    }
    scan(&Token);
    arm_preamble();
    statements();
    arm_postamble();
    fclose(Outfile);
}

测试

输入:

print 2 + 3 * 4 / 5 % 6 - 7 + 8;

输出(out.s):

	.text
	.section	.rodata
	.align  2
.LC0:
	.ascii  "%d\012\000"
	.text
	.align  2
	.global main
	.type   main, %function
main:
	push    {fp, lr}
	add     fp, sp, #4
	mov	r4, #2
	mov	r5, #3
	mov	r6, #4
	mul	r5, r5, r6
	mov	r6, #5
	mov	r0, r5
	mov	r1, r6
	bl	__aeabi_idiv
	mov	r5, r0
	mov	r6, #6
	mov	r7, r5
	mov	r0, r7
	mov	r1, r6
	bl	__aeabi_idiv
	mov	r7, r0
	mul	r7, r7, r6
	sub	r5, r5, r7
	add	r4, r4, r5
	mov	r5, #7
	sub	r4, r4, r5
	mov	r5, #8
	add	r4, r4, r5
	mov     r1, r4
	ldr     r0, .L3
	bl      printf
	mov     r3, #0
	mov     r0, r3
	pop     {fp, pc}
.L3:
	.word   .LC0
	.size   main, .-main

输出(out):

5

输入:

print 1 + 4;
print 13 - 11;
print 2 * 0;
print 20 / 20;
print 1024 % 10;

输出(out.s):

	.text
	.section	.rodata
	.align  2
.LC0:
	.ascii  "%d\012\000"
	.text
	.align  2
	.global main
	.type   main, %function
main:
	push    {fp, lr}
	add     fp, sp, #4
	mov	r4, #1
	mov	r5, #4
	add	r4, r4, r5
	mov     r1, r4
	ldr     r0, .L3
	bl      printf
	mov	r4, #13
	mov	r5, #11
	sub	r4, r4, r5
	mov     r1, r4
	ldr     r0, .L3
	bl      printf
	mov	r4, #2
	mov	r5, #0
	mul	r4, r4, r5
	mov     r1, r4
	ldr     r0, .L3
	bl      printf
	mov	r4, #20
	mov	r5, #20
	mov	r0, r4
	mov	r1, r5
	bl	__aeabi_idiv
	mov	r4, r0
	mov     r1, r4
	ldr     r0, .L3
	bl      printf
	mov	r4, #1024
	mov	r5, #10
	mov	r6, r4
	mov	r0, r6
	mov	r1, r5
	bl	__aeabi_idiv
	mov	r6, r0
	mul	r6, r6, r5
	sub	r4, r4, r6
	mov     r1, r4
	ldr     r0, .L3
	bl      printf
	mov     r3, #0
	mov     r0, r3
	pop     {fp, pc}
.L3:
	.word   .LC0
	.size   main, .-main

输出(out):

5
2
0
1
4

总结

在这一节中,我们已经实现了句子的编译。在下一节中,我们将往编译器中添加变量。


http://www.niftyadmin.cn/n/4136863.html

相关文章

input radio check

jquery 获取 input type radio checked的元素.find(input:radio:checked)&#xff1b;.find("input[typeradio]:checked");.find("input[nameradio]:checked");.find("input[nameradio][checked]");.find("input[nameradio]").filter(…

leetcode中几道与维护窗口相关的问题

leetcode中有几道题使用同一个思路&#xff0c;大致是先维护一个窗口&#xff0c;每次只移动窗口左侧或者右侧的边界&#xff0c;然后针对这个窗口内的元素进行处理。这种方式使用两个指针&#xff0c;可以将问题的运行时间降到O(n)内。 Longest Substring Without Repeating C…

动手实现编译器(六)——实现全局变量

我们在上一节中实现了对语句的编译&#xff0c;在这一节中&#xff0c;我们希望向语句中加入全局变量。实现类似以下语句&#xff1a; int a; int b; int c; int d; int e; int f; int g; a 2; b 3; c 4; d 5; e 6; f 7; g 8; print a b * c / d % e - f g;这需要变量…

POJ 2027

#include<iostream> using namespace std; int main() {int time;cin>>time;int a;int b;while(time--){cin>>a;cin>>b;if(a<b)cout<<"NO BRAINS"<<endl;elsecout<<"MMM BRAINS"<<endl;} } 关注我的公…

Hibernate查询简介

创建实体类 在介绍Hibernate查询语言之前&#xff0c;首先我们来建立一下数据库。这里直接使用了MySQL自带的样例数据库world。如果你没有安装MySQL那么需要安装一下&#xff0c;并且在安装的时候选择安装样例数据库。 安装完成之后&#xff0c;应该能在MySQL中看到一个名为wor…

动手实现编译器(七)——比较运算符

上一节中&#xff0c;我们已经实现了全局变量的声明、赋值和计算&#xff0c;本节中我们将会实现计较运算符的计算。 , !, <, >, <, >修改词法分析 首先增加新的单词类型 // 单词类型 enum {T_EOF,T_ADD, T_SUB, T_MUL, T_DIV, T_MOD, T_EQ, T_NE, T_LT, T_GT, …

linux软件编译流程

首先先总结下linux下一些概念1&#xff0c;gcc可以说是史上最强大的c语言编译器起作用是将软件程序的源代码&#xff08;纯文本文件&#xff09;和利用已存在的函数库通过其本身gcc编译成计算机可识别的二进制文件。该过程为程序编译的普遍流程 2&#xff0c;环境检测程序&…

动手实现编译器(八)——IF条件语句

在上一节中&#xff0c;我们实现了比较运算符的计算&#xff0c;现在&#xff0c;我们可以容易地实现IF条件语句。 首先我们看IF语句的SysY语言的定义&#xff1a; 语句&#xff1a; Stmt → LVal ‘’ Exp ‘;’ | ‘if’ ( Cond ‘)’ Stmt [ ‘else’ Stmt ] 修改词法分析 …