More comments.

This commit is contained in:
Dianne Skoll
2024-06-02 09:46:53 -04:00
parent ee08ce98d7
commit 5e016768af

View File

@@ -363,6 +363,11 @@ get_sysvar(expr_node *node, Value *ans)
/* to 1. The return code is OK if all went well, or an error */ /* to 1. The return code is OK if all went well, or an error */
/* code otherwise. In case of an error, *ans is not updated */ /* code otherwise. In case of an error, *ans is not updated */
/* */ /* */
/* The iif() and choose() functions know how to evaluate */
/* themselves when passed the expr_node. All other functions */
/* use an older API with a func_info object containing the */
/* evaluated arguments and a spot for the return value. */
/* */
/***************************************************************/ /***************************************************************/
static int static int
eval_builtin(expr_node *node, Value *locals, Value *ans, int *nonconst) eval_builtin(expr_node *node, Value *locals, Value *ans, int *nonconst)
@@ -471,6 +476,14 @@ eval_builtin(expr_node *node, Value *locals, Value *ans, int *nonconst)
return r; return r;
} }
/***************************************************************/
/* */
/* debug_enter_userfunc - debugging helper function */
/* */
/* This function prints debugging info when a user-defined */
/* function is invoked. */
/* */
/***************************************************************/
static void static void
debug_enter_userfunc(expr_node *node, Value *locals, int nargs) debug_enter_userfunc(expr_node *node, Value *locals, int nargs)
{ {
@@ -489,6 +502,14 @@ debug_enter_userfunc(expr_node *node, Value *locals, int nargs)
fprintf(ErrFp, ")\n"); fprintf(ErrFp, ")\n");
} }
/***************************************************************/
/* */
/* debug_exit_userfunc - debugging helper function */
/* */
/* This function prints debugging info when a user-defined */
/* function has been evaluated. */
/* */
/***************************************************************/
static void static void
debug_exit_userfunc(expr_node *node, Value *ans, int r, Value *locals, int nargs) debug_exit_userfunc(expr_node *node, Value *ans, int r, Value *locals, int nargs)
{ {
@@ -513,29 +534,46 @@ debug_exit_userfunc(expr_node *node, Value *ans, int r, Value *locals, int nargs
fprintf(ErrFp, "\n"); fprintf(ErrFp, "\n");
} }
/***************************************************************/
/* */
/* eval_userfunc - evaluate a user-defined function */
/* */
/* This function sets up a local value array by evaluating */
/* all of its children, and then evaluated the expr_node */
/* tree associated with the user-defined function. */
/* */
/***************************************************************/
static int static int
eval_userfunc(expr_node *node, Value *locals, Value *ans, int *nonconst) eval_userfunc(expr_node *node, Value *locals, Value *ans, int *nonconst)
{ {
UserFunc *f; UserFunc *f;
UserFunc *previously_executing; UserFunc *previously_executing;
Value *new_locals = NULL;
expr_node *kid;
int i, r, j, pushed;
/* If we have <= STACK_ARGS_MAX, store them on the stack here */
Value stack_locals[STACK_ARGS_MAX]; Value stack_locals[STACK_ARGS_MAX];
/* Get the function name */
char const *fname; char const *fname;
if (node->type == N_SHORT_USER_FUNC) { if (node->type == N_SHORT_USER_FUNC) {
fname = node->u.name; fname = node->u.name;
} else { } else {
fname = node->u.value.v.str; fname = node->u.value.v.str;
} }
f = FindUserFunc(fname);
Value *new_locals = NULL;
expr_node *kid;
int i, r, j, pushed;
/* Find the function */
f = FindUserFunc(fname);
/* Bail if function does not exist */
if (!f) { if (!f) {
Eprint("%s: `%s'", ErrMsg[E_UNDEF_FUNC], fname); Eprint("%s: `%s'", ErrMsg[E_UNDEF_FUNC], fname);
return E_UNDEF_FUNC; return E_UNDEF_FUNC;
} }
/* Make sure we have the right number of arguments */
if (node->num_kids < f->nargs) { if (node->num_kids < f->nargs) {
DBG(fprintf(ErrFp, "%s(...) => %s\n", fname, ErrMsg[E_2FEW_ARGS])); DBG(fprintf(ErrFp, "%s(...) => %s\n", fname, ErrMsg[E_2FEW_ARGS]));
return E_2FEW_ARGS; return E_2FEW_ARGS;
@@ -548,14 +586,17 @@ eval_userfunc(expr_node *node, Value *locals, Value *ans, int *nonconst)
/* Build up the array of locals */ /* Build up the array of locals */
if (node->num_kids) { if (node->num_kids) {
if (node->num_kids > STACK_ARGS_MAX) { if (node->num_kids > STACK_ARGS_MAX) {
/* Too many args to fit on stack; put on heap */
new_locals = malloc(node->num_kids * sizeof(Value)); new_locals = malloc(node->num_kids * sizeof(Value));
if (!new_locals) {
DBG(fprintf(ErrFp, "%s(...) => %s\n", fname, ErrMsg[E_NO_MEM]));
return E_NO_MEM;
}
} else { } else {
new_locals = stack_locals; new_locals = stack_locals;
} }
if (!new_locals) {
DBG(fprintf(ErrFp, "%s(...) => %s\n", fname, ErrMsg[E_NO_MEM])); /* Evaluate each child node and store in new_locals */
return E_NO_MEM;
}
kid = node->child; kid = node->child;
i = 0; i = 0;
while(kid) { while(kid) {
@@ -572,6 +613,7 @@ eval_userfunc(expr_node *node, Value *locals, Value *ans, int *nonconst)
} }
} }
/* Check for deep recursion */
if (FuncRecursionLevel >= MAX_RECURSION_LEVEL) { if (FuncRecursionLevel >= MAX_RECURSION_LEVEL) {
for (j=0; j<node->num_kids; j++) { for (j=0; j<node->num_kids; j++) {
DestroyValue(new_locals[j]); DestroyValue(new_locals[j]);
@@ -580,22 +622,31 @@ eval_userfunc(expr_node *node, Value *locals, Value *ans, int *nonconst)
return E_RECURSIVE; return E_RECURSIVE;
} }
/* Set currently-executing function (for debugging) */
previously_executing = CurrentUserFunc; previously_executing = CurrentUserFunc;
CurrentUserFunc = f; CurrentUserFunc = f;
FuncRecursionLevel++; FuncRecursionLevel++;
/* Add a call to the call stack for better error messages */
pushed = push_call(f->filename, f->name, f->lineno); pushed = push_call(f->filename, f->name, f->lineno);
if (DebugFlag & DB_PRTEXPR) { if (DebugFlag & DB_PRTEXPR) {
debug_enter_userfunc(node, new_locals, f->nargs); debug_enter_userfunc(node, new_locals, f->nargs);
} }
/* Evaluate the function's expr_node tree */
r = evaluate_expr_node(f->node, new_locals, ans, nonconst); r = evaluate_expr_node(f->node, new_locals, ans, nonconst);
if (DebugFlag & DB_PRTEXPR) { if (DebugFlag & DB_PRTEXPR) {
debug_exit_userfunc(node, ans, r, new_locals, f->nargs); debug_exit_userfunc(node, ans, r, new_locals, f->nargs);
} }
if (r != OK) { if (r != OK) {
/* We print the error here in order to get the call stack trace */
Eprint("%s", ErrMsg[r]); Eprint("%s", ErrMsg[r]);
} }
if (pushed == OK) pop_call(); if (pushed == OK) pop_call();
FuncRecursionLevel--; FuncRecursionLevel--;
CurrentUserFunc = previously_executing; CurrentUserFunc = previously_executing;
@@ -612,6 +663,26 @@ eval_userfunc(expr_node *node, Value *locals, Value *ans, int *nonconst)
return r; return r;
} }
/***************************************************************/
/* */
/* evaluate_expr_node - the top-level expression evaluation */
/* entry point. */
/* */
/* This is the entry point for evaluating an expression. */
/* Arguments: */
/* node - the expr_node to evaluate */
/* locals - an array of arguments to a user-defined */
/* function. NULL if we're not in a function */
/* ans - pointer to a Value object that will hold */
/* the result of an evaluation */
/* nonconst - pointer to an integer that will be set to 1 */
/* if a non-constant object or function is */
/* evaluated */
/* Returns: */
/* OK if all goes well; a non-zero error code on failure. */
/* On failure, *ans is not updated. */
/* */
/***************************************************************/
int int
evaluate_expr_node(expr_node *node, Value *locals, Value *ans, int *nonconst) evaluate_expr_node(expr_node *node, Value *locals, Value *ans, int *nonconst)
{ {
@@ -625,50 +696,77 @@ evaluate_expr_node(expr_node *node, Value *locals, Value *ans, int *nonconst)
case N_ERROR: case N_ERROR:
ans->type = ERR_TYPE; ans->type = ERR_TYPE;
return E_SWERR; return E_SWERR;
case N_CONSTANT: case N_CONSTANT:
/* Constant node? Just return a copy of the constant */
return CopyValue(ans, &(node->u.value)); return CopyValue(ans, &(node->u.value));
case N_SHORT_VAR: case N_SHORT_VAR:
/* Global var? Return it and note non-constant expression */
*nonconst = 1; *nonconst = 1;
r = get_var(node, ans); r = get_var(node, ans);
DBG(debug_evaluation(ans, r, "%s", node->u.name)); DBG(debug_evaluation(ans, r, "%s", node->u.name));
return r; return r;
case N_VARIABLE: case N_VARIABLE:
/* Global var? Return it and note non-constant expression */
*nonconst = 1; *nonconst = 1;
r = get_var(node, ans); r = get_var(node, ans);
DBG(debug_evaluation(ans, r, "%s", node->u.value.v.str)); DBG(debug_evaluation(ans, r, "%s", node->u.value.v.str));
return r; return r;
case N_LOCAL_VAR: case N_LOCAL_VAR:
/* User-defined function argument? Copy the value */
r = CopyValue(ans, &(locals[node->u.arg])); r = CopyValue(ans, &(locals[node->u.arg]));
DBG(debug_evaluation(ans, r, "%s", CurrentUserFunc->args[node->u.arg])); DBG(debug_evaluation(ans, r, "%s", CurrentUserFunc->args[node->u.arg]));
return r; return r;
case N_SHORT_SYSVAR: case N_SHORT_SYSVAR:
/* System varr? Return it and note non-constant expression */
*nonconst = 1; *nonconst = 1;
r = get_sysvar(node, ans); r = get_sysvar(node, ans);
DBG(debug_evaluation(ans, r, "$%s", node->u.name)); DBG(debug_evaluation(ans, r, "$%s", node->u.name));
return r; return r;
case N_SYSVAR: case N_SYSVAR:
/* System varr? Return it and note non-constant expression */
*nonconst = 1; *nonconst = 1;
r = get_sysvar(node, ans); r = get_sysvar(node, ans);
DBG(debug_evaluation(ans, r, "$%s", node->u.value.v.str)); DBG(debug_evaluation(ans, r, "$%s", node->u.value.v.str));
return r; return r;
case N_BUILTIN_FUNC: case N_BUILTIN_FUNC:
/* Built-in function? Evaluate and note non-constant where applicable */
if (!node->u.builtin_func->is_constant) { if (!node->u.builtin_func->is_constant) {
*nonconst = 1; *nonconst = 1;
} }
return eval_builtin(node, locals, ans, nonconst); return eval_builtin(node, locals, ans, nonconst);
case N_USER_FUNC: case N_USER_FUNC:
case N_SHORT_USER_FUNC: case N_SHORT_USER_FUNC:
/* User-defined function? Evaluate it */
return eval_userfunc(node, locals, ans, nonconst); return eval_userfunc(node, locals, ans, nonconst);
case N_OPERATOR: case N_OPERATOR:
/* Operator? Evaluate it */
r = node->u.operator_func(node, locals, ans, nonconst); r = node->u.operator_func(node, locals, ans, nonconst);
if (r != OK) { if (r != OK) {
Eprint("`%s': %s", get_operator_name(node), ErrMsg[r]); Eprint("`%s': %s", get_operator_name(node), ErrMsg[r]);
} }
return r; return r;
} }
/* Can't happen */
return E_SWERR; return E_SWERR;
} }
/***************************************************************/
/* */
/* how_to_op - convert a symbolic comparison constant to name */
/* */
/* Converts EQ, NE, etc to "==", "!=", etc for debugging */
/* */
/***************************************************************/
static char const *how_to_op(int how) static char const *how_to_op(int how)
{ {
switch(how) { switch(how) {