Skip to content

Commit 059643d

Browse files
committed
FEAT: allowed function for a recovery of a caught throw
related to: Oldes/Rebol-issues#1521
1 parent 8cdcfcc commit 059643d

File tree

3 files changed

+86
-24
lines changed

3 files changed

+86
-24
lines changed

src/boot/natives.reb

+1-1
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ catch: native [
7777
word [word! block!] {One or more names}
7878
/all {Catches all throws, named and unnamed}
7979
/quit {Special catch for QUIT native}
80-
/recover code [block!] "Code to be evaluated on a catch"
80+
/recover code [block! function!] "Code to be evaluated on a catch"
8181
]
8282

8383
;cause: native [

src/core/n-control.c

+32-17
Original file line numberDiff line numberDiff line change
@@ -423,19 +423,14 @@ enum {
423423
if (quit) {
424424
// We are here because of a QUIT or HALT condition.
425425
if (VAL_ERR_NUM(ret) == RE_QUIT)
426-
ret = VAL_ERR_VALUE(ret);
426+
*DS_RETURN = *(VAL_ERR_VALUE(ret));
427427
else if (VAL_ERR_NUM(ret) == RE_HALT)
428-
VAL_SET(ret, REB_UNSET);
428+
VAL_SET(DS_RETURN, REB_UNSET);
429429
//Halt_Code(RE_HALT, 0); // Don't use this if we want to be able catch all!
430430
else
431431
Crash(RP_NO_CATCH);
432432

433-
if (IS_BLOCK(&recover)) {
434-
DO_BLK(&recover);
435-
}
436-
437-
*DS_RETURN = *ret;
438-
return R_RET;
433+
goto recover;
439434
}
440435
if (!D_REF(ARG_CATCH_NAME)) return R_TOS1;
441436
} else {
@@ -471,19 +466,39 @@ enum {
471466
return R_RET;
472467
}
473468
caught: // Thrown is being caught.
474-
// Store thrown value as the last result.
475-
*ds = *(VAL_ERR_VALUE(ret));
476-
*last_result = *ds;
469+
// Store the thrown value as the return value...
470+
*DS_RETURN = *(VAL_ERR_VALUE(ret));
471+
recover: // ...and the last result.
472+
*last_result = *DS_RETURN;
477473
// If there is a recovery code, then evaluate it.
478-
if (IS_BLOCK(&recover)) {
479-
DS_NEXT;
480-
DO_BLK(&recover);
481-
DS_POP;
474+
if (IS_FUNCTION(&recover)) {
475+
// catch [throw 1] func[value name][value]
476+
// Return result of the recovery function
477+
REBVAL name = *DS_NEXT;
478+
if(sym) {
479+
Set_Word(&name, sym, 0, 0);
480+
VAL_SET(&name, REB_WORD);
481+
} else {
482+
SET_NONE(&name);
483+
}
484+
Apply_Func(0, &recover, last_result, &name, 0);
482485
}
483-
return R_RET;
486+
else if (IS_BLOCK(&recover)) {
487+
// (catch/recover [throw 1][2]) == 2
488+
// Return result of the recovery block evaluation.
489+
*last_result = *DO_BLK(&recover);
490+
}
491+
else {
492+
// (catch [throw 1]) == 1
493+
// Return the thrown value.
494+
return R_RET;
495+
}
496+
// Return the result of the recovery code evaluation.
497+
return R_TOS1;
484498
}
485499
}
486-
// No throw, or a throw with unhandled name... return just result of the block evaluation
500+
// No throw (return just the result of the block evaluation),
501+
// or an unhandled throw (return the thrown error value, so it may be catched later)
487502
return R_TOS1;
488503
}
489504

src/tests/units/evaluation-test.r3

+53-6
Original file line numberDiff line numberDiff line change
@@ -763,19 +763,66 @@ Rebol [
763763
--assert unset? catch/quit [++ a a: a + catch [++ a quit a: 0] a: a * 2 throw 100 a: a * 100]
764764
--assert a == 2
765765

766-
--test-- "catch/recover"
766+
--test-- "catch/recover block!"
767767
;@@ https://github.com/Oldes/Rebol-issues/issues/1521
768-
--assert unset? catch/quit/recover [a: 1 quit a: 2][a: a * 10]
769-
--assert a = 10
768+
--assert 10 = catch/quit/recover [a: 1 quit a: 2][a: a * 10]
769+
--assert a = 10
770+
771+
--assert 40 = catch/quit/recover [a: 1 quit/return 4 a: 2][system/state/last-result * 10]
772+
--assert a = 1
770773

771774
--assert 2 = catch/quit/recover [a: 2][a: a * 10]
772775
--assert a = 2
773776

774-
--assert 'x = catch/recover [a: 1 throw 'x a: 2][a: a * 10]
777+
--assert 10 = catch/recover [a: 1 throw 'x a: 2][a: a * 10]
775778
--assert a = 10
776779

777-
--assert 3 = catch/recover [a: 1 throw 3 a: 2][a: system/state/last-result a: a * 10]
778-
--assert a = 30
780+
--assert 30 = catch/recover [a: 1 throw 3 a: 2][system/state/last-result * 10]
781+
--assert a = 1
782+
783+
--assert all [
784+
2 == catch/recover [throw 1][2 * system/state/last-result]
785+
2 == system/state/last-result
786+
]
787+
--test-- "catch/recover function!"
788+
;@@ https://github.com/Oldes/Rebol-issues/issues/1521
789+
on-catch: func[value name][
790+
if :name = 'foo [return join "b" :value]
791+
if unset? :value [return true]
792+
if integer? :value [return value * 10]
793+
mold value
794+
]
795+
796+
--assert all [
797+
#[true] == catch/quit/recover [a: 1 quit a: 2] :on-catch
798+
a = 1
799+
]
800+
801+
--assert all [
802+
40 = catch/quit/recover [a: 1 quit/return 4 a: 2] :on-catch
803+
a = 1
804+
]
805+
806+
--assert all [
807+
2 = catch/quit/recover [a: 2] :on-catch
808+
a = 2
809+
]
810+
811+
--assert all [
812+
"x" = catch/recover [a: 1 throw 'x a: 2] :on-catch
813+
'x = system/state/last-result ;; or "x" ???
814+
a = 1
815+
]
816+
817+
--assert all [
818+
30 = catch/recover [a: 1 throw 3 a: 2] :on-catch
819+
a = 1
820+
]
821+
822+
--assert all [
823+
"b3" = catch/all/recover [a: 1 throw/name 3 'foo a: 2] :on-catch
824+
a = 1
825+
]
779826

780827
--test-- "catch/quit/name"
781828
;@@ https://github.com/Oldes/Rebol-issues/issues/2549

0 commit comments

Comments
 (0)