A Guide to the S-Lang Language: Error Handling

Next Previous Contents

## 14. Error Handling

Many intrinsic functions signal errors in the event of failure. User defined functions may also generate an error condition via the `error` function. Depending upon the severity of the error, it can be caught and cleared using a construct called an error-block.

## 14.1 Error-Blocks

When the interpreter encounters a recoverable run-time error, it will return to top-level by unwinding its function call stack. Any error-blocks that it encounters as part of this unwinding process will get executed. Errors such as syntax errors and memory allocation errors are not recoverable, and error-blocks will not get executed when such errors are encountered.

An error-block is defined using the syntax

> `

>            ERROR_BLOCK { statement-list }

> `

where statement-list represents a list of statements that comprise the error-block. A simple example of an error-block is

> `

>            define simple (a)
>            {
>               ERROR_BLOCK { message ("error-block executed"); }
>               if (a) error ("Triggering Error");
>               message ("hello");
>            }

> `

Executing this function via `simple(0)` will result in the message `"hello"`. However, calling it using `simple(1)` will generate an error that will be caught, but not cleared, by the error-block and the `"error-block executed"` message will result.

Error-blocks are never executed unless triggered by an error. The only exception to this is when the user explicitly indicates that the error-block in scope should execute. This is indicated by the special keyword `EXECUTE_ERROR_BLOCK`. For example, `simple` could be recoded as

> `

>            define simple (a)
>            {
>               variable err_string = "error-block executed";
>               ERROR_BLOCK { message (err_string); }
>               if (a) error ("Triggering Error");
>               err_string = "hello";
>               EXECUTE_ERROR_BLOCK;
>            }

> `

Please note that `EXECUTE_ERROR_BLOCK` does not initiate an error condition; it simply causes the error-block to be executed and control will pass onto the next statement following the `EXECUTE_ERROR_BLOCK` statement.

## 14.2 Clearing Errors

Once an error has been caught by an error-block, the error can be cleared by the `_clear_error` function. After the error has been cleared, execution will resume at the next statement at the level of the error block following the statement that generated the error. For example, consider:

> `

>            define make_error ()
>            {
>                error ("Error condition created.");
>                message ("This statement is not executed.");
>            }

>            define test ()
>            {
>                ERROR_BLOCK
>                  {
>                     _clear_error ();
>                  }
>                make_error ();
>                message ("error cleared.");
>            }

> `

Calling `test` will trigger an error in the `make_error` function, but will get cleared in the `test` function. The call-stack will unwind from `make_error` back into `test` where the error-block will get executed. As a result, execution resumes after the statement that makes the call to `make_error` since this statement is at the same level as the error-block that cleared the error.

Here is another example that illustrates how multiple error-blocks work:

> `

>            define example ()
>            {
>               variable n = 0, s = "";
>               variable str;

>               ERROR_BLOCK {
>                   str = sprintf ("s=%s,n=%d", s, n);
>                   _clear_error ();
>               }

>               forever
>                 {
>                   ERROR_BLOCK {
>                    s += "0";
>                    _clear_error ();
>                   }

>                   if (n == 0) error ("");

>                   ERROR_BLOCK {
>                    s += "1";
>                   }

>                   if (n == 1) error ("");
>                   n++;
>                 }
>               return str;
>            }

> `

Here, three error-blocks have been declared. One has been declared outside the `forever` loop and the other two have been declared inside the `forever` loop. Each time through the loop, the variable `n` is incremented and a different error-block is triggered. The error-block that gets triggered is the last one encountered, since that will be the one in scope. On the first time through the loop, `n` will be zero and the first error-block in the loop will get executed. This error block clears the error and execution resumes following the `if` statement that triggered the error. The variable `n` will get incremented to `1` and, on the second cycle through the loop the second `if` statement will trigger an error causing the second error-block to execute. This time, the error is not cleared and the call-stack unwinds out of the `forever` loop, at which point the error-block outside the loop is in scope, causing it to execute. This error-block prints out the values of the variables `s` and `n`. It will clear the error and execution resumes on the statement following the `forever` loop. The result of this complicated series of events is that the function will return the string `"s=01,n=1"`.

Next Previous Contents