自己动手写编译器:实现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判断中“||“和“&&“条件判断的中间代码生成