Document all the non-constant tracking machinery.

This commit is contained in:
Dianne Skoll
2025-05-21 12:23:08 -04:00
parent 8ca5c7e56e
commit de2d15009f
5 changed files with 59 additions and 16 deletions

View File

@@ -3649,6 +3649,14 @@ As an example, the following two expressions are equivalent:
.fi
.RE
.TP
.B isconst(x_any)
Evaluates its argument and then \fIthrows away\fR the value, returning
1 if the expression is constant or 0 if it is non-constant. Note that
\fBisconst\fR does not take into account the context; if \fIarg\fR is
a constant expression but evaluated in a non-constant context,
\fBisconst\fR will still return 1. For details about constant vs.
non-constant expressions, see the section "NON-CONSTANT EXPRESSIONS"
.TP
.B isdst([d_date [,t_time]]) \fRor\fB isdst(q_datetime)
Returns a positive number if daylight saving time is in
effect on the specified date and time. \fIDate\fR
@@ -3951,6 +3959,11 @@ The second REM statement sets up the 14-day blue-box cycle with a similar
adjustment made by AFTER in conjunction with _garbhol.
.RE
.TP
.B nonconst(x_arg)
Returns the argument \fIarg\fR unchanged, but forces the expression
to be considered \fInon-constant\fR. For details, see the section
"NON-CONSTANT EXPRESSIONS"
.TP
.B now()
Returns the current system time, as a \fBTIME\fR type. This may be
the actual time, or a time supplied on the command line.
@@ -5691,7 +5704,9 @@ to consider it non-constant:
.RS
.TP
.B o
A global variable
A global variable that has been assigned the result of a non-constant
expression, or that has been assigned a value in a \fInon-constant
context\fR (to be described later.)
.TP
.B o
A system variable
@@ -5713,19 +5728,36 @@ The use of an OMITFUNC
The use of a relative SCANFROM
.RE
.PP
Note that \fBRemind\fR checks expressions for non-constantness
\fIin isolation\fR. It does not trace data flow. For example.
a human can easily see that the expression in the REM command
below is actually constant, but \fBRemind\fR does not look
at the preceding assignment. It simply looks at the expression \fBd\fR
in isolation and considers that the global variable \fBd\fR
might not be constant.
Whenever a variable is assigned a value, \fBRemind\fR tracks whether
or not the expression whose value it was assigned is constant or
non-constant. Additionally, variables that are assigned in a
non-constant context are always assumed to be non-constant.
A non-constant context is the code in the scope of an \fBIF\fR
statement where the \fBIF\fR expression is non-constant.
.PP
Here are some examples that should make things clear:
.PP
.nf
SET d '2025-06-01'
REM [d] MSG Hello!
SET d '2025-06-01' # d is constant
REM [d] MSG Hello! # eligible for purging
SET d today() - 3 # d is non-constant
REM [d] MSG Hello! # not eligible for purging
IF wkdaynum(today()) == 3
set d '2025-06-01' # d is non-constant
ELSE
set d '2026-01-01' # d is non-constant
ENDIF
.fi
.PP
Note that within the \fBIF\fR...\fBENDIF\fR scope, any assignments
are non-constant because the code flow depends on today's date, which
could change in subsequent \fBRemind\fR runs.
.PP
Variables initialized on the command-line with the \fB\-i\fR flag are
\fIalways\fR considered to be non-constant.
.PP
The \fBn\fR debugging flag prints a message to standard error whenever
\fBRemind\fR decides that an expression is non-constant. This can produce
a large amount of output, so if you want to find out why \fBRemind\fR considers

View File

@@ -649,7 +649,7 @@ int ParseRem(ParsePtr s, Trigger *trig, TimeTrig *tim)
strtolower(trig->omitfunc);
/* An OMITFUNC counts as a nonconst_expr! */
s->expr_happened = 1;
nonconst_debug(s->nonconst_expr, "OMITFUNC counts as a non-constant expression");
nonconst_debug(s->nonconst_expr, tr("OMITFUNC counts as a non-constant expression"));
s->nonconst_expr = 1;
DBufFree(&buf);
break;
@@ -1071,7 +1071,7 @@ static int ParseScanFrom(ParsePtr s, Trigger *t, int type)
FromDSE(DSEToday - tok.val, &y, &m, &d);
/* Don't purge reminders with a relative scanfrom */
s->expr_happened = 1;
nonconst_debug(s->nonconst_expr, "Relative SCANFROM counts as a non-constant expression");
nonconst_debug(s->nonconst_expr, tr("Relative SCANFROM counts as a non-constant expression"));
s->nonconst_expr = 1;
break;

View File

@@ -426,7 +426,7 @@ get_var(expr_node *node, Value *ans, int *nonconst)
return E_NOSUCH_VAR;
}
if (v->nonconstant) {
nonconst_debug(*nonconst, "Global variable `%s' makes expression non-constant", str);
nonconst_debug(*nonconst, tr("Global variable `%s' makes expression non-constant"), str);
*nonconst = 1;
}
return CopyValue(ans, &(v->v));
@@ -883,7 +883,7 @@ evaluate_expr_node(expr_node *node, Value *locals, Value *ans, int *nonconst)
case N_SHORT_SYSVAR:
/* System var? Return it and note non-constant expression */
nonconst_debug(*nonconst, "System variable `$%s' makes expression non-constant", node->u.name);
nonconst_debug(*nonconst, tr("System variable `$%s' makes expression non-constant"), node->u.name);
*nonconst = 1;
r = get_sysvar(node, ans);
DBG(debug_evaluation(ans, r, "$%s", node->u.name));
@@ -891,7 +891,7 @@ evaluate_expr_node(expr_node *node, Value *locals, Value *ans, int *nonconst)
case N_SYSVAR:
/* System var? Return it and note non-constant expression */
nonconst_debug(*nonconst, "System variable `$%s' makes expression non-constant", node->u.value.v.str);
nonconst_debug(*nonconst, tr("System variable `$%s' makes expression non-constant"), node->u.value.v.str);
*nonconst = 1;
r = get_sysvar(node, ans);
DBG(debug_evaluation(ans, r, "$%s", node->u.value.v.str));
@@ -900,7 +900,7 @@ evaluate_expr_node(expr_node *node, Value *locals, Value *ans, int *nonconst)
case N_BUILTIN_FUNC:
/* Built-in function? Evaluate and note non-constant where applicable */
if (!node->u.builtin_func->is_constant) {
nonconst_debug(*nonconst, "Non-constant builtin function `%s' makes expression non-constant", node->u.builtin_func->name);
nonconst_debug(*nonconst, tr("Non-constant builtin function `%s' makes expression non-constant"), node->u.builtin_func->name);
*nonconst = 1;
}
return eval_builtin(node, locals, ans, nonconst);

View File

@@ -646,6 +646,11 @@ int DoSet (Parser *p)
} else {
r = SetVar(DBufValue(&buf), &v, 0);
}
if (DebugFlag & DB_NONCONST) {
if (!in_constant_context() && !p->nonconst_expr) {
Wprint(tr("Variable assignment considered non-constant because of context"));
}
}
}
if (buf.len > VAR_NAME_LEN) {
Wprint(tr("Warning: Variable name `%.*s...' truncated to `%.*s'"),

View File

@@ -24808,28 +24808,34 @@ TRANSLATE "Found cached directory listing for `%s'" ""
TRANSLATE "Function `%s' defined at %s(%s) should take %d argument%s, but actually takes %d" ""
TRANSLATE "Function `%s' redefined: previously defined at %s(%s)" ""
TRANSLATE "GetValidHebDate: Bad adarbehave value %d" ""
TRANSLATE "Global variable `%s' makes expression non-constant" ""
TRANSLATE "In" ""
TRANSLATE "Invalid INFO string: Must be of the form \"Header: Value\"" ""
TRANSLATE "Invalid translation: Both original and translated must have the same printf-style formatting sequences in the same order." ""
TRANSLATE "Missing REM type; assuming MSG" ""
TRANSLATE "No Adar A in %d" ""
TRANSLATE "No substition function `%s' defined" ""
TRANSLATE "Non-constant builtin function `%s' makes expression non-constant" ""
TRANSLATE "Not setting $OnceFile: Already processed a reminder with a ONCE clause" ""
TRANSLATE "OMIT: UNTIL not allowed; did you mean THROUGH?" ""
TRANSLATE "OMITFUNC counts as a non-constant expression" ""
TRANSLATE "POP-OMIT-CONTEXT at %s:%d matches PUSH-OMIT-CONTEXT in different file: %s:%d" ""
TRANSLATE "Reading `%s': Found in cache" ""
TRANSLATE "Reading `%s': Opening file on disk" ""
TRANSLATE "Reading `-': Reading stdin" ""
TRANSLATE "Reading command `%s': Found in cache" ""
TRANSLATE "Relative SCANFROM counts as a non-constant expression" ""
TRANSLATE "SATISFY: constant 0 will never be true" ""
TRANSLATE "SATISFY: constant \"\" will never be true" ""
TRANSLATE "SATISFY: expression has no reference to trigdate() or $T..." ""
TRANSLATE "SECURITY: Won't read non-root-owned file or directory when running as root!" ""
TRANSLATE "SECURITY: Won't read world-writable file or directory!" ""
TRANSLATE "Scanning directory `%s' for *.rem files" ""
TRANSLATE "System variable `$%s' makes expression non-constant" ""
TRANSLATE "Undefined %s function: `%s'" ""
TRANSLATE "Unmatched PUSH-OMIT-CONTEXT at %s(%d)" ""
TRANSLATE "Unrecognized command; interpreting as REM" ""
TRANSLATE "Variable assignment considered non-constant because of context" ""
TRANSLATE "Warning: Function name `%s...' truncated to `%s'" ""
TRANSLATE "Warning: OMIT is ignored if you use OMITFUNC" ""
TRANSLATE "Warning: SCANFROM is ignored in two-argument form of evaltrig()" ""