Chapter 12. Exceptions
Exceptions are runtime errors (unexpected issues not noticed by the compiler) that can occur when the program executes. Exceptions are not the same as compiler errors. Compiler errors are syntax errors that occur in the parser or tokenizer. Runtime errors can be from unexpected input, overflows, or conversion errors. By default, an exception will cause the program to quit, display the position in the code where the exception was thrown (refers to the triggering of the exception), and the message associated with the exception. An exception can be caught (refers to the catching of exceptions so that they do not stop the program) and displayed back to the user in a more friendly manner. This chapter will cover some basic exceptions, catching exceptions, and raising your own exceptions.
12.1 - Common Exceptions
12.1.1 Exception - Conversion
A conversion exception occurs when you try and convert an incompatable object. Run this code and examine the output.
func main () {
int a = "not a number".toint (); # The program stops here
println (a); # This will never print anything
}
Output:
Unhandled Exception:
Conversion Failed: Could not convert object of type 'string' to type 'int'
Near:
int a = "not a number".toint (); # The program stops here
^
At:
func main () [/home/reagan/test.has:2:31]
func main () [/home/reagan/test.has:1:5]
The first part of the message is the location of the exception. 2 : 31
means that the exception occurs on the second line of the source code, at the 31st character. The second line of the exception is the message associated with the exception. Since "not a number"
cannot be converted into an integer, an exception is thrown with that message. The third line is the stack trace of the exception. This shows the methods that were called before the exception was thrown. In this case, main
is the only method that was called before the exception was thrown.
12.1.2 Exception - Attribute Not Found
Another common exception is the attribute not found exception. This occurs when the programmer tries to access an attribute that does not exist. Try running the following code.
func main () {
doesNotExist ();
}
Output:
Unhandled Exception:
Attribute Not Found: Could not find attribute 'doesNotExist' in object of type 'Module'
Near:
doesNotExist ();
^
At:
func main () [/home/reagan/test.has:2:13]
func main () [/home/reagan/test.has:1:5]
12.1.3 Exception - Expected Argument Length
Another common exception is the expected argument length exception. This occurs when a function is supposed to take a certain amount of arguments, but the function call had a different amount. For instance, the input
function takes 0 arguments. Let's see what happens when we call the input
function using two arguments.
func main () {
input (3, false);
}
Output:
Unhandled Exception:
Argument Length Error: Expected '0' arguments, '2' given
Near:
input (3, false);
^
At:
func main () [/home/reagan/test.has:2:8]
func main () [/home/reagan/test.has:1:5]
12.2 - The try catch Statement
The try
and catch
statements are used to prevent exceptions from stopping the program. The exception is turned into a variable named value
(or otherwise specified). The try
statement must contain the code where exceptions might be raised, the catch
statement contains the code that runs if an exception is caught.
For instance, let's create a program that asks the user in an infinite loop to enter a number. If the user does not enter a number that can be converted to an integer, the catch
statement will prevent an exception from ending the program. Instead, a friendly error message will be displayed to the terminal.
func main () {
while (true) {
printf ("Enter a number: {0}");
try {
int num = input ().toint ();
println ("Good job!");
} catch {
println ("Enter a real number please!");
}
}
}
Sample run:
Enter a number: 45
Good job!
Enter a number: 16
Good job!
Enter a number: a number
Enter a real number please!
Enter a number: sheep
Enter a real number please!
The catch
statement is only executed if there is an exception, meaning that toInt
failed to convert the string to a number. Instead of stopping the program when this happens, the catch
statement shows a friendly error message to the terminal.
12.3 - Raising Exceptions
The programmer can also throw (or raise) their own exceptions. This is accomplished with the raise
keyword and statement. The syntax is raise <object>
. If there is no try
-catch
handling the exception, the program will stop and display the location of the raised exception as well as the object.tostring ()
.
func main () {
println ("About to self destruct");
raise "This is an exception";
println ("You can't see me!");
}
Output:
About to self destruct
Unhandled Exception:
This is an exception
Near:
raise "This is an exception";
^
At:
func main () [/home/reagan/test.has:3:6]
func main () [/home/reagan/test.has:1:5]
Chapter 13 - Exercises
Exercise 13.1
Create a program that throws three different exceptions. These should be caught and displayed in a way that doesn't stop the program from running.
Exercise 13.2
Create a class called Exception
that can contain helpful data members if the exception is caught, as well as a tostring
function for displaying the message for the terminal if there is no handler.