@@ -10,25 +10,134 @@ import org.kson.parser.NumberParser
1010 */
1111sealed class KsonApi (val location : Location )
1212
13- abstract class KsonValue (location : Location ) : KsonApi(location)
13+ abstract class KsonValue (location : Location ) : KsonApi(location) {
14+ /* *
15+ * Ensure all our [KsonValue] classes implement their [equals] and [hashCode]
16+ */
17+ abstract override fun equals (other : Any? ): Boolean
18+ abstract override fun hashCode (): Int
19+ }
1420
15- class KsonObject (private val propertyList : List <KsonObjectProperty >, location : Location ) : KsonValue(location) {
21+ class KsonObject (val propertyList : List <KsonObjectProperty >, location : Location ) : KsonValue(location) {
1622 val propertyMap: Map <String , KsonValue > by lazy {
1723 propertyList.associate { it.name.value to it.ksonValue }
1824 }
25+
26+ override fun equals (other : Any? ): Boolean {
27+ if (this == = other) return true
28+ if (other !is KsonObject ) return false
29+
30+ if (propertyMap.size != other.propertyMap.size) return false
31+
32+ return propertyMap.all { (key, value) ->
33+ other.propertyMap[key]?.let { value == it } ? : false
34+ }
35+ }
36+
37+ override fun hashCode (): Int {
38+ return propertyMap.hashCode()
39+ }
40+ }
41+
42+ class KsonList (val elements : List <KsonListElement >, location : Location ) : KsonValue(location) {
43+ override fun equals (other : Any? ): Boolean {
44+ if (this == = other) return true
45+ if (other !is KsonList ) return false
46+
47+ if (elements.size != other.elements.size) return false
48+
49+ return elements.zip(other.elements).all { (a, b) ->
50+ a.ksonValue == b.ksonValue
51+ }
52+ }
53+
54+ override fun hashCode (): Int {
55+ return elements.map { it.ksonValue.hashCode() }.hashCode()
56+ }
1957}
20- class KsonList ( val elements : List < KsonListElement >, location : Location ) : KsonValue(location)
58+
2159class KsonListElement (val ksonValue : KsonValue , location : Location ) : KsonApi(location)
2260class KsonObjectProperty (val name : KsonString ,
2361 val ksonValue : KsonValue ,
2462 location : Location ) :KsonApi(location)
2563class EmbedBlock (val embedTag : String ,
2664 val embedContent : String ,
27- location : Location ) : KsonValue(location)
28- class KsonString (val value : String , location : Location ) : KsonValue(location)
29- class KsonNumber (val value : NumberParser .ParsedNumber , location : Location ) : KsonValue(location)
30- class KsonBoolean (val value : Boolean , location : Location ) : KsonValue(location)
31- class KsonNull (location : Location ) : KsonValue(location)
65+ location : Location ) : KsonValue(location) {
66+ override fun equals (other : Any? ): Boolean {
67+ if (this == = other) return true
68+ if (other !is EmbedBlock ) return false
69+
70+ return embedTag == other.embedTag && embedContent == other.embedContent
71+ }
72+
73+ override fun hashCode (): Int {
74+ return 31 * embedTag.hashCode() + embedContent.hashCode()
75+ }
76+ }
77+
78+ class KsonString (val value : String , location : Location ) : KsonValue(location) {
79+ override fun equals (other : Any? ): Boolean {
80+ if (this == = other) return true
81+ if (other !is KsonString ) return false
82+
83+ return value == other.value
84+ }
85+
86+ override fun hashCode (): Int {
87+ return value.hashCode()
88+ }
89+ }
90+
91+ class KsonNumber (val value : NumberParser .ParsedNumber , location : Location ) : KsonValue(location) {
92+ override fun equals (other : Any? ): Boolean {
93+ if (this == = other) return true
94+ if (other !is KsonNumber ) return false
95+
96+ // Numbers are equal if their numeric values are equal (supporting cross-type comparison)
97+ val thisValue = when (value) {
98+ is NumberParser .ParsedNumber .Integer -> value.value.toDouble()
99+ is NumberParser .ParsedNumber .Decimal -> value.value
100+ }
101+ val otherValue = when (other.value) {
102+ is NumberParser .ParsedNumber .Integer -> other.value.value.toDouble()
103+ is NumberParser .ParsedNumber .Decimal -> other.value.value
104+ }
105+
106+ return thisValue == otherValue
107+ }
108+
109+ override fun hashCode (): Int {
110+ // Use the double value for consistent hashing across integer/decimal representations
111+ val doubleValue = when (value) {
112+ is NumberParser .ParsedNumber .Integer -> value.value.toDouble()
113+ is NumberParser .ParsedNumber .Decimal -> value.value
114+ }
115+ return doubleValue.hashCode()
116+ }
117+ }
118+
119+ class KsonBoolean (val value : Boolean , location : Location ) : KsonValue(location) {
120+ override fun equals (other : Any? ): Boolean {
121+ if (this == = other) return true
122+ if (other !is KsonBoolean ) return false
123+
124+ return value == other.value
125+ }
126+
127+ override fun hashCode (): Int {
128+ return value.hashCode()
129+ }
130+ }
131+
132+ class KsonNull (location : Location ) : KsonValue(location) {
133+ override fun equals (other : Any? ): Boolean {
134+ return other is KsonNull
135+ }
136+
137+ override fun hashCode (): Int {
138+ return KsonNull ::class .hashCode()
139+ }
140+ }
32141
33142fun AstNode.toKsonApi (): KsonApi {
34143 if (this !is AstNodeImpl ) {
0 commit comments