Implemented conj, (badly) implemented meta--needs to work for all objects and not...
[jackhill/mal.git] / kotlin / src / mal / types.kt
1 package mal
2
3 import java.util.*
4
5 // TODO clean up exception hierarchy
6 open class MalException(message: String?) : Exception(message), MalType { }
7 class MalContinue() : MalException("continue") { }
8 class MalReaderException(message: String) : MalException(message) { }
9 class MalPrinterException(message: String) : MalException(message) { }
10
11 class MalCoreException(message: String, val value: MalType) : MalException(message) // TODO rename
12
13 interface MalType {
14 }
15
16 open class MalConstant(val value: String) : MalType {
17 override fun equals(other: Any?): Boolean = other is MalConstant && value.equals(other.value)
18 override fun hashCode(): Int = value.hashCode()
19 }
20
21 class MalInteger(val value: Int) : MalType {
22 operator fun plus(a: MalInteger): MalInteger = MalInteger(value + a.value)
23 operator fun minus(a: MalInteger): MalInteger = MalInteger(value - a.value)
24 operator fun times(a: MalInteger): MalInteger = MalInteger(value * a.value)
25 operator fun div(a: MalInteger): MalInteger = MalInteger(value / a.value)
26 operator fun compareTo(a: MalInteger): Int = value.compareTo(a.value)
27
28 override fun equals(other: Any?): Boolean = other is MalInteger && value.equals(other.value)
29 }
30
31 class MalSymbol(val value: String) : MalType {
32 override fun equals(other: Any?): Boolean = other is MalSymbol && value.equals(other.value)
33 }
34
35 open class MalString(value: String) : MalConstant(value)
36
37 class MalKeyword(value: String) : MalString("\u029E" + value)
38
39 interface ILambda : MalType {
40 fun apply(seq: ISeq): MalType
41 }
42
43 open class MalFunction(val lambda: (ISeq) -> MalType) : MalType, ILambda {
44 // TODO make this stuff immutable?
45 var is_macro: Boolean = false
46 var metadata: MalType = NIL
47
48 override fun apply(seq: ISeq): MalType = lambda(seq)
49 }
50
51 class MalFnFunction(val ast: MalType, val params: Sequence<MalSymbol>, val env: Env, lambda: (ISeq) -> MalType) : MalFunction(lambda)
52
53 interface ISeq : MalType {
54 fun seq(): Sequence<MalType>
55 fun first(): MalType
56 fun rest(): ISeq
57 fun nth(n: Int): MalType
58 fun slice(fromIndex: Int, toIndex: Int): ISeq
59 fun conj(s: ISeq): ISeq
60 }
61
62 // TODO could we get rid of this and make conj work on immutables?
63 interface IMutableSeq : ISeq {
64 fun conj_BANG(form: MalType)
65 }
66
67 class MalSequence(val elements : Sequence<MalType>) : MalType, ISeq {
68 override fun seq(): Sequence<MalType> = elements
69 override fun first(): MalType = elements.first()
70 override fun rest(): ISeq = MalSequence(elements.drop(1))
71 override fun nth(n: Int): MalType = elements.elementAt(n)
72
73 override fun slice(fromIndex: Int, toIndex: Int): MalList =
74 MalList(elements.toLinkedList().subList(fromIndex, toIndex))
75
76 override fun conj(s: ISeq): ISeq = MalList(elements.toLinkedList()).conj(s)
77 }
78
79 class MalList(val elements: MutableList<MalType>) : MalType, IMutableSeq {
80 constructor() : this(LinkedList<MalType>())
81 constructor(s: ISeq) : this(s.seq().toLinkedList())
82
83 override fun seq(): Sequence<MalType> = elements.asSequence()
84 override fun first(): MalType = elements.first()
85 override fun rest(): ISeq = MalSequence(elements.drop(1).asSequence())
86 override fun nth(n: Int): MalType = elements.elementAt(n)
87
88 override fun conj_BANG(form: MalType) {
89 elements.add(form)
90 }
91
92 override fun equals(other: Any?): Boolean =
93 (other is ISeq)
94 && elements.size == other.seq().count() // TODO optimize counting?
95 && elements.asSequence().zip(other.seq()).all({ it -> it.first == it.second })
96
97 override fun slice(fromIndex: Int, toIndex: Int): MalList =
98 MalList(elements.subList(fromIndex, toIndex))
99
100 override fun conj(s: ISeq): ISeq {
101 val list = LinkedList<MalType>(elements)
102 s.seq().forEach({ it -> list.addFirst(it) })
103 return MalList(list)
104 }
105 }
106
107 class MalVector(val elements: MutableList<MalType>) : MalType, IMutableSeq {
108 constructor() : this(ArrayList<MalType>())
109 constructor(s: ISeq) : this(s.seq().toArrayList())
110
111 override fun seq(): Sequence<MalType> = elements.asSequence()
112 override fun first(): MalType = elements.first()
113 override fun rest(): ISeq = MalSequence(elements.drop(1).asSequence())
114 override fun nth(n: Int): MalType = elements.elementAt(n)
115
116 override fun conj_BANG(form: MalType) {
117 elements.add(form)
118 }
119
120 override fun equals(other: Any?): Boolean =
121 (other is ISeq)
122 && elements.size == other.seq().count() // TODO optimize counting?
123 && elements.asSequence().zip(other.seq()).all({ it -> it.first == it.second })
124
125 override fun slice(fromIndex: Int, toIndex: Int): MalVector =
126 MalVector(elements.subList(fromIndex, toIndex))
127
128 override fun conj(s: ISeq): ISeq = MalVector(elements.plus(s.seq()).toArrayList())
129 }
130
131 class MalHashMap() : MalType {
132 val elements = HashMap<MalString, MalType>()
133
134 constructor(other: MalHashMap) : this() {
135 other.elements.forEach({ it -> assoc_BANG(it.key, it.value) })
136 }
137
138 fun assoc_BANG(key: MalString, value: MalType) = elements.put(key, value)
139 fun dissoc_BANG(key: MalString) {
140 elements.remove(key)
141 }
142 }
143
144 // TODO add truthiness checking
145 val NIL = MalConstant("nil")
146 val TRUE = MalConstant("true")
147 val FALSE = MalConstant("false")
148 val ZERO = MalInteger(0)