aboutsummaryrefslogtreecommitdiffstats
path: root/src/eval.c
diff options
context:
space:
mode:
authorStefan Monnier2023-12-25 22:32:17 -0500
committerStefan Monnier2024-01-04 16:32:53 -0500
commit5ba75e183c60aff50949587c21066e876dabfbda (patch)
treedb29f1ea2ef53d51ebb7ed1b8be999a664498f89 /src/eval.c
parent225710ba79c10b53b6ba320327ca31192ca72387 (diff)
downloademacs-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.c97
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
1201static void
1202pop_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
1370DEFUN ("handler-bind-1", Fhandler_bind_1, Shandler_bind_1, 1, MANY, 0,
1371 doc: /* Setup error handlers around execution of BODYFUN.
1372BODYFUN be a function and it is called with no arguments.
1373CONDITIONS should be a list of condition names (symbols).
1374When an error is signaled during executon of BODYFUN, if that
1375error matches one of CONDITIONS, then the associated HANDLER is
1376called with the error as argument.
1377HANDLER should either transfer the control via a non-local exit,
1378or return normally.
1379If it returns normally, the search for an error handler continues
1380from where it left off.
1381
1382usage: (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);