diff --git a/docs/tutorial.md b/docs/tutorial.md index 2badb6b..0054e34 100644 --- a/docs/tutorial.md +++ b/docs/tutorial.md @@ -1484,6 +1484,9 @@ A typical set of String functions includes: | lower(), lowercase() | change case to unicode upper | | upper(), uppercase() | change case to unicode lower | | trim() | trim space chars from both ends | +| isEmpty() | true if string is empty | +| isNotEmpty() | true if string is not empty | +| isBlank() | true if empty or contains only whitespace | | startsWith(prefix) | true if starts with a prefix | | endsWith(prefix) | true if ends with a prefix | | last() | get last character of a string or throw | diff --git a/lynglib/src/commonMain/kotlin/net/sergeych/lyng/obj/ObjString.kt b/lynglib/src/commonMain/kotlin/net/sergeych/lyng/obj/ObjString.kt index 92528d8..25ae4d6 100644 --- a/lynglib/src/commonMain/kotlin/net/sergeych/lyng/obj/ObjString.kt +++ b/lynglib/src/commonMain/kotlin/net/sergeych/lyng/obj/ObjString.kt @@ -301,6 +301,18 @@ data class ObjString(val value: String) : Obj() { ) { thisAs().value.trim().let(::ObjString) } + addFnDoc("isBlank", "Whether this string is empty or contains only whitespace characters.", + returns = type("lyng.Bool"), moduleName = "lyng.stdlib") { + ObjBool(thisAs().value.isBlank()) + } + addFnDoc("isEmpty", "Whether this string is empty.", + returns = type("lyng.Bool"), moduleName = "lyng.stdlib") { + ObjBool(thisAs().value.isEmpty()) + } + addFnDoc("isNotEmpty", "Whether this string is not empty.", + returns = type("lyng.Bool"), moduleName = "lyng.stdlib") { + ObjBool(thisAs().value.isNotEmpty()) + } addFnDoc( name = "matches", doc = "Whether this string matches the given regular expression or pattern string.", diff --git a/lynglib/src/commonTest/kotlin/OOTest.kt b/lynglib/src/commonTest/kotlin/OOTest.kt index 3d3c85b..dbc48a1 100644 --- a/lynglib/src/commonTest/kotlin/OOTest.kt +++ b/lynglib/src/commonTest/kotlin/OOTest.kt @@ -779,7 +779,8 @@ class OOTest { @Test fun testOverrideVisibilityRules1() = runTest { - eval(""" + val scope = Script.newScope() + scope.eval(""" interface Base { abstract protected fun foo() @@ -802,12 +803,70 @@ class OOTest { class Derived2: Base { private var value = 42 + private fun fooPrivateImpl() = value + override protected fun foo() { - if( value < 10 ) 10 else value + fooPrivateImpl() + value++ } } + assertEquals("bar!", Derived().bar()) + val d = Derived2() + assertEquals(42, d.bar()) + assertEquals(43, d.bar()) + """.trimIndent()) + scope.createChildScope().eval(""" assertEquals("bar!", Derived().bar()) assertEquals(42, Derived2().bar()) """.trimIndent()) } + @Test + fun testOverrideVisibilityRules2() = runTest { + val scope = Script.newScope() + scope.eval(""" + interface Base { + abstract fun foo() + + fun bar() { + // it must see foo() as it is protected and + // is declared here (even as abstract): + foo() + } + } + class Derived : Base { + protected val suffix = "!" + + private fun fooPrivateImpl() = "bar" + + override fun foo() { + // it should access own private and all protected memberes here: + fooPrivateImpl() + suffix + } + } + class Derived2: Base { + private var value = 42 + + private fun fooPrivateImpl() = value + + override fun foo() { + fooPrivateImpl() + value++ + } + } + assertEquals("bar!", Derived().bar()) + val d = Derived2() + assertEquals(42, d.bar()) + assertEquals(43, d.bar()) + """.trimIndent()) + scope.createChildScope().eval(""" + assertEquals("bar!", Derived().bar()) + assertEquals(42, Derived2().bar()) + import lyng.serialization + for( i in 1..100 ) { + val d2 = Lynon.decode(Lynon.encode(Derived2())) + assertEquals(42, d2.bar()) + assertEquals(43, d2.bar()) + } + """.trimIndent()) + } } \ No newline at end of file