# 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.

## finally block

If `finally` block present, it will be executed after body (until first exception)
and catch block, if any will match. finally statement is executed even if the
exception will be thrown and not caught locally. It does not alter try/catch block result:

    try {
    }
    finally {
        println("called finally")
    }
    >>> called finally
    >>> void

- and yes, there could be try-finally block, no catching, but perform some guaranteed cleanup.

# 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.

# Exception class

Serializable class that conveys information about the exception. Important members and methods are:

| name              | description                                            |
|-------------------|--------------------------------------------------------|
| message           | String message                                           |
| stackTrace()      | lyng stack trace, list of `StackTraceEntry`, see below   |
| printStackTrace() | format and print stack trace using println()             |

> **Note for Kotlin users**: When working with Lyng exceptions from Kotlin, you can use extension methods like `getLyngExceptionMessageWithStackTrace()`. See [Embedding Lyng](embedding.md#12-handling-and-serializing-exceptions) for the full API.

## StackTraceEntry

A simple structire that stores single entry in Lyng stack, it is created automatically on exception creation:

```lyng
class StackTraceEntry(
    val sourceName: String,
    val line: Int,
    val column: Int,
    val sourceString: String
)
```

- `sourceString` is a line extracted from sources. Note that it _is serialized and printed_, so if you want to conceal it, catch all exceptions and filter out sensitive information.


# Custom error classes

You can define your own exception classes by inheriting from the built-in `Exception` class. This allows you to create specific error types for your application logic and catch them specifically.

## Defining a custom exception

To define a custom exception, create a class that inherits from `Exception`:

```lyng
class MyUserException : Exception("something went wrong")
```

You can also pass the message dynamically:

```lyng
class MyUserException(m) : Exception(m)

throw MyUserException("custom error message")
```

If you don't provide a message to the `Exception` constructor, the class name will be used as the default message:

```lyng
class SimpleException : Exception

val e = SimpleException()
assertEquals("SimpleException", e.message)
```

## Throwing and catching custom exceptions

Custom exceptions are thrown using the `throw` keyword and can be caught using `catch` blocks, just like standard exceptions:

```lyng
class ValidationException(m) : Exception(m)

try {
    throw ValidationException("Invalid input")
}
catch(e: ValidationException) {
    println("Caught validation error: " + e.message)
}
catch(e: Exception) {
    println("Caught other exception: " + e.message)
}
```

Since user exceptions are real classes, inheritance works as expected:

```lyng
class BaseError : Exception
class DerivedError : BaseError

try {
    throw DerivedError()
}
catch(e: BaseError) {
    // This will catch DerivedError as well
    assert(e is DerivedError)
}
```

## Accessing extra data

You can add your own fields to custom exception classes to carry additional information:

```lyng
class NetworkException(m, val statusCode) : Exception(m)

try {
    throw NetworkException("Not Found", 404)
}
catch(e: NetworkException) {
    println("Error " + e.statusCode + ": " + e.message)
}
```

# Standard exception classes

| class                      | notes                                                 |
|----------------------------|-------------------------------------------------------|
| Exception                  | root of all throwable objects                         |
| NullReferenceException     |                                                       |
| AssertionFailedException   |                                                       | 
| ClassCastException         |                                                       |
| IndexOutOfBoundsException  |                                                       |
| IllegalArgumentException   |                                                       | 
| IllegalStateException      |                                                       |
| NoSuchElementException     |                                                       |
| IllegalAssignmentException | assigning to val, etc.                                |
| SymbolNotDefinedException  |                                                       |
| IterationEndException      | attempt to read iterator past end, `hasNext == false` |
| IllegalAccessException     | attempt to access private members or like             |
| UnknownException           | unexpected internal exception caught                  |
| NotFoundException          |                                                       |
| IllegalOperationException  |                                                       |
| UnsetException             | access to uninitialized late-init val                 |
| NotImplementedException    | used by `TODO()`                                      |
| SyntaxError                |                                                       |


### Symbol resolution errors

For compatibility, `SymbolNotFound` is an alias of `SymbolNotDefinedException`. You can catch either name in examples and tests.

Example:

```lyng
try {
    nonExistingMethod()
}
catch(e: SymbolNotFound) {
    // handle
}
```
