Commit | Line | Data |
---|---|---|
53c2ea70 JFI |
1 | package mal |
2 | ||
4121b4ee | 3 | import java.io.File |
304ec7f3 | 4 | import java.util.* |
4121b4ee | 5 | |
53c2ea70 | 6 | val ns = hashMapOf( |
99851ac7 JFI |
7 | envPair("+", { a: ISeq -> a.seq().reduce({ x, y -> x as MalInteger + y as MalInteger }) }), |
8 | envPair("-", { a: ISeq -> a.seq().reduce({ x, y -> x as MalInteger - y as MalInteger }) }), | |
9 | envPair("*", { a: ISeq -> a.seq().reduce({ x, y -> x as MalInteger * y as MalInteger }) }), | |
10 | envPair("/", { a: ISeq -> a.seq().reduce({ x, y -> x as MalInteger / y as MalInteger }) }), | |
11 | ||
12 | envPair("list", { a: ISeq -> MalList(a) }), | |
13 | envPair("list?", { a: ISeq -> if (a.first() is MalList) TRUE else FALSE }), | |
14 | envPair("empty?", { a: ISeq -> if (a.first() !is ISeq || !(a.first() as ISeq).seq().any()) TRUE else FALSE }), | |
15 | envPair("count", { a: ISeq -> | |
16 | if (a.first() is ISeq) MalInteger((a.first() as ISeq).count().toLong()) else MalInteger(0) | |
17 | }), | |
18 | ||
19 | envPair("=", { a: ISeq -> pairwiseEquals(a) }), | |
20 | envPair("<", { a: ISeq -> pairwiseCompare(a, { x, y -> x.value < y.value }) }), | |
21 | envPair("<=", { a: ISeq -> pairwiseCompare(a, { x, y -> x.value <= y.value }) }), | |
22 | envPair(">", { a: ISeq -> pairwiseCompare(a, { x, y -> x.value > y.value }) }), | |
23 | envPair(">=", { a: ISeq -> pairwiseCompare(a, { x, y -> x.value >= y.value }) }), | |
24 | ||
25 | envPair("pr-str", { a: ISeq -> | |
26 | MalString(a.seq().map({ it -> pr_str(it, print_readably = true) }).joinToString(" ")) | |
27 | }), | |
28 | envPair("str", { a: ISeq -> | |
29 | MalString(a.seq().map({ it -> pr_str(it, print_readably = false) }).joinToString("")) | |
30 | }), | |
31 | envPair("prn", { a: ISeq -> | |
32 | println(a.seq().map({ it -> pr_str(it, print_readably = true) }).joinToString(" ")) | |
33 | NIL | |
34 | }), | |
35 | envPair("println", { a: ISeq -> | |
36 | println(a.seq().map({ it -> pr_str(it, print_readably = false) }).joinToString(" ")) | |
37 | NIL | |
38 | }), | |
39 | ||
40 | envPair("read-string", { a: ISeq -> | |
4121b4ee JFI |
41 | val string = a.first() as? MalString ?: throw MalException("slurp requires a string parameter") |
42 | read_str(string.value) | |
99851ac7 JFI |
43 | }), |
44 | envPair("slurp", { a: ISeq -> | |
4121b4ee JFI |
45 | val name = a.first() as? MalString ?: throw MalException("slurp requires a filename parameter") |
46 | val text = File(name.value).readText() | |
47 | MalString(text) | |
99851ac7 | 48 | }), |
940bb6ff | 49 | |
99851ac7 | 50 | envPair("cons", { a: ISeq -> |
940bb6ff | 51 | val list = a.nth(1) as? ISeq ?: throw MalException("cons requires a list as its second parameter") |
304ec7f3 | 52 | val mutableList = list.seq().toCollection(LinkedList<MalType>()) |
940bb6ff JFI |
53 | mutableList.addFirst(a.nth(0)) |
54 | MalList(mutableList) | |
99851ac7 | 55 | }), |
304ec7f3 | 56 | envPair("concat", { a: ISeq -> MalList(a.seq().flatMap({ it -> (it as ISeq).seq() }).toCollection(LinkedList<MalType>())) }), |
d8e5df5d | 57 | |
99851ac7 | 58 | envPair("nth", { a: ISeq -> |
d8e5df5d JFI |
59 | val list = a.nth(0) as? ISeq ?: throw MalException("nth requires a list as its first parameter") |
60 | val index = a.nth(1) as? MalInteger ?: throw MalException("nth requires an integer as its second parameter") | |
2a9f3ad2 | 61 | if (index.value >= list.count()) throw MalException("index out of bounds") |
d937c5a1 | 62 | list.nth(index.value.toInt()) |
99851ac7 JFI |
63 | }), |
64 | envPair("first", { a: ISeq -> | |
d8e5df5d JFI |
65 | if (a.nth(0) == NIL) NIL |
66 | else { | |
67 | val list = a.nth(0) as? ISeq ?: throw MalException("first requires a list parameter") | |
68 | if (list.seq().any()) list.first() else NIL | |
69 | } | |
99851ac7 JFI |
70 | }), |
71 | envPair("rest", { a: ISeq -> | |
dfabec93 DM |
72 | if (a.nth(0) == NIL) MalList() |
73 | else { | |
74 | val list = a.nth(0) as? ISeq ?: throw MalException("rest requires a list parameter") | |
75 | MalList(list.rest()) | |
76 | } | |
99851ac7 | 77 | }), |
041bb7eb | 78 | |
99851ac7 | 79 | envPair("throw", { a: ISeq -> |
041bb7eb JFI |
80 | val throwable = a.nth(0) |
81 | throw MalCoreException(pr_str(throwable), throwable) | |
99851ac7 | 82 | }), |
041bb7eb | 83 | |
99851ac7 | 84 | envPair("apply", { a: ISeq -> |
041bb7eb JFI |
85 | val function = a.nth(0) as MalFunction |
86 | val params = MalList() | |
87 | a.seq().drop(1).forEach({ it -> | |
88 | if (it is ISeq) { | |
89 | it.seq().forEach({ x -> params.conj_BANG(x) }) | |
90 | } else { | |
91 | params.conj_BANG(it) | |
92 | } | |
93 | }) | |
94 | function.apply(params) | |
99851ac7 | 95 | }), |
041bb7eb | 96 | |
99851ac7 | 97 | envPair("map", { a: ISeq -> |
041bb7eb JFI |
98 | val function = a.nth(0) as MalFunction |
99 | MalList((a.nth(1) as ISeq).seq().map({ it -> | |
100 | val params = MalList() | |
101 | params.conj_BANG(it) | |
102 | function.apply(params) | |
304ec7f3 | 103 | }).toCollection(LinkedList<MalType>())) |
99851ac7 | 104 | }), |
041bb7eb | 105 | |
99851ac7 JFI |
106 | envPair("nil?", { a: ISeq -> if (a.nth(0) == NIL) TRUE else FALSE }), |
107 | envPair("true?", { a: ISeq -> if (a.nth(0) == TRUE) TRUE else FALSE }), | |
108 | envPair("false?", { a: ISeq -> if (a.nth(0) == FALSE) TRUE else FALSE }), | |
109 | envPair("symbol?", { a: ISeq -> if (a.nth(0) is MalSymbol) TRUE else FALSE }), | |
041bb7eb | 110 | |
99851ac7 JFI |
111 | envPair("symbol", { a: ISeq -> MalSymbol((a.nth(0) as MalString).value) }), |
112 | envPair("keyword", { a: ISeq -> | |
041bb7eb JFI |
113 | val param = a.nth(0) |
114 | if (param is MalKeyword) param else MalKeyword((a.nth(0) as MalString).value) | |
99851ac7 JFI |
115 | }), |
116 | envPair("keyword?", { a: ISeq -> if (a.nth(0) is MalKeyword) TRUE else FALSE }), | |
117 | envPair("vector", { a: ISeq -> MalVector(a) }), | |
118 | envPair("vector?", { a: ISeq -> if (a.nth(0) is MalVector) TRUE else FALSE }), | |
041bb7eb | 119 | |
99851ac7 | 120 | envPair("hash-map", { a: ISeq -> |
26f0b60f | 121 | val map = MalHashMap() |
7ee7a06b | 122 | pairwise(a).forEach({ it -> map.assoc_BANG(it.first as MalString, it.second) }) |
26f0b60f | 123 | map |
99851ac7 JFI |
124 | }), |
125 | envPair("map?", { a: ISeq -> if (a.nth(0) is MalHashMap) TRUE else FALSE }), | |
126 | envPair("assoc", { a: ISeq -> | |
7ee7a06b JFI |
127 | val map = MalHashMap(a.first() as MalHashMap) |
128 | pairwise(a.rest()).forEach({ it -> map.assoc_BANG(it.first as MalString, it.second) }) | |
26f0b60f | 129 | map |
99851ac7 JFI |
130 | }), |
131 | envPair("dissoc", { a: ISeq -> | |
7ee7a06b JFI |
132 | val map = MalHashMap(a.first() as MalHashMap) |
133 | a.rest().seq().forEach({ it -> map.dissoc_BANG(it as MalString) }) | |
26f0b60f | 134 | map |
99851ac7 JFI |
135 | }), |
136 | envPair("get", { a: ISeq -> | |
26f0b60f JFI |
137 | val map = a.nth(0) as? MalHashMap |
138 | val key = a.nth(1) as MalString | |
139 | map?.elements?.get(key) ?: NIL | |
99851ac7 JFI |
140 | }), |
141 | envPair("contains?", { a: ISeq -> | |
26f0b60f JFI |
142 | val map = a.nth(0) as? MalHashMap |
143 | val key = a.nth(1) as MalString | |
144 | if (map?.elements?.get(key) != null) TRUE else FALSE | |
99851ac7 JFI |
145 | }), |
146 | envPair("keys", { a: ISeq -> | |
26f0b60f | 147 | val map = a.nth(0) as MalHashMap |
0ab4fa14 | 148 | MalList(map.elements.keys.toCollection(LinkedList<MalType>())) |
99851ac7 JFI |
149 | }), |
150 | envPair("vals", { a: ISeq -> | |
26f0b60f | 151 | val map = a.nth(0) as MalHashMap |
0ab4fa14 | 152 | MalList(map.elements.values.toCollection(LinkedList<MalType>())) |
99851ac7 JFI |
153 | }), |
154 | envPair("count", { a: ISeq -> | |
26f0b60f | 155 | val seq = a.nth(0) as? ISeq |
d937c5a1 | 156 | if (seq != null) MalInteger(seq.count().toLong()) else ZERO |
99851ac7 JFI |
157 | }), |
158 | envPair("sequential?", { a: ISeq -> if (a.nth(0) is ISeq) TRUE else FALSE }), | |
2ec7fa0b | 159 | |
99851ac7 | 160 | envPair("with-meta", { a: ISeq -> |
2eb2cac2 JFI |
161 | val obj = a.nth(0) |
162 | val metadata = a.nth(1) | |
163 | obj.with_meta(metadata) | |
99851ac7 JFI |
164 | }), |
165 | envPair("meta", { a: ISeq -> a.first().metadata }), | |
166 | envPair("conj", { a: ISeq -> (a.first() as ISeq).conj(a.rest()) }), | |
167 | ||
168 | envPair("atom", { a: ISeq -> MalAtom(a.first()) }), | |
169 | envPair("atom?", { a: ISeq -> if (a.first() is MalAtom) TRUE else FALSE }), | |
170 | envPair("deref", { a: ISeq -> (a.first() as MalAtom).value }), | |
171 | envPair("reset!", { a: ISeq -> | |
7ee7a06b | 172 | val atom = a.nth(0) as MalAtom |
a779c814 JFI |
173 | val value = a.nth(1) |
174 | atom.value = value | |
175 | value | |
99851ac7 JFI |
176 | }), |
177 | envPair("swap!", { a: ISeq -> | |
6132da85 | 178 | val atom = a.nth(0) as MalAtom |
a779c814 JFI |
179 | val function = a.nth(1) as MalFunction |
180 | ||
a779c814 JFI |
181 | val params = MalList() |
182 | params.conj_BANG(atom.value) | |
6132da85 | 183 | a.seq().drop(2).forEach({ it -> params.conj_BANG(it) }) |
a779c814 JFI |
184 | |
185 | val value = function.apply(params) | |
186 | atom.value = value | |
187 | ||
188 | value | |
99851ac7 | 189 | }), |
a779c814 | 190 | |
99851ac7 | 191 | envPair("readline", { a: ISeq -> |
a779c814 JFI |
192 | val prompt = a.first() as MalString |
193 | try { | |
194 | MalString(readline(prompt.value)) | |
195 | } catch (e: java.io.IOException) { | |
196 | throw MalException(e.message) | |
197 | } catch (e: EofException) { | |
198 | NIL | |
199 | } | |
99851ac7 | 200 | }), |
d937c5a1 | 201 | |
99851ac7 | 202 | envPair("time-ms", { a: ISeq -> MalInteger(System.currentTimeMillis()) }) |
53c2ea70 JFI |
203 | ) |
204 | ||
99851ac7 JFI |
205 | private fun envPair(k: String, v: (ISeq) -> MalType): Pair<MalSymbol, MalType> = Pair(MalSymbol(k), MalFunction(v)) |
206 | ||
207 | private fun pairwise(s: ISeq): List<Pair<MalType, MalType>> { | |
7ee7a06b JFI |
208 | val (keys, vals) = s.seq().withIndex().partition({ it -> it.index % 2 == 0 }) |
209 | return keys.map({ it -> it.value }).zip(vals.map({ it -> it.value })) | |
210 | } | |
211 | ||
99851ac7 | 212 | private fun pairwiseCompare(s: ISeq, pred: (MalInteger, MalInteger) -> Boolean): MalConstant = |
7ee7a06b | 213 | if (pairwise(s).all({ it -> pred(it.first as MalInteger, it.second as MalInteger) })) TRUE else FALSE |
53c2ea70 | 214 | |
99851ac7 | 215 | private fun pairwiseEquals(s: ISeq): MalConstant = |
7ee7a06b | 216 | if (pairwise(s).all({ it -> it.first == it.second })) TRUE else FALSE |