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

前言

本章對應官方教程第7章。本章的目的是支持變量var和`=運算符,咱們須要在堆棧分配內存給變量,詳細說明請查看官方教程第7章。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)

倉庫在這

開始

調整現有變量

首先咱們須要改變namedValues的value類型。

var namedValues: [String: IRInstruction] = [:]
複製代碼

此外,咱們須要一個輔助函數來建立Alloca

func createEntryBlockAlloca(function: Function, name: String) -> IRInstruction {
    let instruction = builder.buildAlloca(type: FloatType.double, count: 0, name: name)
    return instruction
}
複製代碼

在本章中,變量改成了存於堆棧中,所以變量的代碼生成也須要從堆棧中加載。咱們修改VariableExprASTcodeGen()方法。

func codeGen() -> IRValue? {
        let value = namedValues[name]
        guard value != nil else {
            fatalError("unknow variable name.")
        }
        return builder.buildLoad(value!, name: name)
    }
複製代碼

如今咱們須要更新定義變量的代碼來設置Alloca。咱們從ForExprASTcodeGen()方法開始。

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
        }
        //在entry block中爲變量建立alloca
        let alloca = createEntryBlockAlloca(function: theFunction!, name: name)
      	把變量存儲在alloca中
        builder.buildStore(startVal!, to: alloca)
        
        let loopBB = theFunction!.appendBasicBlock(named: "loop")
        builder.buildBr(loopBB)
        builder.positionAtEnd(of: loopBB)
        
        let oldVal = namedValues[name]
        namedValues[name] = alloca
        
        guard body.codeGen() != nil else {
            return nil
        }
        
        let stepVal: IRValue?
        if step != nil {
            stepVal = step!.codeGen()
            guard stepVal != nil else {
                return nil
            }
        } else {
            stepVal = FloatType.double.constant(1)
        }
        
        //循環終止條件
        var endCond = end.codeGen()
        guard endCond != nil else {
            return nil
        }
        //build條件時候要使用int類型
        endCond = builder.buildICmp(endCond!, IntType.int1.zero(), .notEqual, name: "loopCond")
        
      	//加載當前變量
        let curVal = builder.buildLoad(alloca)
      	//讓下一個變量爲當前變量+步長,即增加了
        let nextVal = builder.buildAdd(curVal, stepVal!, name: "nextVal")
      	//再從新存儲到alloca中
        builder.buildStore(nextVal, to: alloca)
        
        //循環後的代碼basic block
        let afterBB = theFunction?.appendBasicBlock(named: "afterLoop")
        builder.buildCondBr(condition: endCond!, then: loopBB, else: afterBB!)
        builder.positionAtEnd(of: afterBB!)
        
        if oldVal != nil {
            namedValues[name] = oldVal!
        } else {
            namedValues[name] = nil
        }
        
        //for循環解析老是返回0
        return FloatType.double.constant(0)
    }
複製代碼

這其中最大的變化就是咱們再也不使用phi操做而是使用load/store來根據須要訪問變量。

咱們在FunctionAST中也須要改變codeGen()方法。

func codeGen() -> Function? {
        functionProtos[proto.name] = proto
        let theFunction = getFunction(named: proto.name)
        guard theFunction != nil else {
            return nil
        }
        
        //若是是操做符,把他放在全局的操做符表中
        if proto.isOperator {
            BinOpPrecedence[proto.operatorName!] = proto.precedence
        }
        
        let entry = theFunction!.appendBasicBlock(named: "entry")
        builder.positionAtEnd(of: entry)
        
        namedValues.removeAll()
        var arg = theFunction!.firstParameter
        while arg != nil {
          	//爲參數建立alloca
            let alloca = createEntryBlockAlloca(function: theFunction!, name: arg!.name)
          	//把變量存到alloca中
            builder.buildStore(arg!, to: alloca)
          	//把變量放到符號表裏
            namedValues[arg!.name] = alloca
            arg = arg?.next()
        }
        
        if let retValue = body.codeGen() {
            builder.buildRet(retValue)
            do {
                try theModule.verify()
                return theFunction
            } catch {
                print("\(error)")
            }
        }
        //函數體出現問題,移除函數
        theFunction!.eraseFromParent()
        if proto.isOperator {
            BinOpPrecedence[proto.operatorName!] = nil
        }
        return nil
    }
複製代碼

添加新的運算符

咱們須要在全局操做符表中加入=

var BinOpPrecedence: [String: UInt] = ["=": 2, "<": 10, "+": 20, "-": 20, "*": 40]
複製代碼

接下來我想你們都能想到,那就是去修改BinaryExprASTcodeGen()方法。

咱們只須要在codeGen()方法最開始判斷一下=便可。

if op == "=" {
            let lhse = lhs as? VariableExprAST
            guard lhse != nil else {
                fatalError("Destination of '=' must be a variable.")
            }
            let val = lhse?.codeGen()
            guard val != nil else {
                return nil
            }
          	//獲取符號表中的變量
            let variable = namedValues[lhse!.name]
            guard variable != nil else {
                fatalError("Unknow variable name.")
            }
          	//爲變量賦值
            builder.buildStore(val!, to: variable!)
            return val
        }
複製代碼

用戶定義的局部變量

就像以前所作過的擴展同樣,咱們先要在TokenLexerParser

enum Token {
		...
    case `var`
    ...
}
else if identifierStr == "var" {
		currentToken = CurrentToken(token: .var, val: "var")
}
複製代碼

接着咱們構造VarExprASTAST Node。

class VarExprAST: ExprAST {
    
    let varNames: [(String, ExprAST?)]
    
    let body: ExprAST
    
    init(_ varNames: [(String, ExprAST?)], _ body: ExprAST) {
        self.varNames = varNames
        self.body = body
    }
    
}
複製代碼

咱們容許經過var/in一次定義多個變量以及其初始化的值,而且咱們容許在body中訪問var/in定義的變量。

以後咱們須要定義Parser的方法。

/// 解析Var變量
    ///
    /// - Returns: AST
    private func parseVarExpr() -> ExprAST? {
        lexer.nextToken()
        var varNames: [(String, ExprAST?)] = []
        guard lexer.currentToken!.token == .identifier else {
            fatalError("Expected identifier after val.")
        }
        while true {
            let name = lexer.currentToken!.val
            lexer.nextToken()
            
            let expr: ExprAST? = nil
            if lexer.currentToken!.val == "=" {
                lexer.nextToken()
              	//解析"="右邊
                let expr = parseExpression()
                guard expr != nil else {
                    return nil
                }
            }
            
            varNames.append((name, expr))
            
          	//看看還有沒有下一個
            if lexer.currentToken!.val != "," {
                break
            }
            lexer.nextToken()
            if lexer.currentToken!.token != .identifier {
                fatalError("Expected identifier list after var.")
            }
        }
        if lexer.currentToken!.token != .in {
            fatalError("Expected 'in' keyword after 'var'.")
        }
        lexer.nextToken()
      	//解析body
        let body = parseExpression()
        guard body != nil else {
            return nil
        }
        return VarExprAST(varNames, body!)
    }
複製代碼

最後須要添加的是咱們VarExprAST中的codeGen()方法。

func codeGen() -> IRValue? {
        var oldBindings: [IRInstruction?] = []
        let theFunction = builder.insertBlock?.parent
        guard theFunction != nil else {
            return nil
        }
        //註冊全部變量,並讓他們初始化
        for v in varNames {
            let initVal: IRValue?
            if v.1 != nil {
                initVal = v.1?.codeGen()
                guard initVal != nil else {
                    return nil
                }
            } else {
                //沒有的話就默認0
                initVal = FloatType.double.constant(0)
            }
            
            let alloca = createEntryBlockAlloca(function: theFunction!, name: v.0)
            //初始化變量,把initVal存到alloca中
            builder.buildStore(initVal!, to: alloca)
          	//記錄的目的是防止丟失外部變量名相同的變量,好比說外部有變量a,body裏也有變量a
            oldBindings.append(namedValues[v.0])
            namedValues[v.0] = alloca
        }
        
        let bodyVal = body.codeGen()
        guard bodyVal != nil else {
            return nil
        }
        //恢復以前的變量綁定
        for i in 0..<varNames.count {
            namedValues[varNames[i].0] = oldBindings[i]
        }
        return bodyVal
    }
複製代碼

測試

咱們輸入

def binary : 1 (x y) y;

def fibi(x)
    var a = 1, b = 1, c in
    (for i = 3, i < x in
        c = a + b :
        a = b :
        b = c) :
    b;

fibi(10);
複製代碼

輸出

Read function definition:

define double @"binary:"(double %x, double %y) {
entry:
  %x1 = alloca double, i64 0
  store double %x, double* %x1
  %y2 = alloca double, i64 0
  store double %y, double* %y2
  %y3 = load double, double* %y2
  ret double %y3
}
Read function definition:

define double @fibi(double %x) {
entry:
  %x1 = alloca double, i64 0
  store double %x, double* %x1
  %a = alloca double, i64 0
  store double 0.000000e+00, double* %a
  %b = alloca double, i64 0
  store double 0.000000e+00, double* %b
  %c = alloca double, i64 0
  store double 0.000000e+00, double* %c
  %i = alloca double, i64 0
  store double 3.000000e+00, double* %i
  br label %loop

loop:                                             ; preds = %loop, %entry
  %c2 = load double, double* %c
  store double %c2, double* %c
  %a3 = load double, double* %a
  store double %a3, double* %a
  %binaryOp = call double @"binary:"(double %c2, double %a3)
  %b4 = load double, double* %b
  store double %b4, double* %b
  %binaryOp5 = call double @"binary:"(double %binaryOp, double %b4)
  %i6 = load double, double* %i
  %x7 = load double, double* %x1
  %boolCmp = fcmp olt double %i6, %x7
  %loopCond = icmp ne i1 %boolCmp, false
  %0 = load double, double* %i
  %nextVal = fadd double %0, 1.000000e+00
  store double %nextVal, double* %i
  br i1 %loopCond, label %loop, label %afterLoop

afterLoop:                                        ; preds = %loop
  %b8 = load double, double* %b
  %binaryOp9 = call double @"binary:"(double 0.000000e+00, double %b8)
  ret double %binaryOp9
}
Read top-level expression:

define double @__anon_expr() {
entry:
  %call = call double @fibi(double 1.000000e+01)
  ret double %call
}
0.0
複製代碼
相關文章
相關標籤/搜索