Finished additional core functions for step 9
[jackhill/mal.git] / kotlin / src / mal / core.kt
CommitLineData
53c2ea70
JFI
1package mal
2
4121b4ee
JFI
3import java.io.File
4
53c2ea70
JFI
5val ns = hashMapOf(
6 Pair(MalSymbol("+"), MalFunction({ a: ISeq -> a.seq().reduce({ x, y -> x as MalInteger + y as MalInteger }) })),
7 Pair(MalSymbol("-"), MalFunction({ a: ISeq -> a.seq().reduce({ x, y -> x as MalInteger - y as MalInteger }) })),
8 Pair(MalSymbol("*"), MalFunction({ a: ISeq -> a.seq().reduce({ x, y -> x as MalInteger * y as MalInteger }) })),
9 Pair(MalSymbol("/"), MalFunction({ a: ISeq -> a.seq().reduce({ x, y -> x as MalInteger / y as MalInteger }) })),
10
11 Pair(MalSymbol("list"), MalFunction({ a: ISeq -> MalList(a) })),
12 Pair(MalSymbol("list?"), MalFunction({ a: ISeq -> if (a.first() is MalList) TRUE else FALSE })),
13 Pair(MalSymbol("empty?"), MalFunction({
14 a: ISeq -> if (a.first() !is ISeq || !(a.first() as ISeq).seq().any()) TRUE else FALSE
15 })),
16 Pair(MalSymbol("count"), MalFunction({
17 a: ISeq -> if (a.first() is ISeq) MalInteger((a.first() as ISeq).seq().count()) else MalInteger(0)
18 })),
19
20 Pair(MalSymbol("="), MalFunction({ a: ISeq -> pairwiseEquals(a) })),
21 Pair(MalSymbol("<"), MalFunction({ a: ISeq -> pairwise(a, { x, y -> x.value < y.value }) })),
22 Pair(MalSymbol("<="), MalFunction({ a: ISeq -> pairwise(a, { x, y -> x.value <= y.value }) })),
23 Pair(MalSymbol(">"), MalFunction({ a: ISeq -> pairwise(a, { x, y -> x.value > y.value }) })),
24 Pair(MalSymbol(">="), MalFunction({ a: ISeq -> pairwise(a, { x, y -> x.value >= y.value }) })),
25
26 Pair(MalSymbol("pr-str"), MalFunction({
27 a: ISeq -> MalString(a.seq().map({ it -> pr_str(it, print_readably = true) }).joinToString(" "))
28 })),
29 Pair(MalSymbol("str"), MalFunction({
30 a: ISeq -> MalString(a.seq().map({ it -> pr_str(it, print_readably = false) }).joinToString(""))
31 })),
32 Pair(MalSymbol("prn"), MalFunction({
33 a: ISeq -> println(a.seq().map({ it -> pr_str(it, print_readably = true) }).joinToString(" ")); NIL
34 })),
35 Pair(MalSymbol("println"), MalFunction({
36 a: ISeq -> println(a.seq().map({ it -> pr_str(it, print_readably = false) }).joinToString(" ")); NIL
4121b4ee
JFI
37 })),
38
39 Pair(MalSymbol("read-string"), MalFunction({ a: ISeq ->
40 val string = a.first() as? MalString ?: throw MalException("slurp requires a string parameter")
41 read_str(string.value)
42 })),
43 Pair(MalSymbol("slurp"), MalFunction({ a: ISeq ->
44 val name = a.first() as? MalString ?: throw MalException("slurp requires a filename parameter")
45 val text = File(name.value).readText()
46 MalString(text)
940bb6ff
JFI
47 })),
48
49 Pair(MalSymbol("cons"), MalFunction({ a: ISeq ->
50 val list = a.nth(1) as? ISeq ?: throw MalException("cons requires a list as its second parameter")
51 val mutableList = list.seq().toLinkedList()
52 mutableList.addFirst(a.nth(0))
53 MalList(mutableList)
54 })),
55 Pair(MalSymbol("concat"), MalFunction({ a: ISeq ->
56 MalList(a.seq().flatMap({ it -> (it as ISeq).seq() }).toLinkedList())
d8e5df5d
JFI
57 })),
58
59 Pair(MalSymbol("nth"), MalFunction({ a: ISeq ->
60 val list = a.nth(0) as? ISeq ?: throw MalException("nth requires a list as its first parameter")
61 val index = a.nth(1) as? MalInteger ?: throw MalException("nth requires an integer as its second parameter")
62 if (index.value >= list.seq().count()) throw MalException("index out of bounds")
63 list.nth(index.value)
64 })),
65 Pair(MalSymbol("first"), MalFunction({ a: ISeq ->
66 if (a.nth(0) == NIL) NIL
67 else {
68 val list = a.nth(0) as? ISeq ?: throw MalException("first requires a list parameter")
69 if (list.seq().any()) list.first() else NIL
70 }
71 })),
72 Pair(MalSymbol("rest"), MalFunction({ a: ISeq ->
73 val list = a.nth(0) as? ISeq ?: throw MalException("rest requires a list parameter")
74 MalList(list.rest())
041bb7eb
JFI
75 })),
76
77 Pair(MalSymbol("throw"), MalFunction({ a: ISeq ->
78 val throwable = a.nth(0)
79 throw MalCoreException(pr_str(throwable), throwable)
80 })),
81
82 Pair(MalSymbol("apply"), MalFunction({ a: ISeq ->
83 val function = a.nth(0) as MalFunction
84 val params = MalList()
85 a.seq().drop(1).forEach({ it ->
86 if (it is ISeq) {
87 it.seq().forEach({ x -> params.conj_BANG(x) })
88 } else {
89 params.conj_BANG(it)
90 }
91 })
92 function.apply(params)
93 })),
94
95 Pair(MalSymbol("map"), MalFunction({ a: ISeq ->
96 val function = a.nth(0) as MalFunction
97 MalList((a.nth(1) as ISeq).seq().map({ it ->
98 val params = MalList()
99 params.conj_BANG(it)
100 function.apply(params)
101 }).toLinkedList())
102 })),
103
104 Pair(MalSymbol("nil?"), MalFunction({ a: ISeq -> if (a.nth(0) == NIL) TRUE else FALSE })),
105 Pair(MalSymbol("true?"), MalFunction({ a: ISeq -> if (a.nth(0) == TRUE) TRUE else FALSE })),
106 Pair(MalSymbol("false?"), MalFunction({ a: ISeq -> if (a.nth(0) == FALSE) TRUE else FALSE })),
107 Pair(MalSymbol("symbol?"), MalFunction({ a: ISeq -> if (a.nth(0) is MalSymbol) TRUE else FALSE })),
108
109 Pair(MalSymbol("symbol"), MalFunction({ a: ISeq -> MalSymbol((a.nth(0) as MalString).value) })),
110 Pair(MalSymbol("keyword"), MalFunction({ a: ISeq ->
111 val param = a.nth(0)
112 if (param is MalKeyword) param else MalKeyword((a.nth(0) as MalString).value)
113 })),
114 Pair(MalSymbol("keyword?"), MalFunction({ a: ISeq -> if (a.nth(0) is MalKeyword) TRUE else FALSE })),
115 Pair(MalSymbol("vector"), MalFunction({ a: ISeq -> MalVector(a) })),
116 Pair(MalSymbol("vector?"), MalFunction({ a: ISeq -> if (a.nth(0) is MalVector) TRUE else FALSE })),
117
26f0b60f
JFI
118 Pair(MalSymbol("hash-map"), MalFunction({ a: ISeq ->
119 val map = MalHashMap()
120 val (keys, vals) = a.seq().withIndex().partition({ it -> it.index % 2 == 0 })
121 keys.map({ it -> it.value as MalString }).zip(vals.map({ it -> it.value })).forEach({ it ->
122 map.assoc_BANG(it.first, it.second)
123 })
124 map
125 })),
041bb7eb 126 Pair(MalSymbol("map?"), MalFunction({ a: ISeq -> if (a.nth(0) is MalHashMap) TRUE else FALSE })),
26f0b60f
JFI
127 Pair(MalSymbol("assoc"), MalFunction({ a: ISeq ->
128 val map = MalHashMap(a.nth(0) as MalHashMap)
129 val (keys, vals) = a.seq().drop(1).withIndex().partition({ it -> it.index % 2 == 0 })
130 keys.map({ it -> it.value as MalString }).zip(vals.map({ it -> it.value })).forEach({ it ->
131 map.assoc_BANG(it.first, it.second)
132 })
133 map
134 })),
135 Pair(MalSymbol("dissoc"), MalFunction({ a: ISeq ->
136 val map = MalHashMap(a.nth(0) as MalHashMap)
137 a.seq().drop(1).forEach({ it -> map.dissoc_BANG(it as MalString) })
138 map
139 })),
140 Pair(MalSymbol("get"), MalFunction({ a: ISeq ->
141 val map = a.nth(0) as? MalHashMap
142 val key = a.nth(1) as MalString
143 map?.elements?.get(key) ?: NIL
144 })),
145 Pair(MalSymbol("contains?"), MalFunction({ a: ISeq ->
146 val map = a.nth(0) as? MalHashMap
147 val key = a.nth(1) as MalString
148 if (map?.elements?.get(key) != null) TRUE else FALSE
149 })),
150 Pair(MalSymbol("keys"), MalFunction({ a: ISeq ->
151 val map = a.nth(0) as MalHashMap
152 // Another situation where kotlinc breaks if I don't add this unnecessary cast
153 MalList(map.elements.keys.map({ it -> it as MalType }).asSequence().toLinkedList())
154 })),
155 Pair(MalSymbol("vals"), MalFunction({ a: ISeq ->
156 val map = a.nth(0) as MalHashMap
157 MalList(map.elements.values.asSequence().toLinkedList())
158 })),
159 Pair(MalSymbol("count"), MalFunction({ a: ISeq ->
160 val seq = a.nth(0) as? ISeq
161 if (seq != null) MalInteger(seq.seq().count()) else ZERO
162 })),
041bb7eb 163 Pair(MalSymbol("sequential?"), MalFunction({ a: ISeq -> if (a.nth(0) is ISeq) TRUE else FALSE }))
53c2ea70
JFI
164)
165
166fun pairwiseEquals(s: ISeq): MalConstant =
167 if (s.seq().zip(s.seq().drop(1)).all({ it -> it.first == it.second })) TRUE else FALSE
168
169fun pairwise(s: ISeq, pred: (MalInteger, MalInteger) -> Boolean): MalConstant =
170 if (s.seq().zip(s.seq().drop(1)).all({
171 it -> pred(it.first as MalInteger, it.second as MalInteger)
172 })) TRUE else FALSE