Skip to content

Instantly share code, notes, and snippets.

@teddyknox
Last active February 28, 2017 08:39
Show Gist options
  • Save teddyknox/196c264efce3dd8fae41958d32ea7405 to your computer and use it in GitHub Desktop.
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
//
// 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