Skip to content

Commit

Permalink
FEAT: implemented catch/all which may be used to catch all throws (…
Browse files Browse the repository at this point in the history
…named and unnamed)

resolves: Oldes/Rebol-issues#1518
resolves: Oldes/Rebol-issues#1520
  • Loading branch information
Oldes committed Jun 20, 2023
1 parent 7e25856 commit e3c2247
Show file tree
Hide file tree
Showing 3 changed files with 35 additions and 11 deletions.
1 change: 1 addition & 0 deletions src/boot/natives.reb
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ catch: native [
block [block!] {Block to evaluate}
/name {Catches a named throw}
word [word! block!] {One or more names}
/all {Catches all throws, named and unnamed}
/quit {Special catch for QUIT native}
/recover code [block!] "Code to be evaluated on a catch"
]
Expand Down
31 changes: 20 additions & 11 deletions src/core/n-control.c
Original file line number Diff line number Diff line change
Expand Up @@ -413,11 +413,11 @@ enum {
REBVAL *val;
REBVAL *ret;
REBCNT sym;
REBVAL recover = *D_ARG(ARG_CATCH_RECOVER);
REBVAL recover = *D_ARG(ARG_CATCH_CODE);
REBVAL *last_result = Get_System(SYS_STATE, STATE_LAST_RESULT);
REBOOL quit;

if (D_REF(ARG_CATCH_QUIT)) { //QUIT
if (D_REF(ARG_CATCH_QUIT)) {
quit = Try_Block_Halt(VAL_SERIES(D_ARG(ARG_CATCH_BLOCK)), VAL_INDEX(D_ARG(ARG_CATCH_BLOCK)));
ret = DS_NEXT;
if (quit) {
Expand Down Expand Up @@ -445,32 +445,41 @@ enum {

// If it is a throw, process it:
if (IS_ERROR(ret) && VAL_ERR_NUM(ret) == RE_THROW) {
// Get optional thrown name
sym = VAL_ERR_SYM(ret);

// If a named throw, then check it:
if (D_REF(ARG_CATCH_NAME)) { // /name
if (D_REF(ARG_CATCH_ALL)) goto caught;

sym = VAL_ERR_SYM(ret);
val = D_ARG(ARG_CATCH_WORD); // name symbol
// If a named catch, then check it:
if (D_REF(ARG_CATCH_NAME)) {
val = D_ARG(ARG_CATCH_WORD); // catch/name value

// If name is the same word:
if (IS_WORD(val) && sym == VAL_WORD_CANON(val)) goto got_err;
if (IS_WORD(val) && sym == VAL_WORD_CANON(val)) goto caught;

// If it is a block of words:
else if (IS_BLOCK(val)) {
for (val = VAL_BLK_DATA(val); NOT_END(val); val++) {
if (IS_WORD(val) && sym == VAL_WORD_CANON(val)) goto got_err;
if (IS_WORD(val) && sym == VAL_WORD_CANON(val)) goto caught;
}
}
//else if (IS_LOGIC(val) && VAL_LOGIC(val)) goto caught; // used CATCH/name [] true
} else {
got_err:
// Used catch without name. If there was thrown a name, then let it pass thru.
if (sym != 0) {
*DS_RETURN = *ret;
return R_RET;
}
caught: // Thrown is being caught.
// Store thrown value as the last result.
*ds = *(VAL_ERR_VALUE(ret));
*last_result = *ds;
// If there is a recovery code, then evaluate it.
if (IS_BLOCK(&recover)) {
DS_NEXT;
DO_BLK(&recover);
DS_POP;
}

}
return R_RET;
}
}
Expand Down
14 changes: 14 additions & 0 deletions src/tests/units/evaluation-test.r3
Original file line number Diff line number Diff line change
Expand Up @@ -784,6 +784,20 @@ Rebol [
a = 1
]

--test-- "nested catch"
;@@ https://github.com/Oldes/Rebol-issues/issues/1518
--assert 1 = catch/name [a: 0 catch [++ a throw/name 1 'foo] a: a * 10] 'foo
--assert 1 = a
--assert 1 = catch/name [a: 0 catch/name [++ a throw/name 1 'foo] 'bar a: a * 10] 'foo
--assert 1 = a
--test-- "catch/all"
;@@ https://github.com/Oldes/Rebol-issues/issues/1520
--assert 1 = catch/all [a: 0 throw/name 1 'foo a: a * 10]
--assert 0 = a
--assert 1 = catch/all [a: 0 catch [++ a throw/name 1 'foo] a: a * 10]
--assert 1 = a
--assert 1 = catch/all [a: 0 catch/name [++ a throw/name 1 'foo] 'bar a: a * 10]
--assert 1 = a

===end-group===

Expand Down

0 comments on commit e3c2247

Please sign in to comment.