mirror of
https://salsa.debian.org/dskoll/remind.git
synced 2026-04-17 14:59:20 +02:00
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:
@@ -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"
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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? */
|
||||
/*---------------------------------------------------------------------*/
|
||||
|
||||
@@ -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? */
|
||||
/*---------------------------------------------------------------------*/
|
||||
|
||||
31
src/expr.c
31
src/expr.c
@@ -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:
|
||||
|
||||
33
src/files.c
33
src/files.c
@@ -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. */
|
||||
|
||||
49
src/funcs.c
49
src/funcs.c
@@ -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 */
|
||||
|
||||
@@ -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
214
src/ifelse.c
Normal 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--;
|
||||
}
|
||||
}
|
||||
@@ -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");
|
||||
|
||||
102
src/main.c
102
src/main.c
@@ -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 */
|
||||
|
||||
19
src/protos.h
19
src/protos.h
@@ -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);
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
10
src/types.h
10
src/types.h
@@ -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
|
||||
|
||||
18
src/var.c
18
src/var.c
@@ -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");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
1365
tests/test.cmp
1365
tests/test.cmp
File diff suppressed because one or more lines are too long
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user