mirror of
https://salsa.debian.org/dskoll/remind.git
synced 2026-04-17 23:08:40 +02:00
More comments.
This commit is contained in:
114
src/expr.c
114
src/expr.c
@@ -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) {
|
||||||
|
|||||||
Reference in New Issue
Block a user