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.