教你使用swift寫編譯器玩具(5)

前言

本章對應官方教程第5章,本章介紹如何擴展Kaleidoscope以使用if / then / else表達式和一個簡單的for循環。html

教程以下:git

教你使用swift寫編譯器玩具(0)github

教你使用swift寫編譯器玩具(1)express

教你使用swift寫編譯器玩具(2)swift

教你使用swift寫編譯器玩具(3)bash

教你使用swift寫編譯器玩具(4)app

教你使用swift寫編譯器玩具(5)ide

教你使用swift寫編譯器玩具(6)函數

教你使用swift寫編譯器玩具(7)oop

教你使用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
}
複製代碼
相關文章
相關標籤/搜索