From ead2f7168ef15273363ae1562c1ed997fb555d2d Mon Sep 17 00:00:00 2001 From: sergeych Date: Mon, 25 Aug 2025 12:32:50 +0300 Subject: [PATCH] ref #49 basic regex support (but not in when yet) --- ...py_numbers.lyng => happy_numbers.lyng.bad} | 0 docs/tutorial.md | 25 ++ .../kotlin/net/sergeych/lyng/Compiler.kt | 19 +- .../kotlin/net/sergeych/lyng/Parser.kt | 6 +- .../kotlin/net/sergeych/lyng/Scope.kt | 5 - .../kotlin/net/sergeych/lyng/Script.kt | 2 + .../kotlin/net/sergeych/lyng/obj/Obj.kt | 195 ---------------- .../net/sergeych/lyng/obj/ObjException.kt | 221 ++++++++++++++++++ .../kotlin/net/sergeych/lyng/obj/ObjRegex.kt | 90 +++++++ .../lyng/stdlib_included/root_lyng.kt | 2 + lynglib/src/commonTest/kotlin/ScriptTest.kt | 40 +++- 11 files changed, 397 insertions(+), 208 deletions(-) rename docs/samples/{happy_numbers.lyng => happy_numbers.lyng.bad} (100%) create mode 100644 lynglib/src/commonMain/kotlin/net/sergeych/lyng/obj/ObjException.kt create mode 100644 lynglib/src/commonMain/kotlin/net/sergeych/lyng/obj/ObjRegex.kt diff --git a/docs/samples/happy_numbers.lyng b/docs/samples/happy_numbers.lyng.bad similarity index 100% rename from docs/samples/happy_numbers.lyng rename to docs/samples/happy_numbers.lyng.bad diff --git a/docs/tutorial.md b/docs/tutorial.md index 8d55d53..32261f1 100644 --- a/docs/tutorial.md +++ b/docs/tutorial.md @@ -1184,6 +1184,30 @@ See also [math operations](math.md) The type for the character objects is `Char`. +### String literal escapes + +| escape | ASCII value | +|--------|-----------------------| +| \n | 0x10, newline | +| \r | 0x13, carriage return | +| \t | 0x07, tabulation | +| \\ | \ slash character | +| \" | " double quote | + +Other `\c` combinations, where c is any char except mentioned above, are left intact, e.g.: + + val s = "\a" + assert(s[0] == '\') + assert(s[1] == 'a') + >>> void + +same as: + + val s = "\\a" + assert(s[0] == '\') + assert(s[1] == 'a') + >>> void + ### Char literal escapes Are the same as in string literals with little difference: @@ -1191,6 +1215,7 @@ Are the same as in string literals with little difference: | escape | ASCII value | |--------|-------------------| | \n | 0x10, newline | +| \r | 0x13, carriage return | | \t | 0x07, tabulation | | \\ | \ slash character | | \' | ' apostrophe | diff --git a/lynglib/src/commonMain/kotlin/net/sergeych/lyng/Compiler.kt b/lynglib/src/commonMain/kotlin/net/sergeych/lyng/Compiler.kt index d5db1fd..d0eabcb 100644 --- a/lynglib/src/commonMain/kotlin/net/sergeych/lyng/Compiler.kt +++ b/lynglib/src/commonMain/kotlin/net/sergeych/lyng/Compiler.kt @@ -1376,9 +1376,16 @@ class Compiler( if (sourceObj is ObjRange && sourceObj.isIntRange) { loopIntRange( forContext, - sourceObj.start!!.toInt(), - if (sourceObj.isEndInclusive) sourceObj.end!!.toInt() + 1 else sourceObj.end!!.toInt(), - loopSO, body, elseStatement, label, canBreak + sourceObj.start!!.toLong(), + if (sourceObj.isEndInclusive) + sourceObj.end!!.toLong() + 1 + else + sourceObj.end!!.toLong(), + loopSO, + body, + elseStatement, + label, + canBreak ) } else if (sourceObj.isInstanceOf(ObjIterable)) { loopIterable(forContext, sourceObj, loopSO, body, elseStatement, label, canBreak) @@ -1439,7 +1446,7 @@ class Compiler( } private suspend fun loopIntRange( - forScope: Scope, start: Int, end: Int, loopVar: ObjRecord, + forScope: Scope, start: Long, end: Long, loopVar: ObjRecord, body: Statement, elseStatement: Statement?, label: String?, catchBreak: Boolean ): Obj { var result: Obj = ObjVoid @@ -1447,7 +1454,7 @@ class Compiler( loopVar.value = iVar if (catchBreak) { for (i in start.. {sb.append('\r'); pos.advance()} 't' -> {sb.append('\t'); pos.advance()} '"' -> {sb.append('"'); pos.advance()} - else -> sb.append('\\').append(currentChar) + '\\' -> {sb.append('\\'); pos.advance()} + else -> { + sb.append('\\').append(currentChar) + pos.advance() + } } } diff --git a/lynglib/src/commonMain/kotlin/net/sergeych/lyng/Scope.kt b/lynglib/src/commonMain/kotlin/net/sergeych/lyng/Scope.kt index 3a9031c..9d53fd6 100644 --- a/lynglib/src/commonMain/kotlin/net/sergeych/lyng/Scope.kt +++ b/lynglib/src/commonMain/kotlin/net/sergeych/lyng/Scope.kt @@ -136,11 +136,6 @@ open class Scope( ?: thisObj.objClass .getInstanceMemberOrNull(name) ) -// ?.also { -// if( name == "predicate") { -// println("got predicate $it") -// } -// } } fun copy(pos: Pos, args: Arguments = Arguments.EMPTY, newThisObj: Obj? = null): Scope = diff --git a/lynglib/src/commonMain/kotlin/net/sergeych/lyng/Script.kt b/lynglib/src/commonMain/kotlin/net/sergeych/lyng/Script.kt index fb41b38..da204bd 100644 --- a/lynglib/src/commonMain/kotlin/net/sergeych/lyng/Script.kt +++ b/lynglib/src/commonMain/kotlin/net/sergeych/lyng/Script.kt @@ -241,6 +241,8 @@ class Script( addConst("CompletableDeferred", ObjCompletableDeferred.type) addConst("Mutex", ObjMutex.type) + addConst("Regex", ObjRegex.type) + addFn("launch") { val callable = requireOnlyArg() ObjDeferred(globalDefer { diff --git a/lynglib/src/commonMain/kotlin/net/sergeych/lyng/obj/Obj.kt b/lynglib/src/commonMain/kotlin/net/sergeych/lyng/obj/Obj.kt index 6e58c1b..8c075fc 100644 --- a/lynglib/src/commonMain/kotlin/net/sergeych/lyng/obj/Obj.kt +++ b/lynglib/src/commonMain/kotlin/net/sergeych/lyng/obj/Obj.kt @@ -21,15 +21,11 @@ import kotlinx.coroutines.sync.Mutex import kotlinx.coroutines.sync.withLock import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable -import net.sergeych.bintools.encodeToHex import net.sergeych.lyng.* import net.sergeych.lynon.LynonDecoder import net.sergeych.lynon.LynonEncoder import net.sergeych.lynon.LynonType -import net.sergeych.mptools.CachedExpression import net.sergeych.synctools.ProtectedOp -import net.sergeych.synctools.withLock -import kotlin.contracts.ExperimentalContracts open class Obj { @@ -500,195 +496,4 @@ data class ObjNamespace(val name: String) : Obj() { } } -/** - * note on [getStackTrace]. If [useStackTrace] is not null, it is used instead. Otherwise, it is calculated - * from the current scope which is treated as exception scope. It is used to restore serialized - * exception with stack trace; the scope of the de-serialized exception is not valid - * for stack unwinding. - */ -open class ObjException( - val exceptionClass: ExceptionClass, - val scope: Scope, - val message: ObjString, - @Suppress("unused") val extraData: Obj = ObjNull, - val useStackTrace: ObjList? = null -) : Obj() { - constructor(name: String, scope: Scope, message: String) : this( - getOrCreateExceptionClass(name), - scope, - ObjString(message) - ) - private val cachedStackTrace = CachedExpression(initialValue = useStackTrace) - - suspend fun getStackTrace(): ObjList { - return cachedStackTrace.get { - val result = ObjList() - val cls = scope.get("StackTraceEntry")!!.value as ObjClass - var s: Scope? = scope - var lastPos: Pos? = null - while (s != null) { - val pos = s.pos - if (pos != lastPos && !pos.currentLine.isEmpty()) { - result.list += cls.callWithArgs( - scope, - pos.source.objSourceName, - ObjInt(pos.line.toLong()), - ObjInt(pos.column.toLong()), - ObjString(pos.currentLine) - ) - } - s = s.parent - lastPos = pos - } - result - } - } - - constructor(scope: Scope, message: String) : this(Root, scope, ObjString(message)) - - fun raise(): Nothing { - throw ExecutionError(this) - } - - override val objClass: ObjClass = exceptionClass - - override suspend fun toString(scope: Scope,calledFromLyng: Boolean): ObjString { - val at = getStackTrace().list.firstOrNull()?.toString(scope) - ?: ObjString("(unknown)") - return ObjString("${objClass.className}: $message at $at") - } - - override suspend fun serialize(scope: Scope, encoder: LynonEncoder, lynonType: LynonType?) { - encoder.encodeAny(scope, exceptionClass.classNameObj) - encoder.encodeAny(scope, message) - encoder.encodeAny(scope, extraData) - encoder.encodeAny(scope, getStackTrace()) - } - - - companion object { - - class ExceptionClass(val name: String, vararg parents: ObjClass) : ObjClass(name, *parents) { - override suspend fun callOn(scope: Scope): Obj { - val message = scope.args.getOrNull(0)?.toString(scope) ?: ObjString(name) - return ObjException(this, scope, message) - } - - override fun toString(): String = "ExceptionClass[$name]@${hashCode().encodeToHex()}" - - override suspend fun deserialize(scope: Scope, decoder: LynonDecoder, lynonType: LynonType?): Obj { - return try { - val lyngClass = decoder.decodeAnyAs(scope).value.let { - ((scope[it] ?: scope.raiseIllegalArgument("Unknown exception class: $it")) - .value as? ExceptionClass) - ?: scope.raiseIllegalArgument("Not an exception class: $it") - } - ObjException( - lyngClass, - scope, - decoder.decodeAnyAs(scope), - decoder.decodeAny(scope), - decoder.decodeAnyAs(scope) - ) - } catch (e: ScriptError) { - throw e - } catch (e: Exception) { - e.printStackTrace() - scope.raiseIllegalArgument("Failed to deserialize exception: ${e.message}") - } - } - } - - val Root = ExceptionClass("Throwable").apply { - addConst("message", statement { - (thisObj as ObjException).message.toObj() - }) - addFn("stackTrace") { - (thisObj as ObjException).getStackTrace() - } - } - - private val op = ProtectedOp() - private val existingErrorClasses = mutableMapOf() - - - @OptIn(ExperimentalContracts::class) - protected fun getOrCreateExceptionClass(name: String): ExceptionClass { - return op.withLock { - existingErrorClasses.getOrPut(name) { - ExceptionClass(name, Root) - } - } - } - - /** - * Get [ObjClass] for error class by name if exists. - */ - @OptIn(ExperimentalContracts::class) - fun getErrorClass(name: String): ObjClass? = op.withLock { - existingErrorClasses[name] - } - - fun addExceptionsToContext(scope: Scope) { - scope.addConst("Exception", Root) - existingErrorClasses["Exception"] = Root - for (name in listOf( - "NullReferenceException", - "AssertionFailedException", - "ClassCastException", - "IndexOutOfBoundsException", - "IllegalArgumentException", - "NoSuchElementException", - "IllegalAssignmentException", - "SymbolNotDefinedException", - "IterationEndException", - "AccessException", - "UnknownException", - "NotFoundException" - )) { - scope.addConst(name, getOrCreateExceptionClass(name)) - } - } - } -} - -class ObjNullReferenceException(scope: Scope) : ObjException("NullReferenceException", scope, "object is null") - -class ObjAssertionFailedException(scope: Scope, message: String) : - ObjException("AssertionFailedException", scope, message) - -class ObjClassCastException(scope: Scope, message: String) : ObjException("ClassCastException", scope, message) -class ObjIndexOutOfBoundsException(scope: Scope, message: String = "index out of bounds") : - ObjException("IndexOutOfBoundsException", scope, message) - -class ObjIllegalArgumentException(scope: Scope, message: String = "illegal argument") : - ObjException("IllegalArgumentException", scope, message) - -class ObjIllegalStateException(scope: Scope, message: String = "illegal state") : - ObjException("IllegalStateException", scope, message) - -@Suppress("unused") -class ObjNoSuchElementException(scope: Scope, message: String = "no such element") : - ObjException("IllegalArgumentException", scope, message) - -class ObjIllegalAssignmentException(scope: Scope, message: String = "illegal assignment") : - ObjException("NoSuchElementException", scope, message) - -class ObjSymbolNotDefinedException(scope: Scope, message: String = "symbol is not defined") : - ObjException("SymbolNotDefinedException", scope, message) - -class ObjIterationFinishedException(scope: Scope) : - ObjException("IterationEndException", scope, "iteration finished") - -class ObjAccessException(scope: Scope, message: String = "access not allowed error") : - ObjException("AccessException", scope, message) - -class ObjUnknownException(scope: Scope, message: String = "access not allowed error") : - ObjException("UnknownException", scope, message) - -class ObjIllegalOperationException(scope: Scope, message: String = "Operation is illegal") : - ObjException("IllegalOperationException", scope, message) - -class ObjNotFoundException(scope: Scope, message: String = "not found") : - ObjException("NotFoundException", scope, message) diff --git a/lynglib/src/commonMain/kotlin/net/sergeych/lyng/obj/ObjException.kt b/lynglib/src/commonMain/kotlin/net/sergeych/lyng/obj/ObjException.kt new file mode 100644 index 0000000..f5efcb5 --- /dev/null +++ b/lynglib/src/commonMain/kotlin/net/sergeych/lyng/obj/ObjException.kt @@ -0,0 +1,221 @@ +/* + * Copyright 2025 Sergey S. Chernov real.sergeych@gmail.com + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package net.sergeych.lyng.obj + +import net.sergeych.bintools.encodeToHex +import net.sergeych.lyng.* +import net.sergeych.lynon.LynonDecoder +import net.sergeych.lynon.LynonEncoder +import net.sergeych.lynon.LynonType +import net.sergeych.mptools.CachedExpression +import net.sergeych.synctools.ProtectedOp +import net.sergeych.synctools.withLock +import kotlin.contracts.ExperimentalContracts + +/** + * note on [getStackTrace]. If [useStackTrace] is not null, it is used instead. Otherwise, it is calculated + * from the current scope which is treated as exception scope. It is used to restore serialized + * exception with stack trace; the scope of the de-serialized exception is not valid + * for stack unwinding. + */ +open class ObjException( + val exceptionClass: ExceptionClass, + val scope: Scope, + val message: ObjString, + @Suppress("unused") val extraData: Obj = ObjNull, + val useStackTrace: ObjList? = null +) : Obj() { + constructor(name: String, scope: Scope, message: String) : this( + getOrCreateExceptionClass(name), + scope, + ObjString(message) + ) + + private val cachedStackTrace = CachedExpression(initialValue = useStackTrace) + + suspend fun getStackTrace(): ObjList { + return cachedStackTrace.get { + val result = ObjList() + val cls = scope.get("StackTraceEntry")!!.value as ObjClass + var s: Scope? = scope + var lastPos: Pos? = null + while (s != null) { + val pos = s.pos + if (pos != lastPos && !pos.currentLine.isEmpty()) { + result.list += cls.callWithArgs( + scope, + pos.source.objSourceName, + ObjInt(pos.line.toLong()), + ObjInt(pos.column.toLong()), + ObjString(pos.currentLine) + ) + } + s = s.parent + lastPos = pos + } + result + } + } + + constructor(scope: Scope, message: String) : this(Root, scope, ObjString(message)) + + fun raise(): Nothing { + throw ExecutionError(this) + } + + override val objClass: ObjClass = exceptionClass + + override suspend fun toString(scope: Scope, calledFromLyng: Boolean): ObjString { + val at = getStackTrace().list.firstOrNull()?.toString(scope) + ?: ObjString("(unknown)") + return ObjString("${objClass.className}: $message at $at") + } + + override suspend fun serialize(scope: Scope, encoder: LynonEncoder, lynonType: LynonType?) { + encoder.encodeAny(scope, exceptionClass.classNameObj) + encoder.encodeAny(scope, message) + encoder.encodeAny(scope, extraData) + encoder.encodeAny(scope, getStackTrace()) + } + + + companion object { + + class ExceptionClass(val name: String, vararg parents: ObjClass) : ObjClass(name, *parents) { + override suspend fun callOn(scope: Scope): Obj { + val message = scope.args.getOrNull(0)?.toString(scope) ?: ObjString(name) + return ObjException(this, scope, message) + } + + override fun toString(): String = "ExceptionClass[$name]@${hashCode().encodeToHex()}" + + override suspend fun deserialize(scope: Scope, decoder: LynonDecoder, lynonType: LynonType?): Obj { + return try { + val lyngClass = decoder.decodeAnyAs(scope).value.let { + ((scope[it] ?: scope.raiseIllegalArgument("Unknown exception class: $it")) + .value as? ExceptionClass) + ?: scope.raiseIllegalArgument("Not an exception class: $it") + } + ObjException( + lyngClass, + scope, + decoder.decodeAnyAs(scope), + decoder.decodeAny(scope), + decoder.decodeAnyAs(scope) + ) + } catch (e: ScriptError) { + throw e + } catch (e: Exception) { + e.printStackTrace() + scope.raiseIllegalArgument("Failed to deserialize exception: ${e.message}") + } + } + } + + val Root = ExceptionClass("Throwable").apply { + addConst("message", statement { + (thisObj as ObjException).message.toObj() + }) + addFn("stackTrace") { + (thisObj as ObjException).getStackTrace() + } + } + + private val op = ProtectedOp() + private val existingErrorClasses = mutableMapOf() + + + @OptIn(ExperimentalContracts::class) + protected fun getOrCreateExceptionClass(name: String): ExceptionClass { + return op.withLock { + existingErrorClasses.getOrPut(name) { + ExceptionClass(name, Root) + } + } + } + + /** + * Get [ObjClass] for error class by name if exists. + */ + @OptIn(ExperimentalContracts::class) + fun getErrorClass(name: String): ObjClass? = op.withLock { + existingErrorClasses[name] + } + + fun addExceptionsToContext(scope: Scope) { + scope.addConst("Exception", Root) + existingErrorClasses["Exception"] = Root + for (name in listOf( + "NullReferenceException", + "AssertionFailedException", + "ClassCastException", + "IndexOutOfBoundsException", + "IllegalArgumentException", + "NoSuchElementException", + "IllegalAssignmentException", + "SymbolNotDefinedException", + "IterationEndException", + "AccessException", + "UnknownException", + "NotFoundException" + )) { + scope.addConst(name, getOrCreateExceptionClass(name)) + } + } + } +} + +class ObjNullReferenceException(scope: Scope) : ObjException("NullReferenceException", scope, "object is null") + +class ObjAssertionFailedException(scope: Scope, message: String) : + ObjException("AssertionFailedException", scope, message) + +class ObjClassCastException(scope: Scope, message: String) : ObjException("ClassCastException", scope, message) +class ObjIndexOutOfBoundsException(scope: Scope, message: String = "index out of bounds") : + ObjException("IndexOutOfBoundsException", scope, message) + +class ObjIllegalArgumentException(scope: Scope, message: String = "illegal argument") : + ObjException("IllegalArgumentException", scope, message) + +class ObjIllegalStateException(scope: Scope, message: String = "illegal state") : + ObjException("IllegalStateException", scope, message) + +@Suppress("unused") +class ObjNoSuchElementException(scope: Scope, message: String = "no such element") : + ObjException("IllegalArgumentException", scope, message) + +class ObjIllegalAssignmentException(scope: Scope, message: String = "illegal assignment") : + ObjException("NoSuchElementException", scope, message) + +class ObjSymbolNotDefinedException(scope: Scope, message: String = "symbol is not defined") : + ObjException("SymbolNotDefinedException", scope, message) + +class ObjIterationFinishedException(scope: Scope) : + ObjException("IterationEndException", scope, "iteration finished") + +class ObjAccessException(scope: Scope, message: String = "access not allowed error") : + ObjException("AccessException", scope, message) + +class ObjUnknownException(scope: Scope, message: String = "access not allowed error") : + ObjException("UnknownException", scope, message) + +class ObjIllegalOperationException(scope: Scope, message: String = "Operation is illegal") : + ObjException("IllegalOperationException", scope, message) + +class ObjNotFoundException(scope: Scope, message: String = "not found") : + ObjException("NotFoundException", scope, message) diff --git a/lynglib/src/commonMain/kotlin/net/sergeych/lyng/obj/ObjRegex.kt b/lynglib/src/commonMain/kotlin/net/sergeych/lyng/obj/ObjRegex.kt new file mode 100644 index 0000000..8fbc43b --- /dev/null +++ b/lynglib/src/commonMain/kotlin/net/sergeych/lyng/obj/ObjRegex.kt @@ -0,0 +1,90 @@ +/* + * Copyright 2025 Sergey S. Chernov real.sergeych@gmail.com + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package net.sergeych.lyng.obj + +import net.sergeych.lyng.Scope + +class ObjRegex(val regex: Regex) : Obj() { + override val objClass = type + + companion object { + val type by lazy { + object : ObjClass("Regex") { + override suspend fun callOn(scope: Scope): Obj { + println(scope.requireOnlyArg().value) + return ObjRegex( + scope.requireOnlyArg().value.toRegex() + ) + } + }.apply { + addFn("matches") { + ObjBool(args.firstAndOnly().toString().matches(thisAs().regex)) + } + addFn("find") { + val s = requireOnlyArg().value + thisAs().regex.find(s)?.let { ObjRegexMatch(it) } ?: ObjNull + } + addFn("findAll") { + val s = requireOnlyArg().value + ObjList(thisAs().regex.findAll(s).map { ObjRegexMatch(it) }.toMutableList()) + } + } + } + } +} + +class ObjRegexMatch(val match: MatchResult) : Obj() { + override val objClass = type + + val objGroups: ObjList by lazy { + ObjList( + match.groups.map { it?.let { ObjString(it.value) } ?: ObjNull }.toMutableList() + ) + } + + val objValue by lazy { ObjString(match.value) } + + val objRange: ObjRange by lazy { + val r = match.range + ObjRange( + ObjInt(r.first.toLong()), + ObjInt(r.last.toLong()), + false + ) + } + + companion object { + val type by lazy { + object : ObjClass("RegexMatch") { + override suspend fun callOn(scope: Scope): Obj { + scope.raiseError("RegexMatch can't be constructed directly") + } + }.apply { + addFn("groups") { + thisAs().objGroups + } + addFn("value") { + thisAs().objValue + } + addFn("range") { + thisAs().objRange + } + } + } + } +} \ No newline at end of file diff --git a/lynglib/src/commonMain/kotlin/net/sergeych/lyng/stdlib_included/root_lyng.kt b/lynglib/src/commonMain/kotlin/net/sergeych/lyng/stdlib_included/root_lyng.kt index 536ac43..894c4f8 100644 --- a/lynglib/src/commonMain/kotlin/net/sergeych/lyng/stdlib_included/root_lyng.kt +++ b/lynglib/src/commonMain/kotlin/net/sergeych/lyng/stdlib_included/root_lyng.kt @@ -165,6 +165,8 @@ fun Exception.printStackTrace() { } } +fun String.re() { Regex(this) } + """.trimIndent() diff --git a/lynglib/src/commonTest/kotlin/ScriptTest.kt b/lynglib/src/commonTest/kotlin/ScriptTest.kt index 435b0d6..e029cdd 100644 --- a/lynglib/src/commonTest/kotlin/ScriptTest.kt +++ b/lynglib/src/commonTest/kotlin/ScriptTest.kt @@ -3242,7 +3242,45 @@ class ScriptTest { result.insertAt(-i-1, x) } assertEquals( src.sorted(), result ) - """.trimIndent()) + """.trimIndent() + ) } + +// @Test + fun testMinimumOptimization() = runTest { + val x = Scope().eval( + """ + fun naiveCountHappyNumbers() { + var count = 0 + for( n1 in 0..9 ) + for( n2 in 0..9 ) + for( n3 in 0..9 ) + for( n4 in 0..9 ) + for( n5 in 0..9 ) + for( n6 in 0..9 ) + if( n1 + n2 + n3 == n4 + n5 + n6 ) count++ + count + } + naiveCountHappyNumbers() + """.trimIndent() + ).toInt() + assertEquals(55252, x) + } + + @Test + fun testRegex1() = runTest { + eval( + """ + assert( ! "123".re.matches("abs123def") ) + assert( ".*123.*".re.matches("abs123def") ) +// assertEquals( "123", "123".re.find("abs123def")?.value ) +// assertEquals( "123", "[0-9]{3}".re.find("abs123def")?.value ) + assertEquals( "123", "\d{3}".re.find("abs123def")?.value ) + assertEquals( "123", "\\d{3}".re.find("abs123def")?.value ) + assertEquals( [1,2,3], "\d".re.findAll("abs123def").map { it.value.toInt() } ) + """ + .trimIndent() + ) + } } \ No newline at end of file