Last active
February 28, 2017 08:39
-
-
Save teddyknox/196c264efce3dd8fae41958d32ea7405 to your computer and use it in GitHub Desktop.
Using Swift's Protocols with Associated Types to compile-time check an Abstract Syntax Tree DSL
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// feb25.swift | |
// AbstractSyntaxTree | |
// | |
// Created by Edward Knox on 2/25/17. | |
// Copyright © 2017 Edward Knox. All rights reserved. | |
// | |
import Foundation | |
protocol Exp { | |
associatedtype ExpType | |
func apply() -> ExpType | |
} | |
protocol UnaryExp: Exp { | |
associatedtype OpType | |
init(_ op: OpType) | |
} | |
protocol BinaryExp: Exp { | |
associatedtype LeftOpType | |
associatedtype RightOpType | |
init(_ leftOpExp: LeftOpType, _ rightOpExp: RightOpType) | |
} | |
protocol IntExp: Exp { | |
typealias ExpType = Int | |
} | |
protocol StrExp: Exp { | |
typealias ExpType = String | |
} | |
class IntVal: IntExp, UnaryExp { | |
let value: Int | |
required init(_ value: Int) { | |
self.value = value | |
} | |
func apply() -> Int { | |
return value | |
} | |
} | |
class StrVal: StrExp, UnaryExp { | |
let value: String | |
required init(_ value: String) { | |
self.value = value | |
} | |
func apply() -> String { | |
return value | |
} | |
} | |
class Addition<T: IntExp, U: IntExp>: Exp, BinaryExp { | |
let leftOpExp: T | |
let rightOpExp: U | |
required init(_ leftOpExp: T, _ rightOpExp: U) { | |
self.leftOpExp = leftOpExp | |
self.rightOpExp = rightOpExp | |
} | |
func apply() -> Int { | |
let leftOp = leftOpExp.apply() | |
let rightOp = rightOpExp.apply() | |
return leftOp + rightOp | |
} | |
} | |
class Batman<T: IntExp, U: StrExp>: Exp, BinaryExp { | |
let leftOpExp: T | |
let rightOpExp: U | |
required init(_ leftOpExp: T, _ rightOpExp: U) { | |
self.leftOpExp = leftOpExp | |
self.rightOpExp = rightOpExp | |
} | |
func apply() -> String { | |
let times = leftOpExp.apply() | |
let suffix = rightOpExp.apply() | |
var out = "" | |
for _ in 1...times { | |
out += "na" | |
} | |
return out + " " + suffix | |
} | |
} | |
func execute<T: IntExp>(_ exp: T) -> Int { | |
return exp.apply() | |
} | |
let a = IntVal(8) | |
let b = IntVal(2) | |
let c = StrVal("BATMAN!") | |
let sum = Addition(a, b) | |
let batman = Batman(a, c) | |
let robin = Batman<IntVal, IntVal>(a, b) // Error: Type 'IntVal' does not conform to protocol 'StrExp' | |
print(batman.apply()) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment