Add the First/Second/Third/Fourth/Last syntactic sugar and the

~N/~~N forms of "back"
This commit is contained in:
Dianne Skoll
2022-03-11 20:53:52 -05:00
parent c06c65acf1
commit e1fa11c94c
6 changed files with 237 additions and 7 deletions

View File

@@ -1153,6 +1153,78 @@ Note that \fIduration\fR is specified either in hours and minutes as a
duration of 00:00 or 0, then \fBRemind\fR behaves exactly as if no
\fBDURATION\fR at all had been present.
.PP
.SH SYNTACTIC SUGAR FOR REM
.PP
The REM command has syntactic sugar to let you express common
reminders. The following pairs of reminders are equivalent:
.PP
.nf
REM First Monday April MSG Foo
REM Mon 1 April MSG Foo
REM Second Monday May MSG Bar
REM Mon 8 May MSG Bar
REM Third Monday MSG Third Monday of every month
REM Mon 15 MSG Third Monday of every month
REM Fourth Sunday June 2025 MSG Fourth Sunday in June 2025
REM Sun 22 June 2025 MSG Fourth Sunday in June 2025
REM Last Monday MSG Last Monday of every month
REM Mon 1 --7 MSG Last Monday of every month
REM Last Monday April MSG Last Monday of every April
REM Mon 1 May --7 MSG Last Monday of every April
REM Last Monday December 2025 MSG Last Monday of Dec 2025
REM Monday 1 Jan 2026 --7 MSG Last Monday of Dec 2025
.fi
.PP
Note that \fBLast\fR effectively adjusts the month and year, if necessary, to
make the reminder trigger on the correct date.
.PP
The keyword \fBIN\fR is completely ignored, so you can write (for example):
.PP
.nf
REM Second Monday in May MSG foo
REM Last Monday in December 2025 MSG Bar
.fi
.PP
An alternate form of \fIback\fR makes writing reminders easier.
The following pairs of reminders are equivalent:
.PP
.nf
REM ~~1 MSG Last day of every month
REM 1 --1 MSG Last day of every month
REM May ~~1 MSG Last day of May
REM 1 June --1 MSG Last day of May
REM Dec 2025 ~~1 MSG Last day of December 2025
REM 1 Jan 2026 --1 MSG Last day of December 2025
REM Apr ~1 OMIT SAT SUN MSG Last workday of April
REM 1 May -1 OMIT SAT SUN MSG Last workday of April
REM Apr ~~7 MSG Seventh-last day of April
REM 1 May --7 MSG Seventh-last day of April
REM Apr ~2 OMIT SAT SUN MSG Second-last workday of April
REM 1 May -2 OMIT SAT SUN MSG Second-last workday of April
.fi
.PP
Note that the First/Second/Third/Fourth/Last keywords and the ~ and ~~ form
of \fIback\fR imply a value for the day of the month; as such, they cannot
be combined with a day. Additionally, First/Second/Third/Fourth/Last
must have at least one weekday name. The following are illegal:
.PP
.nf
REM First Monday 3 June MSG Huh?
REM April 3 ~~1 MSG What?
REM Second June MSG Where's the weekday???
.fi
.PP
.SH THE SUBSTITUTION FILTER
.PP

View File

@@ -237,6 +237,9 @@ int ParseRem(ParsePtr s, Trigger *trig, TimeTrig *tim, int save_in_globals)
tim->delta = DefaultTDelta;
tim->rep = NO_REP;
tim->duration = NO_TIME;
trig->need_wkday = 0;
trig->adj_for_last = 0;
if (save_in_globals) {
LastTriggerTime = NO_TIME;
}
@@ -250,6 +253,25 @@ int ParseRem(ParsePtr s, Trigger *trig, TimeTrig *tim, int save_in_globals)
/* Figure out what we've got */
FindToken(DBufValue(&buf), &tok);
switch(tok.type) {
case T_In:
/* Completely ignored */
DBufFree(&buf);
break;
case T_Ordinal:
DBufFree(&buf);
if (trig->d != NO_DAY) return E_DAY_TWICE;
if (tok.val < 0) {
if (trig->back != NO_BACK) return E_BACK_TWICE;
trig->back = -7;
trig->d = 1;
trig->adj_for_last = 1;
} else {
trig->d = 1 + 7 * tok.val;
}
trig->need_wkday = 1;
break;
case T_Date:
DBufFree(&buf);
if (trig->d != NO_DAY) return E_DAY_TWICE;
@@ -379,6 +401,15 @@ int ParseRem(ParsePtr s, Trigger *trig, TimeTrig *tim, int save_in_globals)
trig->back = tok.val;
break;
case T_BackAdj:
DBufFree(&buf);
if (trig->back != NO_BACK) return E_BACK_TWICE;
if (trig->d != NO_DAY) return E_DAY_TWICE;
trig->back = tok.val;
trig->d = 1;
trig->adj_for_last = 1;
break;
case T_Once:
DBufFree(&buf);
if (trig->once != NO_ONCE) return E_ONCE_TWICE;
@@ -480,6 +511,24 @@ int ParseRem(ParsePtr s, Trigger *trig, TimeTrig *tim, int save_in_globals)
}
}
if (trig->need_wkday && trig->wd == NO_WD) {
Eprint("Weekday name(s) required");
return E_PARSE_ERR;
}
/* Adjust month and possibly year */
if (trig->adj_for_last) {
if (trig->m != NO_MON) {
trig->m++;
if (trig->m >= 12) {
trig->m = 0;
if (trig->y != NO_YR) {
trig->y++;
}
}
}
trig->adj_for_last = 0;
}
/* Check for some warning conditions */
if (!s->nonconst_expr) {

View File

@@ -43,30 +43,34 @@ Token TokArray[] = {
{ "at", 2, T_At, 0 },
{ "august", 3, T_Month, 7 },
{ "banner", 3, T_Banner, 0 },
{ "before", 3, T_Skip, BEFORE_SKIP },
{ "before", 3, T_Skip, BEFORE_SKIP },
{ "cal", 3, T_RemType, CAL_TYPE },
{ "clear-omit-context", 5, T_Clr, 0 },
{ "debug", 5, T_Debug, 0 },
{ "debug", 5, T_Debug, 0 },
{ "december", 3, T_Month, 11 },
{ "do", 2, T_IncludeR, 0 },
{ "dumpvars", 4, T_Dumpvars, 0 },
{ "dumpvars", 4, T_Dumpvars, 0 },
{ "duration", 3, T_Duration, 0 },
{ "else", 4, T_Else, 0 },
{ "else", 4, T_Else, 0 },
{ "endif", 5, T_EndIf, 0 },
{ "errmsg", 6, T_ErrMsg, 0 },
{ "errmsg", 6, T_ErrMsg, 0 },
{ "exit", 4, T_Exit, 0 },
{ "february", 3, T_Month, 1 },
{ "first", 5, T_Ordinal, 0 },
{ "flush", 5, T_Flush, 0 },
{ "fourth", 6, T_Ordinal, 3 },
{ "friday", 3, T_WkDay, 4 },
{ "from", 4, T_Scanfrom, FROM_TYPE },
{ "fset", 4, T_Fset, 0 },
{ "if", 2, T_If, 0 },
{ "iftrig", 6, T_IfTrig, 0 },
{ "in", 2, T_In, 0 },
{ "include", 3, T_Include, 0 },
{ "includecmd", 10, T_IncludeCmd, 0 },
{ "january", 3, T_Month, 0 },
{ "july", 3, T_Month, 6 },
{ "june", 3, T_Month, 5 },
{ "last", 4, T_Ordinal, -1 },
{ "march", 3, T_Month, 2 },
{ "may", 3, T_Month, 4 },
{ "maybe-uncomputable", 5, T_MaybeUncomputable, 0},
@@ -90,12 +94,14 @@ Token TokArray[] = {
{ "saturday", 3, T_WkDay, 5 },
{ "scanfrom", 4, T_Scanfrom, SCANFROM_TYPE },
{ "sched", 5, T_Sched, 0 },
{ "second", 6, T_Ordinal, 1 },
{ "september", 3, T_Month, 8 },
{ "set", 3, T_Set, 0 },
{ "skip", 3, T_Skip, SKIP_SKIP },
{ "special", 7, T_RemType, PASSTHRU_TYPE },
{ "sunday", 3, T_WkDay, 6 },
{ "tag", 3, T_Tag, 0 },
{ "third", 5, T_Ordinal, 2 },
{ "through", 7, T_Through, 0 },
{ "thursday", 3, T_WkDay, 3 },
{ "tuesday", 3, T_WkDay, 1 },
@@ -325,6 +331,14 @@ void FindNumericToken(char const *s, Token *t)
t->type = T_Back;
t->val *= mult;
return;
} else if (*s == '~') {
s++;
if (*s == '~') { mult = -1; s++; }
PARSENUM(t->val, s);
if (*s) return; /* Illegal token if followed by non-numeric char */
t->type = T_BackAdj;
t->val *= mult;
return;
}
return; /* Unknown token type */
}

View File

@@ -69,6 +69,8 @@ typedef struct {
int once;
int scanfrom;
int from;
int adj_for_last; /* Adjust month/year for use of LAST */
int need_wkday; /* Set if we *need* a weekday */
int priority;
int duration_days; /* Duration converted to days to search */
int eventstart; /* Original event start (datetime) */
@@ -157,7 +159,8 @@ enum TokTypes
T_AddOmit,
T_WkDay,
T_Month, T_Time, T_Date, T_DateTime,
T_Skip, T_At, T_RemType, T_Until, T_Year, T_Day, T_Rep, T_Delta, T_Back,
T_Skip, T_At, T_RemType, T_Until, T_Year, T_Day, T_Rep, T_Delta,
T_Back, T_BackAdj,
T_Once,
T_Empty,
T_Comment,
@@ -175,7 +178,10 @@ enum TokTypes
T_LongTime,
T_OmitFunc,
T_Through,
T_MaybeUncomputable
T_MaybeUncomputable,
T_Ordinal,
T_In,
T_LastBack
};
/* The structure of a token */

View File

@@ -3836,6 +3836,63 @@ trig("Mon", "Tue", "Wed") => ../tests/test.rem(754): Trig = Monday, 18 February,
1990-01-01
../tests/test.rem(754): Expired
# The new syntactic sugar
REM First Monday January MSG x
../tests/test.rem(757): Trig = Monday, 6 January, 1992
REM Second Tuesday in April MSG x
../tests/test.rem(758): Trig = Tuesday, 9 April, 1991
REM Third Wednesday in October MSG x
../tests/test.rem(759): Trig = Wednesday, 16 October, 1991
REM Fourth Friday in July MSG x
../tests/test.rem(760): Trig = Friday, 26 July, 1991
REM Last Tuesday in August MSG x
../tests/test.rem(761): Trig = Tuesday, 27 August, 1991
REM Last Sunday in December MSG x
../tests/test.rem(762): Trig = Sunday, 29 December, 1991
REM First Monday January 2000 MSG x
../tests/test.rem(764): Trig = Monday, 3 January, 2000
REM Second Tuesday in April 2000 MSG x
../tests/test.rem(765): Trig = Tuesday, 11 April, 2000
REM Third Wednesday in October 2000 MSG x
../tests/test.rem(766): Trig = Wednesday, 18 October, 2000
REM Fourth Friday in July 2000 MSG x
../tests/test.rem(767): Trig = Friday, 28 July, 2000
REM Last Tuesday in August 2000 MSG x
../tests/test.rem(768): Trig = Tuesday, 29 August, 2000
REM Last Sunday in December 2000 MSG x
../tests/test.rem(769): Trig = Sunday, 31 December, 2000
REM January ~~1 MSG y
../tests/test.rem(771): Trig = Friday, 31 January, 1992
REM February ~~1 MSG y
../tests/test.rem(772): Trig = Thursday, 28 February, 1991
REM February ~~2 MSG y
../tests/test.rem(773): Trig = Wednesday, 27 February, 1991
REM February ~~3 MSG y
../tests/test.rem(774): Trig = Tuesday, 26 February, 1991
REM February ~~8 MSG y
../tests/test.rem(775): Trig = Thursday, 21 February, 1991
REM February ~~20 MSG y
../tests/test.rem(776): Trig = Monday, 10 February, 1992
PUSH
OMIT 31 March
REM March ~1 MSG y
../tests/test.rem(779): Trig = Saturday, 30 March, 1991
REM March ~~1 MSG y
../tests/test.rem(780): Trig = Sunday, 31 March, 1991
POP
REM Dec 2000 ~~1 MSG y
../tests/test.rem(782): Trig = Sunday, 31 December, 2000
REM Dec 2000 ~~2 MSG y
../tests/test.rem(783): Trig = Saturday, 30 December, 2000
REM Dec 2000 ~~3 MSG y
../tests/test.rem(784): Trig = Friday, 29 December, 2000
REM Dec 2000 ~~7 MSG y
../tests/test.rem(785): Trig = Monday, 25 December, 2000
REM Jan 2001 ~~1 MSG y
../tests/test.rem(786): Trig = Wednesday, 31 January, 2001
# Don't want Remind to queue reminders
EXIT

View File

@@ -753,6 +753,38 @@ ENDIF
REM [trig("Mon", "Tue", "Wed", "Sat")] MSG foo
REM [trig("Mon", "Tue", "Wed")] MSG bar
# The new syntactic sugar
REM First Monday January MSG x
REM Second Tuesday in April MSG x
REM Third Wednesday in October MSG x
REM Fourth Friday in July MSG x
REM Last Tuesday in August MSG x
REM Last Sunday in December MSG x
REM First Monday January 2000 MSG x
REM Second Tuesday in April 2000 MSG x
REM Third Wednesday in October 2000 MSG x
REM Fourth Friday in July 2000 MSG x
REM Last Tuesday in August 2000 MSG x
REM Last Sunday in December 2000 MSG x
REM January ~~1 MSG y
REM February ~~1 MSG y
REM February ~~2 MSG y
REM February ~~3 MSG y
REM February ~~8 MSG y
REM February ~~20 MSG y
PUSH
OMIT 31 March
REM March ~1 MSG y
REM March ~~1 MSG y
POP
REM Dec 2000 ~~1 MSG y
REM Dec 2000 ~~2 MSG y
REM Dec 2000 ~~3 MSG y
REM Dec 2000 ~~7 MSG y
REM Jan 2001 ~~1 MSG y
# Don't want Remind to queue reminders
EXIT