Pyli/Methods/Conditions
Contents
Conditions, Exceptions, Warnings, Logging
The condition system of Pyli provides enough features for exceptions, warnings, and logging.
Condition
A Condition is an object with at least four attributes:
- type: The type of the condition. This is a specific string.
- msg: The message of the condition. This is a string that should be human readable.
- is-error: Whether the condition signals an error condition, or if it is just a warning or log message.
- stack: The associated stacktrace, or False. (This gets assigned when the Condition is signaled.)
- line-number (TODO): What line number the condition originated from, if applicable.
- file-name (TODO): Which file the condition came from, if applicable.
- expression (TODO): The expression that was being evaluated when signal was called.
(Condition type msg is-error)
(Condition "NameError" (fmt "Unrecognized name: %s" name) True)
signal
signal is the mechanism by which conditions are passed off to the language. Signal simply calls *signal-handler* of the calling environment (*calling-env*, whatever that is currently defined as. It always returns False, if it returns at all.
(signal) # Uses *current-condition* (signal Condition) (signal type msg is-error) # msg and is-error are optional.
(TODO) signal should append information on where the message originated from: line-number, file-name, expression.
Note that when a condition is signaled it is assigned its stack by signal, if one has not already been assigned.
*signal-handler*
*signal-handler* is called with the condition when a Condition is signaled. It may process the condition or simply hand it off to the larger context's *signal-handler*.
The default *signal-handler* simply prints out the error to stderr (TODO) and exits the program.
*current-condition*
This represents the current condition that is being handled within the signal handler.
on-condition
on-condition is a generic wrapper for providing your own *signal-handler*
(on-condition &(condition handler code) body ...)
- If no signal is raised, this behaves like (progn body ...)
- If a signal is raised, then the condition handler code is called. The variable condition is set to the condition. The variable *signal-handler* represents the next signal handler up the calling chain. The variable *with-signal-handler-label* and *current-label* are set to the label, which you can return from if you desire.
- If you evaluate to a value from the signal handler, then execution will continue normally, regardless of the is-error field. This is useful for logging, warning, debugging conditions.
- If you return a value, then that is what is returned from the expression. This would represent an exception case that is handled.
- If you restart, then the body is retried. This represents an exception that is to be retried.
- If you signal a new error (or signal the same error again), then the outer signal handler is called. This represents an unhandled exception, or an exception that should be translated to a new exception.
The following code looks like it should return the condition, but it will not. Instead, it returns the result of (signal ...) which is always False. This is what you would want to do if you want execution to continue as normal---don't return or restart.
(on-condition &condition &(signal "TestError" "This is a test" True))
This code will return the condition. When the condition is signalled, it calls the expression (return condition), which will immediately exit the with-signal-handler block.
(on-condition &(return condition) &(signal "TestError" "This is a test" True))
This code will restart the with-signal-handler body. When the condition is signalled, it calls the expression (return condition), which will immediately restart the body. This is, of course, an infinite loop, which is usually an error. However, if you were keeping track of retries, this may be appropriate.
(on-condition &(restart) &(signal "TestError" "This is a test" True))
This code will pass the condition up to the next handler.
(on-condition &(signal) &(signal "TestError" "This is a test" True))
This code will pass a new condition up:
(on-condition &(signal "AnotherError" "This is a new error" True) &(signal "TestError" "This is a test" True))
on-condition-case
on-condition-case allows the user to handle only specific types of exceptions, as if branching with a case on the type of the condition.
(on-condition-case &((type code ...) (type code ...) ...) &body ...)
It behaves as if:
(on-condition &(case (el condition type) &((type code ...) (type code ...) ...) &body ...)