forked from kanaka/mal
-
Notifications
You must be signed in to change notification settings - Fork 0
/
env.swift
89 lines (82 loc) · 2.81 KB
/
env.swift
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
class Env {
var outer: Env? = nil
var data: Dictionary<String, MalVal> = [:]
init(_ outer: Env? = nil, binds: MalVal? = nil,
exprs: MalVal? = nil) throws {
self.outer = outer
if binds != nil {
var bs = Array<MalVal>(), es = Array<MalVal>()
//print("binds: \(binds), exprs: \(exprs)")
switch (binds!, exprs!) {
case (MalVal.MalList(let l1, _), MalVal.MalList(let l2, _)):
bs = l1; es = l2
case (MalVal.MalVector(let l1, _), MalVal.MalList(let l2, _)):
bs = l1; es = l2
default:
throw MalError.General(msg: "invalid Env init call")
}
var pos = bs.startIndex
bhandle:
while pos < bs.endIndex {
let b = bs[pos]
switch b {
case MalVal.MalSymbol("&"):
switch bs[bs.index(after: pos)] {
case MalVal.MalSymbol(let sym):
if pos < es.endIndex {
let slc = es[pos..<es.endIndex]
data[sym] = list(Array(slc))
} else {
data[sym] = list([])
}
break bhandle
default:
throw MalError.General(msg: "Env invalid varargs")
}
case MalVal.MalSymbol(let sym):
let e = es[pos]
data[sym] = e
default:
throw MalError.General(msg: "Env binds has non-symbol")
}
pos = bs.index(after: pos)
}
}
}
func find(_ key: MalVal) throws -> Env? {
switch key {
case MalVal.MalSymbol(let str):
if data[str] != nil {
return self
} else if outer != nil {
return try outer!.find(key)
} else {
return nil
}
default:
throw MalError.General(msg: "invalid Env.find call")
}
}
func get(_ key: MalVal) throws -> MalVal {
switch key {
case MalVal.MalSymbol(let str):
let env = try self.find(key)
if env == nil {
throw MalError.General(msg: "'\(str)' not found")
}
return env!.data[str]!
default:
throw MalError.General(msg: "invalid Env.find call")
}
}
@discardableResult
func set(_ key: MalVal, _ val: MalVal) throws -> MalVal {
switch key {
case MalVal.MalSymbol(let str):
data[str] = val
return val
default:
throw MalError.General(msg: "invalid Env.find call")
}
}
}