Overhaul how IF/ELSE work so we can track "constant-ness" of variables.

We now keep track of whether a variable holds a "constant" value
(ie, a value that will stay the same on successive Remind runs)
so Purge Mode can be more accurate.
This commit is contained in:
Dianne Skoll
2025-05-20 22:11:39 -04:00
parent a309af731f
commit 1e3657b728
18 changed files with 1071 additions and 816 deletions

View File

@@ -168,11 +168,11 @@
"datepart" "datetime" "dawn" "day" "daysinmon" "defined" "dosubst"
"dusk" "easterdate" "escape" "evaltrig" "filedate" "filedatetime"
"filedir" "filename" "getenv" "hebdate" "hebday" "hebmon" "hebyear"
"hour" "htmlescape" "htmlstriptags" "iif" "index" "isany" "isdst"
"hour" "htmlescape" "htmlstriptags" "iif" "index" "isany" "isconst" "isdst"
"isleap" "isomitted" "language" "localtoutc" "lower" "max" "min"
"minsfromutc" "minute" "mon" "monnum" "moondate" "moondatetime"
"moonphase" "moonrise" "moonrisedir" "moonset" "moonsetdir" "moontime"
"multitrig" "ndawn" "ndusk" "nonomitted" "now" "ord" "orthodoxeaster"
"multitrig" "ndawn" "ndusk" "nonconst" "nonomitted" "now" "ord" "orthodoxeaster"
"ostype" "pad" "plural" "psmoon" "psshade" "realcurrent" "realnow"
"realtoday" "rows" "sgn" "shell" "shellescape" "slide" "soleq"
"stdout" "strlen" "substr" "sunrise" "sunset" "time" "timepart"

View File

@@ -28,8 +28,9 @@ MANS= $(srcdir)/../man/rem2ps.1 $(srcdir)/../man/remind.1 \
REMINDSRCS= calendar.c dedupe.c dynbuf.c dorem.c dosubst.c expr.c \
files.c funcs.c globals.c hashtab.c hashtab_stats.c \
hbcal.c init.c main.c md5.c moon.c omit.c queue.c \
sort.c token.c trans.c trigger.c userfns.c utils.c var.c
hbcal.c ifelse.c init.c main.c md5.c moon.c omit.c \
queue.c sort.c token.c trans.c trigger.c userfns.c \
utils.c var.c
XLATSRC= xlat.c

View File

@@ -1757,12 +1757,11 @@ static void GenerateCalEntries(int col)
s = FindInitialToken(&tok, CurLine);
/* Should we ignore it? */
if (NumIfs &&
tok.type != T_If &&
if (tok.type != T_If &&
tok.type != T_Else &&
tok.type != T_EndIf &&
tok.type != T_IfTrig &&
ShouldIgnoreLine())
should_ignore_line())
{
/* DO NOTHING */
}
@@ -2275,7 +2274,7 @@ static int DoCalRem(ParsePtr p, int col)
}
e->infos = NULL;
e->nonconst_expr = nonconst_expr;
e->if_depth = NumIfs;
e->if_depth = get_if_pointer() - get_base_if_pointer();
e->trig = trig;
e->tt = tim;
#ifdef REM_USE_WCHAR

View File

@@ -108,12 +108,6 @@
/*---------------------------------------------------------------------*/
#define INCLUDE_NEST 9
/*---------------------------------------------------------------------*/
/* IF_NEST: How many nested IFs do we handle? Maximum is the number */
/* of bits in an int, divided by two. Beware! */
/*---------------------------------------------------------------------*/
#define IF_NEST (4*sizeof(unsigned int))
/*---------------------------------------------------------------------*/
/* How many attempts to resolve a weird date spec? */
/*---------------------------------------------------------------------*/

View File

@@ -108,12 +108,6 @@
/*---------------------------------------------------------------------*/
#define INCLUDE_NEST 9
/*---------------------------------------------------------------------*/
/* IF_NEST: How many nested IFs do we handle? Maximum is the number */
/* of bits in an int, divided by two. Beware! */
/*---------------------------------------------------------------------*/
#define IF_NEST (4*sizeof(unsigned int))
/*---------------------------------------------------------------------*/
/* How many attempts to resolve a weird date spec? */
/*---------------------------------------------------------------------*/

View File

@@ -410,13 +410,26 @@ debug_evaluation_unop(Value *ans, int r, Value *v1, char const *fmt, ...)
/* */
/***************************************************************/
static int
get_var(expr_node *node, Value *ans)
get_var(expr_node *node, Value *ans, int *nonconst)
{
Var *v;
char const *str;
if (node->type == N_SHORT_VAR) {
return GetVarValue(node->u.name, ans);
str = node->u.name;
} else {
return GetVarValue(node->u.value.v.str, ans);
str = node->u.value.v.str;
}
v = FindVar(str, 0);
if (!v) {
Eprint("%s: `%s'", GetErr(E_NOSUCH_VAR), str);
return E_NOSUCH_VAR;
}
if (v->nonconstant) {
nonconst_debug(*nonconst, "Global variable `%s' makes expression non-constant", str);
*nonconst = 1;
}
return CopyValue(ans, &(v->v));
}
/***************************************************************/
@@ -853,18 +866,12 @@ evaluate_expr_node(expr_node *node, Value *locals, Value *ans, int *nonconst)
return CopyValue(ans, &(node->u.value));
case N_SHORT_VAR:
/* Global var? Return it and note non-constant expression */
nonconst_debug(*nonconst, "Global variable `%s' makes expression non-constant", node->u.name);
*nonconst = 1;
r = get_var(node, ans);
r = get_var(node, ans, nonconst);
DBG(debug_evaluation(ans, r, "%s", node->u.name));
return r;
case N_VARIABLE:
/* Global var? Return it and note non-constant expression */
nonconst_debug(*nonconst, "Global variable `%s' makes expression non-constant", node->u.value.v.str);
*nonconst = 1;
r = get_var(node, ans);
r = get_var(node, ans, nonconst);
DBG(debug_evaluation(ans, r, "%s", node->u.value.v.str));
return r;
@@ -894,8 +901,8 @@ evaluate_expr_node(expr_node *node, Value *locals, Value *ans, int *nonconst)
/* 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 = 1;
}
*nonconst = 1;
return eval_builtin(node, locals, ans, nonconst);
case N_USER_FUNC:

View File

@@ -79,9 +79,7 @@ typedef struct {
FilenameChain *chain;
int LineNo;
int LineNoStart;
unsigned int IfFlags;
int NumIfs;
int IfLinenos[IF_NEST];
int base_if_pointer;
long offset;
CachedLine *CLine;
int ownedByMe;
@@ -546,15 +544,9 @@ static int NextChainedFile(IncludeStruct *i)
static int PopFile(void)
{
IncludeStruct *i;
int j;
if (!Hush && NumIfs) {
Eprint("%s", GetErr(E_MISS_ENDIF));
for (j=NumIfs-1; j >=0; j--) {
fprintf(ErrFp, tr("%s(%d): IF without ENDIF"), FileName, IfLinenos[j]);
fprintf(ErrFp, "\n");
}
}
pop_excess_ifs(FileName);
if (!IStackPtr) return E_EOF;
i = &IStack[IStackPtr-1];
@@ -574,9 +566,7 @@ static int PopFile(void)
LineNo = i->LineNo;
LineNoStart = i->LineNoStart;
IfFlags = i->IfFlags;
memcpy(IfLinenos, i->IfLinenos, IF_NEST);
NumIfs = i->NumIfs;
set_base_if_pointer(i->base_if_pointer);
CLine = i->CLine;
fp = NULL;
STRSET(FileName, i->filename);
@@ -916,9 +906,7 @@ static int IncludeCmd(char const *cmd)
i->ownedByMe = 1;
i->LineNo = LineNo;
i->LineNoStart = LineNo;
i->NumIfs = NumIfs;
i->IfFlags = IfFlags;
memcpy(i->IfLinenos, IfLinenos, IF_NEST);
i->base_if_pointer = get_base_if_pointer();
i->CLine = CLine;
i->offset = -1L;
i->chain = NULL;
@@ -927,8 +915,8 @@ static int IncludeCmd(char const *cmd)
FCLOSE(fp);
}
IStackPtr++;
NumIfs = 0;
IfFlags = 0;
set_base_if_pointer(get_if_pointer());
/* If the file is cached, use it */
h = CachedFiles;
@@ -1028,9 +1016,7 @@ int IncludeFile(char const *fname)
}
i->LineNo = LineNo;
i->LineNoStart = LineNoStart;
i->NumIfs = NumIfs;
i->IfFlags = IfFlags;
memcpy(i->IfLinenos, IfLinenos, IF_NEST);
i->base_if_pointer = get_base_if_pointer();
i->CLine = CLine;
i->offset = -1L;
i->chain = NULL;
@@ -1045,8 +1031,7 @@ int IncludeFile(char const *fname)
}
IStackPtr++;
NumIfs = 0;
IfFlags = 0;
set_base_if_pointer(get_if_pointer());
#ifdef HAVE_GLOB
/* If it's a directory, set up the glob chain here. */

View File

@@ -115,6 +115,7 @@ static int FHtmlStriptags (func_info *);
static int FIif (expr_node *, Value *, Value *, int *);
static int FIndex (func_info *);
static int FIsAny (expr_node *, Value *, Value *, int *);
static int FIsconst (expr_node *, Value *, Value *, int *);
static int FIsdst (func_info *);
static int FIsleap (func_info *);
static int FIsomitted (func_info *);
@@ -138,6 +139,7 @@ static int FMoontime (func_info *);
static int FMultiTrig (func_info *);
static int FNDawn (func_info *);
static int FNDusk (func_info *);
static int FNonconst (func_info *);
static int FNonomitted (func_info *);
static int FNow (func_info *);
static int FOrd (func_info *);
@@ -282,6 +284,7 @@ BuiltinFunc Func[] = {
{ "iif", 1, NO_MAX, 1, NULL, FIif }, /*NEW-STYLE*/
{ "index", 2, 3, 1, FIndex, NULL },
{ "isany", 1, NO_MAX, 1, NULL, FIsAny }, /*NEW-STYLE*/
{ "isconst", 1, 1, 1, NULL, FIsconst }, /*NEW-STYLE*/
{ "isdst", 0, 2, 0, FIsdst, NULL },
{ "isleap", 1, 1, 1, FIsleap, NULL },
{ "isomitted", 1, 1, 0, FIsomitted, NULL },
@@ -305,6 +308,7 @@ BuiltinFunc Func[] = {
{ "multitrig", 1, NO_MAX, 0, FMultiTrig, NULL },
{ "ndawn", 0, 1, 0, FNDawn, NULL },
{ "ndusk", 0, 1, 0, FNDusk, NULL },
{ "nonconst", 1, 1, 0, FNonconst, NULL },
{ "nonomitted", 2, NO_MAX, 0, FNonomitted, NULL },
{ "now", 0, 0, 0, FNow, NULL },
{ "ord", 1, 1, 1, FOrd, NULL },
@@ -575,6 +579,17 @@ static int FCoerce(func_info *info)
else return E_CANT_COERCE;
}
/***************************************************************/
/* */
/* FNonconst - return arg, but with nonconst_expr flag set */
/* */
/***************************************************************/
static int FNonconst(func_info *info)
{
DCOPYVAL(RetVal, ARG(0));
return OK;
}
/***************************************************************/
/* */
/* FMax - select maximum from a list of args. */
@@ -1244,6 +1259,40 @@ static int FPlural(func_info *info)
}
}
/***************************************************************/
/* */
/* FIsconst */
/* Return 1 if the first arg is constant; 0 otherwise */
/* */
/***************************************************************/
static int FIsconst(expr_node *node, Value *locals, Value *ans, int *nonconst)
{
Value junk;
int my_nonconst;
DynamicBuffer DebugBuf;
int r;
UNUSED(nonconst);
DBG(DBufInit(&DebugBuf));
DBG(PUT("isconst("));
my_nonconst = 0;
r = evaluate_expr_node(node->child, locals, &junk, &my_nonconst);
if (r != OK) {
DBG(DBufFree(&DebugBuf));
return r;
}
ans->type = INT_TYPE;
ans->v.val = (my_nonconst ? 0 : 1);
DBG(PUT(PrintValue(&junk, NULL)));
if (DebugFlag & DB_PRTEXPR) {
PUT(") => ");
PUT(PrintValue(ans, NULL));
OUT();
}
return OK;
}
/***************************************************************/
/* */
/* FIsAny */

View File

@@ -118,9 +118,6 @@ EXTERN INIT( int UseStdin, 0);
EXTERN INIT( int PurgeMode, 0);
EXTERN INIT( int PurgeIncludeDepth, 0);
EXTERN INIT( FILE *PurgeFP, NULL);
EXTERN INIT( int NumIfs, 0);
EXTERN INIT( unsigned int IfFlags, 0);
EXTERN INIT( int IfLinenos[IF_NEST], {0});
EXTERN INIT( int LastTrigValid, 0);
EXTERN Trigger LastTrigger;
EXTERN TimeTrig LastTimeTrig;

214
src/ifelse.c Normal file
View File

@@ -0,0 +1,214 @@
/***************************************************************/
/* */
/* IFELSE.C */
/* */
/* Logic for tracking the state of the IF... ELSE... ENDIF */
/* */
/* This file is part of REMIND. */
/* Copyright (C) 1992-2025 by Dianne Skoll */
/* SPDX-License-Identifier: GPL-2.0-only */
/* */
/***************************************************************/
#include "config.h"
#include "types.h"
#include "protos.h"
#include "globals.h"
#include "err.h"
/* Allow up to 64 levels if IF...ELSE nesting across all files */
#define IF_NEST 64
/* Points to the next unused IF entry structure */
static int if_pointer = 0;
/* The base pointer for the current file */
static int base_pointer = 0;
/*
* The current state of the IF...ELSE...ENDIF context is stored in
* an array of "ifentry" objects, from the outermost to the
* innermost.
*
* The if_true element is true of the IF expression was true; false
* otherwise.
*
* The before_else element is true if the current line is after
* the IF but before any ELSE; false otherwise.
*
* The was_constant element is true if the IF expression was constant.
*
* Each time a new file is INCLUDed, we reset the base pointer to the
* current location of the if_pointer.
*/
typedef struct ifentry_struct {
unsigned int if_true:1;
unsigned int before_else:1;
unsigned int was_constant:1;
int lineno;
} ifentry;
static ifentry IfArray[IF_NEST];
/***************************************************************/
/* */
/* push_if - push an if entry onto the stack. */
/* */
/***************************************************************/
int
push_if(int is_true, int was_constant)
{
if (if_pointer >= IF_NEST) {
return E_NESTED_IF;
}
IfArray[if_pointer].if_true = is_true;
IfArray[if_pointer].before_else = 1;
IfArray[if_pointer].was_constant = was_constant;
IfArray[if_pointer].lineno = LineNo;
if_pointer++;
return OK;
}
/***************************************************************/
/* */
/* if_stack_full - returns OK or E_NESTED_IF */
/* */
/***************************************************************/
int
if_stack_full(void)
{
if (if_pointer >= IF_NEST) {
return E_NESTED_IF;
}
return OK;
}
/***************************************************************/
/* */
/* encounter_else - note that the most-recently-pushed IF */
/* has encountered an ELSE */
/* */
/***************************************************************/
int
encounter_else(void)
{
if (if_pointer <= base_pointer) {
return E_ELSE_NO_IF;
}
if (!IfArray[if_pointer-1].before_else) {
return E_ELSE_NO_IF;
}
IfArray[if_pointer-1].before_else = 0;
return OK;
}
/***************************************************************/
/* */
/* encounter_endif - note that the most-recently-pushed IF */
/* has encountered an ENDIF */
/* */
/***************************************************************/
int
encounter_endif(void)
{
if (if_pointer <= base_pointer) {
return E_ENDIF_NO_IF;
}
if_pointer--;
return OK;
}
/***************************************************************/
/* */
/* get_base_if_pointer - get the current base_pointer */
/* */
/***************************************************************/
int
get_base_if_pointer(void)
{
return base_pointer;
}
/***************************************************************/
/* */
/* get_if_pointer - get the current if_pointer */
/* */
/***************************************************************/
int
get_if_pointer(void)
{
return if_pointer;
}
/***************************************************************/
/* */
/* set_base_if_pointer - set the base_pointer to n */
/* */
/***************************************************************/
void
set_base_if_pointer(int n)
{
base_pointer = n;
}
/***************************************************************/
/* */
/* should_ignore_line - return 1 if current line should be */
/* ignored. */
/* */
/***************************************************************/
int
should_ignore_line(void)
{
int i;
for (i=base_pointer; i<if_pointer; i++) {
if (( IfArray[i].if_true && !IfArray[i].before_else) ||
(!IfArray[i].if_true && IfArray[i].before_else)) {
return 1;
}
}
return 0;
}
/***************************************************************/
/* */
/* in_constant_context - return true if we're in a "constant" */
/* assignment context. */
/* */
/***************************************************************/
int
in_constant_context(void)
{
int i;
for (i=0; i<if_pointer; i++) {
if (!IfArray[i].was_constant) {
return 0;
}
}
return 1;
}
/***************************************************************/
/* */
/* pop_excess_ifs - pop excess IFs from the stack, printing */
/* error messages as needed. */
/* */
/***************************************************************/
void
pop_excess_ifs(char const *fname)
{
if (if_pointer <= base_pointer) {
return;
}
if (!Hush) {
Eprint("%s", GetErr(E_MISS_ENDIF));
}
while(if_pointer > base_pointer) {
if (!Hush) {
fprintf(ErrFp, tr("%s(%d): IF without ENDIF"), fname, IfArray[if_pointer-1].lineno);
fprintf(ErrFp, "\n");
}
if_pointer--;
}
}

View File

@@ -1004,7 +1004,7 @@ static void InitializeVar(char const *str)
}
val.type = INT_TYPE;
val.v.val = 0;
r = SetVar(varname, &val);
r = SetVar(varname, &val, 1);
if (!r) {
r = PreserveVar(varname);
}
@@ -1044,7 +1044,7 @@ static void InitializeVar(char const *str)
return;
}
r=SetVar(varname, &val);
r=SetVar(varname, &val, 1);
if (r) {
fprintf(ErrFp, GetErr(M_I_OPTION), GetErr(r));
fprintf(ErrFp, "\n");

View File

@@ -283,12 +283,11 @@ static void DoReminders(void)
s = FindInitialToken(&tok, CurLine);
/* Should we ignore it? */
if (NumIfs &&
tok.type != T_If &&
if (tok.type != T_If &&
tok.type != T_Else &&
tok.type != T_EndIf &&
tok.type != T_IfTrig &&
ShouldIgnoreLine())
should_ignore_line())
{
/*** IGNORE THE LINE ***/
if (PurgeMode) {
@@ -1129,33 +1128,29 @@ int DoIf(ParsePtr p)
{
Value v;
int r;
unsigned syndrome;
if ((size_t) NumIfs >= IF_NEST) return E_NESTED_IF;
if (if_stack_full()) {
return E_NESTED_IF;
}
if (ShouldIgnoreLine()) {
syndrome = IF_TRUE | BEFORE_ELSE;
if (should_ignore_line()) {
push_if(1, 1);
return OK;
} else {
if ( (r = EvaluateExpr(p, &v)) ) {
syndrome = IF_TRUE | BEFORE_ELSE;
Eprint("%s", GetErr(r));
push_if(1, 0);
} else
if (truthy(&v)) {
syndrome = IF_TRUE | BEFORE_ELSE;
push_if(1, !p->nonconst_expr);
} else {
syndrome = IF_FALSE | BEFORE_ELSE;
push_if(0, !p->nonconst_expr);
if (PurgeMode) {
PurgeEchoLine("%s\n", "#!P: The next IF evaluated false...");
PurgeEchoLine("%s\n", "#!P: REM statements in IF block not checked for purging.");
}
}
}
IfLinenos[NumIfs] = LineNo;
NumIfs++;
IfFlags &= ~(IF_MASK << (2*NumIfs - 2));
IfFlags |= syndrome << (2 * NumIfs - 2);
if (ShouldIgnoreLine()) return OK;
return VerifyEoln(p);
}
@@ -1167,21 +1162,16 @@ int DoIf(ParsePtr p)
/***************************************************************/
int DoElse(ParsePtr p)
{
unsigned syndrome;
int was_ignoring = should_ignore_line();
int was_ignoring = ShouldIgnoreLine();
if (!NumIfs) return E_ELSE_NO_IF;
syndrome = IfFlags >> (2 * NumIfs - 2);
if ((syndrome & IF_ELSE_MASK) == AFTER_ELSE) return E_ELSE_NO_IF;
IfFlags |= AFTER_ELSE << (2 * NumIfs - 2);
if (PurgeMode && ShouldIgnoreLine() && !was_ignoring) {
int r = encounter_else();
if (PurgeMode && should_ignore_line() && !was_ignoring) {
PurgeEchoLine("%s\n", "#!P: The previous IF evaluated true.");
PurgeEchoLine("%s\n", "#!P: REM statements in ELSE block not checked for purging");
}
if (r != OK) {
return r;
}
return VerifyEoln(p);
}
@@ -1192,8 +1182,10 @@ int DoElse(ParsePtr p)
/***************************************************************/
int DoEndif(ParsePtr p)
{
if (!NumIfs) return E_ENDIF_NO_IF;
NumIfs--;
int r = encounter_endif();
if (r != OK) {
return r;
}
return VerifyEoln(p);
}
@@ -1207,15 +1199,18 @@ int DoEndif(ParsePtr p)
int DoIfTrig(ParsePtr p)
{
int r, err;
unsigned syndrome;
Trigger trig;
TimeTrig tim;
int dse;
if ((size_t) NumIfs >= IF_NEST) return E_NESTED_IF;
if (ShouldIgnoreLine()) {
syndrome = IF_TRUE | BEFORE_ELSE;
if (if_stack_full()) {
return E_NESTED_IF;
}
if (should_ignore_line()) {
push_if(1, 0);
return OK;
} else {
if ( (r=ParseRem(p, &trig, &tim)) ) return r;
if (trig.typ != NO_TYPE) return E_PARSE_ERR;
@@ -1226,49 +1221,24 @@ int DoIfTrig(ParsePtr p)
Eprint("%s", GetErr(r));
}
}
syndrome = IF_FALSE | BEFORE_ELSE;
}
else {
push_if(0, 0);
} else {
if (ShouldTriggerReminder(&trig, &tim, dse, &err)) {
syndrome = IF_TRUE | BEFORE_ELSE;
push_if(1, 0);
} else {
syndrome = IF_FALSE | BEFORE_ELSE;
push_if(0, 0);
if (PurgeMode) {
PurgeEchoLine("%s\n", "#!P: The next IFTRIG did not trigger.");
PurgeEchoLine("%s\n", "#!P: REM statements in IFTRIG block not checked for purging.");
}
}
}
if (syndrome == (IF_FALSE | BEFORE_ELSE) && PurgeMode) {
PurgeEchoLine("%s\n", "#!P: The next IFTRIG did not trigger.");
PurgeEchoLine("%s\n", "#!P: REM statements in IFTRIG block not checked for purging.");
}
FreeTrig(&trig);
}
NumIfs++;
IfFlags &= ~(IF_MASK << (2*NumIfs - 2));
IfFlags |= syndrome << (2 * NumIfs - 2);
return OK;
}
/***************************************************************/
/* */
/* ShouldIgnoreLine - given the current state of the IF */
/* stack, should we ignore the current line? */
/* */
/***************************************************************/
int ShouldIgnoreLine(void)
{
register int i, syndrome;
/* Algorithm - go from outer to inner, and if any should be ignored, then
ignore the whole. */
for (i=0; i<NumIfs; i++) {
syndrome = (IfFlags >> (i*2)) & IF_MASK;
if (syndrome == IF_TRUE+AFTER_ELSE ||
syndrome == IF_FALSE+BEFORE_ELSE) return 1;
}
return 0;
}
/***************************************************************/
/* */
/* VerifyEoln */

View File

@@ -151,7 +151,7 @@ void strtolower(char *s);
Var *FindVar (char const *str, int create);
SysVar *FindSysVar (char const *name);
int DeleteVar (char const *str);
int SetVar (char const *str, Value const *val);
int SetVar (char const *str, Value const *val, int nonconst_expr);
int GetVarValue (char const *str, Value *val);
int DoSet (Parser *p);
int DoUnset (Parser *p);
@@ -292,3 +292,20 @@ int GetMoonrise_angle(int dse);
int GetMoonset_angle(int dse);
#define nonconst_debug(nc, ...) do { if ((DebugFlag & DB_NONCONST) && !nc) { Wprint(__VA_ARGS__); } } while(0)
/* if-else handling */
int push_if(int is_true, int was_constant);
int if_stack_full(void);
int encounter_else(void);
int encounter_endif(void);
int get_base_if_pointer(void);
int get_if_pointer(void);
void set_base_if_pointer(int n);
int should_ignore_line(void);
int in_constant_context(void);
void pop_excess_ifs(char const *fname);

View File

@@ -104,6 +104,7 @@ typedef struct var {
struct hash_link link;
char name[VAR_NAME_LEN+1];
char preserve;
char nonconstant;
Value v;
} Var;
@@ -239,15 +240,6 @@ typedef struct {
int val;
} Token;
/* Flags for the state of the "if" stack */
#define IF_TRUE 0x00
#define IF_FALSE 0x01
#define BEFORE_ELSE 0x00
#define AFTER_ELSE 0x02
#define IF_MASK 0x03
#define IF_TRUE_MASK 0x01
#define IF_ELSE_MASK 0x02
/* Flags for the DoSubst function */
#define NORMAL_MODE 0
#define CAL_MODE 1

View File

@@ -562,7 +562,7 @@ int DeleteVar(char const *str)
/* Set the indicated variable to the specified value. */
/* */
/***************************************************************/
int SetVar(char const *str, Value const *val)
int SetVar(char const *str, Value const *val, int nonconst_expr)
{
Var *v = FindVar(str, 1);
@@ -570,6 +570,7 @@ int SetVar(char const *str, Value const *val)
DestroyValue(v->v);
v->v = *val;
v->nonconstant = nonconst_expr;
return OK;
}
@@ -624,6 +625,7 @@ int DoSet (Parser *p)
return E_CANTNEST_FDEF;
}
p->nonconst_expr = 0;
r = EvaluateExpr(p, &v);
if (r) {
DBufFree(&buf);
@@ -638,7 +640,13 @@ int DoSet (Parser *p)
}
DBufFree(&buf2);
if (*DBufValue(&buf) == '$') r = SetSysVar(DBufValue(&buf)+1, &v);
else r = SetVar(DBufValue(&buf), &v);
else {
if (p->nonconst_expr || !in_constant_context()) {
r = SetVar(DBufValue(&buf), &v, 1);
} else {
r = SetVar(DBufValue(&buf), &v, 0);
}
}
if (buf.len > VAR_NAME_LEN) {
Wprint(tr("Warning: Variable name `%.*s...' truncated to `%.*s'"),
VAR_NAME_LEN, DBufValue(&buf), VAR_NAME_LEN, DBufValue(&buf));
@@ -717,6 +725,9 @@ int DoDump(ParsePtr p)
else {
fprintf(ErrFp, "%s ", v->name);
PrintValue(&(v->v), ErrFp);
/* if (!v->nonconstant) {
fprintf(ErrFp, " .");
} */
fprintf(ErrFp, "\n");
}
}
@@ -747,6 +758,9 @@ void DumpVarTable(void)
hash_table_for_each(v, &VHashTbl) {
fprintf(ErrFp, "%s ", v->name);
PrintValue(&(v->v), ErrFp);
/* if (!v->nonconstant) {
fprintf(ErrFp, " .");
} */
fprintf(ErrFp, "\n");
}
}

View File

@@ -60,11 +60,11 @@ OMIT 2 Jan 1992
# Complicated expressions
SET a 3
FSET const(x) x+3
FSET nonconst(x) x+a
FSET not_const(x) x+nonconst(a)
REM [const(5)] Jan 1992 MSG expired... should be commented out
REM [const(a)] Jan 1992 MSG nonconstant expression
REM [nonconst(5)] Jan 1992 MSG nonconstant expression
REM [const(a)] Jan 1992 MSG expired... should be commented out
REM [not_const(5)] Jan 1992 MSG nonconstant expression
REM [value("a")] Jan 1992 MSG nonconstant expression
IF 0

File diff suppressed because one or more lines are too long

View File

@@ -561,6 +561,11 @@ msg [$Wednesday]%
REM Mon 1992 UNTIL 1991-01-01 MSG Not diagnosed - not fully-specified start
REM 1992-01-01 *1 UNTIL 1991-12-31 MSG Diagnosed
set x '1992-01-01'
MSG [isconst(x)]
REM [x] *1 UNTIL 1991-12-31 MSG Diagnosed
set x nonconst('1992-01-01')
MSG [isconst(x)]
REM [x] *1 UNTIL 1991-12-31 MSG Not diagnosed - nonconst expression
REM MON FROM 1992-01-01 UNTIL 1991-12-31 MSG Diagnosed