*** empty log message ***
[bpt/guile.git] / libguile / dynwind.c
CommitLineData
4845bbae 1/* Copyright (C) 1995,1996,1998,1999,2000,2001, 2003, 2004 Free Software Foundation, Inc.
0f2d19dd 2 *
73be1d9e
MV
3 * This library is free software; you can redistribute it and/or
4 * modify it under the terms of the GNU Lesser General Public
5 * License as published by the Free Software Foundation; either
6 * version 2.1 of the License, or (at your option) any later version.
0f2d19dd 7 *
73be1d9e
MV
8 * This library is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11 * Lesser General Public License for more details.
0f2d19dd 12 *
73be1d9e
MV
13 * You should have received a copy of the GNU Lesser General Public
14 * License along with this library; if not, write to the Free Software
92205699 15 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
73be1d9e 16 */
1bbd0b84 17
1bbd0b84 18
0f2d19dd
JB
19\f
20
4845bbae
MV
21#include <assert.h>
22
a0599745
MD
23#include "libguile/_scm.h"
24#include "libguile/eval.h"
25#include "libguile/alist.h"
26#include "libguile/fluids.h"
27#include "libguile/ports.h"
28#include "libguile/smob.h"
0f2d19dd 29
a0599745 30#include "libguile/dynwind.h"
0f2d19dd
JB
31\f
32
33/* {Dynamic wind}
b3460a50
MV
34
35 Things that can be on the wind list:
36
4845bbae
MV
37 #<frame>
38 #<winder>
b3460a50
MV
39 (enter-proc . leave-proc) dynamic-wind
40 (tag . jmpbuf) catch
41 (tag . lazy-catch) lazy-catch
42 tag is either a symbol or a boolean
43
b3460a50 44*/
0f2d19dd
JB
45
46
47
3b3b36dd 48SCM_DEFINE (scm_dynamic_wind, "dynamic-wind", 3, 0, 0,
1e6808ea
MG
49 (SCM in_guard, SCM thunk, SCM out_guard),
50 "All three arguments must be 0-argument procedures.\n"
51 "@var{in_guard} is called, then @var{thunk}, then\n"
52 "@var{out_guard}.\n"
53 "\n"
54 "If, any time during the execution of @var{thunk}, the\n"
55 "continuation of the @code{dynamic_wind} expression is escaped\n"
56 "non-locally, @var{out_guard} is called. If the continuation of\n"
57 "the dynamic-wind is re-entered, @var{in_guard} is called. Thus\n"
58 "@var{in_guard} and @var{out_guard} may be called any number of\n"
59 "times.\n"
60 "@lisp\n"
b380b885 61 "(define x 'normal-binding)\n"
1e6808ea 62 "@result{} x\n"
9401323e 63 "(define a-cont (call-with-current-continuation\n"
b380b885
MD
64 " (lambda (escape)\n"
65 " (let ((old-x x))\n"
66 " (dynamic-wind\n"
67 " ;; in-guard:\n"
68 " ;;\n"
1e6808ea
MG
69 " (lambda () (set! x 'special-binding))\n"
70 "\n"
b380b885
MD
71 " ;; thunk\n"
72 " ;;\n"
73 " (lambda () (display x) (newline)\n"
74 " (call-with-current-continuation escape)\n"
75 " (display x) (newline)\n"
1e6808ea
MG
76 " x)\n"
77 "\n"
b380b885
MD
78 " ;; out-guard:\n"
79 " ;;\n"
1e6808ea
MG
80 " (lambda () (set! x old-x)))))))\n"
81 "\n"
9401323e 82 ";; Prints:\n"
b380b885
MD
83 "special-binding\n"
84 ";; Evaluates to:\n"
1e6808ea 85 "@result{} a-cont\n"
b380b885 86 "x\n"
1e6808ea 87 "@result{} normal-binding\n"
b380b885
MD
88 "(a-cont #f)\n"
89 ";; Prints:\n"
90 "special-binding\n"
91 ";; Evaluates to:\n"
1e6808ea 92 "@result{} a-cont ;; the value of the (define a-cont...)\n"
b380b885 93 "x\n"
1e6808ea 94 "@result{} normal-binding\n"
b380b885
MD
95 "a-cont\n"
96 "@result{} special-binding\n"
1e6808ea 97 "@end lisp")
1bbd0b84 98#define FUNC_NAME s_scm_dynamic_wind
0f2d19dd 99{
9de87eea 100 SCM ans, old_winds;
7888309b 101 SCM_ASSERT (scm_is_true (scm_thunk_p (out_guard)),
1e6808ea 102 out_guard,
1bbd0b84 103 SCM_ARG3, FUNC_NAME);
fdc28395 104 scm_call_0 (in_guard);
9de87eea
MV
105 old_winds = scm_i_dynwinds ();
106 scm_i_set_dynwinds (scm_acons (in_guard, out_guard, old_winds));
fdc28395 107 ans = scm_call_0 (thunk);
9de87eea 108 scm_i_set_dynwinds (old_winds);
fdc28395 109 scm_call_0 (out_guard);
0f2d19dd
JB
110 return ans;
111}
1bbd0b84 112#undef FUNC_NAME
0f2d19dd 113
3346a90f 114SCM
92c2555f
MV
115scm_internal_dynamic_wind (scm_t_guard before,
116 scm_t_inner inner,
117 scm_t_guard after,
3346a90f
MD
118 void *inner_data,
119 void *guard_data)
120{
4845bbae
MV
121 SCM ans;
122
0888de4f 123 scm_frame_begin (SCM_F_FRAME_REWINDABLE);
16c5cac2
MV
124 scm_frame_rewind_handler (before, guard_data, SCM_F_WIND_EXPLICITLY);
125 scm_frame_unwind_handler (after, guard_data, SCM_F_WIND_EXPLICITLY);
3346a90f 126 ans = inner (inner_data);
0888de4f 127 scm_frame_end ();
3346a90f
MD
128 return ans;
129}
1cc91f1b 130
4845bbae
MV
131/* Frames and winders. */
132
133static scm_t_bits tc16_frame;
134#define FRAME_P(f) SCM_SMOB_PREDICATE (tc16_frame, (f))
135
f5710d53
MV
136#define FRAME_F_REWINDABLE (1 << 0)
137#define FRAME_REWINDABLE_P(f) (SCM_SMOB_FLAGS(f) & FRAME_F_REWINDABLE)
4845bbae
MV
138
139static scm_t_bits tc16_winder;
14578fa4 140#define WINDER_P(w) SCM_SMOB_PREDICATE (tc16_winder, (w))
f5710d53
MV
141#define WINDER_PROC(w) ((void (*)(void *))SCM_SMOB_DATA (w))
142#define WINDER_DATA(w) ((void *)SCM_SMOB_DATA_2 (w))
4845bbae 143
f5710d53
MV
144#define WINDER_F_EXPLICIT (1 << 0)
145#define WINDER_F_REWIND (1 << 1)
146#define WINDER_F_MARK (1 << 2)
147#define WINDER_EXPLICIT_P(w) (SCM_SMOB_FLAGS(w) & WINDER_F_EXPLICIT)
148#define WINDER_REWIND_P(w) (SCM_SMOB_FLAGS(w) & WINDER_F_REWIND)
149#define WINDER_MARK_P(w) (SCM_SMOB_FLAGS(w) & WINDER_F_MARK)
4845bbae
MV
150
151void
0888de4f 152scm_frame_begin (scm_t_frame_flags flags)
4845bbae
MV
153{
154 SCM f;
f5710d53
MV
155 SCM_NEWSMOB (f, tc16_frame, 0);
156 if (flags & SCM_F_FRAME_REWINDABLE)
157 SCM_SET_SMOB_FLAGS (f, FRAME_F_REWINDABLE);
9de87eea 158 scm_i_set_dynwinds (scm_cons (f, scm_i_dynwinds ()));
4845bbae
MV
159}
160
161void
0888de4f 162scm_frame_end (void)
4845bbae 163{
9de87eea
MV
164 SCM winds;
165
0888de4f
MV
166 /* Unwind upto and including the next frame entry. We can only
167 encounter #<winder> entries on the way.
4845bbae
MV
168 */
169
9de87eea
MV
170 winds = scm_i_dynwinds ();
171 while (scm_is_pair (winds))
4845bbae 172 {
9de87eea
MV
173 SCM entry = SCM_CAR (winds);
174 winds = SCM_CDR (winds);
175
176 scm_i_set_dynwinds (winds);
0888de4f
MV
177
178 if (FRAME_P (entry))
179 return;
180
181 assert (WINDER_P (entry));
182 if (!WINDER_REWIND_P (entry) && WINDER_EXPLICIT_P (entry))
183 WINDER_PROC(entry) (WINDER_DATA (entry));
4845bbae
MV
184 }
185
186 assert (0);
187}
188
a520e4f0
MV
189static SCM
190winder_mark (SCM w)
191{
192 if (WINDER_MARK_P (w))
78addfa3 193 return SCM_PACK (WINDER_DATA (w));
a520e4f0
MV
194 return SCM_BOOL_F;
195}
196
4845bbae 197void
16c5cac2
MV
198scm_frame_unwind_handler (void (*proc) (void *), void *data,
199 scm_t_wind_flags flags)
4845bbae
MV
200{
201 SCM w;
f5710d53
MV
202 SCM_NEWSMOB2 (w, tc16_winder, (scm_t_bits) proc, (scm_t_bits) data);
203 if (flags & SCM_F_WIND_EXPLICITLY)
204 SCM_SET_SMOB_FLAGS (w, WINDER_F_EXPLICIT);
9de87eea 205 scm_i_set_dynwinds (scm_cons (w, scm_i_dynwinds ()));
4845bbae
MV
206}
207
208void
16c5cac2
MV
209scm_frame_rewind_handler (void (*proc) (void *), void *data,
210 scm_t_wind_flags flags)
4845bbae
MV
211{
212 SCM w;
f5710d53
MV
213 SCM_NEWSMOB2 (w, tc16_winder, (scm_t_bits) proc, (scm_t_bits) data);
214 SCM_SET_SMOB_FLAGS (w, WINDER_F_REWIND);
9de87eea 215 scm_i_set_dynwinds (scm_cons (w, scm_i_dynwinds ()));
a520e4f0
MV
216 if (flags & SCM_F_WIND_EXPLICITLY)
217 proc (data);
218}
219
220void
16c5cac2
MV
221scm_frame_unwind_handler_with_scm (void (*proc) (SCM), SCM data,
222 scm_t_wind_flags flags)
a520e4f0
MV
223{
224 SCM w;
225 scm_t_bits fl = ((flags&SCM_F_WIND_EXPLICITLY)? WINDER_F_EXPLICIT : 0);
f5710d53
MV
226 SCM_NEWSMOB2 (w, tc16_winder, (scm_t_bits) proc, SCM_UNPACK (data));
227 SCM_SET_SMOB_FLAGS (w, fl | WINDER_F_MARK);
9de87eea 228 scm_i_set_dynwinds (scm_cons (w, scm_i_dynwinds ()));
a520e4f0
MV
229}
230
231void
16c5cac2
MV
232scm_frame_rewind_handler_with_scm (void (*proc) (SCM), SCM data,
233 scm_t_wind_flags flags)
a520e4f0
MV
234{
235 SCM w;
f5710d53
MV
236 SCM_NEWSMOB2 (w, tc16_winder, (scm_t_bits) proc, SCM_UNPACK (data));
237 SCM_SET_SMOB_FLAGS (w, WINDER_F_REWIND | WINDER_F_MARK);
9de87eea 238 scm_i_set_dynwinds (scm_cons (w, scm_i_dynwinds ()));
a520e4f0 239 if (flags & SCM_F_WIND_EXPLICITLY)
4845bbae
MV
240 proc (data);
241}
242
6d5649b7
MV
243void
244scm_frame_free (void *mem)
245{
246 scm_frame_unwind_handler (free, mem, SCM_F_WIND_EXPLICITLY);
247}
248
c2654ef0 249#ifdef GUILE_DEBUG
a1ec6916 250SCM_DEFINE (scm_wind_chain, "wind-chain", 0, 0, 0,
1bbd0b84 251 (),
156149ad
MG
252 "Return the current wind chain. The wind chain contains all\n"
253 "information required by @code{dynamic-wind} to call its\n"
254 "argument thunks when entering/exiting its scope.")
1bbd0b84 255#define FUNC_NAME s_scm_wind_chain
c2654ef0 256{
9de87eea 257 return scm_i_dynwinds ();
c2654ef0 258}
1bbd0b84 259#undef FUNC_NAME
c2654ef0
MD
260#endif
261
2e171178 262void
904a077d 263scm_swap_bindings (SCM vars, SCM vals)
6778caf9
MD
264{
265 SCM tmp;
266 while (SCM_NIMP (vals))
267 {
904a077d
MV
268 tmp = SCM_VARIABLE_REF (SCM_CAR (vars));
269 SCM_VARIABLE_SET (SCM_CAR (vars), SCM_CAR (vals));
6778caf9 270 SCM_SETCAR (vals, tmp);
904a077d 271 vars = SCM_CDR (vars);
6778caf9
MD
272 vals = SCM_CDR (vals);
273 }
274}
c2654ef0 275
4845bbae 276void
c014a02e 277scm_dowinds (SCM to, long delta)
4845bbae 278{
14578fa4 279 scm_i_dowinds (to, delta, NULL, NULL);
4845bbae
MV
280}
281
282void
14578fa4 283scm_i_dowinds (SCM to, long delta, void (*turn_func) (void *), void *data)
0f2d19dd
JB
284{
285 tail:
9de87eea 286 if (scm_is_eq (to, scm_i_dynwinds ()))
4845bbae
MV
287 {
288 if (turn_func)
289 turn_func (data);
290 }
1be6b49c 291 else if (delta < 0)
0f2d19dd
JB
292 {
293 SCM wind_elt;
294 SCM wind_key;
295
14578fa4 296 scm_i_dowinds (SCM_CDR (to), 1 + delta, turn_func, data);
0f2d19dd 297 wind_elt = SCM_CAR (to);
4845bbae 298
928e0f42 299 if (FRAME_P (wind_elt))
0f2d19dd 300 {
928e0f42
MV
301 if (!FRAME_REWINDABLE_P (wind_elt))
302 scm_misc_error ("dowinds",
303 "cannot invoke continuation from this context",
304 SCM_EOL);
305 }
306 else if (WINDER_P (wind_elt))
307 {
308 if (WINDER_REWIND_P (wind_elt))
309 WINDER_PROC (wind_elt) (WINDER_DATA (wind_elt));
0f2d19dd
JB
310 }
311 else
0f2d19dd 312 {
928e0f42
MV
313 wind_key = SCM_CAR (wind_elt);
314 /* key = #t | symbol | thunk | list of variables */
315 if (SCM_NIMP (wind_key))
4845bbae 316 {
d2e53ed6 317 if (scm_is_pair (wind_key))
4845bbae 318 {
928e0f42
MV
319 if (SCM_VARIABLEP (SCM_CAR (wind_key)))
320 scm_swap_bindings (wind_key, SCM_CDR (wind_elt));
904a077d 321 }
928e0f42
MV
322 else if (SCM_TYP3 (wind_key) == scm_tc3_closure)
323 scm_call_0 (wind_key);
b3460a50 324 }
0f2d19dd 325 }
928e0f42 326
9de87eea 327 scm_i_set_dynwinds (to);
0f2d19dd
JB
328 }
329 else
330 {
9de87eea 331 SCM wind;
0f2d19dd
JB
332 SCM wind_elt;
333 SCM wind_key;
334
9de87eea
MV
335 wind = scm_i_dynwinds ();
336 wind_elt = SCM_CAR (wind);
337 scm_i_set_dynwinds (SCM_CDR (wind));
4845bbae 338
928e0f42 339 if (FRAME_P (wind_elt))
0f2d19dd 340 {
928e0f42
MV
341 /* Nothing to do. */
342 }
343 else if (WINDER_P (wind_elt))
344 {
345 if (!WINDER_REWIND_P (wind_elt))
346 WINDER_PROC (wind_elt) (WINDER_DATA (wind_elt));
0f2d19dd
JB
347 }
348 else
0f2d19dd 349 {
928e0f42
MV
350 wind_key = SCM_CAR (wind_elt);
351 if (SCM_NIMP (wind_key))
4845bbae 352 {
d2e53ed6 353 if (scm_is_pair (wind_key))
4845bbae 354 {
928e0f42
MV
355 if (SCM_VARIABLEP (SCM_CAR (wind_key)))
356 scm_swap_bindings (wind_key, SCM_CDR (wind_elt));
904a077d 357 }
928e0f42
MV
358 else if (SCM_TYP3 (wind_key) == scm_tc3_closure)
359 scm_call_0 (SCM_CDR (wind_elt));
b3460a50 360 }
0f2d19dd 361 }
928e0f42 362
0f2d19dd
JB
363 delta--;
364 goto tail; /* scm_dowinds(to, delta-1); */
365 }
366}
367
0f2d19dd
JB
368void
369scm_init_dynwind ()
0f2d19dd 370{
4845bbae 371 tc16_frame = scm_make_smob_type ("frame", 0);
4845bbae
MV
372
373 tc16_winder = scm_make_smob_type ("winder", 0);
a520e4f0 374 scm_set_smob_mark (tc16_winder, winder_mark);
4845bbae 375
a0599745 376#include "libguile/dynwind.x"
0f2d19dd 377}
89e00824
ML
378
379/*
380 Local Variables:
381 c-file-style: "gnu"
382 End:
383*/