diff options
| author | Stefan Monnier | 2023-12-25 22:32:17 -0500 |
|---|---|---|
| committer | Stefan Monnier | 2024-01-04 16:32:53 -0500 |
| commit | 5ba75e183c60aff50949587c21066e876dabfbda (patch) | |
| tree | db29f1ea2ef53d51ebb7ed1b8be999a664498f89 /src/eval.c | |
| parent | 225710ba79c10b53b6ba320327ca31192ca72387 (diff) | |
| download | emacs-5ba75e183c60aff50949587c21066e876dabfbda.tar.gz emacs-5ba75e183c60aff50949587c21066e876dabfbda.zip | |
New special form `handler-bind`
AFAIK, this provides the same semantics as Common Lisp's `handler-bind`,
modulo the differences about how error objects and conditions are
represented.
* lisp/subr.el (handler-bind): New macro.
* src/eval.c (pop_handler): New function.
(Fhandler_Bind_1): New function.
(signal_or_quit): Handle new handlertypes `HANDLER` and `SKIP_CONDITIONS`.
(find_handler_clause): Simplify.
(syms_of_eval): Defsubr `Fhandler_bind_1`.
* doc/lispref/control.texi (Handling Errors): Add `handler-bind`.
* test/src/eval-tests.el (eval-tests--handler-bind): New test.
* lisp/emacs-lisp/lisp-mode.el (lisp-font-lock-keywords):
Move 'handler-bind' from CL-only to generic Lisp.
(handler-bind): Remove indentation setting, it now lives in the macro
definition.
Diffstat (limited to 'src/eval.c')
| -rw-r--r-- | src/eval.c | 97 |
1 files changed, 84 insertions, 13 deletions
diff --git a/src/eval.c b/src/eval.c index 7f67b5a9db8..595267f7686 100644 --- a/src/eval.c +++ b/src/eval.c | |||
| @@ -1198,6 +1198,12 @@ usage: (catch TAG BODY...) */) | |||
| 1198 | 1198 | ||
| 1199 | #define clobbered_eassert(E) verify (sizeof (E) != 0) | 1199 | #define clobbered_eassert(E) verify (sizeof (E) != 0) |
| 1200 | 1200 | ||
| 1201 | static void | ||
| 1202 | pop_handler (void) | ||
| 1203 | { | ||
| 1204 | handlerlist = handlerlist->next; | ||
| 1205 | } | ||
| 1206 | |||
| 1201 | /* Set up a catch, then call C function FUNC on argument ARG. | 1207 | /* Set up a catch, then call C function FUNC on argument ARG. |
| 1202 | FUNC should return a Lisp_Object. | 1208 | FUNC should return a Lisp_Object. |
| 1203 | This is how catches are done from within C code. */ | 1209 | This is how catches are done from within C code. */ |
| @@ -1361,6 +1367,43 @@ usage: (condition-case VAR BODYFORM &rest HANDLERS) */) | |||
| 1361 | return internal_lisp_condition_case (var, bodyform, handlers); | 1367 | return internal_lisp_condition_case (var, bodyform, handlers); |
| 1362 | } | 1368 | } |
| 1363 | 1369 | ||
| 1370 | DEFUN ("handler-bind-1", Fhandler_bind_1, Shandler_bind_1, 1, MANY, 0, | ||
| 1371 | doc: /* Setup error handlers around execution of BODYFUN. | ||
| 1372 | BODYFUN be a function and it is called with no arguments. | ||
| 1373 | CONDITIONS should be a list of condition names (symbols). | ||
| 1374 | When an error is signaled during executon of BODYFUN, if that | ||
| 1375 | error matches one of CONDITIONS, then the associated HANDLER is | ||
| 1376 | called with the error as argument. | ||
| 1377 | HANDLER should either transfer the control via a non-local exit, | ||
| 1378 | or return normally. | ||
| 1379 | If it returns normally, the search for an error handler continues | ||
| 1380 | from where it left off. | ||
| 1381 | |||
| 1382 | usage: (handler-bind BODYFUN [CONDITIONS HANDLER]...) */) | ||
| 1383 | (ptrdiff_t nargs, Lisp_Object *args) | ||
| 1384 | { | ||
| 1385 | eassert (nargs >= 1); | ||
| 1386 | Lisp_Object bodyfun = args[0]; | ||
| 1387 | int count = 0; | ||
| 1388 | if (nargs % 2 == 0) | ||
| 1389 | error ("Trailing CONDITIONS withount HANDLER in `handler-bind`"); | ||
| 1390 | for (ptrdiff_t i = nargs - 2; i > 0; i -= 2) | ||
| 1391 | { | ||
| 1392 | Lisp_Object conditions = args[i], handler = args[i + 1]; | ||
| 1393 | if (NILP (conditions)) | ||
| 1394 | continue; | ||
| 1395 | else if (!CONSP (conditions)) | ||
| 1396 | conditions = Fcons (conditions, Qnil); | ||
| 1397 | struct handler *c = push_handler (conditions, HANDLER_BIND); | ||
| 1398 | c->val = handler; | ||
| 1399 | c->bytecode_dest = count++; | ||
| 1400 | } | ||
| 1401 | Lisp_Object ret = call0 (bodyfun); | ||
| 1402 | for (; count > 0; count--) | ||
| 1403 | pop_handler (); | ||
| 1404 | return ret; | ||
| 1405 | } | ||
| 1406 | |||
| 1364 | /* Like Fcondition_case, but the args are separate | 1407 | /* Like Fcondition_case, but the args are separate |
| 1365 | rather than passed in a list. Used by Fbyte_code. */ | 1408 | rather than passed in a list. Used by Fbyte_code. */ |
| 1366 | 1409 | ||
| @@ -1737,6 +1780,7 @@ signal_or_quit (Lisp_Object error_symbol, Lisp_Object data, bool keyboard_quit) | |||
| 1737 | = (NILP (error_symbol) ? Fcar (data) : error_symbol); | 1780 | = (NILP (error_symbol) ? Fcar (data) : error_symbol); |
| 1738 | Lisp_Object clause = Qnil; | 1781 | Lisp_Object clause = Qnil; |
| 1739 | struct handler *h; | 1782 | struct handler *h; |
| 1783 | int skip; | ||
| 1740 | 1784 | ||
| 1741 | if (gc_in_progress || waiting_for_input) | 1785 | if (gc_in_progress || waiting_for_input) |
| 1742 | emacs_abort (); | 1786 | emacs_abort (); |
| @@ -1759,6 +1803,7 @@ signal_or_quit (Lisp_Object error_symbol, Lisp_Object data, bool keyboard_quit) | |||
| 1759 | /* Edebug takes care of restoring these variables when it exits. */ | 1803 | /* Edebug takes care of restoring these variables when it exits. */ |
| 1760 | max_ensure_room (&max_lisp_eval_depth, lisp_eval_depth, 20); | 1804 | max_ensure_room (&max_lisp_eval_depth, lisp_eval_depth, 20); |
| 1761 | 1805 | ||
| 1806 | /* FIXME: 'handler-bind' makes `signal-hook-function' obsolete? */ | ||
| 1762 | call2 (Vsignal_hook_function, error_symbol, data); | 1807 | call2 (Vsignal_hook_function, error_symbol, data); |
| 1763 | } | 1808 | } |
| 1764 | 1809 | ||
| @@ -1778,16 +1823,42 @@ signal_or_quit (Lisp_Object error_symbol, Lisp_Object data, bool keyboard_quit) | |||
| 1778 | Vsignaling_function = backtrace_function (pdl); | 1823 | Vsignaling_function = backtrace_function (pdl); |
| 1779 | } | 1824 | } |
| 1780 | 1825 | ||
| 1781 | for (h = handlerlist; h; h = h->next) | 1826 | for (skip = 0, h = handlerlist; h; skip++, h = h->next) |
| 1782 | { | 1827 | { |
| 1783 | if (h->type == CATCHER_ALL) | 1828 | switch (h->type) |
| 1784 | { | 1829 | { |
| 1830 | case CATCHER_ALL: | ||
| 1785 | clause = Qt; | 1831 | clause = Qt; |
| 1786 | break; | 1832 | break; |
| 1787 | } | 1833 | case CATCHER: |
| 1788 | if (h->type != CONDITION_CASE) | 1834 | continue; |
| 1789 | continue; | 1835 | case CONDITION_CASE: |
| 1790 | clause = find_handler_clause (h->tag_or_ch, conditions); | 1836 | clause = find_handler_clause (h->tag_or_ch, conditions); |
| 1837 | break; | ||
| 1838 | case HANDLER_BIND: | ||
| 1839 | { | ||
| 1840 | if (!NILP (find_handler_clause (h->tag_or_ch, conditions))) | ||
| 1841 | { | ||
| 1842 | Lisp_Object error_data | ||
| 1843 | = (NILP (error_symbol) | ||
| 1844 | ? data : Fcons (error_symbol, data)); | ||
| 1845 | push_handler (make_fixnum (skip + h->bytecode_dest), | ||
| 1846 | SKIP_CONDITIONS); | ||
| 1847 | call1 (h->val, error_data); | ||
| 1848 | pop_handler (); | ||
| 1849 | } | ||
| 1850 | continue; | ||
| 1851 | } | ||
| 1852 | case SKIP_CONDITIONS: | ||
| 1853 | { | ||
| 1854 | int toskip = XFIXNUM (h->tag_or_ch); | ||
| 1855 | while (toskip-- >= 0) | ||
| 1856 | h = h->next; | ||
| 1857 | continue; | ||
| 1858 | } | ||
| 1859 | default: | ||
| 1860 | abort (); | ||
| 1861 | } | ||
| 1791 | if (!NILP (clause)) | 1862 | if (!NILP (clause)) |
| 1792 | break; | 1863 | break; |
| 1793 | } | 1864 | } |
| @@ -1804,7 +1875,7 @@ signal_or_quit (Lisp_Object error_symbol, Lisp_Object data, bool keyboard_quit) | |||
| 1804 | || (CONSP (clause) && !NILP (Fmemq (Qdebug, clause))) | 1875 | || (CONSP (clause) && !NILP (Fmemq (Qdebug, clause))) |
| 1805 | /* Special handler that means "print a message and run debugger | 1876 | /* Special handler that means "print a message and run debugger |
| 1806 | if requested". */ | 1877 | if requested". */ |
| 1807 | || EQ (h->tag_or_ch, Qerror))) | 1878 | || EQ (clause, Qerror))) |
| 1808 | { | 1879 | { |
| 1809 | debugger_called | 1880 | debugger_called |
| 1810 | = maybe_call_debugger (conditions, error_symbol, data); | 1881 | = maybe_call_debugger (conditions, error_symbol, data); |
| @@ -1818,8 +1889,9 @@ signal_or_quit (Lisp_Object error_symbol, Lisp_Object data, bool keyboard_quit) | |||
| 1818 | with debugging. Make sure to use `debug-early' unconditionally | 1889 | with debugging. Make sure to use `debug-early' unconditionally |
| 1819 | to not interfere with ERT or other packages that install custom | 1890 | to not interfere with ERT or other packages that install custom |
| 1820 | debuggers. */ | 1891 | debuggers. */ |
| 1892 | /* FIXME: This could be turned into a `handler-bind` at toplevel? */ | ||
| 1821 | if (!debugger_called && !NILP (error_symbol) | 1893 | if (!debugger_called && !NILP (error_symbol) |
| 1822 | && (NILP (clause) || EQ (h->tag_or_ch, Qerror)) | 1894 | && (NILP (clause) || EQ (clause, Qerror)) |
| 1823 | && noninteractive && backtrace_on_error_noninteractive | 1895 | && noninteractive && backtrace_on_error_noninteractive |
| 1824 | && NILP (Vinhibit_debugger) | 1896 | && NILP (Vinhibit_debugger) |
| 1825 | && !NILP (Ffboundp (Qdebug_early))) | 1897 | && !NILP (Ffboundp (Qdebug_early))) |
| @@ -1833,6 +1905,7 @@ signal_or_quit (Lisp_Object error_symbol, Lisp_Object data, bool keyboard_quit) | |||
| 1833 | 1905 | ||
| 1834 | /* If an error is signaled during a Lisp hook in redisplay, write a | 1906 | /* If an error is signaled during a Lisp hook in redisplay, write a |
| 1835 | backtrace into the buffer *Redisplay-trace*. */ | 1907 | backtrace into the buffer *Redisplay-trace*. */ |
| 1908 | /* FIXME: Turn this into a `handler-bind` installed during redisplay? */ | ||
| 1836 | if (!debugger_called && !NILP (error_symbol) | 1909 | if (!debugger_called && !NILP (error_symbol) |
| 1837 | && backtrace_on_redisplay_error | 1910 | && backtrace_on_redisplay_error |
| 1838 | && (NILP (clause) || h == redisplay_deep_handler) | 1911 | && (NILP (clause) || h == redisplay_deep_handler) |
| @@ -2058,13 +2131,10 @@ find_handler_clause (Lisp_Object handlers, Lisp_Object conditions) | |||
| 2058 | register Lisp_Object h; | 2131 | register Lisp_Object h; |
| 2059 | 2132 | ||
| 2060 | /* t is used by handlers for all conditions, set up by C code. */ | 2133 | /* t is used by handlers for all conditions, set up by C code. */ |
| 2061 | if (EQ (handlers, Qt)) | ||
| 2062 | return Qt; | ||
| 2063 | |||
| 2064 | /* error is used similarly, but means print an error message | 2134 | /* error is used similarly, but means print an error message |
| 2065 | and run the debugger if that is enabled. */ | 2135 | and run the debugger if that is enabled. */ |
| 2066 | if (EQ (handlers, Qerror)) | 2136 | if (!CONSP (handlers)) |
| 2067 | return Qt; | 2137 | return handlers; |
| 2068 | 2138 | ||
| 2069 | for (h = handlers; CONSP (h); h = XCDR (h)) | 2139 | for (h = handlers; CONSP (h); h = XCDR (h)) |
| 2070 | { | 2140 | { |
| @@ -4494,6 +4564,7 @@ alist of active lexical bindings. */); | |||
| 4494 | defsubr (&Sthrow); | 4564 | defsubr (&Sthrow); |
| 4495 | defsubr (&Sunwind_protect); | 4565 | defsubr (&Sunwind_protect); |
| 4496 | defsubr (&Scondition_case); | 4566 | defsubr (&Scondition_case); |
| 4567 | defsubr (&Shandler_bind_1); | ||
| 4497 | DEFSYM (QCsuccess, ":success"); | 4568 | DEFSYM (QCsuccess, ":success"); |
| 4498 | defsubr (&Ssignal); | 4569 | defsubr (&Ssignal); |
| 4499 | defsubr (&Scommandp); | 4570 | defsubr (&Scommandp); |