An exception in langur is always a hash that is guaranteed to contain certain fields, even if they are empty. You may include other fields in a hash that you throw. You can also throw anything else and it becomes the required hash.
If you do not specify a source in what you throw, langur sometimes determines and adds the function name automatically.
Any exception hash is guaranteed to contain the following fields.
| "cat" | an exception general category string |
| "msg" | the message string |
| "src" | exception source string |
| "hst" | previous exception or null |
There is no explicit try block. All the statements preceding a catch within a block are the implicit try block. You could put a catch at the end of a script to cover the entire script.
If a catch is embedded within a catch, they may each use the same exception variable name as they each have their own scope.
Using throw within a catch expression does not require you to specify what to throw. It implicitly rethrows the caught exception.
If an exception variable is not specified, it is implicitly _err.
There are 3 ways to write a catch.
A catch switch or a catch block may have else and else if sections added to them (to be executed if there is no exception).
A catch switch uses the syntax and power of a switch expression without the switch keyword.
A catch switch also propagates an error, if none of the conditions match and you do not use a default section. That is, an exception could be allowed to continue backing out of scope until it is caught.
catch _err'cat, _err'msg { case "math", -> re/division/: ... case "general", _: ... # no default specified; therefore, exception may continue if no conditions matched }
catch[e] e'cat, e'msg { case "math", -> re/division/: ... case "general", _: ... default: ... ... # default specified; therefore, exception stops here even without a condition match }
As a switch, a catch switch may include an alternate logical operator on the catch or on the case keyword. If on the catch keyword, it must be preceeded by the exception variable name, as follows.
catch[ex][and] ex'cat, ex'msg { ...
A catch block (not a catch switch) does not include any switch test conditions, and is just what it sounds like, a code block.
catch { ... # a catch block, executed regardless of the type of exception }
An else section is optional. It will only be run if there is no exception.
You can also use else if on catch.
123 / 0 catch[e] e'cat { case "math": 456 default: 789 } else { # no exception 890 } # result == 456
Catch and else have scope. Since try does not have scope, variables declared within the implicit try section but still in scope after the catch will be the default value (null) if they are not initialized before an exception occurs.
You can also use simple catch, using a colon. Simple catch has fewer options. You cannot use an else section with a simple catch.
123 / 0 catch : writeln "a boo boo: ", _err'msg
You can emulate a try block by wrapping into a scoped block.
{ ... catch[e] e'cat { ... } }
While a panic (a Go language exception) could indicate a bug in langur, we decided it best to catch them and convert them to langur exceptions. This still does not work with out-of-memory (OOM) errors. As a concise explanation, someone wrote "All OOM conditions terminate a Go program."
If a Go panic occurs and is caught and converted to a langur exception, the source name starts with "panic:" and may or may not have more information.