Add catcherr() built-in function.

This commit is contained in:
Dianne Skoll
2025-05-10 09:33:24 -04:00
parent 76bf57af60
commit 3fb1db0880
7 changed files with 78 additions and 3 deletions

View File

@@ -164,7 +164,7 @@
(defconst remind-builtin-functions
(sort
(list "_" "abs" "access" "adawn" "adusk" "ampm" "ansicolor" "args" "asc"
"baseyr" "catch" "char" "choose" "coerce" "columns" "current" "date"
"baseyr" "catch" "catcherr" "char" "choose" "coerce" "columns" "current" "date"
"datepart" "datetime" "dawn" "day" "daysinmon" "defined" "dosubst"
"dusk" "easterdate" "escape" "evaltrig" "filedate" "filedatetime"
"filedir" "filename" "getenv" "hebdate" "hebday" "hebmon" "hebyear"

View File

@@ -3288,6 +3288,29 @@ Note in the last example that catch does \fInot\fR protect you from errors
in the evaluation of \fBarg2\fR.
.RE
.TP
.B catcherr()
Returns a string representing the error message from the most
recently-evaluated \fBcatch()\fR function whose first argument yielded
an error. Note that the error message is always in English even if
\fBRemind\fR has been localized; this lets you reliably test the return
value. Here are some examples:
.RS
.PP
.nf
# No "catch" invocations yet: sets a to "Ok"
set a catcherr()
# Sets b to "oops" and a to "Division by zero"
set b catch(1/0, "oops")
set a catcherr()
# Sets b to 1 and a to "Division by zero". The catch error
# is never cleared by a non-error catch()
set b catch(4-3, "not-evaluated")
set a catcherr()
.fi
.RE
.TP
.B char(i_i1 [,i_i2...])
This function can take any number of \fBINT\fR arguments. It returns
a \fBSTRING\fR consisting of the bytes specified by the arguments.

View File

@@ -64,6 +64,9 @@
#define PUT(x) DBufPuts(&DebugBuf, x)
#define OUT() do { fprintf(ErrFp, "%s\n", DBufValue(&DebugBuf)); DBufFree(&DebugBuf); } while(0)
/* Last error from catch() */
static int LastCatchError = OK;
static int
solstice_equinox_for_year(int y, int which);
@@ -79,6 +82,7 @@ static int FArgs (func_info *);
static int FAsc (func_info *);
static int FBaseyr (func_info *);
static int FCatch (expr_node *, Value *, Value *, int *);
static int FCatchErr (func_info *);
static int FChar (func_info *);
static int FChoose (expr_node *, Value *, Value *, int *);
static int FCoerce (func_info *);
@@ -245,6 +249,7 @@ BuiltinFunc Func[] = {
{ "asc", 1, 1, 1, FAsc, NULL },
{ "baseyr", 0, 0, 1, FBaseyr, NULL },
{ "catch", 2, 2, 1, NULL, FCatch }, /* NEW-STYLE */
{ "catcherr", 0, 0, 0, FCatchErr, NULL },
{ "char", 1, NO_MAX, 1, FChar, NULL },
{ "choose", 2, NO_MAX, 1, NULL, FChoose }, /*NEW-STYLE*/
{ "coerce", 2, 2, 1, FCoerce, NULL },
@@ -1320,6 +1325,9 @@ static int FCatch(expr_node *node, Value *locals, Value *ans, int *nonconst)
}
return r;
}
/* Save the catch error */
LastCatchError = r;
if (DebugFlag & DB_PRTEXPR) {
PUT("*");
PUT(GetErr(r));
@@ -1344,6 +1352,19 @@ static int FCatch(expr_node *node, Value *locals, Value *ans, int *nonconst)
}
return r;
}
/***************************************************************/
/* */
/* FCatchErr */
/* Return (as a string) the English error thrown by the last */
/* catch() expression that errored out. */
/* */
/***************************************************************/
static int FCatchErr(func_info *info)
{
return RetStrVal(GetEnglishErr(LastCatchError), info);
}
/***************************************************************/
/* */
/* FChoose */

View File

@@ -2138,6 +2138,14 @@ int GetOnceDate(void)
return OnceDate;
}
char const *GetEnglishErr(int r)
{
if (r < 0 || r >= NumErrs) {
r = E_SWERR;
}
return ErrMsg[r];
}
char const *GetErr(int r)
{
char const *msg;

View File

@@ -271,6 +271,7 @@ void InitTranslationTable(void);
char const *GetTranslatedString(char const *orig);
int GetTranslatedStringTryingVariants(char const *orig, DynamicBuffer *out);
char const *GetErr(int r);
char const *GetEnglishErr(int r);
char const *tr(char const *s);
void print_escaped_string(FILE *fp, char const *s);
void print_escaped_string_helper(FILE *fp, char const *s, int esc_for_remind, int json);

View File

@@ -16256,27 +16256,39 @@ D'oh, a file whose name has spaces! ../tests/with space.rem
DEBUG +x
# Test catch() built-in function
set m catcherr()
catcherr() => "Ok"
set a catch(4/2, 33)
4 / 2 => 2
catch(2, ?) => 2
set m catcherr()
catcherr() => "Ok"
set a catch(4/0, 33)
4 / 0 => Division by zero
catch(*Division by zero*, 33) => 33
set m catcherr()
catcherr() => "Division by zero"
set a catch(4/0, 1/0)
4 / 0 => Division by zero
1 / 0 => Division by zero
../tests/test.rem(1444): `/': Division by zero
../tests/test.rem(1447): `/': Division by zero
catch(*Division by zero*, *Division by zero*) => Division by zero
set m catcherr()
catcherr() => "Division by zero"
set a catch(catch(4/0, 1/0), 39)
4 / 0 => Division by zero
1 / 0 => Division by zero
catch(*Division by zero*, *Division by zero*) => Division by zero
catch(*Division by zero*, 39) => 39
set m catcherr()
catcherr() => "Division by zero"
set a catch(4/0, catch(4/0, 39))
4 / 0 => Division by zero
4 / 0 => Division by zero
catch(*Division by zero*, 39) => 39
catch(*Division by zero*, 39) => 39
set m catcherr()
catcherr() => "Division by zero"
set a catch(1/0, catch("f" * "g", catch($IntMin*88, 42)))
1 / 0 => Division by zero
"f" * "g" => Type mismatch
@@ -16285,10 +16297,12 @@ $IntMin => -2147483648
catch(*Number too high*, 42) => 42
catch(*Type mismatch*, 42) => 42
catch(*Division by zero*, 42) => 42
set m catcherr()
catcherr() => "Number too high"
DEBUG -x
DEBUG -e
Variable hash table statistics:
Entries: 100141; Buckets: 87719; Non-empty Buckets: 66299
Entries: 100142; Buckets: 87719; Non-empty Buckets: 66300
Maxlen: 5; Minlen: 0; Avglen: 1.142; Stddev: 0.878; Avg nonempty len: 1.510
Growths: 13; Shrinks: 0
Function hash table statistics:
@@ -24110,6 +24124,7 @@ args
asc
baseyr
catch
catcherr
char
choose
coerce

View File

@@ -1439,12 +1439,19 @@ do "with space.rem"
DEBUG +x
# Test catch() built-in function
set m catcherr()
set a catch(4/2, 33)
set m catcherr()
set a catch(4/0, 33)
set m catcherr()
set a catch(4/0, 1/0)
set m catcherr()
set a catch(catch(4/0, 1/0), 39)
set m catcherr()
set a catch(4/0, catch(4/0, 39))
set m catcherr()
set a catch(1/0, catch("f" * "g", catch($IntMin*88, 42)))
set m catcherr()
DEBUG -x
DEBUG -e