% Escape Operations
Npt documentation.
Reference: ANSI Common Lisp npt
Prev: 5. LISP ABORT
Next: 7. Start Up
This section describes the operation of the escape.
In the previous explanation, there were five reasons for escaping
tagbody
/go
block
/return-from
catch
/throw
handler-case
restart-case
The tagbody
/go
and block
/return-from
are intended for
use in lexical environments and are not available in C.
The remaining three operations can be implemented in C.
The escape operation must be considered not only to generate and capture,
but also to interrupt.
Escape interrupt is unwind-protect
.
In summary, this chapter describes the following features.
catch
/throw
handler-case
restart-case
unwind-protect
catch
/throw
The easiest escape operation is catch
/throw
.
The catch
registers a symbol
to a stack frame.
In other words, lisp_push_control
must be executed.
The following function is used for registration.
void lisp_catch(addr symbol);
The following function is used for throw
.
int lisp_throw_(addr symbol);
The throw
function goes backward from the current stack frame and
escape if the corresponding catch
argument is present.
If there is no catch
for the argument, an error occurs.
In both cases, the return value is non-zero without judgment.
The following function is used to check if the current stack frame has reached the escape point.
int lisp_break_control(void);
True (non-zero) is returned if the current escape is in progress
and the current stack frame is at the destination point.
In case of catch
/throw
, when the function is true,
the function switches to non-escape and the throw
is completed.
The function to switch to non-escape is as follows.
void lisp_reset_control(void);
The summary of the procedure is as follows.
lisp_push_control
.symbol
by lisp_catch
.lisp_throw_
lisp_break_control
.lisp_reset_control
makes it a non-escapeThe following statement is an example.
(catch 'hello
(throw 'hello 222)
333)
Since throw
returns 222
, 333
is ignored
and the total return value is 222
.
int throw_hello_222_(addr x)
{
lisp_fixnum(x, 222);
lisp_set_result_control(x);
if (lisp_intern8_(x, NULL, "HELLO"))
return 1;
return lisp_throw_(x);
}
int main_call_(addr x)
{
addr control;
lisp_push_control(&control);
/* (catch 'hello ...) */
if (lisp_intern8_(x, NULL, "HELLO"))
goto escape;
lisp_catch(x);
/* (throw 'hello 222) */
if (throw_hello_222_(x))
goto escape;
/* (values 333) */
lisp_fixnum(x, 333);
lisp_set_result_control(x);
escape:
if (lisp_break_control())
lisp_reset_control();
return lisp_pop_control_(control);
}
int main_lisp(void *ignore)
{
addr control, x;
lisp_push_control(&control);
x = Lisp_hold();
if (main_call_(x))
goto escape;
lisp_result_control(x);
if (lisp_format8_(NULL, "RESULT: ~A~%", x, NULL))
goto escape;
escape:
return lisp_pop_control_(control);
}
The result is RESULT: 222
.
handler-case
The basic construction is the same as catch
/throw
.
The following function is used to register the condition
.
int lisp_handler_case_(addr name, addr call);
And signal
or error
corresponds to throw
.
The following is an example.
int main_call_(addr x)
{
addr control, y;
lisp_push_control(&control);
y = Lisp_hold();
/* handler-case */
if (lisp_intern8_(x, NULL, "AAA"))
goto escape;
if (lisp_eval8_(y, "(lambda (c) (declare (ignore c)) 222)"))
goto escape;
if (lisp_handler_case_(x, y))
goto escape;
/* (signal 'aaa) */
if (lisp_eval8_(NULL, "(signal 'aaa)"))
goto escape;
/* (values 333) */
lisp_fixnum(x, 333);
lisp_set_result_control(x);
escape:
if (lisp_break_control())
lisp_reset_control();
return lisp_pop_control_(control);
}
int main_lisp(void *ignore)
{
addr control, x;
lisp_push_control(&control);
x = Lisp_hold();
if (lisp_eval8_(NULL, "(define-condition aaa () ())"))
goto escape;
if (main_call_(x))
goto escape;
lisp_result_control(x);
if (lisp_format8_(NULL, "RESULT: ~A~%", x, NULL))
goto escape;
escape:
return lisp_pop_control_(control);
}
The result is RESULT: 222
.
restart-case
Same as handler-case
.
Create a restart
object and register it to the current stack frame.
The following functions are used to create the restart
object.
void lisp_restart_make(addr x, addr name, addr call, int casep);
The following function is used in order to register the restart
.
void lisp_restart_push(addr restart);
Examples of use are as follows.
int main_call_(addr x)
{
addr control, y;
lisp_push_control(&control);
y = Lisp_hold();
/* restart-case */
if (lisp_intern8_(x, NULL, "HELLO"))
goto escape;
if (lisp_eval8_(y, "(lambda () 222)"))
goto escape;
lisp_restart_make(x, x, y, 1);
lisp_restart_push(x);
/* (invoke_restart 'hello) */
if (lisp_eval8_(NULL, "(invoke-restart 'hello)"))
goto escape;
/* (values 333) */
lisp_fixnum(x, 333);
lisp_set_result_control(x);
escape:
if (lisp_break_control())
lisp_reset_control();
return lisp_pop_control_(control);
}
int main_lisp(void *ignore)
{
addr control, x;
lisp_push_control(&control);
x = Lisp_hold();
if (main_call_(x))
goto escape;
lisp_result_control(x);
if (lisp_format8_(NULL, "RESULT: ~A~%", x, NULL))
goto escape;
escape:
return lisp_pop_control_(control);
}
The result is RESULT: 222
.
unwind-protect
unwind-protect
requires the following values to be saved
after executing the expression of the first argument and
before executing the cleanup form.
Escape information is the information of the current escape mode
and the stack frame of the destination.
Returned value information is a value obtained by list_result_control
and is the value of values
itself.
The value of values
is not one, but 0 or multiple values.
Before executing unwind-protect
, the current state is evacuated at once
and the state is rolled back after the cleanup
form is executed.
The following functions are used to save the escape information and the return value at the same time.
void lisp_save_control(addr *ret);
The information is stored using a stack frame as well as the hold variable.
After saving the information,
the following functions are used to transition to non-escape.
void lisp_reset_control(void);
This function can be executed even if it is a non-escape.
When the save is done, the cleanup
process is carried out.
If a new escape is created during the cleanup
process,
the information saved by lisp_save_control
is ignored
and the latest escape process takes precedence.
When the cleanup
process is successfully completed,
the following function will be used to restore it.
void lisp_rollback_control(addr value);
Finally, the command lisp_pop_control_
is executed
to discard the stored information.
Here is an example.
The following example shows that the returned values are saved and restored.
int main_lisp(void *ignore)
{
addr control, save, x;
lisp_push_control(&control);
x = Lisp_hold();
/* 100 */
lisp_fixnum(x, 100);
lisp_set_result_control(x);
/* save */
lisp_save_control(&save);
lisp_reset_control();
/* 200 */
lisp_fixnum(x, 200);
lisp_set_result_control(x);
/* rollback */
lisp_rollback_control(save);
lisp_result_control(x);
lisp_format8_(NULL, "RESULT: ~A~%", x, NULL);
return lisp_pop_control_(control);
}
The result is RESULT: 100
.