自己动手写编译器:实现else语句块的中间代码生成

Posted tyler_download

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了自己动手写编译器:实现else语句块的中间代码生成相关的知识,希望对你有一定的参考价值。

前面几节我们完成了if语句以及判断条件成立时代码对应的中间代码生成,这次我们完成最后一笔,那就是针对else部分代码完成相应的中间代码生成。本质上这一步比较简单,它会在原来if语句中间代码的基础上稍作修改即可,我们先看看这次我们要编译的代码内容:

`int a; int b; int c; int d;
		        int e;
		        a = 1;
				b = 2;
				c = 3;
				d = 4;
				if (b == a && c != d) 
					e = 2;
				 else 
                    e = 3;
                
				
	`

我们在代码中增加了else语句块,我们看看完成本节代码后的执行结果:

从结果看,e=2对应if大括号里面的代码,e=3对应else部分代码,与前面不同的是,编译器在实现if里面代码后,在末尾添加一个goto语句直接越过else部分代码,进入到else之后的代码,从输出看,逻辑应该没有问题。

下面我们看看代码实现,首先为else语句部分增加一个节点,因此添加else.go文件,然后编写代码如下:

package inter

import (
	"errors"
	"strconv"
)

type Else struct 
	stmt  *Stmt
	expr  ExprInterface
	stmt1 StmtInterface
	stmt2 StmtInterface


func NewElse(line uint32, expr ExprInterface, stmt1 StmtInterface, stmt2 StmtInterface) *Else 
	if expr.Type().Lexeme != "bool" 
		err := errors.New("bool type required in if")
		panic(err)
	
	return &Else
		stmt:  NewStmt(line),
		expr:  expr,
		stmt1: stmt1,
		stmt2: stmt2,
	


func (e *Else) Errors(str string) error 
	return e.stmt.Errors(str)


func (e *Else) NewLabel() uint32 
	return e.stmt.NewLabel()


func (e *Else) EmitLabel(i uint32) 
	e.stmt.EmitLabel(i)


func (e *Else) Emit(code string) 
	e.stmt.Emit(code)


func (e *Else) Gen(_ uint32, end uint32) 
	label1 := e.NewLabel()
	label2 := e.NewLabel()

	e.expr.Jumping(0, label2)
	e.EmitLabel(label1) //生成if条件判断中代码
	e.stmt1.Gen(label1, end) //生成if成立后大括号里面代码的中间代码
	e.Emit("goto L" + strconv.Itoa(int(end))) //增加goto语句跳过else部分代码
	e.EmitLabel(label2) 
	e.stmt2.Gen(label2, end) //生成else里面代码对应中间代码


上面代码跟我们前面实现的if节点类似,它的创建需要输入三部分,首先在构造函数中,第一个参数expr对应if里面的条件判断表达式,stmt1对应if成立时大括号里面的语句集合,stmt2对应else部分的语句集合,值得关注的地方在它的gen函数,它首先执行s.xpr.Jumping, e.stmt1.Gen生成条件判断语句和if成立时语句块的中间代码,最重要的是它在if语句块里面的代码完成生成后加入一条goto语句,这个goto语句的作用是越过else部分的代码。很显然当if语句判断成立后,我们执行了if内部代码就肯定不能再执行else部分代码,所以在if内部语句块的后面加上goto越过else部分指令是合理的。

接下来我们看看语法解析部分的修改:

func (s *SimpleParser) stmt() inter.StmtInterface 
	/*
		if "(" bool ")"
		if -> "(" bool ")" ELSE stmt

		bool -> bool "||"" join | join
		join -> join "&&" equality | equality
		equality -> equality "==" rel | equality != rel | rel
		rel -> expr < expr | expr <= expr | expr >= expr | expr > expr | expr
		rel : a > b , a < b, a <= b
		a < b && c > d || e < f
	*/
	switch s.cur_tok.Tag 
	case lexer.IF:
		s.move_forward()
		err := s.matchLexeme("(")
		if err != nil 
			panic(err)
		
		s.move_forward()
		x := s.bool()
		err = s.matchLexeme(")")
		if err != nil 
			panic(err)
		
		s.move_forward() //越过 )
		s.move_forward() //越过
		s1 := s.stmt()
		err = s.matchLexeme("")
		if err != nil 
			panic(err)
		
		s.move_forward() //越过

		//判断if 后面是否跟着else
		if s.cur_tok.Tag != lexer.ELSE 
			return inter.NewIf(s.lexer.Line, x, s1)
		 else 
			s.move_forward() //越过else关键字
			err = s.matchLexeme("")
			if err != nil 
				panic(err)
			
			s.move_forward() //越过
			s2 := s.stmt()   //else 里面包含的代码块
			err = s.matchLexeme("")
			if err != nil 
				panic(err)
			
			return inter.NewElse(s.lexer.Line, x, s1, s2)
		

	default:
		return s.expression()
	


在代码中我们增加了对else关键字的解析,我们解析完if部分后,接着判断是否有else关键字跟随其后,如果有我们则对else内部代码调用stmt来进行解析,完成上面代码后运行起来就能得到相应结果。更详细的调试演示请在b站搜索coding迪斯尼,更多干货:http://m.study.163.com/provider/7600199/index.htm?share=2&shareId=7600199

以上是关于自己动手写编译器:实现else语句块的中间代码生成的主要内容,如果未能解决你的问题,请参考以下文章

自己动手写编译器:实现简单if语句的跳转代码生成

自己动手写编译器:手动构造语法树实现中间代码生成

自己动手写编译器:实现if判断中“||“和“&&“条件判断的中间代码生成

自己动手写编译器:实现if判断中“||“和“&&“条件判断的中间代码生成

自己动手写编译器:while,for,do等循环语句的中间代码生成

自己动手写编译器:while,for,do等循环语句的中间代码生成