Sather 1.0 has fully implemented exception handling. An exception may be raised by calling the raised statement
foo is
raise("foo not implemented. This is a test");
Within the raise statement may be any arbitrary expression which returns some object. The type of the object determines the class of the exception. Exceptions may be caught by simply protecting for the appropriate type. In the protect clause, the exception may be referred to by the built in variable "exception" In fact, you can look at the tail half of the protect as a typecase on the exception object.
protect
foo;
when STR then #OUT+exception+"\n" end;
Exceptions are passed to higher contexts until a handler is
found and the exception is caught. The compiler provides a default
handler for string exceptions, which is why most of the exceptions raised
are string exceptions.
As a more complex example, here's an exception that could be used in the STR_CURSOR class
class STR_CURSOR_EX < $STR is
-- Hold information that may be used when the exception is raised
readonly attr s: STR_CURSOR;
readonly attr error_type: STR;
create(s: STR_CURSOR,m: STR): SAME is
res ::= new;
res.s := s;
res.error_type := m;
return(res);
end;
str: STR is
res: STR := "Error type:\n"+error_type+"\n";
res := res + "String cursor location:"+s.current_loc+"\n";
return(res);
end;
end;
In the STR_CURSOR class we may then raise the exception as follows
advance_one_char is
if (current_loc = buf.size -1) then -- some error condition
raise #STR_CURSOR_EX(self,"Attempt to read past the end of buf");
end;
end;
We may then catch this exception by:
protect
my_string_cursor.advance_one_char;
when STR_CURSOR_EX then #OUT+exception.str+"\n"; end;
Note that the exception object in this case has a whole bunch of other information that we might make use of - the exact location in the string cursor, for instance. This information may be used to report the error in a manner that is suitable to the client.
Exceptions are very convenient, but create a significant overhead and should be used only for error states. Programming with them is tempting, but should be avoided. For instance, in the STR_CURSOR class, we can make use of exceptions for parsing. Testing whether a bool exists can be done as follows
test_bool: BOOL is
protect
current_state ::= save_state;
b ::= get_bool;
restore_state(current_state);
when STR_CURSOR_EX then return(false); end;
return(true);
end;
However, this is a much higher overhead way of accomplishing
the basic test.
The alternative to using exceptions is to use a sticky error flag in the class, as is done by IEEE exceptions and the current FILE classes. This has problems such as the fact that the outermost error is logged, not the most immediate one, and it is very easy to forget to catch the exception. However, it is of low overhead and would be suitable for use in the previous test_bool case.