1
1
mirror of https://github.com/kanaka/mal.git synced 2024-09-21 02:27:10 +03:00
mal/swift3/Sources/types.swift
2016-10-17 22:37:42 -05:00

213 lines
6.1 KiB
Swift

enum MalError: Error {
case Reader(msg: String)
case General(msg: String)
case MalException(obj: MalVal)
}
class MutableAtom {
var val: MalVal
init(val: MalVal) {
self.val = val
}
}
enum MalVal {
case MalNil
case MalTrue
case MalFalse
case MalInt(Int)
case MalFloat(Float)
case MalString(String)
case MalSymbol(String)
case MalList(Array<MalVal>, meta: Array<MalVal>?)
case MalVector(Array<MalVal>, meta: Array<MalVal>?)
case MalHashMap(Dictionary<String,MalVal>, meta: Array<MalVal>?)
// TODO: internal MalVals are wrapped in arrays because otherwise
// compiler throws a fault
case MalFunc((Array<MalVal>) throws -> MalVal,
ast: Array<MalVal>?,
env: Env?,
params: Array<MalVal>?,
macro: Bool,
meta: Array<MalVal>?)
case MalAtom(MutableAtom)
}
typealias MV = MalVal
// General functions
func wraptf(_ a: Bool) -> MalVal {
return a ? MV.MalTrue : MV.MalFalse
}
// equality functions
func cmp_seqs(_ a: Array<MalVal>, _ b: Array<MalVal>) -> Bool {
if a.count != b.count { return false }
var idx = a.startIndex
while idx < a.endIndex {
if !equal_Q(a[idx], b[idx]) { return false }
idx = a.index(after:idx)
}
return true
}
func cmp_maps(_ a: Dictionary<String,MalVal>,
_ b: Dictionary<String,MalVal>) -> Bool {
if a.count != b.count { return false }
for (k,v1) in a {
if b[k] == nil { return false }
if !equal_Q(v1, b[k]!) { return false }
}
return true
}
func equal_Q(_ a: MalVal, _ b: MalVal) -> Bool {
switch (a, b) {
case (MV.MalNil, MV.MalNil): return true
case (MV.MalFalse, MV.MalFalse): return true
case (MV.MalTrue, MV.MalTrue): return true
case (MV.MalInt(let i1), MV.MalInt(let i2)): return i1 == i2
case (MV.MalString(let s1), MV.MalString(let s2)): return s1 == s2
case (MV.MalSymbol(let s1), MV.MalSymbol(let s2)): return s1 == s2
case (MV.MalList(let l1,_), MV.MalList(let l2,_)):
return cmp_seqs(l1, l2)
case (MV.MalList(let l1,_), MV.MalVector(let l2,_)):
return cmp_seqs(l1, l2)
case (MV.MalVector(let l1,_), MV.MalList(let l2,_)):
return cmp_seqs(l1, l2)
case (MV.MalVector(let l1,_), MV.MalVector(let l2,_)):
return cmp_seqs(l1, l2)
case (MV.MalHashMap(let d1,_), MV.MalHashMap(let d2,_)):
return cmp_maps(d1, d2)
default:
return false
}
}
// list and vector functions
func list(_ lst: Array<MalVal>) -> MalVal {
return MV.MalList(lst, meta:nil)
}
func list(_ lst: Array<MalVal>, meta: MalVal) -> MalVal {
return MV.MalList(lst, meta:[meta])
}
func vector(_ lst: Array<MalVal>) -> MalVal {
return MV.MalVector(lst, meta:nil)
}
func vector(_ lst: Array<MalVal>, meta: MalVal) -> MalVal {
return MV.MalVector(lst, meta:[meta])
}
// hash-map functions
func _assoc(_ src: Dictionary<String,MalVal>, _ mvs: Array<MalVal>)
throws -> Dictionary<String,MalVal> {
var d = src
if mvs.count % 2 != 0 {
throw MalError.General(msg: "Odd number of args to assoc_BANG")
}
var pos = mvs.startIndex
while pos < mvs.count {
switch (mvs[pos], mvs[pos+1]) {
case (MV.MalString(let k), let mv):
d[k] = mv
default:
throw MalError.General(msg: "Invalid _assoc call")
}
pos += 2
}
return d
}
func _dissoc(_ src: Dictionary<String,MalVal>, _ mvs: Array<MalVal>)
throws -> Dictionary<String,MalVal> {
var d = src
for mv in mvs {
switch mv {
case MV.MalString(let k): d.removeValue(forKey: k)
default: throw MalError.General(msg: "Invalid _dissoc call")
}
}
return d
}
func hash_map(_ dict: Dictionary<String,MalVal>) -> MalVal {
return MV.MalHashMap(dict, meta:nil)
}
func hash_map(_ dict: Dictionary<String,MalVal>, meta:MalVal) -> MalVal {
return MV.MalHashMap(dict, meta:[meta])
}
func hash_map(_ arr: Array<MalVal>) throws -> MalVal {
let d = Dictionary<String,MalVal>();
return MV.MalHashMap(try _assoc(d, arr), meta:nil)
}
// function functions
func malfunc(_ fn: @escaping (Array<MalVal>) throws -> MalVal) -> MalVal {
return MV.MalFunc(fn, ast: nil, env: nil, params: nil,
macro: false, meta: nil)
}
func malfunc(_ fn: @escaping (Array<MalVal>) throws -> MalVal,
ast: Array<MalVal>?,
env: Env?,
params: Array<MalVal>?) -> MalVal {
return MV.MalFunc(fn, ast: ast, env: env, params: params,
macro: false, meta: nil)
}
func malfunc(_ fn: @escaping (Array<MalVal>) throws -> MalVal,
ast: Array<MalVal>?,
env: Env?,
params: Array<MalVal>?,
macro: Bool,
meta: MalVal?) -> MalVal {
return MV.MalFunc(fn, ast: ast, env: env, params: params,
macro: macro, meta: meta != nil ? [meta!] : nil)
}
func malfunc(_ fn: @escaping (Array<MalVal>) throws -> MalVal,
ast: Array<MalVal>?,
env: Env?,
params: Array<MalVal>?,
macro: Bool,
meta: Array<MalVal>?) -> MalVal {
return MV.MalFunc(fn, ast: ast, env: env, params: params,
macro: macro, meta: meta)
}
// sequence functions
func _rest(_ a: MalVal) throws -> Array<MalVal> {
switch a {
case MV.MalList(let lst,_):
let start = lst.index(after: lst.startIndex)
let slc = lst[start..<lst.endIndex]
return Array(slc)
case MV.MalVector(let lst,_):
let start = lst.index(after: lst.startIndex)
let slc = lst[start..<lst.endIndex]
return Array(slc)
default:
throw MalError.General(msg: "Invalid rest call")
}
}
func rest(_ a: MalVal) throws -> MalVal {
return list(try _rest(a))
}
func _nth(_ a: MalVal, _ idx: Int) throws -> MalVal {
switch a {
case MV.MalList(let l,_): return l[l.startIndex.advanced(by: idx)]
case MV.MalVector(let l,_): return l[l.startIndex.advanced(by: idx)]
default: throw MalError.General(msg: "Invalid nth call")
}
}