parent
							
								
									5ed8b2f123
								
							
						
					
					
						commit
						c3bf536bab
					
				
							
								
								
									
										129
									
								
								docs/exceptions_handling.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										129
									
								
								docs/exceptions_handling.md
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,129 @@
 | 
			
		||||
# Exceptions handling
 | 
			
		||||
 | 
			
		||||
Exceptions are widely used in modern programming languages, so
 | 
			
		||||
they are implemented also in Lyng and in the most complete way.
 | 
			
		||||
 | 
			
		||||
# Exception classes
 | 
			
		||||
 | 
			
		||||
Exceptions are throwing instances of some class that inherits `Exception`
 | 
			
		||||
across the code. Below is the list of built-in exceptions. Note that
 | 
			
		||||
only objects that inherit `Exception` can be thrown. For example:
 | 
			
		||||
 | 
			
		||||
    assert( IllegalArgumentException() is Exception)
 | 
			
		||||
    >>> void
 | 
			
		||||
 | 
			
		||||
# Try statement: catching exceptions
 | 
			
		||||
 | 
			
		||||
There general pattern is:
 | 
			
		||||
 | 
			
		||||
```
 | 
			
		||||
try_statement = try_clause, [catch_clause, ...], [finally_clause] 
 | 
			
		||||
 | 
			
		||||
try_clause = "try", "{", statements, "}"
 | 
			
		||||
 | 
			
		||||
catch_clause = "catch", [(full_catch | shorter_catch)], "{", statements "}"
 | 
			
		||||
 | 
			
		||||
full_catch = "(", catch_var, ":", exception_class [, excetpion_class...], ")
 | 
			
		||||
 | 
			
		||||
shorter_catch = "(", catch_var, ")"
 | 
			
		||||
 | 
			
		||||
finally_clause = "{", statements, "}"
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
Let's in details.
 | 
			
		||||
 | 
			
		||||
## Full catch block:
 | 
			
		||||
 | 
			
		||||
    val result = try {
 | 
			
		||||
        throw IllegalArgumentException("the test")
 | 
			
		||||
    }
 | 
			
		||||
    catch( x: IndexOutOfBoundsException, IllegalArgumentException) {
 | 
			
		||||
        x.message
 | 
			
		||||
    }
 | 
			
		||||
    catch(x: Exception) {
 | 
			
		||||
        "bad"
 | 
			
		||||
    }
 | 
			
		||||
    assertEquals(result, "the test")
 | 
			
		||||
    >>> void
 | 
			
		||||
 | 
			
		||||
Because our exception is listed in a first catch block, it is processed there.
 | 
			
		||||
 | 
			
		||||
The full form allow a single catch block to process exceptions with specified classes and bind actual caught object to
 | 
			
		||||
the given variable. This is most common and well known form, implemented like this or similar in many other languages,
 | 
			
		||||
like Kotlin, Java or C++.
 | 
			
		||||
 | 
			
		||||
## Shorter form
 | 
			
		||||
 | 
			
		||||
When you want to catch _all_ the exceptions, you should write `catch(e: Exception)`,
 | 
			
		||||
but it is somewhat redundant, so there is simpler variant:
 | 
			
		||||
 | 
			
		||||
    val sample2 = try {
 | 
			
		||||
        throw IllegalArgumentException("sample 2")
 | 
			
		||||
    }
 | 
			
		||||
    catch(x) {
 | 
			
		||||
        x.message
 | 
			
		||||
    }
 | 
			
		||||
    assertEquals( sample2, "sample 2" )
 | 
			
		||||
    >>> void
 | 
			
		||||
 | 
			
		||||
But well most likely you will find default variable `it`, like in Kotlin, more than enough
 | 
			
		||||
to catch all exceptions to, then you can write it even shorter:
 | 
			
		||||
 | 
			
		||||
    val sample2 = try {
 | 
			
		||||
        throw IllegalArgumentException("sample 3")
 | 
			
		||||
    }
 | 
			
		||||
    catch {
 | 
			
		||||
        it.message
 | 
			
		||||
    }
 | 
			
		||||
    assertEquals( sample2, "sample 3" )
 | 
			
		||||
    >>> void
 | 
			
		||||
 | 
			
		||||
You can even check the type of the `it` and create more convenient and sophisticated processing logic. Such approach is
 | 
			
		||||
used, for example, in Scala.
 | 
			
		||||
 | 
			
		||||
# Conveying data with exceptions
 | 
			
		||||
 | 
			
		||||
The simplest way is to provide exception string and `Exception` class:
 | 
			
		||||
 | 
			
		||||
    try {
 | 
			
		||||
        throw Exception("this is my exception")
 | 
			
		||||
    }
 | 
			
		||||
    catch {
 | 
			
		||||
        it.message
 | 
			
		||||
    }
 | 
			
		||||
    >>> "this is my exception"
 | 
			
		||||
 | 
			
		||||
This way, in turn, can also be shortened, as it is overly popular:
 | 
			
		||||
 | 
			
		||||
    try {
 | 
			
		||||
        throw "this is my exception"
 | 
			
		||||
    }
 | 
			
		||||
    catch {
 | 
			
		||||
        it.message
 | 
			
		||||
    }
 | 
			
		||||
    >>> "this is my exception"
 | 
			
		||||
 | 
			
		||||
The trick, though, works with strings only, and always provide `Exception` instances, which is good for debugging but
 | 
			
		||||
most often not enough.
 | 
			
		||||
 | 
			
		||||
# Custom error classes
 | 
			
		||||
 | 
			
		||||
_this functionality is not yet released_
 | 
			
		||||
 | 
			
		||||
# Standard exception classes
 | 
			
		||||
 | 
			
		||||
| class                      | notes                                                 |
 | 
			
		||||
|----------------------------|-------------------------------------------------------|
 | 
			
		||||
| Exception                  | root of al throwable objects                          |
 | 
			
		||||
| NullPointerException       |                                                       |
 | 
			
		||||
| AssertionFailedException   |                                                       | 
 | 
			
		||||
| ClassCastException         |                                                       |
 | 
			
		||||
| IndexOutOfBoundsException  |                                                       |
 | 
			
		||||
| IllegalArgumentException   |                                                       | 
 | 
			
		||||
| IllegalAssignmentException | assigning to val, etc.                                |
 | 
			
		||||
| SymbolNotDefinedException  |                                                       |
 | 
			
		||||
| IterationEndException      | attempt to read iterator past end, `hasNext == false` |
 | 
			
		||||
| AccessException            | attempt to access private members or like             |
 | 
			
		||||
| UnknownException           | unexpected kotlin exception caught                    |
 | 
			
		||||
|                            |                                                       |
 | 
			
		||||
 | 
			
		||||
@ -12,7 +12,7 @@ In other word, the code usually works as expected when you see it. So, nothing u
 | 
			
		||||
__Other documents to read__ maybe after this one:
 | 
			
		||||
 | 
			
		||||
- [Advanced topics](advanced_topics.md), [declaring arguments](declaring_arguments.md)
 | 
			
		||||
- [OOP notes](OOP.md)
 | 
			
		||||
- [OOP notes](OOP.md), [exception handling](exceptions_handling.md)
 | 
			
		||||
- [math in Lyng](math.md)
 | 
			
		||||
- Some class references: [List], [Real], [Range], [Iterable], [Iterator]
 | 
			
		||||
- Some samples: [combinatorics](samples/combinatorics.lyng.md), national vars and loops: [сумма ряда](samples/сумма_ряда.lyng.md). More at [samples folder](samples)
 | 
			
		||||
 | 
			
		||||
@ -725,7 +725,9 @@ class Compiler(
 | 
			
		||||
    private fun parseThrowStatement(cc: CompilerContext): Statement {
 | 
			
		||||
        val throwStatement = parseStatement(cc) ?: throw ScriptError(cc.currentPos(), "throw object expected")
 | 
			
		||||
        return statement {
 | 
			
		||||
            val errorObject = throwStatement.execute(this)
 | 
			
		||||
            var errorObject = throwStatement.execute(this)
 | 
			
		||||
            if( errorObject is ObjString )
 | 
			
		||||
                errorObject = ObjException(this, errorObject.value)
 | 
			
		||||
            if( errorObject is ObjException )
 | 
			
		||||
                raiseError(errorObject)
 | 
			
		||||
            else raiseError("this is not an exception object: $errorObject")
 | 
			
		||||
@ -816,11 +818,10 @@ class Compiler(
 | 
			
		||||
                    for (exceptionClassName in cdata.classNames) {
 | 
			
		||||
                        val exObj = ObjException.getErrorClass(exceptionClassName)
 | 
			
		||||
                            ?: raiseSymbolNotFound("error clas not exists: $exceptionClassName")
 | 
			
		||||
                        println("exObj: $exObj")
 | 
			
		||||
                        println("objException: ${objException.objClass}")
 | 
			
		||||
                        if( objException.isInstanceOf(exObj) )
 | 
			
		||||
                        if( objException.isInstanceOf(exObj) ) {
 | 
			
		||||
                            exceptionObject = objException
 | 
			
		||||
                            break
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                    if( exceptionObject != null ) {
 | 
			
		||||
                        val catchContext = this.copy(pos = cdata.catchVar.pos)
 | 
			
		||||
 | 
			
		||||
@ -347,13 +347,16 @@ open class ObjException(exceptionClass: ExceptionClass, val context: Context, va
 | 
			
		||||
 | 
			
		||||
        class ExceptionClass(val name: String,vararg parents: ObjClass) : ObjClass(name, *parents) {
 | 
			
		||||
            override suspend fun callOn(context: Context): Obj {
 | 
			
		||||
                return ObjException(this, context, name).apply {
 | 
			
		||||
                    println(">>>> "+this)
 | 
			
		||||
                }
 | 
			
		||||
                val message = context.args.getOrNull(0)?.toString() ?: name
 | 
			
		||||
                return ObjException(this, context, message)
 | 
			
		||||
            }
 | 
			
		||||
            override fun toString(): String = "ExceptionClass[$name]@${hashCode().encodeToHex()}"
 | 
			
		||||
        }
 | 
			
		||||
        val Root = ExceptionClass("Throwable")
 | 
			
		||||
        val Root = ExceptionClass("Throwable").apply {
 | 
			
		||||
            addConst("message", statement {
 | 
			
		||||
                (thisObj as ObjException).message.toObj()
 | 
			
		||||
            })
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        private val op = ProtectedOp()
 | 
			
		||||
        private val existingErrorClasses = mutableMapOf<String, ExceptionClass>()
 | 
			
		||||
 | 
			
		||||
@ -1813,4 +1813,24 @@ class ScriptTest {
 | 
			
		||||
        """.trimIndent())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Test
 | 
			
		||||
    fun testAccessEHData() = runTest {
 | 
			
		||||
        eval("""
 | 
			
		||||
            val x = IllegalArgumentException("test")
 | 
			
		||||
            val m = try {
 | 
			
		||||
                throw x
 | 
			
		||||
                null
 | 
			
		||||
            }
 | 
			
		||||
            catch(e) {
 | 
			
		||||
                println(e)
 | 
			
		||||
                println(e::class)
 | 
			
		||||
                println(e.message)
 | 
			
		||||
                println("--------------")
 | 
			
		||||
                e.message    
 | 
			
		||||
            }
 | 
			
		||||
            println(m)
 | 
			
		||||
            assert( m == "test" )
 | 
			
		||||
            """.trimIndent())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@ -268,4 +268,9 @@ class BookTest {
 | 
			
		||||
    fun testArgumentBooks() = runTest {
 | 
			
		||||
        runDocTests("../docs/declaring_arguments.md")
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Test
 | 
			
		||||
    fun testExceptionsBooks() = runTest {
 | 
			
		||||
        runDocTests("../docs/exceptions_handling.md")
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user