Haxe, Matlab: add number?, fn? and macro?
[jackhill/mal.git] / 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>())) }),
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 }),
7ccabbff
JM
109 envPair("string?", { a: ISeq ->
110 if (a.nth(0) is MalString && !(a.nth(0) is MalKeyword)) TRUE else FALSE
111 }),
99851ac7 112 envPair("symbol?", { a: ISeq -> if (a.nth(0) is MalSymbol) TRUE else FALSE }),
041bb7eb 113
99851ac7
JFI
114 envPair("symbol", { a: ISeq -> MalSymbol((a.nth(0) as MalString).value) }),
115 envPair("keyword", { a: ISeq ->
041bb7eb
JFI
116 val param = a.nth(0)
117 if (param is MalKeyword) param else MalKeyword((a.nth(0) as MalString).value)
99851ac7
JFI
118 }),
119 envPair("keyword?", { a: ISeq -> if (a.nth(0) is MalKeyword) TRUE else FALSE }),
120 envPair("vector", { a: ISeq -> MalVector(a) }),
121 envPair("vector?", { a: ISeq -> if (a.nth(0) is MalVector) TRUE else FALSE }),
041bb7eb 122
99851ac7 123 envPair("hash-map", { a: ISeq ->
26f0b60f 124 val map = MalHashMap()
7ee7a06b 125 pairwise(a).forEach({ it -> map.assoc_BANG(it.first as MalString, it.second) })
26f0b60f 126 map
99851ac7
JFI
127 }),
128 envPair("map?", { a: ISeq -> if (a.nth(0) is MalHashMap) TRUE else FALSE }),
129 envPair("assoc", { a: ISeq ->
7ee7a06b
JFI
130 val map = MalHashMap(a.first() as MalHashMap)
131 pairwise(a.rest()).forEach({ it -> map.assoc_BANG(it.first as MalString, it.second) })
26f0b60f 132 map
99851ac7
JFI
133 }),
134 envPair("dissoc", { a: ISeq ->
7ee7a06b
JFI
135 val map = MalHashMap(a.first() as MalHashMap)
136 a.rest().seq().forEach({ it -> map.dissoc_BANG(it as MalString) })
26f0b60f 137 map
99851ac7
JFI
138 }),
139 envPair("get", { a: ISeq ->
26f0b60f
JFI
140 val map = a.nth(0) as? MalHashMap
141 val key = a.nth(1) as MalString
142 map?.elements?.get(key) ?: NIL
99851ac7
JFI
143 }),
144 envPair("contains?", { a: ISeq ->
26f0b60f
JFI
145 val map = a.nth(0) as? MalHashMap
146 val key = a.nth(1) as MalString
147 if (map?.elements?.get(key) != null) TRUE else FALSE
99851ac7
JFI
148 }),
149 envPair("keys", { a: ISeq ->
26f0b60f 150 val map = a.nth(0) as MalHashMap
0ab4fa14 151 MalList(map.elements.keys.toCollection(LinkedList<MalType>()))
99851ac7
JFI
152 }),
153 envPair("vals", { a: ISeq ->
26f0b60f 154 val map = a.nth(0) as MalHashMap
0ab4fa14 155 MalList(map.elements.values.toCollection(LinkedList<MalType>()))
99851ac7
JFI
156 }),
157 envPair("count", { a: ISeq ->
26f0b60f 158 val seq = a.nth(0) as? ISeq
d937c5a1 159 if (seq != null) MalInteger(seq.count().toLong()) else ZERO
99851ac7
JFI
160 }),
161 envPair("sequential?", { a: ISeq -> if (a.nth(0) is ISeq) TRUE else FALSE }),
2ec7fa0b 162
99851ac7 163 envPair("with-meta", { a: ISeq ->
2eb2cac2
JFI
164 val obj = a.nth(0)
165 val metadata = a.nth(1)
166 obj.with_meta(metadata)
99851ac7
JFI
167 }),
168 envPair("meta", { a: ISeq -> a.first().metadata }),
7ccabbff 169
99851ac7 170 envPair("conj", { a: ISeq -> (a.first() as ISeq).conj(a.rest()) }),
7ccabbff
JM
171 envPair("seq", { a: ISeq ->
172 val obj = a.nth(0)
173 if (obj is ISeq) {
174 if (obj.count() == 0) NIL
175 else MalList(obj.seq().toCollection(LinkedList<MalType>()))
176 } else if (obj is MalString && !(obj is MalKeyword)) {
177 if (obj.value.length == 0) NIL
178 else {
179 var strs = obj.value.map({ c -> MalString(c.toString()) })
180 MalList(strs.toCollection(LinkedList<MalType>()))
181 }
182 } else {
183 NIL
184 }
185 }),
99851ac7
JFI
186
187 envPair("atom", { a: ISeq -> MalAtom(a.first()) }),
188 envPair("atom?", { a: ISeq -> if (a.first() is MalAtom) TRUE else FALSE }),
189 envPair("deref", { a: ISeq -> (a.first() as MalAtom).value }),
190 envPair("reset!", { a: ISeq ->
7ee7a06b 191 val atom = a.nth(0) as MalAtom
a779c814
JFI
192 val value = a.nth(1)
193 atom.value = value
194 value
99851ac7
JFI
195 }),
196 envPair("swap!", { a: ISeq ->
6132da85 197 val atom = a.nth(0) as MalAtom
a779c814
JFI
198 val function = a.nth(1) as MalFunction
199
a779c814
JFI
200 val params = MalList()
201 params.conj_BANG(atom.value)
6132da85 202 a.seq().drop(2).forEach({ it -> params.conj_BANG(it) })
a779c814
JFI
203
204 val value = function.apply(params)
205 atom.value = value
206
207 value
99851ac7 208 }),
a779c814 209
99851ac7 210 envPair("readline", { a: ISeq ->
a779c814
JFI
211 val prompt = a.first() as MalString
212 try {
213 MalString(readline(prompt.value))
214 } catch (e: java.io.IOException) {
215 throw MalException(e.message)
216 } catch (e: EofException) {
217 NIL
218 }
99851ac7 219 }),
d937c5a1 220
99851ac7 221 envPair("time-ms", { a: ISeq -> MalInteger(System.currentTimeMillis()) })
53c2ea70
JFI
222)
223
99851ac7
JFI
224private fun envPair(k: String, v: (ISeq) -> MalType): Pair<MalSymbol, MalType> = Pair(MalSymbol(k), MalFunction(v))
225
226private fun pairwise(s: ISeq): List<Pair<MalType, MalType>> {
7ee7a06b
JFI
227 val (keys, vals) = s.seq().withIndex().partition({ it -> it.index % 2 == 0 })
228 return keys.map({ it -> it.value }).zip(vals.map({ it -> it.value }))
229}
230
99851ac7 231private fun pairwiseCompare(s: ISeq, pred: (MalInteger, MalInteger) -> Boolean): MalConstant =
7ee7a06b 232 if (pairwise(s).all({ it -> pred(it.first as MalInteger, it.second as MalInteger) })) TRUE else FALSE
53c2ea70 233
99851ac7 234private fun pairwiseEquals(s: ISeq): MalConstant =
7ee7a06b 235 if (pairwise(s).all({ it -> it.first == it.second })) TRUE else FALSE