diff --git a/docs/OOP.md b/docs/OOP.md index 2be4d38..febc801 100644 --- a/docs/OOP.md +++ b/docs/OOP.md @@ -141,7 +141,24 @@ set at construction but not available outside the class: void >>> void +## Default class methods +In many cases it is necessary to implement custom comparison and `toString`, still +each class is provided with default implementations: + +- default toString outputs class name and its _public_ fields. +- default comparison compares compares all public fields in order of appearance. + +For example, for our class Point: + + class Point(x,y) + assert( Point(1,2) == Point(1,2) ) + assert( Point(1,2) !== Point(1,2) ) + assert( Point(1,2) != Point(1,3) ) + assert( Point(1,2) < Point(2,2) ) + assert( Point(1,2) < Point(1,3) ) + Point(1,1+1) + >>> Point(1, 2) # Theory diff --git a/docs/declaring_arguments.md b/docs/declaring_arguments.md index b12ecfe..f769be4 100644 --- a/docs/declaring_arguments.md +++ b/docs/declaring_arguments.md @@ -1,7 +1,7 @@ # Declaring arguments in Lyng It is a common thing that occurs in many places in Lyng, function declarations, -lambdas, struct and class declarations. +lambdas and class declarations. ## Regular diff --git a/library/src/commonMain/kotlin/net/sergeych/lyng/ObjInstance.kt b/library/src/commonMain/kotlin/net/sergeych/lyng/ObjInstance.kt index 6b59711..935f0ce 100644 --- a/library/src/commonMain/kotlin/net/sergeych/lyng/ObjInstance.kt +++ b/library/src/commonMain/kotlin/net/sergeych/lyng/ObjInstance.kt @@ -26,4 +26,23 @@ class ObjInstance(override val objClass: ObjClass) : Obj() { if( name in publicFields ) return instanceContext[name]!!.value.invoke(context, this, args) return super.invokeInstanceMethod(context, name, args) } + + override fun toString(): String { + val fields = publicFields.map { + instanceContext[it]?.value?.toString() ?: "??" + }.joinToString(", ") + return "${objClass.className}($fields)" + } + + override suspend fun compareTo(context: Context, other: Obj): Int { + if( other !is ObjInstance ) return -1 + if( other.objClass != objClass ) return -1 + for( f in publicFields ) { + val a = instanceContext[f]!!.value + val b = other.instanceContext[f]!!.value + val d = a.compareTo(context, b) + if (d != 0) return d + } + return 0 + } } \ No newline at end of file diff --git a/library/src/commonTest/kotlin/ScriptTest.kt b/library/src/commonTest/kotlin/ScriptTest.kt index 1053d80..9d33a4e 100644 --- a/library/src/commonTest/kotlin/ScriptTest.kt +++ b/library/src/commonTest/kotlin/ScriptTest.kt @@ -1386,7 +1386,7 @@ class ScriptTest { fun testSimpleStruct() = runTest { val c = Context() c.eval(""" - struct Point(x,y) + class Point(x,y) assert( Point::class is Class ) val p = Point(2,3) assert(p is Point) @@ -1405,7 +1405,7 @@ class ScriptTest { fun testNonAssignalbeFieldInStruct() = runTest { val c = Context() c.eval(""" - struct Point(x,y) + class Point(x,y) val p = Point("2",3) assert(p is Point) assert( p.x == "2" ) @@ -1420,7 +1420,7 @@ class ScriptTest { fun testStructBodyVal() = runTest { val c = Context() c.eval(""" - struct Point(x,y) { + class Point(x,y) { val length = sqrt(x*x+y*y) var foo = "zero" } @@ -1440,7 +1440,7 @@ class ScriptTest { fun testStructBodyFun() = runTest { val c = Context() c.eval(""" - struct Point(x,y) { + class Point(x,y) { fun length() { sqrt(x*x+y*y) } @@ -1487,4 +1487,30 @@ class ScriptTest { assertEquals( 1, cond { 1 } ) """.trimIndent()) } + + @Test + fun testClasstoString() = runTest { + eval(""" + class Point { + var x + var y + } + val p = Point() + p.x = 1 + p.y = 2 + println(p) + """.trimIndent()) + } + + @Test + fun testClassDefaultCompare() = runTest { + eval(""" + class Point(x,y) + assert( Point(1,2) == Point(1,2) ) + assert( Point(1,2) !== Point(1,2) ) + assert( Point(1,2) != Point(1,3) ) + assert( Point(1,2) < Point(2,2) ) + assert( Point(1,2) < Point(1,3) ) + """.trimIndent()) + } } \ No newline at end of file