Change quasiquote algorithm
[jackhill/mal.git] / impls / kotlin / src / mal / core.kt
CommitLineData
53c2ea70
JFI
1package mal
2
4121b4ee 3import java.io.File
304ec7f3 4import java.util.*
4121b4ee 5
53c2ea70 6val 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>())) }),
fbfe6784
NB
57 envPair("vec", { a: ISeq ->
58 val list = a.first() as? ISeq ?: throw MalException("vec requires a sequence")
59 MalVector(list)
60 }),
99851ac7 61 envPair("nth", { a: ISeq ->
d8e5df5d
JFI
62 val list = a.nth(0) as? ISeq ?: throw MalException("nth requires a list as its first parameter")
63 val index = a.nth(1) as? MalInteger ?: throw MalException("nth requires an integer as its second parameter")
2a9f3ad2 64 if (index.value >= list.count()) throw MalException("index out of bounds")
d937c5a1 65 list.nth(index.value.toInt())
99851ac7
JFI
66 }),
67 envPair("first", { a: ISeq ->
d8e5df5d
JFI
68 if (a.nth(0) == NIL) NIL
69 else {
70 val list = a.nth(0) as? ISeq ?: throw MalException("first requires a list parameter")
71 if (list.seq().any()) list.first() else NIL
72 }
99851ac7
JFI
73 }),
74 envPair("rest", { a: ISeq ->
dfabec93
DM
75 if (a.nth(0) == NIL) MalList()
76 else {
77 val list = a.nth(0) as? ISeq ?: throw MalException("rest requires a list parameter")
78 MalList(list.rest())
79 }
99851ac7 80 }),
041bb7eb 81
99851ac7 82 envPair("throw", { a: ISeq ->
041bb7eb
JFI
83 val throwable = a.nth(0)
84 throw MalCoreException(pr_str(throwable), throwable)
99851ac7 85 }),
041bb7eb 86
99851ac7 87 envPair("apply", { a: ISeq ->
041bb7eb
JFI
88 val function = a.nth(0) as MalFunction
89 val params = MalList()
90 a.seq().drop(1).forEach({ it ->
91 if (it is ISeq) {
92 it.seq().forEach({ x -> params.conj_BANG(x) })
93 } else {
94 params.conj_BANG(it)
95 }
96 })
97 function.apply(params)
99851ac7 98 }),
041bb7eb 99
99851ac7 100 envPair("map", { a: ISeq ->
041bb7eb
JFI
101 val function = a.nth(0) as MalFunction
102 MalList((a.nth(1) as ISeq).seq().map({ it ->
103 val params = MalList()
104 params.conj_BANG(it)
105 function.apply(params)
304ec7f3 106 }).toCollection(LinkedList<MalType>()))
99851ac7 107 }),
041bb7eb 108
99851ac7
JFI
109 envPair("nil?", { a: ISeq -> if (a.nth(0) == NIL) TRUE else FALSE }),
110 envPair("true?", { a: ISeq -> if (a.nth(0) == TRUE) TRUE else FALSE }),
111 envPair("false?", { a: ISeq -> if (a.nth(0) == FALSE) TRUE else FALSE }),
7ccabbff
JM
112 envPair("string?", { a: ISeq ->
113 if (a.nth(0) is MalString && !(a.nth(0) is MalKeyword)) TRUE else FALSE
114 }),
99851ac7 115 envPair("symbol?", { a: ISeq -> if (a.nth(0) is MalSymbol) TRUE else FALSE }),
041bb7eb 116
99851ac7
JFI
117 envPair("symbol", { a: ISeq -> MalSymbol((a.nth(0) as MalString).value) }),
118 envPair("keyword", { a: ISeq ->
041bb7eb
JFI
119 val param = a.nth(0)
120 if (param is MalKeyword) param else MalKeyword((a.nth(0) as MalString).value)
99851ac7
JFI
121 }),
122 envPair("keyword?", { a: ISeq -> if (a.nth(0) is MalKeyword) TRUE else FALSE }),
9d7af552
DM
123 envPair("number?", { a: ISeq -> if (a.nth(0) is MalInteger) TRUE else FALSE }),
124 envPair("fn?", { a: ISeq -> if ((a.nth(0) as? MalFunction)?.is_macro ?: true) FALSE else TRUE }),
125 envPair("macro?", { a: ISeq -> if ((a.nth(0) as? MalFunction)?.is_macro ?: false) TRUE else FALSE }),
126
99851ac7
JFI
127 envPair("vector", { a: ISeq -> MalVector(a) }),
128 envPair("vector?", { a: ISeq -> if (a.nth(0) is MalVector) TRUE else FALSE }),
041bb7eb 129
99851ac7 130 envPair("hash-map", { a: ISeq ->
26f0b60f 131 val map = MalHashMap()
7ee7a06b 132 pairwise(a).forEach({ it -> map.assoc_BANG(it.first as MalString, it.second) })
26f0b60f 133 map
99851ac7
JFI
134 }),
135 envPair("map?", { a: ISeq -> if (a.nth(0) is MalHashMap) TRUE else FALSE }),
136 envPair("assoc", { a: ISeq ->
7ee7a06b
JFI
137 val map = MalHashMap(a.first() as MalHashMap)
138 pairwise(a.rest()).forEach({ it -> map.assoc_BANG(it.first as MalString, it.second) })
26f0b60f 139 map
99851ac7
JFI
140 }),
141 envPair("dissoc", { a: ISeq ->
7ee7a06b
JFI
142 val map = MalHashMap(a.first() as MalHashMap)
143 a.rest().seq().forEach({ it -> map.dissoc_BANG(it as MalString) })
26f0b60f 144 map
99851ac7
JFI
145 }),
146 envPair("get", { a: ISeq ->
26f0b60f
JFI
147 val map = a.nth(0) as? MalHashMap
148 val key = a.nth(1) as MalString
149 map?.elements?.get(key) ?: NIL
99851ac7
JFI
150 }),
151 envPair("contains?", { a: ISeq ->
26f0b60f
JFI
152 val map = a.nth(0) as? MalHashMap
153 val key = a.nth(1) as MalString
154 if (map?.elements?.get(key) != null) TRUE else FALSE
99851ac7
JFI
155 }),
156 envPair("keys", { a: ISeq ->
26f0b60f 157 val map = a.nth(0) as MalHashMap
0ab4fa14 158 MalList(map.elements.keys.toCollection(LinkedList<MalType>()))
99851ac7
JFI
159 }),
160 envPair("vals", { a: ISeq ->
26f0b60f 161 val map = a.nth(0) as MalHashMap
0ab4fa14 162 MalList(map.elements.values.toCollection(LinkedList<MalType>()))
99851ac7
JFI
163 }),
164 envPair("count", { a: ISeq ->
26f0b60f 165 val seq = a.nth(0) as? ISeq
d937c5a1 166 if (seq != null) MalInteger(seq.count().toLong()) else ZERO
99851ac7
JFI
167 }),
168 envPair("sequential?", { a: ISeq -> if (a.nth(0) is ISeq) TRUE else FALSE }),
2ec7fa0b 169
99851ac7 170 envPair("with-meta", { a: ISeq ->
2eb2cac2
JFI
171 val obj = a.nth(0)
172 val metadata = a.nth(1)
173 obj.with_meta(metadata)
99851ac7
JFI
174 }),
175 envPair("meta", { a: ISeq -> a.first().metadata }),
7ccabbff 176
99851ac7 177 envPair("conj", { a: ISeq -> (a.first() as ISeq).conj(a.rest()) }),
7ccabbff
JM
178 envPair("seq", { a: ISeq ->
179 val obj = a.nth(0)
180 if (obj is ISeq) {
181 if (obj.count() == 0) NIL
182 else MalList(obj.seq().toCollection(LinkedList<MalType>()))
183 } else if (obj is MalString && !(obj is MalKeyword)) {
184 if (obj.value.length == 0) NIL
185 else {
186 var strs = obj.value.map({ c -> MalString(c.toString()) })
187 MalList(strs.toCollection(LinkedList<MalType>()))
188 }
189 } else {
190 NIL
191 }
192 }),
99851ac7
JFI
193
194 envPair("atom", { a: ISeq -> MalAtom(a.first()) }),
195 envPair("atom?", { a: ISeq -> if (a.first() is MalAtom) TRUE else FALSE }),
196 envPair("deref", { a: ISeq -> (a.first() as MalAtom).value }),
197 envPair("reset!", { a: ISeq ->
7ee7a06b 198 val atom = a.nth(0) as MalAtom
a779c814
JFI
199 val value = a.nth(1)
200 atom.value = value
201 value
99851ac7
JFI
202 }),
203 envPair("swap!", { a: ISeq ->
6132da85 204 val atom = a.nth(0) as MalAtom
a779c814
JFI
205 val function = a.nth(1) as MalFunction
206
a779c814
JFI
207 val params = MalList()
208 params.conj_BANG(atom.value)
6132da85 209 a.seq().drop(2).forEach({ it -> params.conj_BANG(it) })
a779c814
JFI
210
211 val value = function.apply(params)
212 atom.value = value
213
214 value
99851ac7 215 }),
a779c814 216
99851ac7 217 envPair("readline", { a: ISeq ->
a779c814
JFI
218 val prompt = a.first() as MalString
219 try {
220 MalString(readline(prompt.value))
221 } catch (e: java.io.IOException) {
222 throw MalException(e.message)
223 } catch (e: EofException) {
224 NIL
225 }
99851ac7 226 }),
d937c5a1 227
99851ac7 228 envPair("time-ms", { a: ISeq -> MalInteger(System.currentTimeMillis()) })
53c2ea70
JFI
229)
230
99851ac7
JFI
231private fun envPair(k: String, v: (ISeq) -> MalType): Pair<MalSymbol, MalType> = Pair(MalSymbol(k), MalFunction(v))
232
233private fun pairwise(s: ISeq): List<Pair<MalType, MalType>> {
7ee7a06b
JFI
234 val (keys, vals) = s.seq().withIndex().partition({ it -> it.index % 2 == 0 })
235 return keys.map({ it -> it.value }).zip(vals.map({ it -> it.value }))
236}
237
99851ac7 238private fun pairwiseCompare(s: ISeq, pred: (MalInteger, MalInteger) -> Boolean): MalConstant =
7ee7a06b 239 if (pairwise(s).all({ it -> pred(it.first as MalInteger, it.second as MalInteger) })) TRUE else FALSE
53c2ea70 240
99851ac7 241private fun pairwiseEquals(s: ISeq): MalConstant =
7ee7a06b 242 if (pairwise(s).all({ it -> it.first == it.second })) TRUE else FALSE