Completely revamp expression engine.

This commit is contained in:
Dianne Skoll
2024-05-29 22:55:11 -04:00
parent 7728e09337
commit d2b43605ad
22 changed files with 2716 additions and 1654 deletions

View File

@@ -166,6 +166,10 @@
#define PASSTHRU_LEN 32
#define MAX_RECURSION_LEVEL 1000
#define MAX_FUNC_ARGS 64
#define PSBEGIN "# rem2ps begin"
#define PSEND "# rem2ps end"

View File

@@ -166,6 +166,10 @@
#define PASSTHRU_LEN 32
#define MAX_RECURSION_LEVEL 1000
#define MAX_FUNC_ARGS 64
#define PSBEGIN "# rem2ps begin"
#define PSEND "# rem2ps end"

View File

@@ -1268,20 +1268,28 @@ int DoSatRemind(Trigger *trig, TimeTrig *tt, ParsePtr p)
{
int iter, dse, r, start;
Value v;
char const *s;
char const *t;
expr_node *sat_node;
int nonconst = 0;
t = p->pos;
sat_node = ParseExpr(p, &r);
if (r != OK) {
return r;
}
if (!sat_node) {
return E_SWERR;
}
iter = 0;
start = trig->scanfrom;
while (iter++ < MaxSatIter) {
dse = ComputeTriggerNoAdjustDuration(start, trig, tt, &r, 1, 0);
if (r) {
free_expr_tree(sat_node);
if (r == E_CANT_TRIG) return OK; else return r;
}
if (dse != start && trig->duration_days) {
dse = ComputeTriggerNoAdjustDuration(start, trig, tt, &r, 1, trig->duration_days);
if (r) {
free_expr_tree(sat_node);
if (r == E_CANT_TRIG) return OK; else return r;
}
} else if (dse == start) {
@@ -1294,13 +1302,18 @@ int DoSatRemind(Trigger *trig, TimeTrig *tt, ParsePtr p)
SaveAllTriggerInfo(trig, tt, dse, tt->ttime, 1);
}
if (dse == -1) {
free_expr_tree(sat_node);
return E_EXPIRED;
}
s = p->pos;
r = EvaluateExpr(p, &v);
t = p->pos;
if (r) return r;
if (v.type != INT_TYPE && v.type != STR_TYPE) return E_BAD_TYPE;
r = evaluate_expr_node(sat_node, NULL, &v, &nonconst);
if (r) {
free_expr_tree(sat_node);
return r;
}
if (v.type != INT_TYPE && v.type != STR_TYPE) {
free_expr_tree(sat_node);
return E_BAD_TYPE;
}
if ((v.type == INT_TYPE && v.v.val) ||
(v.type == STR_TYPE && *v.v.str)) {
AdjustTriggerForDuration(trig->scanfrom, dse, trig, tt, 1);
@@ -1325,17 +1338,17 @@ int DoSatRemind(Trigger *trig, TimeTrig *tt, ParsePtr p)
}
fprintf(ErrFp, "\n");
}
free_expr_tree(sat_node);
return OK;
}
p->pos = s;
if (dse+trig->duration_days < start) {
start++;
} else {
start = dse+trig->duration_days+1;
}
}
p->pos = t;
LastTrigValid = 0;
free_expr_tree(sat_node);
return E_CANT_TRIG;
}

View File

@@ -12,6 +12,7 @@
/***************************************************************/
#include "config.h"
#include "types.h"
#include "expr.h"
#define L_IN_DOSUBST
#include <stdio.h>
@@ -20,7 +21,6 @@
#include <stdlib.h>
#include "types.h"
#include "globals.h"
#include "err.h"
#include "protos.h"

View File

@@ -124,7 +124,9 @@ int DBufPuts(DynamicBuffer *dbuf, char const *str)
**********************************************************************/
void DBufFree(DynamicBuffer *dbuf)
{
if (dbuf->buffer != dbuf->staticBuf) free(dbuf->buffer);
if (dbuf->buffer != NULL && dbuf->buffer != dbuf->staticBuf) {
free(dbuf->buffer);
}
DBufInit(dbuf);
}

View File

@@ -184,7 +184,7 @@ EXTERN char *ErrMsg[]
"RUN disabled",
"Domain error",
"Invalid identifier",
"Recursive function call detected",
"Too many recursive function calls",
"",
"Cannot modify system variable",
"C library function can't represent date/time",

3140
src/expr.c

File diff suppressed because it is too large Load Diff

View File

@@ -1,15 +1,19 @@
/***************************************************************/
/* */
/* EXPR.H */
/* EXPR_NEW.H */
/* */
/* Contains a few definitions used by expression evaluator. */
/* Contains a few definitions used by expression pareser and */
/* evaluator. */
/* */
/* This file is part of REMIND. */
/* Copyright (C) 1992-2024 by Dianne Skoll */
/* SPDX-License-Identifier: GPL-2.0-only */
/* Copyright (C) 2022 by Dianne Skoll */
/* */
/***************************************************************/
typedef struct udf_struct UserFunc;
expr_node *parse_expression(char const **e, int *r, Var *locals);
/* Define the types of values */
#define ERR_TYPE 0
#define INT_TYPE 1
@@ -20,43 +24,10 @@
#define SPECIAL_TYPE 6 /* Only for system variables */
#define CONST_INT_TYPE 7 /* Only for system variables */
/* Define stuff for parsing expressions */
#define BEG_OF_EXPR '['
#define END_OF_EXPR ']'
#define COMMA ','
#define UN_OP 0 /* Unary operator */
#define BIN_OP 1 /* Binary Operator */
#define FUNC 2 /* Function */
/* Make the pushing and popping of values and operators in-line code
for speed. BEWARE: These macros invoke return if an error happens ! */
#define PushOpStack(op) \
do { if (OpStackPtr >= OP_STACK_SIZE) return E_OP_STK_OVER; \
else { OpStack[OpStackPtr++] = (op); if (OpStackPtr > OpStackHiWater) OpStackHiWater = OpStackPtr; } } while(0)
#define PopOpStack(op) \
if (OpStackPtr <= 0) \
return E_OP_STK_UNDER; \
else \
(op) = OpStack[--OpStackPtr]
#define PushValStack(val) \
do { if (ValStackPtr >= VAL_STACK_SIZE) { \
DestroyValue(val); \
return E_VA_STK_OVER; \
} else { \
ValStack[ValStackPtr++] = (val); \
if (ValStackPtr > ValStackHiWater) ValStackHiWater = ValStackPtr; \
} } while (0);
#define PopValStack(val) \
if (ValStackPtr <= 0) \
return E_VA_STK_UNDER; \
else \
(val) = ValStack[--ValStackPtr]
/* These functions are in utils.c and are used to detect overflow
in various arithmetic operators. They have to be in separate
functions with extern linkage to defeat compiler optimizations
@@ -64,4 +35,3 @@ else \
extern int _private_mul_overflow(int a, int b);
extern int _private_add_overflow(int a, int b);
extern int _private_sub_overflow(int a, int b);

View File

@@ -60,6 +60,8 @@
#define Nargs (info->nargs)
#define RetVal (info->retval)
#define DBG(x) do { if (DebugFlag & DB_PRTEXPR) { x; } } while(0)
static int
solstice_equinox_for_year(int y, int which);
@@ -74,7 +76,7 @@ static int FArgs (func_info *);
static int FAsc (func_info *);
static int FBaseyr (func_info *);
static int FChar (func_info *);
static int FChoose (func_info *);
static int FChoose (expr_node *, Value *, Value *, int *);
static int FCoerce (func_info *);
static int FColumns (func_info *);
static int FCurrent (func_info *);
@@ -101,7 +103,7 @@ static int FHebyear (func_info *);
static int FHour (func_info *);
static int FHtmlEscape (func_info *);
static int FHtmlStriptags (func_info *);
static int FIif (func_info *);
static int FIif (expr_node *, Value *, Value *, int *);
static int FIndex (func_info *);
static int FIsAny (func_info *);
static int FIsdst (func_info *);
@@ -180,14 +182,9 @@ static int FWkday (func_info *);
static int FWkdaynum (func_info *);
static int FYear (func_info *);
static int CleanUpAfterFunc (func_info *);
static int CheckArgs (BuiltinFunc *f, int nargs);
static int SunStuff (int rise, double cosz, int dse);
static int tz_set_tz (char const *tz);
/* "Overload" the struct Operator definition */
#define NO_MAX 127
/* Caches for extracting months, days, years from dates - may
improve performance slightly. */
static int CacheDse = -1;
@@ -196,14 +193,9 @@ static int CacheYear, CacheMon, CacheDay;
static int CacheHebDse = -1;
static int CacheHebYear, CacheHebMon, CacheHebDay;
/* We need access to the value stack */
extern Value ValStack[];
extern int ValStackPtr;
extern int ValStackHiWater;
/* Macro for accessing arguments from the value stack - args are numbered
from 0 to (Nargs - 1) */
#define ARG(x) (ValStack[ValStackPtr - Nargs + (x)])
#define ARG(x) (info->args[x])
#define ARGV(x) ARG(x).v.val
#define ARGSTR(x) ARG(x).v.str
@@ -231,215 +223,127 @@ extern int ValStackHiWater;
/* The array holding the built-in functions. */
BuiltinFunc Func[] = {
/* Name minargs maxargs is_constant func */
/* Name minargs maxargs is_constant func newfunc*/
{ "abs", 1, 1, 1, FAbs },
{ "access", 2, 2, 0, FAccess },
{ "adawn", 0, 1, 0, FADawn},
{ "adusk", 0, 1, 0, FADusk},
{ "ampm", 1, 3, 1, FAmpm },
{ "ansicolor", 1, 5, 1, FAnsicolor },
{ "args", 1, 1, 0, FArgs },
{ "asc", 1, 1, 1, FAsc },
{ "baseyr", 0, 0, 1, FBaseyr },
{ "char", 1, NO_MAX, 1, FChar },
{ "choose", 2, NO_MAX, 1, FChoose },
{ "coerce", 2, 2, 1, FCoerce },
{ "columns", 0, 1, 0, FColumns },
{ "current", 0, 0, 0, FCurrent },
{ "date", 3, 3, 1, FDate },
{ "datepart", 1, 1, 1, FDatepart },
{ "datetime", 2, 5, 1, FDateTime },
{ "dawn", 0, 1, 0, FDawn},
{ "day", 1, 1, 1, FDay },
{ "daysinmon", 2, 2, 1, FDaysinmon },
{ "defined", 1, 1, 0, FDefined },
{ "dosubst", 1, 3, 0, FDosubst },
{ "dusk", 0, 1, 0, FDusk },
{ "easterdate", 0, 1, 0, FEasterdate },
{ "evaltrig", 1, 2, 0, FEvalTrig },
{ "filedate", 1, 1, 0, FFiledate },
{ "filedatetime", 1, 1, 0, FFiledatetime },
{ "filedir", 0, 0, 0, FFiledir },
{ "filename", 0, 0, 0, FFilename },
{ "getenv", 1, 1, 0, FGetenv },
{ "hebdate", 2, 5, 0, FHebdate },
{ "hebday", 1, 1, 0, FHebday },
{ "hebmon", 1, 1, 0, FHebmon },
{ "hebyear", 1, 1, 0, FHebyear },
{ "hour", 1, 1, 1, FHour },
{ "htmlescape", 1, 1, 1, FHtmlEscape },
{ "htmlstriptags",1, 1, 1, FHtmlStriptags },
{ "iif", 1, NO_MAX, 1, FIif },
{ "index", 2, 3, 1, FIndex },
{ "isany", 1, NO_MAX, 1, FIsAny },
{ "isdst", 0, 2, 0, FIsdst },
{ "isleap", 1, 1, 1, FIsleap },
{ "isomitted", 1, 1, 0, FIsomitted },
{ "language", 0, 0, 1, FLanguage },
{ "localtoutc", 1, 1, 1, FLocalToUTC },
{ "lower", 1, 1, 1, FLower },
{ "max", 1, NO_MAX, 1, FMax },
{ "min", 1, NO_MAX, 1, FMin },
{ "minsfromutc", 0, 2, 0, FMinsfromutc },
{ "minute", 1, 1, 1, FMinute },
{ "mon", 1, 1, 1, FMon },
{ "monnum", 1, 1, 1, FMonnum },
{ "moondate", 1, 3, 0, FMoondate },
{ "moondatetime", 1, 3, 0, FMoondatetime },
{ "moonphase", 0, 2, 0, FMoonphase },
{ "moontime", 1, 3, 0, FMoontime },
{ "multitrig", 1, NO_MAX, 0, FMultiTrig },
{ "ndawn", 0, 1, 0, FNDawn},
{ "ndusk", 0, 1, 0, FNDusk},
{ "nonomitted", 2, NO_MAX, 0, FNonomitted },
{ "now", 0, 0, 0, FNow },
{ "ord", 1, 1, 1, FOrd },
{ "orthodoxeaster",0, 1, 0, FOrthodoxeaster },
{ "ostype", 0, 0, 1, FOstype },
{ "pad", 3, 4, 1, FPad },
{ "plural", 1, 3, 1, FPlural },
{ "psmoon", 1, 4, 1, FPsmoon},
{ "psshade", 1, 3, 1, FPsshade},
{ "realcurrent", 0, 0, 0, FRealCurrent},
{ "realnow", 0, 0, 0, FRealnow},
{ "realtoday", 0, 0, 0, FRealtoday },
{ "rows", 0, 0, 0, FRows },
{ "sgn", 1, 1, 1, FSgn },
{ "shell", 1, 2, 0, FShell },
{ "shellescape", 1, 1, 1, FShellescape },
{ "slide", 2, NO_MAX, 0, FSlide },
{ "soleq", 1, 2, 0, FSoleq },
{ "stdout", 0, 0, 1, FStdout },
{ "strlen", 1, 1, 1, FStrlen },
{ "substr", 2, 3, 1, FSubstr },
{ "sunrise", 0, 1, 0, FSunrise},
{ "sunset", 0, 1, 0, FSunset },
{ "time", 2, 2, 1, FTime },
{ "timepart", 1, 1, 1, FTimepart },
{ "timezone", 0, 1, 1, FTimezone },
{ "today", 0, 0, 0, FToday },
{ "trig", 0, NO_MAX, 0, FTrig },
{ "trigback", 0, 0, 0, FTrigback },
{ "trigdate", 0, 0, 0, FTrigdate },
{ "trigdatetime", 0, 0, 0, FTrigdatetime },
{ "trigdelta", 0, 0, 0, FTrigdelta },
{ "trigduration", 0, 0, 0, FTrigduration },
{ "trigeventduration", 0, 0, 0, FTrigeventduration },
{ "trigeventstart", 0, 0, 0, FTrigeventstart },
{ "trigfrom", 0, 0, 0, FTrigfrom },
{ "trigger", 1, 3, 0, FTrigger },
{ "trigpriority", 0, 0, 0, FTrigpriority },
{ "trigrep", 0, 0, 0, FTrigrep },
{ "trigscanfrom", 0, 0, 0, FTrigscanfrom },
{ "trigtags", 0, 0, 0, FTrigtags },
{ "trigtime", 0, 0, 0, FTrigtime },
{ "trigtimedelta",0, 0, 0, FTrigtimedelta },
{ "trigtimerep", 0, 0, 0, FTrigtimerep },
{ "triguntil", 0, 0, 0, FTriguntil },
{ "trigvalid", 0, 0, 0, FTrigvalid },
{ "typeof", 1, 1, 1, FTypeof },
{ "tzconvert", 2, 3, 0, FTzconvert },
{ "upper", 1, 1, 1, FUpper },
{ "utctolocal", 1, 1, 1, FUTCToLocal },
{ "value", 1, 2, 0, FValue },
{ "version", 0, 0, 1, FVersion },
{ "weekno", 0, 3, 1, FWeekno },
{ "wkday", 1, 1, 1, FWkday },
{ "wkdaynum", 1, 1, 1, FWkdaynum },
{ "year", 1, 1, 1, FYear }
{ "abs", 1, 1, 1, FAbs, NULL },
{ "access", 2, 2, 0, FAccess, NULL },
{ "adawn", 0, 1, 0, FADawn, NULL},
{ "adusk", 0, 1, 0, FADusk, NULL},
{ "ampm", 1, 3, 1, FAmpm, NULL },
{ "ansicolor", 1, 5, 1, FAnsicolor, NULL },
{ "args", 1, 1, 0, FArgs, NULL },
{ "asc", 1, 1, 1, FAsc, NULL },
{ "baseyr", 0, 0, 1, FBaseyr, NULL },
{ "char", 1, NO_MAX, 1, FChar, NULL },
{ "choose", 2, NO_MAX, 1, NULL, FChoose }, /*NEW-STYLE*/
{ "coerce", 2, 2, 1, FCoerce, NULL },
{ "columns", 0, 1, 0, FColumns, NULL },
{ "current", 0, 0, 0, FCurrent, NULL },
{ "date", 3, 3, 1, FDate, NULL },
{ "datepart", 1, 1, 1, FDatepart, NULL },
{ "datetime", 2, 5, 1, FDateTime, NULL },
{ "dawn", 0, 1, 0, FDawn, NULL },
{ "day", 1, 1, 1, FDay, NULL },
{ "daysinmon", 2, 2, 1, FDaysinmon, NULL },
{ "defined", 1, 1, 0, FDefined, NULL },
{ "dosubst", 1, 3, 0, FDosubst, NULL },
{ "dusk", 0, 1, 0, FDusk, NULL },
{ "easterdate", 0, 1, 0, FEasterdate, NULL },
{ "evaltrig", 1, 2, 0, FEvalTrig, NULL },
{ "filedate", 1, 1, 0, FFiledate, NULL },
{ "filedatetime", 1, 1, 0, FFiledatetime, NULL },
{ "filedir", 0, 0, 0, FFiledir, NULL },
{ "filename", 0, 0, 0, FFilename, NULL },
{ "getenv", 1, 1, 0, FGetenv, NULL },
{ "hebdate", 2, 5, 0, FHebdate, NULL },
{ "hebday", 1, 1, 0, FHebday, NULL },
{ "hebmon", 1, 1, 0, FHebmon, NULL },
{ "hebyear", 1, 1, 0, FHebyear, NULL },
{ "hour", 1, 1, 1, FHour, NULL },
{ "htmlescape", 1, 1, 1, FHtmlEscape, NULL },
{ "htmlstriptags",1, 1, 1, FHtmlStriptags, NULL },
{ "iif", 1, NO_MAX, 1, NULL, FIif }, /*NEW-STYLE*/
{ "index", 2, 3, 1, FIndex, NULL },
{ "isany", 1, NO_MAX, 1, FIsAny, NULL },
{ "isdst", 0, 2, 0, FIsdst, NULL },
{ "isleap", 1, 1, 1, FIsleap, NULL },
{ "isomitted", 1, 1, 0, FIsomitted, NULL },
{ "language", 0, 0, 1, FLanguage, NULL },
{ "localtoutc", 1, 1, 1, FLocalToUTC, NULL },
{ "lower", 1, 1, 1, FLower, NULL },
{ "max", 1, NO_MAX, 1, FMax, NULL },
{ "min", 1, NO_MAX, 1, FMin, NULL },
{ "minsfromutc", 0, 2, 0, FMinsfromutc, NULL },
{ "minute", 1, 1, 1, FMinute, NULL },
{ "mon", 1, 1, 1, FMon, NULL },
{ "monnum", 1, 1, 1, FMonnum, NULL },
{ "moondate", 1, 3, 0, FMoondate, NULL },
{ "moondatetime", 1, 3, 0, FMoondatetime, NULL },
{ "moonphase", 0, 2, 0, FMoonphase, NULL },
{ "moontime", 1, 3, 0, FMoontime, NULL },
{ "multitrig", 1, NO_MAX, 0, FMultiTrig, NULL },
{ "ndawn", 0, 1, 0, FNDawn, NULL },
{ "ndusk", 0, 1, 0, FNDusk, NULL },
{ "nonomitted", 2, NO_MAX, 0, FNonomitted, NULL },
{ "now", 0, 0, 0, FNow, NULL },
{ "ord", 1, 1, 1, FOrd, NULL },
{ "orthodoxeaster",0, 1, 0, FOrthodoxeaster, NULL },
{ "ostype", 0, 0, 1, FOstype, NULL },
{ "pad", 3, 4, 1, FPad, NULL },
{ "plural", 1, 3, 1, FPlural, NULL },
{ "psmoon", 1, 4, 1, FPsmoon, NULL },
{ "psshade", 1, 3, 1, FPsshade, NULL },
{ "realcurrent", 0, 0, 0, FRealCurrent, NULL },
{ "realnow", 0, 0, 0, FRealnow, NULL },
{ "realtoday", 0, 0, 0, FRealtoday, NULL },
{ "rows", 0, 0, 0, FRows, NULL },
{ "sgn", 1, 1, 1, FSgn, NULL },
{ "shell", 1, 2, 0, FShell, NULL },
{ "shellescape", 1, 1, 1, FShellescape, NULL },
{ "slide", 2, NO_MAX, 0, FSlide, NULL },
{ "soleq", 1, 2, 0, FSoleq, NULL },
{ "stdout", 0, 0, 1, FStdout, NULL },
{ "strlen", 1, 1, 1, FStrlen, NULL },
{ "substr", 2, 3, 1, FSubstr, NULL },
{ "sunrise", 0, 1, 0, FSunrise, NULL },
{ "sunset", 0, 1, 0, FSunset, NULL },
{ "time", 2, 2, 1, FTime, NULL },
{ "timepart", 1, 1, 1, FTimepart, NULL },
{ "timezone", 0, 1, 1, FTimezone, NULL },
{ "today", 0, 0, 0, FToday, NULL },
{ "trig", 0, NO_MAX, 0, FTrig, NULL },
{ "trigback", 0, 0, 0, FTrigback, NULL },
{ "trigdate", 0, 0, 0, FTrigdate, NULL },
{ "trigdatetime", 0, 0, 0, FTrigdatetime, NULL },
{ "trigdelta", 0, 0, 0, FTrigdelta, NULL },
{ "trigduration", 0, 0, 0, FTrigduration, NULL },
{ "trigeventduration", 0, 0, 0, FTrigeventduration, NULL },
{ "trigeventstart", 0, 0, 0, FTrigeventstart, NULL },
{ "trigfrom", 0, 0, 0, FTrigfrom, NULL },
{ "trigger", 1, 3, 0, FTrigger, NULL },
{ "trigpriority", 0, 0, 0, FTrigpriority, NULL },
{ "trigrep", 0, 0, 0, FTrigrep, NULL },
{ "trigscanfrom", 0, 0, 0, FTrigscanfrom, NULL },
{ "trigtags", 0, 0, 0, FTrigtags, NULL },
{ "trigtime", 0, 0, 0, FTrigtime, NULL },
{ "trigtimedelta",0, 0, 0, FTrigtimedelta, NULL },
{ "trigtimerep", 0, 0, 0, FTrigtimerep, NULL },
{ "triguntil", 0, 0, 0, FTriguntil, NULL },
{ "trigvalid", 0, 0, 0, FTrigvalid, NULL },
{ "typeof", 1, 1, 1, FTypeof, NULL },
{ "tzconvert", 2, 3, 0, FTzconvert, NULL },
{ "upper", 1, 1, 1, FUpper, NULL },
{ "utctolocal", 1, 1, 1, FUTCToLocal, NULL },
{ "value", 1, 2, 0, FValue, NULL },
{ "version", 0, 0, 1, FVersion, NULL },
{ "weekno", 0, 3, 1, FWeekno, NULL },
{ "wkday", 1, 1, 1, FWkday, NULL },
{ "wkdaynum", 1, 1, 1, FWkdaynum, NULL },
{ "year", 1, 1, 1, FYear, NULL }
};
/* Need a variable here - Func[] array not really visible to outside. */
int NumFuncs = sizeof(Func) / sizeof(Operator) ;
/***************************************************************/
/* */
/* CallFunc */
/* */
/* Call a function given a pointer to it, and the number */
/* of arguments supplied. */
/* */
/***************************************************************/
int CallFunc(BuiltinFunc *f, int nargs)
{
register int r = CheckArgs(f, nargs);
int i;
func_info info_obj;
func_info *info = &info_obj;
Nargs = nargs;
RetVal.type = ERR_TYPE;
if (DebugFlag & DB_PRTEXPR) {
fprintf(ErrFp, "%s(", f->name);
for (i=0; i<nargs; i++) {
PrintValue(&ARG(i), ErrFp);
if (i<nargs-1) fprintf(ErrFp, ", ");
}
fprintf(ErrFp, ") => ");
if (r) {
fprintf(ErrFp, "%s\n", ErrMsg[r]);
return r;
}
}
if (r) {
Eprint("%s(): %s", f->name, ErrMsg[r]);
return r;
}
r = (*(f->func))(info);
if (r) {
DestroyValue(RetVal);
if (DebugFlag & DB_PRTEXPR)
fprintf(ErrFp, "%s\n", ErrMsg[r]);
else
Eprint("%s(): %s", f->name, ErrMsg[r]);
return r;
}
if (DebugFlag & DB_PRTEXPR) {
PrintValue(&RetVal, ErrFp);
fprintf(ErrFp, "\n");
}
r = CleanUpAfterFunc(info);
return r;
}
/***************************************************************/
/* */
/* CheckArgs */
/* */
/* Check that the right number of args have been supplied */
/* for a function. */
/* */
/***************************************************************/
static int CheckArgs(BuiltinFunc *f, int nargs)
{
if (nargs < f->minargs) return E_2FEW_ARGS;
if (nargs > f->maxargs && f->maxargs != NO_MAX) return E_2MANY_ARGS;
return OK;
}
/***************************************************************/
/* */
/* CleanUpAfterFunc */
/* */
/* Clean up the stack after a function call - remove */
/* args and push the new value. */
/* */
/***************************************************************/
static int CleanUpAfterFunc(func_info *info)
{
Value v;
int i;
for (i=0; i<Nargs; i++) {
PopValStack(v);
DestroyValue(v);
}
PushValStack(RetVal);
return OK;
}
int NumFuncs = sizeof(Func) / sizeof(BuiltinFunc) ;
/***************************************************************/
/* */
@@ -1279,6 +1183,9 @@ static int FIsAny(func_info *info)
return OK;
}
/* Debugging helpers for "choose()" and "iif() */
#define PUT(x) DBufPuts(&DebugBuf, x)
#define OUT() do { fprintf(ErrFp, "%s\n", DBufValue(&DebugBuf)); DBufFree(&DebugBuf); } while(0)
/***************************************************************/
/* */
/* FChoose */
@@ -1287,15 +1194,63 @@ static int FIsAny(func_info *info)
/* from 1. */
/* */
/***************************************************************/
static int FChoose(func_info *info)
static int FChoose(expr_node *node, Value *locals, Value *ans, int *nonconst)
{
int v;
DynamicBuffer DebugBuf;
expr_node *cur;
int r;
int n;
int nargs = node->num_kids;
Value(v);
DBG(DBufInit(&DebugBuf));
PUT("choose(");
ASSERT_TYPE(0, INT_TYPE);
v = ARGV(0);
if (v < 1) v = 1;
if (v > Nargs-1) v = Nargs-1;
DCOPYVAL(RetVal, ARG(v));
cur = node->child;
r = evaluate_expr_node(cur, locals, &v, nonconst);
if (r != OK) {
DBG(DBufFree(&DebugBuf));
return r;
}
DBG(PUT(PrintValue(&v, NULL)));
if (v.type != INT_TYPE) {
if (DebugFlag & DB_PRTEXPR) {
cur = cur->sibling;
while(cur) {
PUT(", ?");
cur = cur->sibling;
}
PUT(") => ");
PUT(ErrMsg[E_BAD_TYPE]);
OUT();
}
return E_BAD_TYPE;
}
n = v.v.val;
if (n < 1) n = 1;
if (n > nargs-1) n = nargs-1;
while(n--) {
cur = cur->sibling;
DBG(if (n) { PUT(", ?"); });
if (!cur) return E_SWERR; /* Should not happen! */
}
r = evaluate_expr_node(cur, locals, ans, nonconst);
if (r != OK) {
DBG(DBufFree(&DebugBuf));
return r;
}
if (DebugFlag & DB_PRTEXPR) {
PUT(", ");
PUT(PrintValue(ans, NULL));
cur = cur->sibling;
while(cur) {
PUT(", ?");
cur = cur->sibling;
}
PUT(") => ");
PUT(PrintValue(ans, NULL));
OUT();
}
return OK;
}
@@ -1481,7 +1436,7 @@ static int FValue(func_info *info)
ASSERT_TYPE(0, STR_TYPE);
switch(Nargs) {
case 1:
return GetVarValue(ARGSTR(0), &RetVal, NULL, NULL);
return GetVarValue(ARGSTR(0), &RetVal);
case 2:
v = FindVar(ARGSTR(0), 0);
@@ -1953,33 +1908,101 @@ static int FIndex(func_info *info)
/* */
/* FIif */
/* */
/* The IIF function. */
/* The IIF function. Uses new-style evaluation */
/* */
/***************************************************************/
static int FIif(func_info *info)
static int FIif(expr_node *node, Value *locals, Value *ans, int *nonconst)
{
int istrue;
int arg;
int r;
int done;
Value v;
expr_node *cur;
DynamicBuffer DebugBuf;
if (!(Nargs % 2)) return E_IIF_ODD;
DBG(DBufInit(&DebugBuf));
DBG(PUT("iif("));
cur = node->child;
for (arg=0; arg<Nargs-1; arg += 2) {
if (ARG(arg).type != STR_TYPE && ARG(arg).type != INT_TYPE)
return E_BAD_TYPE;
if (ARG(arg).type == INT_TYPE)
istrue = ARG(arg).v.val;
else
istrue = *(ARG(arg).v.str);
if (istrue) {
DCOPYVAL(RetVal, ARG(arg+1));
return OK;
}
if (!(node->num_kids % 2)) {
if (DebugFlag & DB_PRTEXPR) {
r = 0;
while(cur) {
if (r) PUT(", ");
r=1;
PUT("?");
cur = cur->sibling;
}
PUT(") => ");
PUT(ErrMsg[E_IIF_ODD]);
OUT();
}
return E_IIF_ODD;
}
DCOPYVAL(RetVal, ARG(Nargs-1));
return OK;
done = 0;
while(cur->sibling) {
r = evaluate_expr_node(cur, locals, &v, nonconst);
if (r != OK) {
DBG(DBufFree(&DebugBuf));
return r;
}
if (DebugFlag & DB_PRTEXPR) {
if (done) PUT(", ");
done = 1;
PUT(PrintValue(&v, NULL));
}
if (v.type != STR_TYPE && v.type != INT_TYPE) {
if (DebugFlag & DB_PRTEXPR) {
cur = cur->sibling;
while(cur) {
PUT(", ?");
cur = cur->sibling;
}
PUT(") => ");
PUT(ErrMsg[E_BAD_TYPE]);
OUT();
}
return E_BAD_TYPE;
}
if (v.type == INT_TYPE) {
istrue = v.v.val;
} else {
istrue = *(v.v.str);
}
if (istrue) {
r = evaluate_expr_node(cur->sibling, locals, ans, nonconst);
if (r == OK && (DebugFlag & DB_PRTEXPR)) {
PUT(", ");
PUT(PrintValue(ans, NULL));
cur = cur->sibling->sibling;
while(cur) {
PUT(", ?");
cur = cur->sibling;
}
PUT(") => ");
PUT(PrintValue(ans, NULL));
OUT();
}
DBG(DBufFree(&DebugBuf));
return r;
}
DBG(PUT(", ?"));
cur = cur->sibling->sibling;
}
/* Return the last arg */
r = evaluate_expr_node(cur, locals, ans, nonconst);
if (DebugFlag & DB_PRTEXPR) {
if (done) PUT(", ");
PUT(PrintValue(ans, NULL));
PUT(") => ");
PUT(PrintValue(ans, NULL));
OUT();
}
return r;
}
/***************************************************************/
@@ -2373,14 +2396,18 @@ static int FEasterdate(func_info *info)
{
int y, m, d;
int g, c, x, z, e, n;
int base;
if (Nargs == 0) {
base = DSEToday;
FromDSE(DSEToday, &y, &m, &d);
} else {
if (ARG(0).type == INT_TYPE) {
base = -1;
y = ARGV(0);
if (y < BASE) return E_2LOW;
else if (y > BASE+YR_RANGE) return E_2HIGH;
} else if (HASDATE(ARG(0))) {
base = DATEPART(ARG(0));
FromDSE(DATEPART(ARG(0)), &y, &m, &d); /* We just want the year */
} else return E_BAD_TYPE;
}
@@ -2406,7 +2433,7 @@ static int FEasterdate(func_info *info)
RetVal.type = DATE_TYPE;
RETVAL = DSE(y, m, d);
y++; } while (HASDATE(ARG(0)) && RETVAL < DATEPART(ARG(0)));
y++; } while (base > -1 && RETVAL < base);
return OK;
}
@@ -2422,7 +2449,9 @@ static int FOrthodoxeaster(func_info *info)
{
int y, m, d;
int a, b, c, dd, e, f, dse;
int base = -1;
if (Nargs == 0) {
base = DSEToday;
FromDSE(DSEToday, &y, &m, &d);
} else {
if (ARG(0).type == INT_TYPE) {
@@ -2430,6 +2459,7 @@ static int FOrthodoxeaster(func_info *info)
if (y < BASE) return E_2LOW;
else if (y > BASE+YR_RANGE) return E_2HIGH;
} else if (HASDATE(ARG(0))) {
base = DATEPART(ARG(0));
FromDSE(DATEPART(ARG(0)), &y, &m, &d); /* We just want the year */
} else return E_BAD_TYPE;
}
@@ -2449,7 +2479,7 @@ static int FOrthodoxeaster(func_info *info)
RetVal.type = DATE_TYPE;
RETVAL = dse;
y++;
} while (HASDATE(ARG(0)) && RETVAL < DATEPART(ARG(0)));
} while (base > -1 && RETVAL < base);
return OK;
}
@@ -3896,3 +3926,38 @@ FSoleq(func_info *info)
RETVAL = ret;
return OK;
}
/* Compare two strings case-insensitively, where we KNOW
that the second string is definitely lower-case */
static int strcmp_lcfirst(char const *s1, char const *s2)
{
int r;
while (*s1 && *s2) {
r = tolower(*s1) - *s2;
if (r) return r;
s1++;
s2++;
}
return tolower(*s1) - *s2;
}
/***************************************************************/
/* */
/* FindBuiltinFunc */
/* */
/* Find a built-in function. */
/* */
/***************************************************************/
BuiltinFunc *FindBuiltinFunc(char const *name)
{
int top=NumFuncs-1, bot=0;
int mid, r;
while (top >= bot) {
mid = (top + bot) / 2;
r = strcmp_lcfirst(name, Func[mid].name);
if (!r) return &Func[mid];
else if (r > 0) bot = mid+1;
else top = mid-1;
}
return NULL;
}

View File

@@ -159,6 +159,9 @@ EXTERN DynamicBuffer Banner;
EXTERN DynamicBuffer LineBuffer;
EXTERN DynamicBuffer ExprBuf;
/* User-func recursion level */
EXTERN INIT( unsigned int FuncRecursionLevel, 0);
extern int NumFullOmits, NumPartialOmits;
/* List of months */

View File

@@ -605,7 +605,7 @@ void InitRemind(int argc, char const *argv[])
case 'D':
while (*arg) {
switch(*arg++) {
case 's': case 'S': DebugFlag |= DB_EXPR_STACKS; break;
case 's': case 'S': DebugFlag |= DB_PARSE_EXPR; break;
case 'e': case 'E': DebugFlag |= DB_ECHO_LINE; break;
case 'x': case 'X': DebugFlag |= DB_PRTEXPR; break;
case 't': case 'T': DebugFlag |= DB_PRTTRIG; break;

View File

@@ -246,7 +246,9 @@ EXTERN char *ErrMsg[] =
"String too long",
"Time specified twice",
"Cannot specify DURATION without specifying AT",
"Odotettu viikonpäivän nimi"
"Odotettu viikonpäivän nimi",
"Päällekkäinen argumentin nimi"
};
#endif /* MK_GLOBALS */

View File

@@ -221,6 +221,7 @@ EXTERN char *ErrMsg[] =
"Time specified twice",
"Cannot specify DURATION without specifying AT",
"Nom du jour de la semaine attendu",
"Nom de l'argument en double",
};
#endif /* MK_GLOBALS */

View File

@@ -236,7 +236,8 @@ EXTERN char *ErrMsg[] =
"String too long",
"Time specified twice",
"Cannot specify DURATION without specifying AT",
"Oczekiwana nazwa dnia tygodnia"
"Oczekiwana nazwa dnia tygodnia",
"Zduplikowana nazwa argumentu",
};
#endif /* MK_GLOBALS */

View File

@@ -246,6 +246,7 @@ EXTERN char *ErrMsg[] =
"Time specified twice",
"Cannot specify DURATION without specifying AT",
"Esperando nome do dia da semana",
"Nome de argumento duplicado"
};
#endif /* MK_GLOBALS */

View File

@@ -55,6 +55,15 @@ static void DoReminders(void);
/* Macro for simplifying common block so as not to litter code */
#define OUTPUT(c) do { if (output) { DBufPutc(output, c); } else { putchar(c); } } while(0)
void
exitfunc(void)
{
if (DebugFlag & DB_PARSE_EXPR) {
UnsetAllUserFuncs();
print_expr_nodes_stats();
}
}
/***************************************************************/
/***************************************************************/
/** **/
@@ -81,7 +90,7 @@ int main(int argc, char *argv[])
DBufInit(&(LastTrigger.tags));
ClearLastTriggers();
atexit(DebugExitFunc);
atexit(exitfunc);
if (DoCalendar || (DoSimpleCalendar && (!NextMode || PsCal))) {
ProduceCalendar();
@@ -289,6 +298,7 @@ static void DoReminders(void)
case T_RemType: if (tok.val == RUN_TYPE) {
r=DoRun(&p);
} else {
DestroyParser(&p);
CreateParser(CurLine, &p);
r=DoRem(&p);
purge_handled = 1;
@@ -297,10 +307,8 @@ static void DoReminders(void)
/* If we don't recognize the command, do a REM by default */
/* Note: Since the parser hasn't been used yet, we don't */
/* need to destroy it here. */
default: CreateParser(CurLine, &p); purge_handled = 1; r=DoRem(&p); break;
default: DestroyParser(&p); CreateParser(CurLine, &p); purge_handled = 1; r=DoRem(&p); break;
}
if (r && (!Hush || r != E_RUN_DISABLED)) {
@@ -582,6 +590,53 @@ int ParseIdentifier(ParsePtr p, DynamicBuffer *dbuf)
}
}
/***************************************************************/
/* */
/* ParseExpr */
/* */
/* We are expecting an expression here. Parse it and return */
/* the value node tree. */
/* */
/***************************************************************/
expr_node * ParseExpr(ParsePtr p, int *r)
{
int bracketed = 0;
expr_node *node;
if (p->isnested) {
*r = E_PARSE_ERR; /* Can't nest expressions */
return NULL;
}
if (!p->pos) {
*r = E_PARSE_ERR; /* Missing expression */
return NULL;
}
while (isempty(*p->pos)) (p->pos)++;
if (!*(p->pos)) {
*r = E_EOLN;
return NULL;
}
if (*p->pos == BEG_OF_EXPR) {
(p->pos)++;
bracketed = 1;
}
node = parse_expression(&(p->pos), r, NULL);
if (*r) {
return free_expr_tree(node);
}
if (bracketed) {
if (*p->pos != END_OF_EXPR) {
*r = E_MISS_END;
return free_expr_tree(node);
}
(p->pos)++;
}
return node;
}
/***************************************************************/
/* */
/* EvaluateExpr */
@@ -593,21 +648,22 @@ int ParseIdentifier(ParsePtr p, DynamicBuffer *dbuf)
int EvaluateExpr(ParsePtr p, Value *v)
{
int bracketed = 0;
int r;
int nonconst = 0;
expr_node *node = ParseExpr(p, &r);
if (p->isnested) return E_PARSE_ERR; /* Can't nest expressions */
if (!p->pos) return E_PARSE_ERR; /* Missing expression */
while (isempty(*p->pos)) (p->pos)++;
if (*p->pos == BEG_OF_EXPR) {
(p->pos)++;
bracketed = 1;
if (r != OK) {
return r;
}
r = EvalExpr(&(p->pos), v, p);
if (!node) {
return E_SWERR;
}
r = evaluate_expr_node(node, NULL, v, &nonconst);
free_expr_tree(node);
if (r) return r;
if (bracketed) {
if (*p->pos != END_OF_EXPR) return E_MISS_END;
(p->pos)++;
if (nonconst) {
p->nonconst_expr = 1;
}
return OK;
}
@@ -651,27 +707,34 @@ void Eprint(char const *fmt, ...)
FreshLine = 0;
if (strcmp(FileName, "-")) {
(void) fprintf(ErrFp, "%s(%d): ", FileName, LineNo);
va_start(argptr, fmt);
(void) vfprintf(ErrFp, fmt, argptr);
(void) fputc('\n', ErrFp);
va_end(argptr);
if (print_callstack(ErrFp)) {
(void) fprintf(ErrFp, ": ");
(void) fprintf(ErrFp, "\n");
}
} else {
(void) fprintf(ErrFp, "-stdin-(%d): ", LineNo);
va_start(argptr, fmt);
(void) vfprintf(ErrFp, fmt, argptr);
(void) fputc('\n', ErrFp);
va_end(argptr);
if (print_callstack(ErrFp)) {
(void) fprintf(ErrFp, ": ");
(void) fprintf(ErrFp, "\n");
}
}
if (DebugFlag & DB_PRTLINE) OutputLine(ErrFp);
} else if (FileName) {
fprintf(ErrFp, " ");
va_start(argptr, fmt);
(void) vfprintf(ErrFp, fmt, argptr);
(void) fputc('\n', ErrFp);
va_end(argptr);
if (print_callstack(ErrFp)) {
(void) fprintf(ErrFp, ": ");
(void) fprintf(ErrFp, "\n");
}
}
va_start(argptr, fmt);
(void) vfprintf(ErrFp, fmt, argptr);
(void) fputc('\n', ErrFp);
va_end(argptr);
return;
}
@@ -1025,8 +1088,8 @@ int DoDebug(ParsePtr p)
case 's':
case 'S':
if (val) DebugFlag |= DB_EXPR_STACKS;
else DebugFlag &= ~DB_EXPR_STACKS;
if (val) DebugFlag |= DB_PARSE_EXPR;
else DebugFlag &= ~DB_PARSE_EXPR;
break;
case 'x':

View File

@@ -43,6 +43,7 @@
int CallUserFunc (char const *name, int nargs, ParsePtr p);
int DoFset (ParsePtr p);
int DoFunset (ParsePtr p);
void UnsetAllUserFuncs(void);
void ProduceCalendar (void);
char const *SimpleTime (int tim);
char const *CalendarTime (int tim, int duration);
@@ -56,9 +57,12 @@ int DoSubst (ParsePtr p, DynamicBuffer *dbuf, Trigger *t, TimeTrig *tt, int dse,
int DoSubstFromString (char const *source, DynamicBuffer *dbuf, int dse, int tim);
int ParseLiteralDate (char const **s, int *dse, int *tim);
int ParseLiteralTime (char const **s, int *tim);
int evaluate_expr_node(expr_node *node, Value *locals, Value *ans, int *nonconst);
void print_expr_tree(expr_node *node, FILE *fp);
expr_node *free_expr_tree(expr_node *node);
int EvalExpr (char const **e, Value *v, ParsePtr p);
int DoCoerce (char type, Value *v);
void PrintValue (Value *v, FILE *fp);
char const *PrintValue (Value *v, FILE *fp);
int CopyValue (Value *dest, const Value *src);
int ReadLine (void);
int OpenFile (char const *fname);
@@ -77,8 +81,9 @@ int JulianToGregorianOffset(int y, int m);
int ParseChar (ParsePtr p, int *err, int peek);
int ParseToken (ParsePtr p, DynamicBuffer *dbuf);
int ParseIdentifier (ParsePtr p, DynamicBuffer *dbuf);
expr_node * ParseExpr(ParsePtr p, int *r);
void print_expr_nodes_stats(void);
int EvaluateExpr (ParsePtr p, Value *v);
int Evaluate (char const **s, Var *locals, ParsePtr p);
int FnPopValStack (Value *val);
void Eprint (char const *fmt, ...);
void Wprint (char const *fmt, ...);
@@ -129,9 +134,10 @@ int StrCmpi (char const *s1, char const *s2);
#endif
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 GetVarValue (char const *str, Value *val, Var *locals, ParsePtr p);
int GetVarValue (char const *str, Value *val);
int DoSet (Parser *p);
int DoUnset (Parser *p);
int DoDump (ParsePtr p);
@@ -145,9 +151,10 @@ int ParseNonSpaceChar (ParsePtr p, int *err, int peek);
unsigned int HashVal (char const *str);
int DateOK (int y, int m, int d);
Operator *FindOperator (char const *name, Operator where[], int num);
BuiltinFunc *FindFunc (char const *name, BuiltinFunc where[], int num);
BuiltinFunc *FindBuiltinFunc (char const *name);
int InsertIntoSortBuffer (int dse, int tim, char const *body, int typ, int prio);
void IssueSortedReminders (void);
UserFunc *FindUserFunc(char const *name);
int UserFuncExists (char const *fn);
void DSEToHeb (int dse, int *hy, int *hm, int *hd);
int HebNameToNum (char const *mname);
@@ -207,6 +214,7 @@ void set_cloexec(FILE *fp);
int push_call(char const *filename, char const *func, int lineno);
void clear_callstack(void);
int print_callstack(FILE *fp);
int have_callstack(void);
void pop_call(void);
void FixSpecialType(Trigger *trig);
void WriteJSONTrigger(Trigger const *t, int include_tags, int today);

View File

@@ -30,21 +30,62 @@ typedef struct {
int (*func)(void);
} Operator;
/* New-style expr_node structure and constants */
enum expr_node_type
{
N_FREE,
N_ERROR,
N_CONSTANT,
N_LOCAL_VAR,
N_SHORT_VAR,
N_VARIABLE,
N_SHORT_SYSVAR,
N_SYSVAR,
N_BUILTIN_FUNC,
N_SHORT_USER_FUNC,
N_USER_FUNC,
N_BINARY_OPERATOR,
N_UNARY_OPERATOR,
};
/* Structure for passing in Nargs and out RetVal from functions */
typedef struct {
int nargs;
Value *args;
Value retval;
} func_info;
/* Forward reference */
typedef struct expr_node_struct expr_node;
/* Define the type of user-functions */
typedef struct {
char const *name;
char minargs;
char maxargs;
char is_constant;
/* Old-style function calling convention */
int (*func)(func_info *);
/* New-style function calling convention */
int (*newfunc)(expr_node *node, Value *locals, Value *ans, int *nonconst);
} BuiltinFunc;
#define SHORT_NAME_BUF 16
typedef struct expr_node_struct {
struct expr_node_struct *child;
struct expr_node_struct *sibling;
enum expr_node_type type;
int num_kids;
union {
Value value;
int arg;
BuiltinFunc *builtin_func;
char name[SHORT_NAME_BUF];
int (*operator_func) (struct expr_node_struct *node, Value *locals, Value *ans, int *nonconst);
} u;
} expr_node;
/* Define the structure of a variable */
typedef struct var {
struct var *next;
@@ -142,6 +183,8 @@ typedef Parser *ParsePtr; /* Pointer to parser structure */
#define MSF_TYPE 7
#define PASSTHRU_TYPE 8
/* For function arguments */
#define NO_MAX 127
/* DEFINES for debugging flags */
#define DB_PRTLINE 1
@@ -150,7 +193,7 @@ typedef Parser *ParsePtr; /* Pointer to parser structure */
#define DB_DUMP_VARS 8
#define DB_ECHO_LINE 16
#define DB_TRACE_FILES 32
#define DB_EXPR_STACKS 64
#define DB_PARSE_EXPR 64
/* Enumeration of the tokens */
enum TokTypes
@@ -244,3 +287,26 @@ typedef struct {
#define TERMINAL_BACKGROUND_UNKNOWN -1
#define TERMINAL_BACKGROUND_DARK 0
#define TERMINAL_BACKGROUND_LIGHT 1
typedef int (*SysVarFunc)(int, Value *);
/* The structure of a system variable */
typedef struct {
char const *name;
char modifiable;
int type;
void *value;
int min; /* Or const-value */
int max;
} SysVar;
/* Define the data structure used to hold a user-defined function */
typedef struct udf_struct {
struct udf_struct *next;
char name[VAR_NAME_LEN+1];
expr_node *node;
char **args;
int nargs;
char const *filename;
int lineno;
} UserFunc;

View File

@@ -30,34 +30,12 @@
#define FUNC_HASH_SIZE 32 /* Size of User-defined function hash table */
/* Define the data structure used to hold a user-defined function */
typedef struct udf_struct {
struct udf_struct *next;
char name[VAR_NAME_LEN+1];
char const *text;
Var *locals;
char IsActive;
int nargs;
char const *filename;
int lineno;
} UserFunc;
/* The hash table */
static UserFunc *FuncHash[FUNC_HASH_SIZE];
/* Access to built-in functions */
extern int NumFuncs;
extern BuiltinFunc Func[];
/* We need access to the expression evaluation stack */
extern Value ValStack[];
extern int ValStackPtr;
static void DestroyUserFunc (UserFunc *f);
static void FUnset (char const *name);
static void FSet (UserFunc *f);
static int SetUpLocalVars (UserFunc *f);
static void DestroyLocalVals (UserFunc *f);
/***************************************************************/
/* */
@@ -98,9 +76,11 @@ int DoFset(ParsePtr p)
{
int r;
int c;
int i;
UserFunc *func;
Var *v;
Var *local;
UserFunc *existing;
Var *locals = NULL;
Var local_array[MAX_FUNC_ARGS];
int orig_namelen;
DynamicBuffer buf;
@@ -125,6 +105,18 @@ int DoFset(ParsePtr p)
return E_PARSE_ERR;
}
/* If the function exists and was defined at the same line of the same
file, do nothing */
existing = FindUserFunc(DBufValue(&buf));
if (existing) {
if (!strcmp(existing->filename, FileName) &&
strcmp(existing->filename, "[cmdline]") &&
existing->lineno == LineNo) {
DBufFree(&buf);
/* We already have it! Our work here is done. */
return OK;
}
}
func = NEW(UserFunc);
if (!func) {
DBufFree(&buf);
@@ -143,25 +135,22 @@ int DoFset(ParsePtr p)
StrnCpy(func->name, DBufValue(&buf), VAR_NAME_LEN);
DBufFree(&buf);
if (!Hush) {
if (FindFunc(func->name, Func, NumFuncs)) {
if (FindBuiltinFunc(func->name)) {
Eprint("%s: `%s'", ErrMsg[E_REDEF_FUNC], func->name);
}
}
func->locals = NULL;
func->text = NULL;
func->IsActive = 0;
func->node = NULL;
func->nargs = 0;
func->args = NULL;
/* Get the local variables - we insert the local variables in REVERSE
order, but that's OK, because we pop them off the stack in reverse
order, too, so everything works out just fine. */
/* Get the local variables */
c=ParseNonSpaceChar(p, &r, 1);
if (r) return r;
if (c == ')') {
(void) ParseNonSpaceChar(p, &r, 0);
}
else {
} else {
locals = local_array;
while(1) {
if ( (r=ParseIdentifier(p, &buf)) ) return r;
if (*DBufValue(&buf) == '$') {
@@ -170,27 +159,24 @@ int DoFset(ParsePtr p)
return E_BAD_ID;
}
/* If we've already seen this local variable, error */
local = func->locals;
while(local) {
if (!StrinCmp(DBufValue(&buf), local->name, VAR_NAME_LEN)) {
for (i=0; i<func->nargs; i++) {
if (!StrinCmp(DBufValue(&buf), local_array[i].name, VAR_NAME_LEN)) {
DBufFree(&buf);
DestroyUserFunc(func);
return E_REPEATED_ARG;
}
local = local->next;
}
v = NEW(Var);
if (!v) {
DBufFree(&buf);
DestroyUserFunc(func);
return E_NO_MEM;
}
i = func->nargs;
if (i >= MAX_FUNC_ARGS-1) {
DBufFree(&buf);
DestroyUserFunc(func);
return E_2MANY_ARGS;
}
local_array[i].v.type = ERR_TYPE;
StrnCpy(local_array[i].name, DBufValue(&buf), VAR_NAME_LEN);
local_array[i].next = &(local_array[i+1]);
local_array[i+1].next = NULL;
func->nargs++;
v->v.type = ERR_TYPE;
StrnCpy(v->name, DBufValue(&buf), VAR_NAME_LEN);
DBufFree(&buf);
v->next = func->locals;
func->locals = v;
c = ParseNonSpaceChar(p, &r, 0);
if (c == ')') break;
else if (c != ',') {
@@ -205,7 +191,6 @@ int DoFset(ParsePtr p)
if (c == '=') {
(void) ParseNonSpaceChar(p, &r, 0);
}
/* Copy the text over */
if (p->isnested) {
Eprint("%s", ErrMsg[E_CANTNEST_FDEF]);
DestroyUserFunc(func);
@@ -219,10 +204,23 @@ int DoFset(ParsePtr p)
DestroyUserFunc(func);
return E_EOLN;
}
func->text = StrDup(p->pos);
if (!func->text) {
DestroyUserFunc(func);
return E_NO_MEM;
/* Parse the expression */
func->node = parse_expression(&(p->pos), &r, locals);
if (!func->node) {
DestroyUserFunc(func);
return r;
}
/* Save the argument names */
if (func->nargs) {
func->args = calloc(sizeof(char *), func->nargs);
for (i=0; i<func->nargs; i++) {
func->args[i] = StrDup(local_array[i].name);
if (!func->args[i]) {
DestroyUserFunc(func);
return E_NO_MEM;
}
}
}
/* If an old definition of this function exists, destroy it */
@@ -246,23 +244,22 @@ int DoFset(ParsePtr p)
/***************************************************************/
static void DestroyUserFunc(UserFunc *f)
{
Var *v, *prev;
/* Free the local variables first */
v = f->locals;
while(v) {
DestroyValue(v->v);
prev = v;
v = v->next;
free(prev);
}
int i;
/* Free the function definition */
if (f->text) free( (char *) f->text);
if (f->node) free_expr_tree(f->node);
/* Free the filename */
if (f->filename) free( (char *) f->filename);
/* Free arg names */
if (f->args) {
for (i=0; i<f->nargs; i++) {
if (f->args[i]) free(f->args[i]);
}
free(f->args);
}
/* Free the data structure itself */
free(f);
}
@@ -308,129 +305,17 @@ static void FSet(UserFunc *f)
FuncHash[h] = f;
}
/***************************************************************/
/* */
/* CallUserFunc */
/* */
/* Call a user-defined function. */
/* */
/***************************************************************/
int CallUserFunc(char const *name, int nargs, ParsePtr p)
UserFunc *FindUserFunc(char const *name)
{
UserFunc *f;
int h = HashVal(name) % FUNC_HASH_SIZE;
int i;
char const *s;
UserFunc *f;
int h = HashVal(name) % FUNC_HASH_SIZE;
/* Search for the function */
f = FuncHash[h];
while (f && StrinCmp(name, f->name, VAR_NAME_LEN)) f = f->next;
if (!f) {
Eprint("%s: `%s'", ErrMsg[E_UNDEF_FUNC], name);
return E_UNDEF_FUNC;
}
/* Debugging stuff */
if (DebugFlag & DB_PRTEXPR) {
fprintf(ErrFp, "%s %s(", ErrMsg[E_ENTER_FUN], f->name);
for (i=0; i<nargs; i++) {
PrintValue(&ValStack[ValStackPtr - nargs + i], ErrFp);
if (i<nargs-1) fprintf(ErrFp, ", ");
}
fprintf(ErrFp, ")\n");
}
/* Detect illegal recursive call */
if (f->IsActive) {
if (DebugFlag &DB_PRTEXPR) {
fprintf(ErrFp, "%s %s() => ", ErrMsg[E_LEAVE_FUN], name);
fprintf(ErrFp, "%s\n", ErrMsg[E_RECURSIVE]);
}
return E_RECURSIVE;
}
/* Check number of args */
if (nargs != f->nargs) {
if (DebugFlag &DB_PRTEXPR) {
fprintf(ErrFp, "%s %s() => ", ErrMsg[E_LEAVE_FUN], name);
fprintf(ErrFp, "%s\n",
ErrMsg[(nargs < f->nargs) ? E_2FEW_ARGS : E_2MANY_ARGS]);
}
return (nargs < f->nargs) ? E_2FEW_ARGS : E_2MANY_ARGS;
}
/* Found the function - set up a local variable frame */
h = SetUpLocalVars(f);
if (h) {
if (DebugFlag &DB_PRTEXPR) {
fprintf(ErrFp, "%s %s() => ", ErrMsg[E_LEAVE_FUN], name);
fprintf(ErrFp, "%s\n", ErrMsg[h]);
}
return h;
}
/* Evaluate the expression */
f->IsActive = 1;
s = f->text;
/* Skip the opening bracket, if there's one */
while (isempty(*s)) s++;
if (*s == BEG_OF_EXPR) {
s++;
}
push_call(f->filename, f->name, f->lineno);
h = Evaluate(&s, f->locals, p);
if (h == OK) {
pop_call();
}
f->IsActive = 0;
DestroyLocalVals(f);
if (DebugFlag &DB_PRTEXPR) {
fprintf(ErrFp, "%s %s() => ", ErrMsg[E_LEAVE_FUN], name);
if (h) fprintf(ErrFp, "%s\n", ErrMsg[h]);
else {
PrintValue(&ValStack[ValStackPtr-1], ErrFp);
fprintf(ErrFp, "\n");
}
}
return h;
/* Search for the function */
f = FuncHash[h];
while (f && StrinCmp(name, f->name, VAR_NAME_LEN)) f = f->next;
return f;
}
/***************************************************************/
/* */
/* SetUpLocalVars */
/* */
/* Set up the local variables from the stack frame. */
/* */
/***************************************************************/
static int SetUpLocalVars(UserFunc *f)
{
int i, r;
Var *var;
for (i=0, var=f->locals; var && i<f->nargs; var=var->next, i++) {
if ( (r=FnPopValStack(&(var->v))) ) {
DestroyLocalVals(f);
return r;
}
}
return OK;
}
/***************************************************************/
/* */
/* DestroyLocalVals */
/* */
/* Destroy the values of all local variables after evaluating */
/* the function. */
/* */
/***************************************************************/
static void DestroyLocalVals(UserFunc *f)
{
Var *v = f->locals;
while(v) {
DestroyValue(v->v);
v = v->next;
}
}
/***************************************************************/
/* */
/* UserFuncExists */
@@ -441,12 +326,33 @@ static void DestroyLocalVals(UserFunc *f)
/***************************************************************/
int UserFuncExists(char const *fn)
{
UserFunc *f;
int h = HashVal(fn) % FUNC_HASH_SIZE;
UserFunc *f = FindUserFunc(fn);
f = FuncHash[h];
while (f && StrinCmp(fn, f->name, VAR_NAME_LEN)) f = f->next;
if (!f) return -1;
else return f->nargs;
}
/***************************************************************/
/* */
/* UnsetAllUserFuncs */
/* */
/* Call FUNSET on all user funcs. Used with -ds flag to */
/* ensure no expr_node memory leaks. */
/* */
/***************************************************************/
void
UnsetAllUserFuncs(void)
{
UserFunc *f;
UserFunc *next;
int i;
for (i=0; i<FUNC_HASH_SIZE; i++) {
f = FuncHash[i];
while(f) {
next = f->next;
DestroyUserFunc(f);
f = next;
}
FuncHash[i] = NULL;
}
}

View File

@@ -187,31 +187,33 @@ typedef struct cs_s {
} cs;
static cs *callstack = NULL;
static cs *freecs = NULL;
static void
destroy_cs(cs *entry)
{
if (entry->filename) free( (void *) entry->filename);
if (entry->func) free( (void *) entry->func);
free( (void *) entry);
entry->next = freecs;
freecs = entry;
}
int
push_call(char const *filename, char const *func, int lineno)
{
cs *entry = NEW(cs);
if (!entry) {
return E_NO_MEM;
cs *entry;
if (freecs) {
entry = freecs;
freecs = freecs->next;
} else {
entry = NEW(cs);
if (!entry) {
return E_NO_MEM;
}
}
entry->next = NULL;
entry->filename = StrDup(filename);
entry->func = StrDup(func);
entry->filename = filename;
entry->func = func;
entry->lineno = lineno;
if (!entry->filename || !entry->func) {
destroy_cs(entry);
return E_NO_MEM;
}
entry->next = callstack;
callstack = entry;
return OK;
@@ -233,11 +235,31 @@ clear_callstack(void)
static void
print_callstack_aux(FILE *fp, cs *entry)
{
if (entry) {
print_callstack_aux(fp, entry->next);
fprintf(fp, "\n");
(void) fprintf(fp, "%s(%d): In function `%s'", entry->filename, entry->lineno, entry->func);
int i = 0;
char const *in = "In";
cs *prev = NULL;
while(entry) {
if (prev) {
fprintf(fp, "\n");
in = "Called from";
}
(void) fprintf(fp, " %s(%d): [#%d] %s function `%s'", entry->filename, entry->lineno, i, in, entry->func);
prev = entry;
entry = entry->next;
i++;
if (i > 10) {
break;
}
}
if (entry) {
(void) fprintf(fp, "\n [remaining call frames omitted]");
}
}
int
have_callstack(void)
{
return (callstack != NULL);
}
int

View File

@@ -39,8 +39,6 @@ static int IntMax = INT_MAX;
static Var *VHashTbl[VAR_HASH_SIZE];
typedef int (*SysVarFunc)(int, Value *);
static double
strtod_in_c_locale(char const *str, char **endptr)
{
@@ -511,20 +509,10 @@ int SetVar(char const *str, Value const *val)
/* Get a copy of the value of the variable. */
/* */
/***************************************************************/
int GetVarValue(char const *str, Value *val, Var *locals, ParsePtr p)
int GetVarValue(char const *str, Value *val)
{
Var *v;
/* Try searching local variables first */
v = locals;
while (v) {
if (! StrinCmp(str, v->name, VAR_NAME_LEN))
return CopyValue(val, &v->v);
v = v->next;
}
/* Global variable... mark expression as non-constant */
if (p) p->nonconst_expr = 1;
v=FindVar(str, 0);
if (!v) {
@@ -785,16 +773,6 @@ int DoPreserve (Parser *p)
/* */
/***************************************************************/
/* The structure of a system variable */
typedef struct {
char const *name;
char modifiable;
int type;
void *value;
int min; /* Or const-value */
int max;
} SysVar;
/* Macro to access "min" but as a constval. Just to make source more
readable */
#define constval min
@@ -919,7 +897,6 @@ static SysVar SysVarArr[] = {
};
#define NUMSYSVARS ( sizeof(SysVarArr) / sizeof(SysVar) )
static SysVar *FindSysVar (char const *name);
static void DumpSysVar (char const *name, const SysVar *v);
/***************************************************************/
/* */
@@ -1016,7 +993,7 @@ int GetSysVar(char const *name, Value *val)
/* Find a system var with specified name. */
/* */
/***************************************************************/
static SysVar *FindSysVar(char const *name)
SysVar *FindSysVar(char const *name)
{
int top=NUMSYSVARS-1, bottom=0;
int mid=(top + bottom) / 2;

View File

@@ -35,8 +35,8 @@ set a ansicolor(-1, 0, 0)
set a ansicolor(42, 42, 256)
set a ansicolor("foo")
set a ansicolor("1 1")
set a ansicolor("-1 -1 0");
set a ansicolor("256 1 1");
set a ansicolor("-1 -1 0")
set a ansicolor("256 1 1")
set a ansicolor(128, 128, 128, 2)
set a ansicolor(128, 128, 128, -1)
set a ansicolor(128, 128, 128, 0, 2)