Skip to content

Commit 6edaaa0

Browse files
committed
FEAT: implemented try/all to catch also thrown exceptions
related to: Oldes/Rebol-issues#583 Oldes/Rebol-issues#1506
1 parent 6ed8da9 commit 6edaaa0

File tree

8 files changed

+105
-38
lines changed

8 files changed

+105
-38
lines changed

make/tools/make-headers.reb

+1
Original file line numberDiff line numberDiff line change
@@ -320,6 +320,7 @@ context [
320320
request-file
321321
request-dir
322322
catch
323+
try
323324
] [make-arg-enums word]
324325

325326
;?? output

src/boot/errors.reb

+1-1
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@ Throw: [
1919
type: "throw error"
2020
break: {no loop to break}
2121
return: {return or exit not in function}
22-
unnamed-throw: [{no catch for unnamed throw with value:} :arg1]
2322
throw: [{no catch for throw:} :arg2 "with value:" :arg1]
2423
continue: {no loop to continue}
2524
halt: [{halted by user or script}]
@@ -247,3 +246,4 @@ Internal: [
247246
not-done: {reserved for future use (or not yet implemented)}
248247
invalid-error: {error object or fields were not valid}
249248
]
249+

src/boot/natives.reb

+1
Original file line numberDiff line numberDiff line change
@@ -311,6 +311,7 @@ trace: native [
311311
try: native [
312312
{Tries to DO a block and returns its value or an error!.}
313313
block [block! paren!]
314+
/all "Catch also BREAK, CONTINUE, RETURN, EXIT and THROW exceptions."
314315
/with "On error, evaluate the handler and return its result"
315316
handler [block! any-function!]
316317
/except "** DEPRERCATED **"

src/core/c-error.c

+37
Original file line numberDiff line numberDiff line change
@@ -443,6 +443,9 @@ static REBOL_STATE Top_State; // Boot var: holds error state during boot
443443
// Make a copy of the error object template:
444444
err = CLONE_OBJECT(VAL_OBJ_FRAME(ROOT_ERROBJ));
445445
error = ERR_VALUES(err);
446+
447+
if (code >= THROWN_DISARM_OFFSET)
448+
code -= THROWN_DISARM_OFFSET;
446449

447450
// Set error number:
448451
SET_INTEGER(&error->code, (REBINT)code);
@@ -465,6 +468,40 @@ static REBOL_STATE Top_State; // Boot var: holds error state during boot
465468
}
466469

467470

471+
/***********************************************************************
472+
**
473+
*/ REBSER *Disarm_Throw_Error(REBVAL *err)
474+
/*
475+
** Creates real error object from an internal thrown one
476+
**
477+
***********************************************************************/
478+
{
479+
REBINT code;
480+
REBCNT sym;
481+
REBSER *obj;
482+
REBVAL *arg1 = NULL;
483+
REBVAL word = {0};
484+
485+
code = VAL_ERR_NUM(err);
486+
if (code > RE_THROW_MAX) return VAL_ERR_OBJECT(err);
487+
488+
sym = VAL_ERR_SYM(err);
489+
arg1 = VAL_ERR_VALUE(err);
490+
491+
if (sym) {
492+
Set_Word(&word, sym, 0, 0);
493+
VAL_SET(&word, REB_WORD);
494+
VAL_ERR_SYM(err) = 0;
495+
}
496+
497+
code += THROWN_DISARM_OFFSET;
498+
499+
obj = Make_Error(code, arg1, sym ? &word : 0, 0);
500+
VAL_ERR_OBJECT(err) = obj;
501+
VAL_ERR_NUM(err) = code;
502+
}
503+
504+
468505
/***********************************************************************
469506
**
470507
*/ void Trap0(REBCNT num)

src/core/n-control.c

+17-12
Original file line numberDiff line numberDiff line change
@@ -886,41 +886,46 @@ enum {
886886
/*
887887
***********************************************************************/
888888
{
889-
REBFLG with = D_REF(2);
889+
REBFLG with = D_REF(ARG_TRY_WITH);
890890
REBVAL handler;
891-
REBVAL *last_error = Get_System(SYS_STATE, STATE_LAST_ERROR);
892-
SET_NONE(last_error);
891+
REBVAL *error = Get_System(SYS_STATE, STATE_LAST_ERROR);
892+
SET_NONE(error); // reset the last error
893+
893894

894895
// If not used the new /with refine, try to use the deprecated /except
895896
if (with) {
896-
handler = *D_ARG(3);
897+
handler = *D_ARG(ARG_TRY_HANDLER);
897898
} else {
898-
with = D_REF(4);
899-
handler = *D_ARG(5);
899+
with = D_REF(ARG_TRY_EXCEPT);
900+
handler = *D_ARG(ARG_TRY_CODE);
900901
}
901902
// TRY exception will trim the stack
902-
if (Try_Block(VAL_SERIES(D_ARG(1)), VAL_INDEX(D_ARG(1)))) {
903+
if (Try_Block(VAL_SERIES(D_ARG(ARG_TRY_BLOCK)), VAL_INDEX(D_ARG(ARG_TRY_BLOCK)))) {
903904
// save the error as a system/state/last-error value
904-
*last_error = *DS_NEXT;
905+
on_error:
906+
*error = *DS_NEXT;
905907

906908
if (with) {
907909
if (IS_BLOCK(&handler)) {
908910
DO_BLK(&handler);
909911
}
910912
else { // do func[err] error
911-
REBVAL error = *DS_NEXT; // will get overwritten
912913
REBVAL *args = BLK_SKIP(VAL_FUNC_ARGS(&handler), 1);
913-
if (NOT_END(args) && !TYPE_CHECK(args, VAL_TYPE(&error))) {
914+
if (NOT_END(args) && !TYPE_CHECK(args, VAL_TYPE(error))) {
914915
// TODO: This results in an error message such as "action!
915916
// does not allow error! for its value1 argument". A better
916917
// message would be more like "except handler does not
917918
// allow error! for its value1 argument."
918-
Trap3(RE_EXPECT_ARG, Of_Type(&handler), args, Of_Type(&error));
919+
Trap3(RE_EXPECT_ARG, Of_Type(&handler), args, Of_Type(error));
919920
}
920-
Apply_Func(0, &handler, &error, 0);
921+
Apply_Func(0, &handler, error, 0);
921922
}
922923
}
923924
}
925+
else if (D_REF(ARG_TRY_ALL) && THROWN(DS_NEXT)) {
926+
Disarm_Throw_Error(DS_NEXT);
927+
goto on_error;
928+
}
924929

925930
return R_TOS1;
926931
}

src/core/s-mold.c

+5-25
Original file line numberDiff line numberDiff line change
@@ -1051,35 +1051,15 @@ STOID Mold_Error(REBVAL *value, REB_MOLD *mold, REBFLG molded)
10511051

10521052
// Protect against recursion. !!!!
10531053

1054+
if (VAL_ERR_NUM(value) < RE_THROW_MAX) {
1055+
Disarm_Throw_Error(value);
1056+
}
1057+
10541058
if (molded) {
1055-
if (VAL_OBJ_FRAME(value) && VAL_ERR_NUM(value) >= RE_NOTE && VAL_ERR_OBJECT(value))
1056-
Mold_Object(value, mold);
1057-
else {
1058-
// Happens if throw or return is molded.
1059-
// make error! 0-3
1060-
Pre_Mold(value, mold);
1061-
Append_Int(mold->series, VAL_ERR_NUM(value));
1062-
End_Mold(mold);
1063-
}
1059+
Mold_Object(value, mold);
10641060
return;
10651061
}
10661062

1067-
// If it is an unprocessed THROW:
1068-
if (VAL_ERR_NUM(value) == RE_THROW) {
1069-
sym = VAL_ERR_SYM(value);
1070-
if (!sym) {
1071-
VAL_ERR_OBJECT(value) = Make_Error(RE_UNNAMED_THROW, VAL_ERR_VALUE(value), 0, 0);
1072-
}
1073-
else {
1074-
Set_Word(&word, sym, 0, 0);
1075-
VAL_SET(&word, REB_WORD);
1076-
VAL_ERR_OBJECT(value) = Make_Error(VAL_ERR_NUM(value), VAL_ERR_VALUE(value), &word, 0);
1077-
}
1078-
}
1079-
// If it is an unprocessed BREAK, CONTINUE, RETURN:
1080-
else if (VAL_ERR_NUM(value) < RE_NOTE || !VAL_ERR_OBJECT(value)) {
1081-
VAL_ERR_OBJECT(value) = Make_Error(VAL_ERR_NUM(value), VAL_ERR_VALUE(value), 0, 0); // spoofs field
1082-
}
10831063
err = VAL_ERR_VALUES(value);
10841064

10851065
// Form: ** <type> Error:

src/include/sys-value.h

+2
Original file line numberDiff line numberDiff line change
@@ -952,6 +952,8 @@ typedef struct Reb_Error {
952952
#define IS_CONTINUE(v) (VAL_ERR_NUM(v) == RE_CONTINUE)
953953
#define THROWN(v) (IS_ERROR(v) && IS_THROW(v))
954954

955+
#define THROWN_DISARM_OFFSET 100000 // used to disarm thrown errors (converted to complete error object)
956+
955957
#define SET_ERROR(v,n,a) VAL_SET(v, REB_ERROR), VAL_ERR_NUM(v)=n, VAL_ERR_OBJECT(v)=a, VAL_ERR_SYM(v)=0
956958
#define SET_THROW(v,n,a) VAL_SET(v, REB_ERROR), VAL_ERR_NUM(v)=n, VAL_ERR_VALUE(v)=a, VAL_ERR_SYM(v)=0
957959

src/tests/units/evaluation-test.r3

+41
Original file line numberDiff line numberDiff line change
@@ -728,6 +728,47 @@ Rebol [
728728
--assert string? try/with [1 / 0] :mold
729729
--assert system/state/last-error/id = 'zero-divide
730730

731+
--test-- "try/all"
732+
;@@ https://github.com/Oldes/Rebol-issues/issues/1506
733+
--assert error? try/all [break]
734+
--assert error? try/all [continue]
735+
--assert error? try/all [exit]
736+
--assert error? try/all [return 1]
737+
--assert error? try/all [throw 1]
738+
739+
--test-- "try/all/with block handler"
740+
handler: [system/state/last-error/id]
741+
--assert 'break = try/all/with [break ] :handler
742+
--assert 'continue = try/all/with [continue] :handler
743+
--assert 'return = try/all/with [exit ] :handler
744+
--assert 'return = try/all/with [return 1] :handler
745+
--assert 'throw = try/all/with [throw 1 ] :handler
746+
747+
--test-- "try/all/with function handler"
748+
--assert 'break = try/all/with [break] func[e][e/id]
749+
750+
--assert all [
751+
string? try/all/with [break] :mold
752+
system/state/last-error/id = 'break
753+
system/state/last-error/arg1 = none
754+
]
755+
--assert all [
756+
string? try/all/with [exit] :mold
757+
system/state/last-error/id = 'return
758+
system/state/last-error/arg1 = none
759+
]
760+
--assert all [
761+
string? try/all/with [throw 1] :mold
762+
system/state/last-error/id = 'throw
763+
system/state/last-error/arg1 = 1
764+
]
765+
--assert all [
766+
string? try/all/with [throw/name 1 'foo] :mold
767+
system/state/last-error/id = 'throw
768+
system/state/last-error/arg1 = 1
769+
system/state/last-error/arg2 = 'foo
770+
]
771+
731772
===end-group===
732773

733774

0 commit comments

Comments
 (0)