ref #49 basic regex support (but not in when yet)
This commit is contained in:
		
							parent
							
								
									2743511b62
								
							
						
					
					
						commit
						ead2f7168e
					
				@ -1184,6 +1184,30 @@ See also [math operations](math.md)
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
The type for the character objects is `Char`.
 | 
					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
 | 
					### Char literal escapes
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Are the same as in string literals with little difference:
 | 
					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       |
 | 
					| escape | ASCII value       |
 | 
				
			||||||
|--------|-------------------|
 | 
					|--------|-------------------|
 | 
				
			||||||
| \n     | 0x10, newline     |
 | 
					| \n     | 0x10, newline     |
 | 
				
			||||||
 | 
					| \r     | 0x13, carriage return |
 | 
				
			||||||
| \t     | 0x07, tabulation  |
 | 
					| \t     | 0x07, tabulation  |
 | 
				
			||||||
| \\     | \ slash character |
 | 
					| \\     | \ slash character |
 | 
				
			||||||
| \'     | ' apostrophe      |
 | 
					| \'     | ' apostrophe      |
 | 
				
			||||||
 | 
				
			|||||||
@ -1376,9 +1376,16 @@ class Compiler(
 | 
				
			|||||||
                if (sourceObj is ObjRange && sourceObj.isIntRange) {
 | 
					                if (sourceObj is ObjRange && sourceObj.isIntRange) {
 | 
				
			||||||
                    loopIntRange(
 | 
					                    loopIntRange(
 | 
				
			||||||
                        forContext,
 | 
					                        forContext,
 | 
				
			||||||
                        sourceObj.start!!.toInt(),
 | 
					                        sourceObj.start!!.toLong(),
 | 
				
			||||||
                        if (sourceObj.isEndInclusive) sourceObj.end!!.toInt() + 1 else sourceObj.end!!.toInt(),
 | 
					                        if (sourceObj.isEndInclusive)
 | 
				
			||||||
                        loopSO, body, elseStatement, label, canBreak
 | 
					                            sourceObj.end!!.toLong() + 1
 | 
				
			||||||
 | 
					                        else
 | 
				
			||||||
 | 
					                            sourceObj.end!!.toLong(),
 | 
				
			||||||
 | 
					                        loopSO,
 | 
				
			||||||
 | 
					                        body,
 | 
				
			||||||
 | 
					                        elseStatement,
 | 
				
			||||||
 | 
					                        label,
 | 
				
			||||||
 | 
					                        canBreak
 | 
				
			||||||
                    )
 | 
					                    )
 | 
				
			||||||
                } else if (sourceObj.isInstanceOf(ObjIterable)) {
 | 
					                } else if (sourceObj.isInstanceOf(ObjIterable)) {
 | 
				
			||||||
                    loopIterable(forContext, sourceObj, loopSO, body, elseStatement, label, canBreak)
 | 
					                    loopIterable(forContext, sourceObj, loopSO, body, elseStatement, label, canBreak)
 | 
				
			||||||
@ -1439,7 +1446,7 @@ class Compiler(
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private suspend fun loopIntRange(
 | 
					    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
 | 
					        body: Statement, elseStatement: Statement?, label: String?, catchBreak: Boolean
 | 
				
			||||||
    ): Obj {
 | 
					    ): Obj {
 | 
				
			||||||
        var result: Obj = ObjVoid
 | 
					        var result: Obj = ObjVoid
 | 
				
			||||||
@ -1447,7 +1454,7 @@ class Compiler(
 | 
				
			|||||||
        loopVar.value = iVar
 | 
					        loopVar.value = iVar
 | 
				
			||||||
        if (catchBreak) {
 | 
					        if (catchBreak) {
 | 
				
			||||||
            for (i in start..<end) {
 | 
					            for (i in start..<end) {
 | 
				
			||||||
                iVar.value = i.toLong()
 | 
					                iVar.value = i//.toLong()
 | 
				
			||||||
                try {
 | 
					                try {
 | 
				
			||||||
                    result = body.execute(forScope)
 | 
					                    result = body.execute(forScope)
 | 
				
			||||||
                } catch (lbe: LoopBreakContinueException) {
 | 
					                } catch (lbe: LoopBreakContinueException) {
 | 
				
			||||||
@ -1459,7 +1466,7 @@ class Compiler(
 | 
				
			|||||||
                }
 | 
					                }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        } else {
 | 
					        } else {
 | 
				
			||||||
            for (i in start.toLong()..<end.toLong()) {
 | 
					            for (i in start ..< end) {
 | 
				
			||||||
                iVar.value = i
 | 
					                iVar.value = i
 | 
				
			||||||
                result = body.execute(forScope)
 | 
					                result = body.execute(forScope)
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
				
			|||||||
@ -437,7 +437,11 @@ private class Parser(fromPos: Pos) {
 | 
				
			|||||||
                        'r' -> {sb.append('\r'); pos.advance()}
 | 
					                        'r' -> {sb.append('\r'); pos.advance()}
 | 
				
			||||||
                        't' -> {sb.append('\t'); pos.advance()}
 | 
					                        't' -> {sb.append('\t'); pos.advance()}
 | 
				
			||||||
                        '"' -> {sb.append('"'); pos.advance()}
 | 
					                        '"' -> {sb.append('"'); pos.advance()}
 | 
				
			||||||
                        else -> sb.append('\\').append(currentChar)
 | 
					                        '\\' -> {sb.append('\\'); pos.advance()}
 | 
				
			||||||
 | 
					                        else -> {
 | 
				
			||||||
 | 
					                            sb.append('\\').append(currentChar)
 | 
				
			||||||
 | 
					                            pos.advance()
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -136,11 +136,6 @@ open class Scope(
 | 
				
			|||||||
                ?: thisObj.objClass
 | 
					                ?: thisObj.objClass
 | 
				
			||||||
                    .getInstanceMemberOrNull(name)
 | 
					                    .getInstanceMemberOrNull(name)
 | 
				
			||||||
                    )
 | 
					                    )
 | 
				
			||||||
//                ?.also {
 | 
					 | 
				
			||||||
//                        if( name == "predicate") {
 | 
					 | 
				
			||||||
//                            println("got predicate $it")
 | 
					 | 
				
			||||||
//                        }
 | 
					 | 
				
			||||||
//                }
 | 
					 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    fun copy(pos: Pos, args: Arguments = Arguments.EMPTY, newThisObj: Obj? = null): Scope =
 | 
					    fun copy(pos: Pos, args: Arguments = Arguments.EMPTY, newThisObj: Obj? = null): Scope =
 | 
				
			||||||
 | 
				
			|||||||
@ -241,6 +241,8 @@ class Script(
 | 
				
			|||||||
            addConst("CompletableDeferred", ObjCompletableDeferred.type)
 | 
					            addConst("CompletableDeferred", ObjCompletableDeferred.type)
 | 
				
			||||||
            addConst("Mutex", ObjMutex.type)
 | 
					            addConst("Mutex", ObjMutex.type)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            addConst("Regex", ObjRegex.type)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            addFn("launch") {
 | 
					            addFn("launch") {
 | 
				
			||||||
                val callable = requireOnlyArg<Statement>()
 | 
					                val callable = requireOnlyArg<Statement>()
 | 
				
			||||||
                ObjDeferred(globalDefer {
 | 
					                ObjDeferred(globalDefer {
 | 
				
			||||||
 | 
				
			|||||||
@ -21,15 +21,11 @@ import kotlinx.coroutines.sync.Mutex
 | 
				
			|||||||
import kotlinx.coroutines.sync.withLock
 | 
					import kotlinx.coroutines.sync.withLock
 | 
				
			||||||
import kotlinx.serialization.SerialName
 | 
					import kotlinx.serialization.SerialName
 | 
				
			||||||
import kotlinx.serialization.Serializable
 | 
					import kotlinx.serialization.Serializable
 | 
				
			||||||
import net.sergeych.bintools.encodeToHex
 | 
					 | 
				
			||||||
import net.sergeych.lyng.*
 | 
					import net.sergeych.lyng.*
 | 
				
			||||||
import net.sergeych.lynon.LynonDecoder
 | 
					import net.sergeych.lynon.LynonDecoder
 | 
				
			||||||
import net.sergeych.lynon.LynonEncoder
 | 
					import net.sergeych.lynon.LynonEncoder
 | 
				
			||||||
import net.sergeych.lynon.LynonType
 | 
					import net.sergeych.lynon.LynonType
 | 
				
			||||||
import net.sergeych.mptools.CachedExpression
 | 
					 | 
				
			||||||
import net.sergeych.synctools.ProtectedOp
 | 
					import net.sergeych.synctools.ProtectedOp
 | 
				
			||||||
import net.sergeych.synctools.withLock
 | 
					 | 
				
			||||||
import kotlin.contracts.ExperimentalContracts
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
open class Obj {
 | 
					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<ObjString>(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<ObjString>(scope),
 | 
					 | 
				
			||||||
                        decoder.decodeAny(scope),
 | 
					 | 
				
			||||||
                        decoder.decodeAnyAs<ObjList>(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<String, ExceptionClass>()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        @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)
 | 
					 | 
				
			||||||
 | 
				
			|||||||
@ -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<ObjString>(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<ObjString>(scope),
 | 
				
			||||||
 | 
					                        decoder.decodeAny(scope),
 | 
				
			||||||
 | 
					                        decoder.decodeAnyAs<ObjList>(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<String, ExceptionClass>()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        @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)
 | 
				
			||||||
@ -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<ObjString>().value)
 | 
				
			||||||
 | 
					                    return ObjRegex(
 | 
				
			||||||
 | 
					                        scope.requireOnlyArg<ObjString>().value.toRegex()
 | 
				
			||||||
 | 
					                    )
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }.apply {
 | 
				
			||||||
 | 
					                addFn("matches") {
 | 
				
			||||||
 | 
					                    ObjBool(args.firstAndOnly().toString().matches(thisAs<ObjRegex>().regex))
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                addFn("find") {
 | 
				
			||||||
 | 
					                    val s = requireOnlyArg<ObjString>().value
 | 
				
			||||||
 | 
					                    thisAs<ObjRegex>().regex.find(s)?.let { ObjRegexMatch(it) } ?: ObjNull
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                addFn("findAll") {
 | 
				
			||||||
 | 
					                    val s = requireOnlyArg<ObjString>().value
 | 
				
			||||||
 | 
					                    ObjList(thisAs<ObjRegex>().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<ObjRegexMatch>().objGroups
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                addFn("value") {
 | 
				
			||||||
 | 
					                    thisAs<ObjRegexMatch>().objValue
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                addFn("range") {
 | 
				
			||||||
 | 
					                    thisAs<ObjRegexMatch>().objRange
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@ -165,6 +165,8 @@ fun Exception.printStackTrace() {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					fun String.re() { Regex(this) }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
""".trimIndent()
 | 
					""".trimIndent()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -3242,7 +3242,45 @@ class ScriptTest {
 | 
				
			|||||||
                result.insertAt(-i-1, x)
 | 
					                result.insertAt(-i-1, x)
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            assertEquals( src.sorted(), result )
 | 
					            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()
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user