本章對應官方教程第5章,本章介紹如何擴展Kaleidoscope以使用if / then / else
表達式和一個簡單的for
循環。html
教程以下:git
教你使用swift寫編譯器玩具(0)github
教你使用swift寫編譯器玩具(1)express
教你使用swift寫編譯器玩具(2)swift
if / then / else
也是一種表達式,咱們須要把它計算爲int1類型,0是假,1是真。若是if
表達式計算爲真返回then
表達式,不然返回else
表達式。
首先咱們須要作的第一件事情是擴展咱們的Token枚舉
...
case `if`
case then
case `else`
...
複製代碼
接着咱們在Lexer
的nextToken()
方法中補充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")
}
複製代碼
爲了解析新的表達式咱們須要添加新的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`
}
}
複製代碼
有了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")
}
}
複製代碼
咱們須要在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
}
複製代碼
Kaleidoscope的for循環長下面這樣,1.0是可選的步長,默認即爲1.0。
for i = 1, i < n, 1.0 in
複製代碼
for循環表達式的處理會複雜一些,但仍是一樣運用了Phi
操做來處理。
同控制流語句的擴展,咱們仍是先要擴展Token
和Lexer
。
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表達式
///
/// - 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()
複製代碼
話很少說直接看代碼,過程都會體如今註釋中。
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
}
複製代碼