本章對應官方教程第7章。本章的目的是支持變量var
和`=運算符,咱們須要在堆棧分配內存給變量,詳細說明請查看官方教程第7章。html
教程以下:git
教你使用swift寫編譯器玩具(0)github
教你使用swift寫編譯器玩具(1)express
教你使用swift寫編譯器玩具(2)swift
首先咱們須要改變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
}
複製代碼
在本章中,變量改成了存於堆棧中,所以變量的代碼生成也須要從堆棧中加載。咱們修改VariableExprAST
的codeGen()
方法。
func codeGen() -> IRValue? {
let value = namedValues[name]
guard value != nil else {
fatalError("unknow variable name.")
}
return builder.buildLoad(value!, name: name)
}
複製代碼
如今咱們須要更新定義變量的代碼來設置Alloca
。咱們從ForExprAST
的codeGen()
方法開始。
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]
複製代碼
接下來我想你們都能想到,那就是去修改BinaryExprAST
的codeGen()
方法。
咱們只須要在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
}
複製代碼
就像以前所作過的擴展同樣,咱們先要在Token
、Lexer
和Parser
。
enum Token {
...
case `var`
...
}
else if identifierStr == "var" {
currentToken = CurrentToken(token: .var, val: "var")
}
複製代碼
接着咱們構造VarExprAST
AST 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
複製代碼