教你使用swift写编译器玩具(5)

前言

本章对应官方教程第5章,本章介绍如何扩展Kaleidoscope以使用if / then / else表达式和一个简单的for循环。

教程如下:

教你使用swift写编译器玩具(0)

教你使用swift写编译器玩具(1)

教你使用swift写编译器玩具(2)

教你使用swift写编译器玩具(3)

教你使用swift写编译器玩具(4)

教你使用swift写编译器玩具(5)

教你使用swift写编译器玩具(6)

教你使用swift写编译器玩具(7)

教你使用swift写编译器玩具(8)

仓库在这

开始

if / then / else

if / then / else也是一种表达式,我们需要把它计算为int1类型,0是假,1是真。如果if表达式计算为真返回then表达式,否则返回else表达式。

首先我们需要做的第一件事情是扩展我们的Token枚举

...
case `if`
case then
case `else`
...

接着我们在LexernextToken()方法中补充token的解析

} else if identifierStr == "if" {
        currentToken = CurrentToken(token: .if, val: "if")
} else if identifierStr == "then" {
        currentToken = CurrentToken(token: .then, val: "then")
} else if identifierStr == "else" {
        currentToken = CurrentToken(token: .else, val: "else")
}

if / then / else的AST扩展

为了解析新的表达式我们需要添加新的AST Node。

class IfExprAST: ExprAST {
    
    let cond: ExprAST
    
    let then: ExprAST
    
    let `else`: ExprAST
    
    init(_ cond: ExprAST, _ then: ExprAST, _ `else`: ExprAST) {
        self.cond = cond
        self.then = then
        self.else = `else`
    }
    
}

if / then / else的Parser扩展

有了AST之后我们要做的事情那就是扩展Parser了。

    /// 解析条件语句
    ///
    /// - Returns: AST
    private func parseIfExpr() -> ExprAST? {
        lexer.nextToken()
        //解析if表达式
        let cond = parseExpression()
        guard cond != nil else {
            return nil
        }
        //if表达式后面不是then就报错
        guard lexer.currentToken!.token == .then else {
            fatalError("expected then.")
        }
        lexer.nextToken()
        //解析then表达式
        let then = parseExpression()
        guard then != nil else {
            return nil
        }
        //then表达式后面不是else表达式就报错
        guard lexer.currentToken!.token == .else else {
            fatalError("expected else.")
        }
        lexer.nextToken()
        let `else` = parseExpression()
        guard `else` != nil else {
            return nil
        }
        return IfExprAST(cond!, then!, `else`!)
    }

接下来我们把它放在parsePrimary中。

    /// 解析基本表达式的入口
    ///
    /// - Returns: AST
    private func parsePrimary() -> ExprAST? {
        guard lexer.currentToken != nil else {
            return nil
        }
        if lexer.currentToken!.val == "(" {
            return parseParenExpr()
        }
        switch lexer.currentToken!.token {
        case .identifier:
            return parseIdentifierExpr()
        case .number:
            return parseNumberExpr()
        case .if:
            return parseIfExpr()
        default:
            fatalError("unknow token when expecting an expression")
        }
    }

if / then / else的代码生成

我们需要在IfExprAST中实现方法codeGen()。这里我们需要使用的是一个SSA操作:Phi操作

    func codeGen() -> IRValue? {
        var condV = cond.codeGen()
        guard condV != nil else {
            return nil
        }
        //这里有个神坑就是build条件时候要使用int1类型
        condV = builder.buildICmp(condV!, IntType.int1.zero(), .equal, name: "ifCond")
        
        let theFunction = builder.insertBlock?.parent
        guard theFunction != nil else {
            return nil
        }
        
        //为then else merge创建basic block并放在函数里
        let thenBB = theFunction!.appendBasicBlock(named: "then")
        let elseBB = theFunction!.appendBasicBlock(named: "else")
        let mergeBB = theFunction!.appendBasicBlock(named: "merge")
        
        //构建控制流表达式
        builder.buildCondBr(condition: condV!, then: thenBB, else: elseBB)
        
        //让builder移动到then的基本块里
        builder.positionAtEnd(of: thenBB)
        //插入then
        let thenVal = then.codeGen()
        guard thenVal != nil else {
            return nil
        }
        builder.buildBr(mergeBB)
        //让builer移动到else的基本块里
        builder.positionAtEnd(of: elseBB)
        let elseVal = `else`.codeGen()
        guard elseVal != nil else {
            return nil
        }
        builder.buildBr(mergeBB)
        //让builder移动到merge的基本块里
        builder.positionAtEnd(of: mergeBB)
        let phi = builder.buildPhi(FloatType.double, name: "phi")
        phi.addIncoming([(thenVal!, thenBB), (elseVal!, elseBB)])
        
        return phi
    }

for循环表达式

Kaleidoscope的for循环长下面这样,1.0是可选的步长,默认即为1.0。

for i = 1, i < n, 1.0 in

for循环表达式的处理会复杂一些,但还是同样运用了Phi操作来处理。

同控制流语句的扩展,我们还是先要扩展TokenLexer

case `for`

else if identifierStr == "for" {
        currentToken = CurrentToken(token: .for, val: "for")
}

接着我们扩展for循环的AST NodeForExprAST

class ForExprAST: ExprAST {
    
    let name: String
    
    let start: ExprAST
    
    let end: ExprAST
    
    let step: ExprAST?
    
    let body: ExprAST
    
    init(_ name: String, _ start: ExprAST, _ end: ExprAST, _ step: ExprAST?, _ body: ExprAST) {
        self.name = name
        self.start = start
        self.end = end
        self.step = step
        self.body = body
    }
  
}

step用来表示for循环的步长,即每次变量的增长值。编译器通过检查第二个逗号是否存在来判断,如果不存在我们把它设为nil

for循环的Parser扩展

    /// 解析For表达式
    ///
    /// - Returns: AST
    private func parseForExpr() -> ExprAST? {
        lexer.nextToken()
        //第一个得是变量,比如说`i`
        guard lexer.currentToken!.token == .identifier else {
            fatalError("expected identifier after for.")
        }
        let idName = lexer.currentToken!.val
        lexer.nextToken()
        guard lexer.currentToken!.val == "=" else {
            fatalError("expected '=' after for.")
        }
        
        lexer.nextToken()
        //循环开始值
        let start = parseExpression()
        guard start != nil else {
            return nil
        }
        guard lexer.currentToken!.val == "," else {
            fatalError("expected ',' after start value.")
        }
        
        lexer.nextToken()
        //循环结束值
        let end = parseExpression()
        guard end != nil else {
            return nil
        }
        
        //步长
        var step: ExprAST!
        if lexer.currentToken!.val == "," {
            lexer.nextToken()
            step = parseExpression()
            guard step != nil else {
                return nil
            }
        }
        //in作为for循环的关键字不可缺少
        guard lexer.currentToken!.token == .in else {
            fatalError("expected 'in' after for.")
        }
        lexer.nextToken()
        //for循环的循环体解析
        let body = parseExpression()
        guard body != nil else {
            return nil
        }
        
        return ForExprAST(idName, start!, end!, step, body!)
    }

我们在parsePrimary()方法中补充调用。

case .for:
        return parseForExpr()

for循环的代码生成

话不多说直接看代码,过程都会体现在注释中。

    func codeGen() -> IRValue? {
        let startVal = start.codeGen()
        guard startVal != nil else {
            return nil
        }
        
        //for循环,插在当前的block之后
        let theFunction = builder.insertBlock?.parent
        guard theFunction != nil else {
            return nil
        }
        let preHeaderBB = builder.insertBlock
        //循环体的基本块
        let loopBB = theFunction!.appendBasicBlock(named: "loop")
        builder.buildBr(loopBB)
        //让builder移动到
        builder.positionAtEnd(of: loopBB)
        
        //这里控制循环或退出
        let phi = builder.buildPhi(FloatType.double, name: name)
        phi.addIncoming([(startVal!, preHeaderBB!)])
        
        //防止for循环作用域与外部产生变量命名冲突,所以先记录一下,是nil也无所谓
        let oldVal = namedValues[name]
        namedValues[name] = phi
        
        guard body.codeGen() != nil else {
            return nil
        }
        
        let stepVal: IRValue?
        if step != nil {
            //有步长就要解析
            stepVal = step!.codeGen()
            guard stepVal != nil else {
                return nil
            }
        } else {
            //默认步长为1.0
            stepVal = FloatType.double.constant(1)
        }
        //步长的增长指令
        let nextVar = builder.buildAdd(phi, stepVal!, name: "nextVar")
        
        //循环终止条件
        var endCond = end.codeGen()
        guard endCond != nil else {
            return nil
        }
        endCond = builder.buildICmp(endCond!, IntType.int1.zero(), .equal, name: "loopCond")
        
        //循环后的代码basic block
        let loopEndBB = builder.insertBlock
        let afterBB = theFunction?.appendBasicBlock(named: "afterLoop")
        builder.buildCondBr(condition: endCond!, then: loopBB, else: afterBB!)
        builder.positionAtEnd(of: afterBB!)
        
        phi.addIncoming([(nextVar, loopEndBB!)])
        
        if oldVal != nil {
            namedValues[name] = oldVal!
        } else {
            namedValues[name] = nil
        }
        
        //for循环解析总是返回0
        return FloatType.double.constant(0)
    }

测试

控制流语句

extern foo();
Read extern:

declare i64 @foo()
extern bar();
Read extern:

declare i64 @bar()
def baz(x) if x then foo() else bar();
Read function definition:

define i64 @baz(i64 %x) {
entry:
  %ifCond = icmp eq i64 %x, 0
  br i1 %ifCond, label %then, label %else

then:                                             ; preds = %entry
  %call = call i64 @foo()
  br label %merge

else:                                             ; preds = %entry
  %call1 = call i64 @bar()
  br label %merge

merge:                                            ; preds = %else, %then
  %phi = phi i64 [ %call, %then ], [ %call1, %else ]
  ret i64 %phi
}

for循环语句

extern putchard(char);
Read extern:

declare i64 @putchard(i64 %char)
def printstar(n) for i = 1, i < n, 1 in putchard(42);
Read function definition:

define i64 @printstar(i64 %n) {
entry:
  br label %loop

loop:                                             ; preds = %loop, %entry
  %i = phi i64 [ 1, %entry ], [ %nextVar, %loop ]
  %call = call i64 @putchard(i64 42)
  %nextVar = add i64 %i, 1
  %boolCmp = icmp slt i64 %i, %n
  %0 = sext i1 %boolCmp to i64
  %loopCond = icmp eq i64 %0, 0
  br i1 %loopCond, label %loop, label %afterLoop

afterLoop:                                        ; preds = %loop
  ret i64 0
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,372评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,368评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,415评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,157评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,171评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,125评论 1 297
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,028评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,887评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,310评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,533评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,690评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,411评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,004评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,659评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,812评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,693评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,577评论 2 353