expressions: Major work to improve error messages.
authorBen Pfaff <blp@cs.stanford.edu>
Sun, 19 Dec 2021 23:41:50 +0000 (15:41 -0800)
committerBen Pfaff <blp@cs.stanford.edu>
Sun, 26 Dec 2021 21:28:53 +0000 (13:28 -0800)
This makes the error messages related to expressions much better, by being
more precise about the location of the problem.  It also fixes a number of
documentation and compatibility issues encountered along the way, as well
as redoing the testsuite for expressions to make it easier to understand
and to work on.

18 files changed:
doc/expressions.texi
doc/language.texi
src/data/calendar.c
src/data/calendar.h
src/data/dataset.c
src/data/format.c
src/data/format.h
src/language/expressions/evaluate.c
src/language/expressions/generate.py
src/language/expressions/helpers.c
src/language/expressions/helpers.h
src/language/expressions/operations.def
src/language/expressions/optimize.c
src/language/expressions/parse.c
src/language/expressions/private.h
tests/data/calendar.at
tests/language/expressions/evaluate.at
tests/language/expressions/parse.at

index 8e28ad5e24457e33c05161062f2ee546be707954..cd214380e7326a5d5e23fd165fc85d867404f264 100644 (file)
@@ -447,9 +447,10 @@ Returns 1 if @var{expr} has the system-missing value, 0 otherwise.
 @end deftypefn
 
 @deftypefn {Function} {} VALUE (@var{variable})
-Prevents the user-missing values of @var{variable} from being
-transformed into system-missing values, and always results in the
-actual value of @var{variable}, whether it is valid, user-missing, or
+@deftypefnx {Function} {} VALUE (@var{vector}(@var{index}))
+Prevents the user-missing values of the variable or vector element
+from being transformed into system-missing values, and always results
+in its actual value, whether it is valid, user-missing, or
 system-missing.
 @end deftypefn
 
@@ -463,23 +464,25 @@ They take a set of numeric arguments or a set of string arguments, and
 produce Boolean results.
 
 String comparisons are performed according to the rules given in
-@ref{Relational Operators}.
+@ref{Relational Operators}.  User-missing string values are treated as
+valid values.
 
 @deftypefn {Function} {} ANY (@var{value}, @var{set} [, @var{set}]@dots{})
-Results in true if @var{value} is equal to any of the @var{set}
-values.  Otherwise, results in false.  If @var{value} is
-system-missing, returns system-missing.  System-missing values in
-@var{set} do not cause @func{ANY} to return system-missing.
+Returns true if @var{value} is equal to any of the @var{set} values,
+and false otherwise.  For numeric arguments, returns system-missing if
+@var{value} is system-missing or if all the values in @var{set} are
+system-missing.  If @var{value}
 @end deftypefn
 
 @deftypefn {Function} {} RANGE (@var{value}, @var{low}, @var{high} [, @var{low}, @var{high}]@dots{})
-Results in true if @var{value} is in any of the intervals bounded by
-@var{low} and @var{high} inclusive.  Otherwise, results in false.
-Each @var{low} must be less than or equal to its corresponding
-@var{high} value.  @var{low} and @var{high} must be given in pairs.
-If @var{value} is system-missing, returns system-missing.
-System-missing values in @var{set} do not cause @func{RANGE} to return
-system-missing.
+Returns true if @var{value} is in any of the intervals bounded by
+@var{low} and @var{high} inclusive, and false otherwise.  @var{low}
+and @var{high} must be given in pairs.  Returns system-missing if any
+@var{high} is less than its @var{low} or, for numeric arguments, if
+@var{value} is system-missing or if all the @var{low}-@var{high} pairs
+contain one (or two) system-missing values.  A pair does not match
+@var{value} if either @var{low} or @var{high} is missing, even if
+@var{value} equals the non-missing endpoint.
 @end deftypefn
 
 @node Statistical Functions
@@ -567,30 +570,33 @@ String functions take various arguments and return various results.
 @deftypefn {Function} {} CONCAT (@var{string}, @var{string}[, @dots{}])
 Returns a string consisting of each @var{string} in sequence.
 @code{CONCAT("abc", "def", "ghi")} has a value of @code{"abcdefghi"}.
-The resultant string is truncated to a maximum of 255 characters.
+The resultant string is truncated to a maximum of 32767 bytes.
 @end deftypefn
 
 @cindex searching strings
 @deftypefn {Function} {} INDEX (@var{haystack}, @var{needle})
-Returns a positive integer indicating the position of the first
-occurrence of @var{needle} in @var{haystack}.  Returns 0 if @var{haystack}
-does not contain @var{needle}.  Returns system-missing if @var{needle}
-is an empty string.
+@deftypefnx {Function} {} RINDEX (@var{haystack}, @var{needle})
+Returns a positive integer indicating the position of the first (for
+@code{INDEX}) or last (for @code{RINDEX}) occurrence of @var{needle}
+in @var{haystack}.  Returns 0 if @var{haystack} does not contain
+@var{needle}.  Returns 1 if @var{needle} is the empty string.
 @end deftypefn
 
 @deftypefn {Function} {} INDEX (@var{haystack}, @var{needles}, @var{needle_len})
-Divides @var{needles} into one or more needles, each with length
-@var{needle_len}.
-Searches @var{haystack} for the first occurrence of each needle, and
-returns the smallest value.  Returns 0 if @var{haystack} does not
-contain any part in @var{needle}.  It is an error if @var{needle_len}
-does not evenly divide the length of @var{needles}.  Returns
-system-missing if @var{needles} is an empty string.
+@deftypefnx {Function} {} RINDEX (@var{haystack}, @var{needle}, @var{needle_len})
+Divides @var{needles} into multiple needles, each with length
+@var{needle_len}, which must be a positive integer that evenly divides
+the length of @var{needles}.  Searches @var{haystack} for the
+occurrences of each needle and returns a positive integer indicating
+the byte index of the beginning of the first (for @code{INDEX}) or
+last (for @code{RINDEX}) needle it finds.  Returns 0 if @var{haystack}
+does not contain any of the needles, or if @var{needles} is the empty
+string.
 @end deftypefn
 
 @cindex strings, finding length of
 @deftypefn {Function} {} LENGTH (@var{string})
-Returns the number of characters in @var{string}.
+Returns the number of bytes in @var{string}.
 @end deftypefn
 
 @cindex strings, case of
@@ -601,33 +607,30 @@ letters are changed to lowercase letters.  The definitions of
 @end deftypefn
 
 @cindex strings, padding
-@deftypefn {Function} {} LPAD (@var{string}, @var{length})
-If @var{string} is at least @var{length} characters in length, returns
-@var{string} unchanged.  Otherwise, returns @var{string} padded with
-spaces on the left side to length @var{length}.  Returns an empty string
-if @var{length} is system-missing, negative, or greater than 255.
-@end deftypefn
+@deftypefn {Function} {} LPAD (@var{string}, @var{length}[, @var{padding}])
+@deftypefnx {Function} {} RPAD (@var{string}, @var{length}[, @var{padding}])
+If @var{string} is at least @var{length} bytes long, these functions
+return @var{string} unchanged.  Otherwise, they return @var{string}
+padded with @var{padding} on the left side (for @code{LPAD}) or right
+side (for @code{RPAD}) to @var{length} bytes.  These functions report
+an error and return @var{string} unchanged if @var{length} is missing
+or bigger than 32767.
 
-@deftypefn {Function} {} LPAD (@var{string}, @var{length}, @var{padding})
-If @var{string} is at least @var{length} characters in length, returns
-@var{string} unchanged.  Otherwise, returns @var{string} padded with
-@var{padding} on the left side to length @var{length}.  Returns an empty
-string if @var{length} is system-missing, negative, or greater than 255, or
-if @var{padding} does not contain exactly one character.
+The @var{padding} argument must not be an empty string and defaults to
+a space if not specified.  If its length does not evenly fit the
+amount of space needed for padding, the returned string will be
+shorter than @var{length}.
 @end deftypefn
 
 @cindex strings, trimming
 @cindex white space, trimming
-@deftypefn {Function} {} LTRIM (@var{string})
-Returns @var{string}, after removing leading spaces.  Other white space,
-such as tabs, carriage returns, line feeds, and vertical tabs, is not
-removed.
-@end deftypefn
-
-@deftypefn {Function} {} LTRIM (@var{string}, @var{padding})
-Returns @var{string}, after removing leading @var{padding} characters.
-If @var{padding} does not contain exactly one character, returns an
-empty string.
+@deftypefn {Function} {} LTRIM (@var{string}[, @var{padding}])
+@deftypefnx {Function} {} RTRIM (@var{string}[, @var{padding}])
+These functions return @var{string}, after removing leading (for
+@code{LTRIM}) or trailing (for @code{RTRIM}) copies of @var{padding}.
+If @var{padding} is omitted, these functions remove spaces (but not
+tabs or other white space).  These functions return @var{string}
+unchanged if @var{padding} is the empty string.
 @end deftypefn
 
 @cindex numbers, converting from strings
@@ -635,9 +638,9 @@ empty string.
 @deftypefn {Function} {} NUMBER (@var{string}, @var{format})
 Returns the number produced when @var{string} is interpreted according
 to format specifier @var{format}.  If the format width @var{w} is less
-than the length of @var{string}, then only the first @var{w}
-characters in @var{string} are used, @i{e.g.}@: @code{NUMBER("123", F3.0)}
-and @code{NUMBER("1234", F3.0)} both have value 123.  If @var{w} is
+than the length of @var{string}, then only the first @var{w} bytes in
+@var{string} are used, @i{e.g.}@: @code{NUMBER("123", F3.0)} and
+@code{NUMBER("1234", F3.0)} both have value 123.  If @var{w} is
 greater than @var{string}'s length, then it is treated as if it were
 right-padded with spaces.  If @var{string} is not in the correct
 format for @var{format}, system-missing is returned.
@@ -652,53 +655,6 @@ limits the maximum number of replacements; otherwise, all instances of
 @var{needle} are replaced.
 @end deftypefn
 
-@cindex strings, searching backwards
-@deftypefn {Function} {} RINDEX (@var{haystack}, @var{needle})
-Returns a positive integer indicating the position of the last
-occurrence of @var{needle} in @var{haystack}.  Returns 0 if
-@var{haystack} does not contain @var{needle}.  Returns system-missing if
-@var{needle} is an empty string.
-@end deftypefn
-
-@deftypefn {Function} {} RINDEX (@var{haystack}, @var{needle}, @var{needle_len})
-Divides @var{needle} into parts, each with length @var{needle_len}.
-Searches @var{haystack} for the last occurrence of each part, and
-returns the largest value.  Returns 0 if @var{haystack} does not contain
-any part in @var{needle}.  It is an error if @var{needle_len} does not
-evenly divide the length of @var{needle}.  Returns system-missing
-if @var{needle} is an empty string or if needle_len is less than 1.
-@end deftypefn
-
-@cindex padding strings
-@cindex strings, padding
-@deftypefn {Function} {} RPAD (@var{string}, @var{length})
-If @var{string} is at least @var{length} characters in length, returns
-@var{string} unchanged.  Otherwise, returns @var{string} padded with
-spaces on the right to length @var{length}.  Returns an empty string if
-@var{length} is system-missing, negative, or greater than 255.
-@end deftypefn
-
-@deftypefn {Function} {} RPAD (@var{string}, @var{length}, @var{padding})
-If @var{string} is at least @var{length} characters in length, returns
-@var{string} unchanged.  Otherwise, returns @var{string} padded with
-@var{padding} on the right to length @var{length}.  Returns an empty
-string if @var{length} is system-missing, negative, or greater than 255,
-or if @var{padding} does not contain exactly one character.
-@end deftypefn
-
-@cindex strings, trimming
-@cindex white space, trimming
-@deftypefn {Function} {} RTRIM (@var{string})
-Returns @var{string}, after removing trailing spaces.  Other types of
-white space are not removed.
-@end deftypefn
-
-@deftypefn {Function} {} RTRIM (@var{string}, @var{padding})
-Returns @var{string}, after removing trailing @var{padding} characters.
-If @var{padding} does not contain exactly one character, returns an
-empty string.
-@end deftypefn
-
 @cindex strings, converting from numbers
 @cindex numbers, converting to strings
 @deftypefn {Function} {} STRING (@var{number}, @var{format})
@@ -712,8 +668,9 @@ has the value @code{"123.6"}.
 @cindex white space, trimming
 @deftypefn {Function} {} STRUNC (@var{string}, @var{n})
 Returns @var{string}, first trimming it to at most @var{n} bytes, then
-removing trailing spaces.  Returns an empty string if @var{n} is
-missing or negative.
+removing trailing spaces (but not tabs or other white space).  Returns
+an empty string if @var{n} is zero or negative, or @var{string}
+unchanged if @var{n} is missing.
 @end deftypefn
 
 @cindex substrings
@@ -725,15 +682,15 @@ less than 1, or greater than the length of @var{string}.
 @end deftypefn
 
 @deftypefn {Function} {} SUBSTR (@var{string}, @var{start}, @var{count})
-Returns a string consisting of the first @var{count} characters from
-@var{string} beginning at position @var{start}.  Returns an empty string
-if @var{start} or @var{count} is system-missing, if @var{start} is less
-than 1 or greater than the number of characters in @var{string}, or if
-@var{count} is less than 1.  Returns a string shorter than @var{count}
-characters if @var{start} + @var{count} - 1 is greater than the number
-of characters in @var{string}.  Examples: @code{SUBSTR("abcdefg", 3, 2)}
-has value @code{"cd"}; @code{SUBSTR("nonsense", 4, 10)} has the value
-@code{"sense"}.
+Returns a string consisting of the first @var{count} bytes from
+@var{string} beginning at position @var{start}.  Returns an empty
+string if @var{start} or @var{count} is system-missing, if @var{start}
+is less than 1 or greater than the number of bytes in @var{string}, or
+if @var{count} is less than 1.  Returns a string shorter than
+@var{count} bytes if @var{start} + @var{count} - 1 is greater than the
+number of bytes in @var{string}.  Examples: @code{SUBSTR("abcdefg", 3,
+2)} has value @code{"cd"}; @code{SUBSTR("nonsense", 4, 10)} has the
+value @code{"sense"}.
 @end deftypefn
 
 @cindex case conversion
index 77270c80b436e4599ac562aaf86c044c94f1921f..71dd6a5fb73673b02b2d35f08f5bdbfa12024695 100644 (file)
@@ -537,7 +537,12 @@ shuffled around.
 @cindex @code{$DATE}
 @item $DATE
 Date the @pspp{} process was started, in format A9, following the
-pattern @code{DD MMM YY}.
+pattern @code{DD-MMM-YY}.
+
+@cindex @code{$DATE11}
+@item $DATE11
+Date the @pspp{} process was started, in format A11, following the
+pattern @code{DD-MMM-YYYY}.
 
 @cindex @code{$JDATE}
 @item $JDATE
index e07e7d63aca2e9043f7edf692c351147183137e4..3e3349fdd01ddc44789d959c49360a2663390f98 100644 (file)
@@ -54,8 +54,8 @@ is_leap_year (int y)
   return y % 4 == 0 && (y % 100 != 0 || y % 400 == 0);
 }
 
-static int
-raw_gregorian_to_offset (int y, int m, int d)
+int
+calendar_raw_gregorian_to_offset (int y, int m, int d)
 {
   return (EPOCH - 1
           + 365 * (y - 1)
@@ -67,66 +67,77 @@ raw_gregorian_to_offset (int y, int m, int d)
           + d);
 }
 
-/* Returns the number of days from 14 Oct 1582 to (Y,M,D) in the
-   Gregorian calendar.  Returns SYSMIS for dates before 14 Oct
-   1582. */
-double
-calendar_gregorian_to_offset (int y, int m, int d,
-                              const struct fmt_settings *settings,
-                              char **errorp)
+int *
+calendar_gregorian_adjust (int *y, int *m, int *d,
+                           const struct fmt_settings *settings)
 {
   /* Normalize year. */
-  if (y >= 0 && y < 100)
+  if (*y >= 0 && *y < 100)
     {
       int epoch = fmt_settings_get_epoch (settings);
-      int century = epoch / 100 + (y < epoch % 100);
-      y += century * 100;
+      int century = epoch / 100 + (*y < epoch % 100);
+      *y += century * 100;
     }
 
   /* Normalize month. */
-  if (m < 1 || m > 12)
+  if (*m < 1 || *m > 12)
     {
-      if (m == 0)
+      if (*m == 0)
         {
-          y--;
-          m = 12;
+          *y -= 1;
+          *m = 12;
         }
-      else if (m == 13)
+      else if (*m == 13)
         {
-          y++;
-          m = 1;
+          *y += 1;
+          *m = 1;
         }
       else
-        {
-          if (errorp != NULL)
-            *errorp = xasprintf (_("Month %d is not in acceptable range of "
-                                   "0 to 13."), m);
-          return SYSMIS;
-        }
+        return m;
     }
 
   /* Normalize day. */
-  if (d < 0 || d > 31)
-    {
-      if (errorp != NULL)
-        *errorp = xasprintf (_("Day %d is not in acceptable range of "
-                               "0 to 31."), d);
-      return SYSMIS;
-    }
+  if (*d < 0 || *d > 31)
+    return d;
 
   /* Validate date. */
-  if (y < 1582 || (y == 1582 && (m < 10 || (m == 10 && d < 15))))
+  if (*y < 1582 || (*y == 1582 && (*m < 10 || (*m == 10 && *d < 15))))
+    return y;
+
+  return NULL;
+}
+
+/* Returns the number of days from 14 Oct 1582 to (Y,M,D) in the
+   Gregorian calendar.  Returns SYSMIS for dates before 14 Oct
+   1582. */
+double
+calendar_gregorian_to_offset (int y, int m, int d,
+                              const struct fmt_settings *settings,
+                              char **errorp)
+{
+  int *bad_value = calendar_gregorian_adjust (&y, &m, &d, settings);
+  if (!bad_value)
+    {
+      if (errorp)
+        *errorp = NULL;
+      return calendar_raw_gregorian_to_offset (y, m, d);
+    }
+  else
     {
-      if (errorp != NULL)
-        *errorp = xasprintf (_("Date %04d-%d-%d is before the earliest "
-                               "acceptable date of 1582-10-15."), y, m, d);
+      if (errorp)
+        {
+          if (bad_value == &y)
+            *errorp = xasprintf (_("Date %04d-%d-%d is before the earliest "
+                                   "supported date 1582-10-15."), y, m, d);
+          else if (bad_value == &m)
+            *errorp = xasprintf (_("Month %d is not in the acceptable range of "
+                                   "0 to 13."), m);
+          else
+            *errorp = xasprintf (_("Day %d is not in the acceptable range of "
+                                   "0 to 31."), d);
+        }
       return SYSMIS;
     }
-
-  /* Calculate offset. */
-  if (errorp != NULL)
-    *errorp = NULL;
-  return raw_gregorian_to_offset (y, m, d);
 }
 
 /* Returns the number of days in the given YEAR from January 1 up
@@ -187,7 +198,7 @@ void
 calendar_offset_to_gregorian (int ofs, int *y, int *m, int *d, int *yd)
 {
   int year = *y = calendar_offset_to_year (ofs);
-  int january1 = raw_gregorian_to_offset (year, 1, 1);
+  int january1 = calendar_raw_gregorian_to_offset (year, 1, 1);
   int yday = *yd = ofs - january1 + 1;
   int march1 = january1 + cum_month_days (year, 3);
   int correction = ofs < march1 ? 0 : (is_leap_year (year) ? 1 : 2);
@@ -202,7 +213,7 @@ int
 calendar_offset_to_yday (int ofs)
 {
   int year = calendar_offset_to_year (ofs);
-  int january1 = raw_gregorian_to_offset (year, 1, 1);
+  int january1 = calendar_raw_gregorian_to_offset (year, 1, 1);
   int yday = ofs - january1 + 1;
   return yday;
 }
index 02a9de28ace572aa9ddf5923e8fb61cdf8fcb85a..ce3fcc2ee352f73193a44895fcaa70d63548a98e 100644 (file)
@@ -21,9 +21,14 @@ along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
 struct fmt_settings;
 
+int *calendar_gregorian_adjust (int *y, int *m, int *d,
+                                const struct fmt_settings *);
+int calendar_raw_gregorian_to_offset (int y, int m, int d);
+
 double calendar_gregorian_to_offset (int y, int m, int d,
                                      const struct fmt_settings *,
                                      char **errorp);
+
 void calendar_offset_to_gregorian (int ofs, int *y, int *m, int *d, int *yd);
 int calendar_offset_to_year (int ofs);
 int calendar_offset_to_month (int ofs);
index 648e00f04ee1c094ea7a60bf8068b3f60452f0c0..e11f173948cc0ec1293ffa5f6800a8702e69e074 100644 (file)
@@ -376,6 +376,8 @@ dataset_set_display (struct dataset *ds, enum dataset_display display)
 time_t
 time_of_last_procedure (struct dataset *ds)
 {
+  if (!ds)
+    return time (NULL);
   if (ds->last_proc_invocation == 0)
     update_last_proc_invocation (ds);
   return ds->last_proc_invocation;
index f323424d0868fbf128629bbf845e7bb94726a813..79c6240a9125423ec986148a82d067701c4e048c 100644 (file)
@@ -334,10 +334,11 @@ fmt_default_for_width (int width)
           : fmt_for_output (FMT_A, width, 0));
 }
 
-/* Checks whether SPEC is valid for USE and returns nonzero if so.
-   Otherwise, emits an error message and returns zero. */
-bool
-fmt_check (const struct fmt_spec *spec, enum fmt_use use)
+/* Checks whether SPEC is valid for USE and returns NULL if so.  Otherwise,
+   returns a malloc()'d string that describes the error.  The caller must
+   eventually free() the string. */
+char *
+fmt_check__ (const struct fmt_spec *spec, enum fmt_use use)
 {
   char str[FMT_STRING_LEN_MAX + 1];
   int min_w, max_w, max_d;
@@ -346,107 +347,115 @@ fmt_check (const struct fmt_spec *spec, enum fmt_use use)
   fmt_to_string (spec, str);
 
   if (use == FMT_FOR_INPUT && !fmt_usable_for_input (spec->type))
-    {
-      msg (SE, _("Format %s may not be used for input."), str);
-      return false;
-    }
+    return xasprintf (_("Format %s may not be used for input."), str);
 
   if (spec->w % fmt_step_width (spec->type))
     {
       assert (fmt_step_width (spec->type) == 2);
-      if (use == FMT_FOR_INPUT)
-        msg (SE, _("Input format %s specifies width %d, "
-                   "but %s requires an even width."),
-             str, spec->w, fmt_name (spec->type));
-      else
-        msg (SE, _("Output format %s specifies width %d, "
-                   "but %s requires an even width."),
-             str, spec->w, fmt_name (spec->type));
-      return false;
+      return (use == FMT_FOR_INPUT
+              ? xasprintf (_("Input format %s specifies width %d, "
+                             "but %s requires an even width."),
+                           str, spec->w, fmt_name (spec->type))
+              : xasprintf (_("Output format %s specifies width %d, "
+                             "but %s requires an even width."),
+                           str, spec->w, fmt_name (spec->type)));
     }
 
   min_w = fmt_min_width (spec->type, use);
   max_w = fmt_max_width (spec->type, use);
   if (spec->w < min_w || spec->w > max_w)
-    {
-      if (use == FMT_FOR_INPUT)
-        msg (SE, _("Input format %s specifies width %d, but "
-                   "%s requires a width between %d and %d."),
-             str, spec->w, fmt_name (spec->type), min_w, max_w);
-      else
-        msg (SE, _("Output format %s specifies width %d, but "
-                   "%s requires a width between %d and %d."),
-             str, spec->w, fmt_name (spec->type), min_w, max_w);
-      return false;
-    }
+    return (use == FMT_FOR_INPUT
+            ? xasprintf (_("Input format %s specifies width %d, but "
+                           "%s requires a width between %d and %d."),
+                         str, spec->w, fmt_name (spec->type), min_w, max_w)
+            : xasprintf (_("Output format %s specifies width %d, but "
+                           "%s requires a width between %d and %d."),
+                         str, spec->w, fmt_name (spec->type), min_w, max_w));
 
   max_d = fmt_max_decimals (spec->type, spec->w, use);
   if (!fmt_takes_decimals (spec->type) && spec->d != 0)
-    {
-      if (use == FMT_FOR_INPUT)
-        msg (SE, ngettext ("Input format %s specifies %d decimal place, but "
-                           "%s does not allow any decimals.",
-                           "Input format %s specifies %d decimal places, but "
-                           "%s does not allow any decimals.",
+    return (use == FMT_FOR_INPUT
+            ? xasprintf (ngettext (
+                           "Input format %s specifies %d decimal "
+                           "place, but %s does not allow any decimals.",
+                           "Input format %s specifies %d decimal "
+                           "places, but %s does not allow any "
+                           "decimals.",
                            spec->d),
-             str, spec->d, fmt_name (spec->type));
-      else
-        msg (SE, ngettext ("Output format %s specifies %d decimal place, but "
-                           "%s does not allow any decimals.",
+                         str, spec->d, fmt_name (spec->type))
+            : xasprintf (ngettext (
+                           "Output format %s specifies %d decimal "
+                           "place, but %s does not allow any decimals.",
                            "Output format %s specifies %d decimal places, but "
                            "%s does not allow any decimals.",
                            spec->d),
-             str, spec->d, fmt_name (spec->type));
-      return false;
-    }
+                         str, spec->d, fmt_name (spec->type)));
   else if (spec->d > max_d)
     {
       if (max_d > 0)
-        {
-          if (use == FMT_FOR_INPUT)
-            msg (SE, ngettext ("Input format %s specifies %d decimal place, "
+        return (use == FMT_FOR_INPUT
+                ? xasprintf (ngettext (
+                               "Input format %s specifies %d decimal place, "
                                "but the given width allows at most "
                                "%d decimals.",
                                "Input format %s specifies %d decimal places, "
                                "but the given width allows at most "
                                "%d decimals.",
                                spec->d),
-                 str, spec->d, max_d);
-          else
-            msg (SE, ngettext ("Output format %s specifies %d decimal place, "
+                             str, spec->d, max_d)
+                : xasprintf (ngettext (
+                               "Output format %s specifies %d decimal place, "
                                "but the given width allows at most "
                                "%d decimals.",
                                "Output format %s specifies %d decimal places, "
                                "but the given width allows at most "
                                "%d decimals.",
                                spec->d),
-                 str, spec->d, max_d);
-        }
+                             str, spec->d, max_d));
       else
-        {
-          if (use == FMT_FOR_INPUT)
-            msg (SE, ngettext ("Input format %s specifies %d decimal place, "
+        return (use == FMT_FOR_INPUT
+                ? xasprintf (ngettext (
+                               "Input format %s specifies %d decimal place, "
                                "but the given width does not allow "
                                "for any decimals.",
                                "Input format %s specifies %d decimal places, "
                                "but the given width does not allow "
                                "for any decimals.",
                                spec->d),
-                 str, spec->d);
-          else
-            msg (SE, ngettext ("Output format %s specifies %d decimal place, "
+                             str, spec->d)
+                : xasprintf (ngettext (
+                               "Output format %s specifies %d decimal place, "
                                "but the given width does not allow "
                                "for any decimals.",
                                "Output format %s specifies %d decimal places, "
                                "but the given width does not allow "
                                "for any decimals.",
                                spec->d),
-                 str, spec->d);
-        }
+                             str, spec->d));
+    }
+
+  return NULL;
+}
+
+static bool
+fmt_emit_and_free_error (char *error)
+{
+  if (error)
+    {
+      msg (SE, "%s", error);
+      free (error);
       return false;
     }
+  else
+    return true;
+}
 
-  return true;
+/* Checks whether SPEC is valid for USE and returns nonzero if so.  Otherwise,
+   emits an error message for the current source location and returns zero. */
+bool
+fmt_check (const struct fmt_spec *spec, enum fmt_use use)
+{
+  return fmt_emit_and_free_error (fmt_check__ (spec, use));
 }
 
 /* Checks whether SPEC is valid as an input format and returns
@@ -466,23 +475,52 @@ fmt_check_output (const struct fmt_spec *spec)
   return fmt_check (spec, FMT_FOR_OUTPUT);
 }
 
+/* Checks that FORMAT is appropriate for a variable of the given VAR_TYPE and
+   returns NULL if so.  Otherwise returns a malloc()'d error message that the
+   calelr must eventually free(). */
+char *
+fmt_check_type_compat__ (const struct fmt_spec *format, enum val_type var_type)
+{
+  assert (val_type_is_valid (var_type));
+  if ((var_type == VAL_STRING) != (fmt_is_string (format->type) != 0))
+    {
+      char str[FMT_STRING_LEN_MAX + 1];
+      return xasprintf (_("%s variables are not compatible with %s format %s."),
+                        var_type == VAL_STRING ? _("String") : _("Numeric"),
+                        var_type == VAL_STRING ? _("numeric") : _("string"),
+                        fmt_to_string (format, str));
+    }
+  return NULL;
+}
+
 /* Checks that FORMAT is appropriate for a variable of the given
    VAR_TYPE and returns true if so.  Otherwise returns false and
    emits an error message. */
 bool
 fmt_check_type_compat (const struct fmt_spec *format, enum val_type var_type)
 {
-  assert (val_type_is_valid (var_type));
-  if ((var_type == VAL_STRING) != (fmt_is_string (format->type) != 0))
+  return fmt_emit_and_free_error (fmt_check_type_compat__ (format, var_type));
+}
+
+/* Checks that FORMAT is appropriate for a variable of the given WIDTH and
+   returns NULL if so.  Otherwise returns a malloc()'d error message that the
+   calelr must eventually free(). */
+char *
+fmt_check_width_compat__ (const struct fmt_spec *format, int width)
+{
+  char *error = fmt_check_type_compat__ (format, val_type_from_width (width));
+  if (error)
+    return error;
+
+  if (fmt_var_width (format) != width)
     {
       char str[FMT_STRING_LEN_MAX + 1];
-      msg (SE, _("%s variables are not compatible with %s format %s."),
-           var_type == VAL_STRING ? _("String") : _("Numeric"),
-           var_type == VAL_STRING ? _("numeric") : _("string"),
-           fmt_to_string (format, str));
-      return false;
+      return xasprintf (_("String variable with width %d is not compatible "
+                          "with format %s."),
+                        width, fmt_to_string (format, str));
     }
-  return true;
+
+  return NULL;
 }
 
 /* Checks that FORMAT is appropriate for a variable of the given
@@ -491,17 +529,7 @@ fmt_check_type_compat (const struct fmt_spec *format, enum val_type var_type)
 bool
 fmt_check_width_compat (const struct fmt_spec *format, int width)
 {
-  if (!fmt_check_type_compat (format, val_type_from_width (width)))
-    return false;
-  if (fmt_var_width (format) != width)
-    {
-      char str[FMT_STRING_LEN_MAX + 1];
-      msg (SE, _("String variable with width %d is not compatible with "
-                 "format %s."),
-           width, fmt_to_string (format, str));
-      return false;
-    }
-  return true;
+  return fmt_emit_and_free_error (fmt_check_width_compat__ (format, width));
 }
 
 /* Returns the width corresponding to FORMAT.  The return value
index 4cde16b17a0b1352356f6b522c602bc18150b70e..d6779d4b3f5f6dccab20e952d55006cf835eaf0e 100644 (file)
@@ -25,6 +25,7 @@
 #include "libpspp/str.h"
 
 struct fmt_settings;
+struct msg_location;
 
 /* How a format is going to be used. */
 enum fmt_use
@@ -96,6 +97,10 @@ bool fmt_check_output (const struct fmt_spec *);
 bool fmt_check_type_compat (const struct fmt_spec *, enum val_type);
 bool fmt_check_width_compat (const struct fmt_spec *, int var_width);
 
+char *fmt_check__ (const struct fmt_spec *, enum fmt_use);
+char *fmt_check_type_compat__ (const struct fmt_spec *, enum val_type);
+char *fmt_check_width_compat__ (const struct fmt_spec *, int var_width);
+
 /* Working with formats. */
 int fmt_var_width (const struct fmt_spec *);
 char *fmt_to_string (const struct fmt_spec *, char s[FMT_STRING_LEN_MAX + 1]);
index 1ad2be802e437eadd50a481f0ae0499c5d5b6a83..09f237e0cd4e3664a0a57353ce2b0f0d2292becf 100644 (file)
@@ -16,6 +16,7 @@
 
 #include <config.h>
 
+#include "language/expressions/private.h"
 #include "evaluate.h"
 
 #include <ctype.h>
@@ -23,7 +24,6 @@
 #include "libpspp/assertion.h"
 #include "libpspp/message.h"
 #include "language/expressions/helpers.h"
-#include "language/expressions/private.h"
 #include "language/lexer/value-parser.h"
 #include "libpspp/pool.h"
 #include "output/driver.h"
@@ -108,32 +108,40 @@ expr_evaluate_str (struct expression *e, const struct ccase *c, int case_idx,
 #include "language/lexer/lexer.h"
 #include "language/command.h"
 
+static bool default_optimize = true;
+
 int
 cmd_debug_evaluate (struct lexer *lexer, struct dataset *dsother UNUSED)
 {
-  bool optimize = true;
+  bool optimize = default_optimize;
   int retval = CMD_FAILURE;
   bool dump_postfix = false;
+  bool set_defaults = false;
 
   struct ccase *c = NULL;
 
   struct dataset *ds = NULL;
 
   char *name = NULL;
+  char *title = NULL;
 
   struct expression *expr;
 
+  struct dictionary *d = NULL;
+
   for (;;)
     {
-      struct dictionary *d = NULL;
       if (lex_match_id (lexer, "NOOPTIMIZE"))
-        optimize = 0;
+        optimize = false;
+      else if (lex_match_id (lexer, "OPTIMIZE"))
+        optimize = true;
       else if (lex_match_id (lexer, "POSTFIX"))
         dump_postfix = 1;
+      else if (lex_match_id (lexer, "SET"))
+        set_defaults = true;
       else if (lex_match (lexer, T_LPAREN))
         {
           struct variable *v;
-          int width;
 
           if (!lex_force_id (lexer))
             goto done;
@@ -143,10 +151,25 @@ cmd_debug_evaluate (struct lexer *lexer, struct dataset *dsother UNUSED)
           if (!lex_force_match (lexer, T_EQUALS))
             goto done;
 
+          union value value;
+          int width;
           if (lex_is_number (lexer))
-            width = 0;
+            {
+              width = 0;
+              value.f = lex_number (lexer);
+              lex_get (lexer);
+            }
+          else if (lex_match_id (lexer, "SYSMIS"))
+            {
+              width = 0;
+              value.f = SYSMIS;
+            }
           else if (lex_is_string (lexer))
-            width = ss_length (lex_tokss (lexer));
+            {
+              width = ss_length (lex_tokss (lexer));
+              value.s = CHAR_CAST (uint8_t *, ss_xstrdup (lex_tokss (lexer)));
+              lex_get (lexer);
+            }
           else
             {
               lex_error (lexer, _("expecting number or string"));
@@ -163,35 +186,66 @@ cmd_debug_evaluate (struct lexer *lexer, struct dataset *dsother UNUSED)
           if (v == NULL)
             {
               msg (SE, _("Duplicate variable name %s."), name);
+              value_destroy (&value, width);
               goto done;
             }
           free (name);
           name = NULL;
 
+          if (lex_match_id (lexer, "MISSING"))
+            {
+              struct missing_values mv;
+              mv_init (&mv, width);
+              mv_add_value (&mv, &value);
+              var_set_missing_values (v, &mv);
+              mv_destroy (&mv);
+            }
+
           if (c == NULL)
             c = case_create (dict_get_proto (d));
           else
             c = case_unshare_and_resize (c, dict_get_proto (d));
-
-          if (!parse_value (lexer, case_data_rw (c, v), v))
-            NOT_REACHED ();
+          value_swap (case_data_rw (c, v), &value);
+          value_destroy (&value, width);
 
           if (!lex_force_match (lexer, T_RPAREN))
             goto done;
         }
+      else if (lex_match_id (lexer, "VECTOR"))
+        {
+          struct variable **vars;
+          size_t n;
+          dict_get_vars_mutable (d, &vars, &n, 0);
+          dict_create_vector_assert (d, "V", vars, n);
+          free (vars);
+        }
       else
         break;
     }
 
-  if (!lex_force_match (lexer, T_SLASH))
+  if (set_defaults)
+    {
+      retval = CMD_SUCCESS;
+      default_optimize = optimize;
       goto done;
+    }
+
+  if (!lex_force_match (lexer, T_SLASH))
+    goto done;
+
+  for (size_t i = 1; ; i++)
+    if (lex_next_token (lexer, i) == T_ENDCMD)
+      {
+        title = lex_next_representation (lexer, 0, i - 1);
+        break;
+      }
 
   expr = expr_parse_any (lexer, ds, optimize);
   if (!expr || lex_end_of_command (lexer) != CMD_SUCCESS)
     {
       if (expr != NULL)
         expr_free (expr);
-      output_log ("error");
+      output_log ("%s => error", title);
       goto done;
     }
 
@@ -201,19 +255,20 @@ cmd_debug_evaluate (struct lexer *lexer, struct dataset *dsother UNUSED)
     switch (expr->type)
       {
       case OP_number:
+      case OP_num_vec_elem:
         {
           double d = expr_evaluate_num (expr, c, 0);
           if (d == SYSMIS)
-            output_log ("sysmis");
+            output_log ("%s => sysmis", title);
           else
-            output_log ("%.2f", d);
+            output_log ("%s => %.2f", title, d);
         }
         break;
 
       case OP_boolean:
         {
           double b = expr_evaluate_num (expr, c, 0);
-          output_log ("%s",
+          output_log ("%s => %s", title,
                       b == SYSMIS ? "sysmis" : b == 0.0 ? "false" : "true");
         }
         break;
@@ -222,7 +277,7 @@ cmd_debug_evaluate (struct lexer *lexer, struct dataset *dsother UNUSED)
         {
           struct substring out;
           expr_evaluate (expr, c, 0, &out);
-          output_log ("\"%.*s\"", (int) out.length, out.string);
+          output_log ("%s => \"%.*s\"", title, (int) out.length, out.string);
           break;
         }
 
@@ -239,6 +294,7 @@ cmd_debug_evaluate (struct lexer *lexer, struct dataset *dsother UNUSED)
   case_unref (c);
 
   free (name);
+  free (title);
 
   return retval;
 }
@@ -294,6 +350,9 @@ expr_debug_print_postfix (const struct expression *e)
         case OP_integer:
           ds_put_format (&s, "i<%d>", op->integer);
           break;
+        case OP_expr_node:
+          ds_put_cstr (&s, "expr_node");
+          break;
         default:
           NOT_REACHED ();
         }
index e530cd8208a11542c71e62f364e98890258aa974..ffe780a3c35ab9cbfdd07f016dcc857890605a06 100644 (file)
@@ -46,6 +46,8 @@ def init_all_types():
                      'string', 'ss', 'empty_string'),
         Type.new_any('boolean', 'double', 'number', 'n',
                      'boolean', 'ns', 'SYSMIS'),
+        Type.new_any('integer', 'int', 'number', 'n',
+                     'integer', 'ns', 'SYSMIS'),
 
         # Format types.
         Type.new_atom('format'),
@@ -55,8 +57,6 @@ def init_all_types():
                       'format', 'f', 'num_output_format'),
 
         # Integer types.
-        Type.new_leaf('integer', 'int', 'integer', 'n',
-                      'integer'),
         Type.new_leaf('pos_int', 'int', 'integer', 'n',
                       'positive_integer_constant'),
 
@@ -72,6 +72,12 @@ def init_all_types():
         # Vectors.
         Type.new_leaf('vector', 'const struct vector *',
                       'vector', 'v', 'vector'),
+        Type.new_any('num_vec_elem', 'double', 'number', 'n',
+                     'number', 'ns', 'SYSMIS'),
+
+        # Types as leaves or auxiliary data.
+        Type.new_leaf('expr_node', 'const struct expr_node *',
+                      'expr_node', 'e', 'expr_node'),
 
         # Types that appear only as auxiliary data.
         Type.new_auxonly('expression', 'struct expression *', 'e'),
@@ -118,14 +124,14 @@ class Type:
 
         'c_type' is the type used for C objects of this type.
 
+        'atom' should be the name of the member of "union
+        operation_data" that holds a value of this type.
+
         'mangle' should be a short string for name mangling purposes,
         to allow overloading functions with the same name but
         different argument types.  Use the same 'mangle' for two
         different types if those two types should not be overloaded.
 
-        'atom' should be the name of the member of "union
-        operation_data" that holds a value of this type.
-
         'human_name' should be a name to use when describing this type
         to the user (see Op.prototype()).
 
@@ -236,7 +242,7 @@ class Op:
             for arg in self.args:
                 arg_name = 'arg_%s' % arg.name
                 if arg.idx is None:
-                    if arg.type_.name in ['number', 'boolean']:
+                    if arg.type_.name in ['number', 'boolean', 'integer']:
                         sysmis_cond += ['!is_valid (%s)' % arg_name]
                 elif arg.type_.name == 'number':
                     a = arg_name
@@ -313,6 +319,10 @@ class Op:
             flags += ['OPF_PERM_ONLY']
         if self.no_abbrev:
             flags += ['OPF_NO_ABBREV']
+        for aux in self.aux:
+            if aux['TYPE'].name == 'expr_node':
+                flags += ['OPF_EXPR_NODE']
+                break
         return ' | '.join(flags) if flags else '0'
 
 
@@ -361,7 +371,7 @@ def parse_input():
         return_type = Type.parse()
         if return_type is None:
             return_type = types['number']
-        if return_type.name not in ['number', 'string', 'boolean']:
+        if return_type.name not in ['number', 'string', 'boolean', 'num_vec_elem']:
             die('%s is not a valid return type' % return_type.name)
 
         if token == 'operator':
@@ -665,17 +675,14 @@ class Arg:
 
 def print_header():
     """Prints the output file header."""
-    out_file.write("""\
-/* %s
-   Generated from %s by generate.py.
-   Do not modify! */
-
-""" % (out_file_name, in_file_name))
+    sys.stdout.write("""\
+/* Generated by generate.py. Do not modify! */
+""")
 
 
 def print_trailer():
     """Prints the output file trailer."""
-    out_file.write("""\
+    sys.stdout.write("""\
 
 /*
    Local Variables:
@@ -687,7 +694,7 @@ def print_trailer():
 
 
 def generate_evaluate_h():
-    out_file.write('#include "helpers.h"\n\n')
+    sys.stdout.write('#include "helpers.h"\n\n')
 
     for op in order:
         if op.unimplemented:
@@ -710,26 +717,34 @@ def generate_evaluate_h():
         else:
             statements = '  return %s;\n' % op.expression
 
-        out_file.write('static inline %s\n' % op.returns.c_type)
-        out_file.write('eval_%s (%s)\n' % (op.opname, ', '.join(args)))
-        out_file.write('{\n')
-        out_file.write(statements)
-        out_file.write('}\n\n')
+        sys.stdout.write('static inline %s\n' % op.returns.c_type)
+        sys.stdout.write('eval_%s (%s)\n' % (op.opname, ', '.join(args)))
+        sys.stdout.write('{\n')
+        sys.stdout.write(statements)
+        sys.stdout.write('}\n\n')
 
 
 def generate_evaluate_inc():
     for op in order:
         if op.unimplemented:
-            out_file.write('case %s:\n' % op.opname)
-            out_file.write('  NOT_REACHED ();\n\n')
+            sys.stdout.write('case %s:\n' % op.opname)
+            sys.stdout.write('  NOT_REACHED ();\n\n')
             continue
 
         decls = []
         args = []
         for arg in op.args:
             type_ = arg.type_
-            c_type = type_.c_type
-            args += ['arg_%s' % arg.name]
+            if type_.c_type == 'int ':
+                c_type = 'double '
+                if op.absorb_miss:
+                    args += ['arg_%s == SYSMIS ? INT_MIN : arg_%s'
+                             % (arg.name, arg.name)]
+                else:
+                    args += ['arg_%s' % arg.name]
+            else:
+                c_type = type_.c_type
+                args += ['arg_%s' % arg.name]
             if arg.idx is None:
                 decl = '%sarg_%s' % (c_type, arg.name)
                 if type_.role == 'any':
@@ -755,6 +770,10 @@ def generate_evaluate_inc():
                 decls += ['%saux_%s = op++->%s'
                           % (type_.c_type, name, type_.atom)]
                 args += ['aux_%s' % name]
+            elif type_.name == 'expr_node':
+                decls += ['%saux_%s = op++->node'
+                          % (type_.c_type, name)]
+                args += ['aux_%s' % name]
             elif type_.role == 'auxonly':
                 args += [type_.auxonly_value]
 
@@ -766,29 +785,29 @@ def generate_evaluate_inc():
 
         stack = op.returns.stack
 
-        out_file.write('case %s:\n' % op.opname)
+        sys.stdout.write('case %s:\n' % op.opname)
         if decls:
-            out_file.write('  {\n')
+            sys.stdout.write('  {\n')
             for decl in decls:
-                out_file.write('    %s;\n' % decl)
+                sys.stdout.write('    %s;\n' % decl)
             if sysmis_cond is not None:
                 miss_ret = op.returns.missing_value
-                out_file.write('    *%s++ = force_sysmis ? %s : %s;\n'
+                sys.stdout.write('    *%s++ = force_sysmis ? %s : %s;\n'
                                % (stack, miss_ret, result))
             else:
-                out_file.write('    *%s++ = %s;\n' % (stack, result))
-            out_file.write('  }\n')
+                sys.stdout.write('    *%s++ = %s;\n' % (stack, result))
+            sys.stdout.write('  }\n')
         else:
-            out_file.write('  *%s++ = %s;\n' % (stack, result))
-        out_file.write('  break;\n\n')
+            sys.stdout.write('  *%s++ = %s;\n' % (stack, result))
+        sys.stdout.write('  break;\n\n')
 
 
 def generate_operations_h():
-    out_file.write('#include <stdlib.h>\n')
-    out_file.write('#include <stdbool.h>\n\n')
+    sys.stdout.write('#include <stdlib.h>\n')
+    sys.stdout.write('#include <stdbool.h>\n\n')
 
-    out_file.write('typedef enum')
-    out_file.write('  {\n')
+    sys.stdout.write('typedef enum')
+    sys.stdout.write('  {\n')
     atoms = []
     for type_ in types.values():
         if type_.role != 'auxonly':
@@ -800,10 +819,10 @@ def generate_operations_h():
     print_operations('operator', 'OP_function_last + 1',
                      [o.opname for o in opers])
     print_range('OP_composite', 'OP_function_first', 'OP_operator_last')
-    out_file.write(',\n\n')
+    sys.stdout.write(',\n\n')
     print_range('OP', 'OP_atom_first', 'OP_composite_last')
-    out_file.write('\n  }\n')
-    out_file.write('operation_type, atom_type;\n')
+    sys.stdout.write('\n  }\n')
+    sys.stdout.write('operation_type, atom_type;\n')
 
     print_predicate('is_operation', 'OP')
     for key in ('atom', 'composite', 'function', 'operator'):
@@ -811,37 +830,37 @@ def generate_operations_h():
 
 
 def print_operations(type_, first, names):
-    out_file.write('    /* %s types. */\n' % type_.title())
-    out_file.write('    %s = %s,\n' % (names[0], first))
+    sys.stdout.write('    /* %s types. */\n' % type_.title())
+    sys.stdout.write('    %s = %s,\n' % (names[0], first))
     for name in names[1:]:
-        out_file.write('    %s,\n' % name)
+        sys.stdout.write('    %s,\n' % name)
     print_range('OP_%s' % type_, names[0], names[-1])
-    out_file.write(',\n\n')
+    sys.stdout.write(',\n\n')
 
 
 def print_range(prefix, first, last):
-    out_file.write('    %s_first = %s,\n' % (prefix, first))
-    out_file.write('    %s_last = %s,\n' % (prefix, last))
-    out_file.write('    n_%s = %s_last - %s_first + 1'
+    sys.stdout.write('    %s_first = %s,\n' % (prefix, first))
+    sys.stdout.write('    %s_last = %s,\n' % (prefix, last))
+    sys.stdout.write('    n_%s = %s_last - %s_first + 1'
                    % (prefix, prefix, prefix))
 
 
 def print_predicate(function, category):
-    out_file.write('\nstatic inline bool\n')
-    out_file.write('%s (operation_type op)\n' % function)
-    out_file.write('{\n')
+    sys.stdout.write('\nstatic inline bool\n')
+    sys.stdout.write('%s (operation_type op)\n' % function)
+    sys.stdout.write('{\n')
     if function != 'is_operation':
-        out_file.write('  assert (is_operation (op));\n')
-    out_file.write('  return op >= %s_first && op <= %s_last;\n'
+        sys.stdout.write('  assert (is_operation (op));\n')
+    sys.stdout.write('  return op >= %s_first && op <= %s_last;\n'
                    % (category, category))
-    out_file.write('}\n')
+    sys.stdout.write('}\n')
 
 
 def generate_optimize_inc():
     for op in order:
         if not op.optimizable or op.unimplemented:
-            out_file.write('case %s:\n' % op.opname)
-            out_file.write('  NOT_REACHED ();\n\n')
+            sys.stdout.write('case %s:\n' % op.opname)
+            sys.stdout.write('  NOT_REACHED ();\n\n')
             continue
 
         decls = []
@@ -881,9 +900,8 @@ def generate_optimize_inc():
         for aux in op.aux:
             type_ = aux['TYPE']
             if type_.role == 'leaf':
-                func = 'get_%s_arg' % type_.atom
-                args += '%s (node, %s)' % (func, arg_idx)
-                arg_idx += 1
+                assert type_.name == 'expr_node'
+                args += ['node']
             elif type_.role == 'auxonly':
                 args += [type_.auxonly_value]
             else:
@@ -896,28 +914,28 @@ def generate_optimize_inc():
                       % (op.returns.c_type, miss_ret, result)]
             result = 'result'
 
-        out_file.write('case %s:\n' % op.opname)
+        sys.stdout.write('case %s:\n' % op.opname)
         alloc_func = 'expr_allocate_%s' % op.returns.name
         if decls:
-            out_file.write('  {\n')
+            sys.stdout.write('  {\n')
             for decl in decls:
-                out_file.write('    %s;\n' % decl)
-            out_file.write('    return %s (e, %s);\n' % (alloc_func, result))
-            out_file.write('  }\n')
+                sys.stdout.write('    %s;\n' % decl)
+            sys.stdout.write('    return %s (e, %s);\n' % (alloc_func, result))
+            sys.stdout.write('  }\n')
         else:
-            out_file.write('  return %s (e, %s);\n' % (alloc_func, result))
-        out_file.write('\n')
+            sys.stdout.write('  return %s (e, %s);\n' % (alloc_func, result))
+        sys.stdout.write('\n')
 
 
 def generate_parse_inc():
     members = ['""', '""', '0', '0', '0', '{}', '0', '0']
-    out_file.write('{%s},\n' % ', '.join(members))
+    sys.stdout.write('{%s},\n' % ', '.join(members))
 
     for type_ in types.values():
         if type_.role != 'auxonly':
             members = ('"%s"' % type_.name, '"%s"' % type_.human_name,
                        '0', 'OP_%s' % type_.name, '0', '{}', '0', '0')
-            out_file.write('{%s},\n' % ', '.join(members))
+            sys.stdout.write('{%s},\n' % ', '.join(members))
 
     for op in order:
         members = []
@@ -939,15 +957,16 @@ def generate_parse_inc():
 
         members += ['%s' % (op.array_arg().times if op.array_arg() else 0)]
 
-        out_file.write('{%s},\n' % ', '.join(members))
+        sys.stdout.write('{%s},\n' % ', '.join(members))
 
 
 def usage():
     print("""\
 %s, for generating expression parsers and evaluators from definitions
-usage: generate.py -o OUTPUT [-i INPUT] [-h]
+usage: generate.py -o OUTPUT_TYPE [-i INPUT] [-h] > OUTPUT
   -i INPUT    input file containing definitions (default: operations.def)
-  -o OUTPUT   output file
+  -o OUTPUT   output file type, one of: evaluate.h, evaluate.inc,
+              operations.h, optimize.inc, parse.inc    
   -h          display this help message
 """ % argv0)
     sys.exit(0)
@@ -979,21 +998,20 @@ if __name__ == '__main__':
             '(use --help for help)' % argv0)
 
     in_file = open(in_file_name, 'r')
-    out_file = open(out_file_name, 'w')
 
     init_all_types()
     parse_input()
 
     print_header()
-    if out_file_name.endswith('evaluate.h'):
+    if out_file_name == 'evaluate.h':
         generate_evaluate_h()
-    elif out_file_name.endswith('evaluate.inc'):
+    elif out_file_name == 'evaluate.inc':
         generate_evaluate_inc()
-    elif out_file_name.endswith('operations.h'):
+    elif out_file_name == 'operations.h':
         generate_operations_h()
-    elif out_file_name.endswith('optimize.inc'):
+    elif out_file_name == 'optimize.inc':
         generate_optimize_inc()
-    elif out_file_name.endswith('parse.inc'):
+    elif out_file_name == 'parse.inc':
         generate_parse_inc()
     else:
         die('%s: unknown output type' % argv0)
index 053ada56b555b5215d40b61eddbb7e0f9cf5dea0..d91365de8523c4ee53b3edd792d9f59ab87438b3 100644 (file)
 const struct substring empty_string = {NULL, 0};
 
 double
-expr_ymd_to_ofs (double year, double month, double day)
+expr_ymd_to_ofs (int y, int m, int d,
+                 const struct expression *e, const struct expr_node *node,
+                 int ya, int ma, int da)
 {
-  int y = year;
-  int m = month;
-  int d = day;
-  char *error;
-  double ofs;
-
-  if (y != year || m != month || d != day)
-    {
-      msg (SE, _("One of the arguments to a DATE function is not an integer.  "
-                 "The result will be system-missing."));
-      return SYSMIS;
-    }
-
-  ofs = calendar_gregorian_to_offset (y, m, d, settings_get_fmt_settings (),
-                                      &error);
-  if (error != NULL)
-    {
-      msg (SE, "%s", error);
-      free (error);
-    }
-  return ofs;
-}
-
-double
-expr_ymd_to_date (double year, double month, double day)
-{
-  double ofs = expr_ymd_to_ofs (year, month, day);
-  return ofs != SYSMIS ? ofs * DAY_S : SYSMIS;
-}
-
-double
-expr_wkyr_to_date (double week, double year)
-{
-  int w = week;
-
-  if (w != week)
-    {
-      msg (SE, _("The week argument to DATE.WKYR is not an integer.  "
-                 "The result will be system-missing."));
-      return SYSMIS;
-    }
-  else if (w < 1 || w > 53)
-    {
-      msg (SE, _("The week argument to DATE.WKYR is outside the acceptable "
-                 "range of 1 to 53.  "
-                 "The result will be system-missing."));
-      return SYSMIS;
-    }
+  int *error = calendar_gregorian_adjust (&y, &m, &d,
+                                          settings_get_fmt_settings ());
+  if (!error)
+    return calendar_raw_gregorian_to_offset (y, m, d);
   else
     {
-      double yr_1_1 = expr_ymd_to_ofs (year, 1, 1);
-      if (yr_1_1 != SYSMIS)
-        return DAY_S * (yr_1_1 + WEEK_DAY * (w - 1));
-      else
-        return SYSMIS;
-    }
-}
-
-double
-expr_yrday_to_date (double year, double yday)
-{
-  int yd = yday;
-
-  if (yd != yday)
-    {
-      msg (SE, _("The day argument to DATE.YRDAY is not an integer.  "
-                 "The result will be system-missing."));
+      msg_at (SE, expr_location (e, node),
+              _("Invalid arguments to %s function."),
+              operations[node->type].name);
+
+      if (error == &y && ya > 0)
+        msg_at (SN, expr_location (e, y < 1582 ? node->args[ya - 1] : node),
+                _("Date %04d-%d-%d is before the earliest supported date "
+                  "1582-10-15."), y, m, d);
+      else if (error == &m && ma > 0)
+        msg_at (SN, expr_location (e, node->args[ma - 1]),
+                _("Month %d is not in the acceptable range of 0 to 13."), m);
+      else if (error == &d && da > 0)
+        msg_at (SN, expr_location (e, node->args[da - 1]),
+                _("Day %d is not in the acceptable range of 0 to 31."), d);
       return SYSMIS;
     }
-  else if (yd < 1 || yd > 366)
-    {
-      msg (SE, _("The day argument to DATE.YRDAY is outside the acceptable "
-                 "range of 1 to 366.  "
-                 "The result will be system-missing."));
-      return SYSMIS;
-    }
-  else
-    {
-      double yr_1_1 = expr_ymd_to_ofs (year, 1, 1);
-      if (yr_1_1 != SYSMIS)
-        return DAY_S * (yr_1_1 + yd - 1.);
-      else
-        return SYSMIS;
-    }
 }
 
 double
-expr_yrmoda (double year, double month, double day)
+expr_ymd_to_date (int y, int m, int d,
+                  const struct expression *e, const struct expr_node *n,
+                  int ya, int ma, int da)
 {
-  if (year >= 0 && year <= 99)
-    year += 1900;
-  else if (year != (int) year && year > 47516)
-    {
-      msg (SE, _("The year argument to YRMODA is greater than 47516.  "
-                 "The result will be system-missing."));
-      return SYSMIS;
-    }
-
-  return expr_ymd_to_ofs (year, month, day);
+  double ofs = expr_ymd_to_ofs (y, m, d, e, n, ya, ma, da);
+  return ofs != SYSMIS ? ofs * DAY_S : SYSMIS;
 }
 \f
 /* A date unit. */
@@ -149,7 +83,8 @@ enum date_unit
 /* Stores in *UNIT the unit whose name is NAME.
    Return success. */
 static enum date_unit
-recognize_unit (struct substring name, enum date_unit *unit)
+recognize_unit (struct substring name, const struct expression *e,
+                const struct expr_node *n, enum date_unit *unit)
 {
   struct unit_name
     {
@@ -178,12 +113,13 @@ recognize_unit (struct substring name, enum date_unit *unit)
         return true;
       }
 
-  msg (SE, _("Unrecognized date unit `%.*s'.  "
-             "Valid date units are `%s', `%s', `%s', "
-             "`%s', `%s', `%s', `%s', and `%s'."),
-       (int) ss_length (name), ss_data (name),
-       "years", "quarters", "months",
-       "weeks", "days", "hours", "minutes", "seconds");
+  msg_at (SE, expr_location (e, n),
+          _("Unrecognized date unit `%.*s'.  "
+            "Valid date units are `%s', `%s', `%s', "
+            "`%s', `%s', `%s', `%s', and `%s'."),
+          (int) ss_length (name), ss_data (name),
+          "years", "quarters", "months",
+          "weeks", "days", "hours", "minutes", "seconds");
 
   return false;
 }
@@ -272,11 +208,11 @@ date_unit_duration (enum date_unit unit)
 
 /* Returns the span from DATE1 to DATE2 in terms of UNIT_NAME. */
 double
-expr_date_difference (double date1, double date2, struct substring unit_name)
+expr_date_difference (double date1, double date2, struct substring unit_name,
+                      const struct expression *e, const struct expr_node *n)
 {
   enum date_unit unit;
-
-  if (!recognize_unit (unit_name, &unit))
+  if (!recognize_unit (unit_name, e, n->args[2], &unit))
     return SYSMIS;
 
   switch (unit)
@@ -317,7 +253,9 @@ enum date_sum_method
 /* Stores in *METHOD the method whose name is NAME.
    Return success. */
 static bool
-recognize_method (struct substring method_name, enum date_sum_method *method)
+recognize_method (struct substring method_name,
+                  const struct expression *e, const struct expr_node *n,
+                  enum date_sum_method *method)
 {
   if (ss_equals_case (method_name, ss_cstr ("closest")))
     {
@@ -331,8 +269,9 @@ recognize_method (struct substring method_name, enum date_sum_method *method)
     }
   else
     {
-      msg (SE, _("Invalid DATESUM method.  "
-                 "Valid choices are `%s' and `%s'."), "closest", "rollover");
+      msg_at (SE, expr_location (e, n),
+              _("Invalid DATESUM method.  "
+                "Valid choices are `%s' and `%s'."), "closest", "rollover");
       return false;
     }
 }
@@ -340,7 +279,8 @@ recognize_method (struct substring method_name, enum date_sum_method *method)
 /* Returns DATE advanced by the given number of MONTHS, with
    day-of-month overflow resolved using METHOD. */
 static double
-add_months (double date, int months, enum date_sum_method method)
+add_months (double date, int months, enum date_sum_method method,
+            const struct expression *e, const struct expr_node *n)
 {
   int y, m, d, yd;
   double output;
@@ -370,7 +310,7 @@ add_months (double date, int months, enum date_sum_method method)
     output = (output * DAY_S) + fmod (date, DAY_S);
   else
     {
-      msg (SE, "%s", error);
+      msg_at (SE, expr_location (e, n), "%s", error);
       free (error);
     }
   return output;
@@ -379,27 +319,25 @@ add_months (double date, int months, enum date_sum_method method)
 /* Returns DATE advanced by the given QUANTITY of units given in
    UNIT_NAME, with day-of-month overflow resolved using
    METHOD_NAME. */
-double
-expr_date_sum (double date, double quantity, struct substring unit_name,
-               struct substring method_name)
+static double
+expr_date_sum__ (double date, double quantity, struct substring unit_name,
+                 enum date_sum_method method,
+                 const struct expression *e, const struct expr_node *n)
 {
   enum date_unit unit;
-  enum date_sum_method method;
-
-  if (!recognize_unit (unit_name, &unit)
-      || !recognize_method (method_name, &method))
+  if (!recognize_unit (unit_name, e, n->args[2], &unit))
     return SYSMIS;
 
   switch (unit)
     {
     case DATE_YEARS:
-      return add_months (date, trunc (quantity) * 12, method);
+      return add_months (date, trunc (quantity) * 12, method, e, n);
 
     case DATE_QUARTERS:
-      return add_months (date, trunc (quantity) * 3, method);
+      return add_months (date, trunc (quantity) * 3, method, e, n);
 
     case DATE_MONTHS:
-      return add_months (date, trunc (quantity), method);
+      return add_months (date, trunc (quantity), method, e, n);
 
     case DATE_WEEKS:
     case DATE_DAYS:
@@ -412,6 +350,31 @@ expr_date_sum (double date, double quantity, struct substring unit_name,
   NOT_REACHED ();
 }
 
+/* Returns DATE advanced by the given QUANTITY of units given in
+   UNIT_NAME, with day-of-month overflow resolved using
+   METHOD_NAME. */
+double
+expr_date_sum (double date, double quantity, struct substring unit_name,
+               struct substring method_name,
+               const struct expression *e, const struct expr_node *n)
+{
+  enum date_sum_method method;
+  if (!recognize_method (method_name, e, n->args[3], &method))
+    return SYSMIS;
+
+  return expr_date_sum__ (date, quantity, unit_name, method, e, n);
+}
+
+/* Returns DATE advanced by the given QUANTITY of units given in
+   UNIT_NAME, with day-of-month overflow resolved using
+   METHOD_NAME. */
+double
+expr_date_sum_closest (double date, double quantity, struct substring unit_name,
+                       const struct expression *e, const struct expr_node *n)
+{
+  return expr_date_sum__ (date, quantity, unit_name, SUM_CLOSEST, e, n);
+}
+
 int
 compare_string_3way (const struct substring *a, const struct substring *b)
 {
@@ -484,12 +447,9 @@ replace_string (struct expression *e,
                 struct substring haystack,
                 struct substring needle,
                 struct substring replacement,
-                double n)
+                int n)
 {
-  if (!needle.length
-      || haystack.length < needle.length
-      || n <= 0
-      || n == SYSMIS)
+  if (!needle.length || haystack.length < needle.length || n <= 0)
     return haystack;
 
   struct substring result = alloc_string (e, MAX_STRING);
@@ -547,3 +507,23 @@ median (double *a, size_t n)
           : n % 2 ? a[n / 2]
           : (a[n / 2 - 1] + a[n / 2]) / 2.0);
 }
+
+const struct variable *
+expr_index_vector (const struct expression *e, const struct expr_node *n,
+                   const struct vector *v, double idx)
+{
+  if (idx >= 1 && idx <= vector_get_n_vars (v))
+    return vector_get_var (v, idx - 1);
+
+  msg_at (SE, expr_location (e, n),
+          _("Index outside valid range 1 to %zu, inclusive, for vector %s.  "
+            "The value will be treated as system-missing."),
+          vector_get_n_vars (v), vector_get_name (v));
+  if (idx == SYSMIS)
+    msg_at (SN, expr_location (e, n->args[0]),
+            _("The index is system-missing."));
+  else
+    msg_at (SN, expr_location (e, n->args[0]),
+            _("The index has value %g."), idx);
+  return NULL;
+}
index 1bd0ac8cb87dae0ab7c39bf357dc741e8ce10418..c9347da7b73202261c59d1ccf3038599fbaf7e5a 100644 (file)
@@ -39,6 +39,7 @@ along with this program.  If not, see <http://www.gnu.org/licenses/>.
 #include "data/variable.h"
 #include "data/vector.h"
 #include "language/expressions/public.h"
+#include "libpspp/assertion.h"
 #include "libpspp/compiler.h"
 #include "libpspp/i18n.h"
 #include "libpspp/message.h"
@@ -51,6 +52,8 @@ along with this program.  If not, see <http://www.gnu.org/licenses/>.
 #include "gettext.h"
 #define _(msgid) gettext (msgid)
 
+struct expr_node;
+
 static inline double check_errno (double x)
 {
   return errno == 0 ? x : SYSMIS;
@@ -70,15 +73,22 @@ extern const struct substring empty_string;
 
 int compare_string_3way (const struct substring *, const struct substring *);
 
-double expr_ymd_to_date (double year, double month, double day);
-double expr_ymd_to_ofs (double year, double month, double day);
-double expr_wkyr_to_date (double wk, double yr);
-double expr_yrday_to_date (double yr, double day);
-double expr_yrmoda (double year, double month, double day);
+double expr_ymd_to_date (int year, int month, int day,
+                         const struct expression *, const struct expr_node *,
+                         int ya, int ma, int da);
+double expr_ymd_to_ofs (int y, int m, int d,
+                        const struct expression *, const struct expr_node *,
+                        int ya, int ma, int da);
 double expr_date_difference (double date1, double date2,
-                             struct substring unit);
+                             struct substring unit, const struct expression *,
+                             const struct expr_node *);
 double expr_date_sum (double date, double quantity, struct substring unit_name,
-                      struct substring method_name);
+                      struct substring method_name,
+                      const struct expression *, const struct expr_node *);
+double expr_date_sum_closest (double date, double quantity,
+                              struct substring unit_name,
+                              const struct expression *,
+                              const struct expr_node *);
 
 struct substring alloc_string (struct expression *, size_t length);
 struct substring copy_string (struct expression *,
@@ -99,8 +109,12 @@ struct substring replace_string (struct expression *,
                                  struct substring haystack,
                                  struct substring needle,
                                  struct substring replacement,
-                                 double n);
+                                 int n);
 
 double median (double *, size_t n);
 
+const struct variable *expr_index_vector (const struct expression *,
+                                          const struct expr_node *,
+                                          const struct vector *, double idx);
+
 #endif /* expressions/helpers.h */
index cc24f98751ef84db7861baadaef01e90b33321d9..d3a31a746011e5fb3b22574ee1a9d3daea9736cb 100644 (file)
@@ -103,18 +103,18 @@ absorb_miss function MOD (n, d)
 }
 
 // N-ary numeric functions.
-absorb_miss boolean function ANY (x != SYSMIS, a[n])
+absorb_miss boolean function ANY (x, a[n])
 {
-  int sysmis = 0;
-  size_t i;
-
-  for (i = 0; i < n; i++)
-    if (a[i] == x)
-      return 1.;
-    else if (a[i] == SYSMIS)
-      sysmis = 1;
-
-  return sysmis ? SYSMIS : 0.;
+  double retval = SYSMIS;
+  if (x != SYSMIS)
+    {
+      for (size_t i = 0; i < n; i++)
+        if (a[i] == x)
+          return 1.;
+        else if (a[i] != SYSMIS)
+          retval = 0.;
+    }
+  return retval;
 }
 
 boolean function ANY (string x, string a[n])
@@ -217,36 +217,38 @@ absorb_miss function NVALID (a[n])
 
 absorb_miss boolean function RANGE (x != SYSMIS, a[n*2])
 {
-  size_t i;
-  int sysmis = 0;
-
-  for (i = 0; i < n; i++)
+  bool found = false;
+  bool valid = false;
+  for (size_t i = 0; i < n; i++)
     {
       double w = a[2 * i];
       double y = a[2 * i + 1];
       if (w != SYSMIS && y != SYSMIS)
         {
           if (w <= x && x <= y)
-            return 1.0;
+            found = true;
+          else if (w <= y)
+            valid = true;
+          else
+            return SYSMIS;
         }
-      else
-        sysmis = 1;
     }
-  return sysmis ? SYSMIS : 0.;
+  return found ? true : valid ? false : SYSMIS;
 }
 
 boolean function RANGE (string x, string a[n*2])
 {
-  int i;
-
-  for (i = 0; i < n; i++)
+  bool found = false;
+  for (size_t i = 0; i < n; i++)
     {
       struct substring *w = &a[2 * i];
       struct substring *y = &a[2 * i + 1];
       if (compare_string_3way (w, &x) <= 0 && compare_string_3way (&x, y) <= 0)
-        return 1.;
+        found = true;
+      else if (compare_string_3way (w, y) > 0)
+        return SYSMIS;
     }
-  return 0.;
+  return found;
 }
 
 function SD.2 (a[n])
@@ -277,10 +279,22 @@ function VARIANCE.2 (a[n])
 
 // Time construction & extraction functions.
 function TIME.HMS (h, m, s)
+  expression e;
+  expr_node n;
 {
   if ((h > 0. || m > 0. || s > 0.) && (h < 0. || m < 0. || s < 0.))
     {
-      msg (SW, _("TIME.HMS cannot mix positive and negative arguments."));
+      msg_at (SW, expr_location (e, n),
+              _("TIME.HMS cannot accept a mix of positive and negative "
+                "arguments."));
+      double args[] = { h, m, s };
+      for (size_t i = 0; i < 3; i++)
+        if (args[i] > 0)
+          msg_at (SN, expr_location (e, n->args[i]),
+                  _("This argument has positive value %g."), args[i]);
+        else if (args[i] < 0)
+          msg_at (SN, expr_location (e, n->args[i]),
+                  _("This argument has negative value %g."), args[i]);
       return SYSMIS;
     }
   else
@@ -293,21 +307,93 @@ function CTIME.MINUTES (time) = time / MIN_S;
 function CTIME.SECONDS (time) = time;
 
 // Date construction functions.
-function DATE.DMY (d, m, y) = expr_ymd_to_date (y, m, d);
-function DATE.MDY (m, d, y) = expr_ymd_to_date (y, m, d);
-function DATE.MOYR (m, y) = expr_ymd_to_date (y, m, 1);
-function DATE.QYR (q, y)
+function DATE.DMY (integer d, integer m, integer y)
+  expression e;
+  expr_node n;
+= expr_ymd_to_date (y, m, d, e, n, 3, 2, 1);
+
+function DATE.MDY (integer m, integer d, integer y)
+  expression e;
+  expr_node n;
+= expr_ymd_to_date (y, m, d, e, n, 3, 1, 2);
+
+function DATE.MOYR (integer m, integer y)
+  expression e;
+  expr_node n;
+= expr_ymd_to_date (y, m, 1, e, n, 2, 1, 0);
+
+function DATE.QYR (integer q, integer y)
+  expression e;
+  expr_node n;
+{
+  if (q < 1 || q > 4)
+    {
+      msg_at (SW, expr_location (e, n->args[0]),
+              _("Argument 1 to DATE.QYR must be 1, 2, 3, or 4 (not %d)."), q);
+      return SYSMIS;
+    }
+  return expr_ymd_to_date (y, q * 3 - 2, 1, e, n, 2, 0, 0);
+}
+
+function DATE.WKYR (integer w, integer y)
+  expression e;
+  expr_node n;
+{
+  if (w < 1 || w > 53)
+    {
+      msg_at (SE, expr_location (e, n->args[0]),
+              _("The week argument to DATE.WKYR is outside the acceptable "
+                "range of 1 to 53.  The result will be system-missing."));
+      return SYSMIS;
+    }
+  else
+    {
+      double yr_1_1 = expr_ymd_to_ofs (y, 1, 1, e, n, 2, 0, 0);
+      if (yr_1_1 != SYSMIS)
+        return DAY_S * (yr_1_1 + WEEK_DAY * (w - 1));
+      else
+        return SYSMIS;
+    }
+}
+
+function DATE.YRDAY (integer y, integer yd)
+  expression e;
+  expr_node n;
+{
+  if (yd < 1 || yd > 366)
+    {
+      msg_at (SE, expr_location (e, n->args[1]),
+              _("The value %d as day argument to DATE.YRDAY is outside the "
+                "acceptable range of 1 to 366.  "
+                "The result will be system-missing."), yd);
+      return SYSMIS;
+    }
+  else
+    {
+      double yr_1_1 = expr_ymd_to_ofs (y, 1, 1, e, n, 1, 0, 0);
+      if (yr_1_1 != SYSMIS)
+        return DAY_S * (yr_1_1 + yd - 1.);
+      else
+        return SYSMIS;
+    }
+}
+
+function YRMODA (integer y, integer m, integer d)
+  expression e;
+  expr_node n;
 {
-  if (q < 1.0 || q > 4.0 || q != (int) q)
+  if (y >= 0 && y <= 99)
+    y += 1900;
+  else if (y > 47516)
     {
-      msg (SW, _("The first argument to DATE.QYR must be 1, 2, 3, or 4."));
+      msg_at (SE, expr_location (e, n->args[0]),
+              _("The year argument to YRMODA is greater than 47516.  "
+                "The result will be system-missing."));
       return SYSMIS;
     }
-   return expr_ymd_to_date (y, q * 3 - 2, 1);
+
+  return expr_ymd_to_ofs (y, m, d, e, n, 1, 2, 3);
 }
-function DATE.WKYR (w, y) = expr_wkyr_to_date (w, y);
-function DATE.YRDAY (y, yday) = expr_yrday_to_date (y, yday);
-function YRMODA (y, m, d) = expr_yrmoda (y, m, d);
 
 // Date extraction functions.
 function XDATE.TDAY (date) = floor (date / DAY_S);
@@ -330,11 +416,18 @@ function XDATE.YEAR (date >= DAY_S) = calendar_offset_to_year (date / DAY_S);
 
 // Date arithmetic functions.
 no_abbrev function DATEDIFF (date2 >= DAY_S, date1 >= DAY_S, string unit)
-     = expr_date_difference (date1, date2, unit);
+  expression e;
+  expr_node n;
+= expr_date_difference (date1, date2, unit, e, n);
+
 no_abbrev function DATESUM (date, quantity, string unit)
-     = expr_date_sum (date, quantity, unit, ss_cstr ("closest"));
+  expression e;
+  expr_node n;
+= expr_date_sum_closest (date, quantity, unit, e, n);
 no_abbrev function DATESUM (date, quantity, string unit, string method)
-     = expr_date_sum (date, quantity, unit, method);
+  expression e;
+  expr_node n;
+= expr_date_sum (date, quantity, unit, method, e, n);
 
 
 // String functions.
@@ -363,84 +456,83 @@ string function CONCAT (string a[n])
 
 function INDEX (string haystack, string needle)
 {
-  if (needle.length == 0)
-    return SYSMIS;
-  else
+  if (haystack.length >= needle.length)
     {
-      int limit = haystack.length - needle.length + 1;
-      int i;
-      for (i = 1; i <= limit; i++)
+      size_t limit = haystack.length - needle.length + 1;
+      for (size_t i = 1; i <= limit; i++)
         if (!memcmp (&haystack.string[i - 1], needle.string, needle.length))
           return i;
-      return 0;
     }
+  return 0;
 }
 
-function INDEX (string haystack, string needles, needle_len_d)
+function INDEX (string haystack, string needles, integer needle_len)
+  expression e;
+  expr_node n;
 {
-  if (needle_len_d <= INT_MIN || needle_len_d >= INT_MAX
-      || (int) needle_len_d != needle_len_d
-      || needles.length == 0)
-    return SYSMIS;
-  else
+  if (needle_len <= 0 || needles.length % needle_len != 0)
     {
-      int needle_len = needle_len_d;
-      if (needle_len < 0 || needle_len > needles.length
-          || needles.length % needle_len != 0)
-        return SYSMIS;
-      else
-        {
-          int limit = haystack.length - needle_len + 1;
-          int i, j;
-          for (i = 1; i <= limit; i++)
-            for (j = 0; j < needles.length; j += needle_len)
-              if (!memcmp (&haystack.string[i - 1], &needles.string[j],
-                           needle_len))
-                return i;
-          return 0;
-        }
+      msg_at (SE, expr_location (e, n),
+              _("INDEX needle length argument must evenly divide the "
+                "length of the needles argument."));
+      msg_at (SN, expr_location (e, n->args[1]),
+              _("The needles argument has length %zu."), needles.length);
+      msg_at (SN, expr_location (e, n->args[2]),
+              _("The needle length argument has value %d."), needle_len);
+      return SYSMIS;
     }
+
+  if (haystack.length >= needle_len)
+    {
+      size_t limit = haystack.length - needle_len + 1;
+      for (size_t i = 1; i <= limit; i++)
+        for (size_t j = 0; j < needles.length; j += needle_len)
+          if (!memcmp (&haystack.string[i - 1], &needles.string[j], needle_len))
+            return i;
+    }
+
+  return 0;
 }
 
 function RINDEX (string haystack, string needle)
 {
-  if (needle.length == 0)
-    return SYSMIS;
-  else
+  if (haystack.length >= needle.length)
     {
-      int limit = haystack.length - needle.length + 1;
-      int i;
-      for (i = limit; i >= 1; i--)
+      size_t limit = haystack.length - needle.length + 1;
+      for (size_t i = limit; i >= 1; i--)
         if (!memcmp (&haystack.string[i - 1], needle.string, needle.length))
           return i;
-      return 0;
     }
+
+  return 0;
 }
 
-function RINDEX (string haystack, string needles, needle_len_d)
+function RINDEX (string haystack, string needles, integer needle_len)
+  expression e;
+  expr_node n;
 {
-  if (needle_len_d <= 0 || needle_len_d >= INT_MAX
-      || (int) needle_len_d != needle_len_d
-      || needles.length == 0)
-    return SYSMIS;
-  else
+  if (needle_len <= 0 || needles.length % needle_len != 0)
     {
-      int needle_len = needle_len_d;
-      if (needle_len < 0 || needle_len > needles.length
-          || needles.length % needle_len != 0)
-        return SYSMIS;
-      else
-        {
-          int limit = haystack.length - needle_len + 1;
-          int i, j;
-          for (i = limit; i >= 1; i--)
-            for (j = 0; j < needles.length; j += needle_len)
-              if (!memcmp (&haystack.string[i - 1],
-                           &needles.string[j], needle_len))
-                return i;
-          return 0;
-        }
+      msg_at (SE, expr_location (e, n),
+              _("RINDEX needle length argument must evenly divide the "
+                "length of the needles argument."));
+      msg_at (SN, expr_location (e, n->args[1]),
+              _("The needles argument has length %zu."), needles.length);
+      msg_at (SN, expr_location (e, n->args[2]),
+              _("The needle length argument has value %d."), needle_len);
+      return SYSMIS;
+    }
+
+  if (haystack.length >= needle_len)
+    {
+      size_t limit = haystack.length - needle_len + 1;
+      for (size_t i = limit; i >= 1; i--)
+        for (size_t j = 0; j < needles.length; j += needle_len)
+          if (!memcmp (&haystack.string[i - 1], &needles.string[j], needle_len))
+            return i;
     }
+
+  return 0;
 }
 
 function LENGTH (string s)
@@ -474,75 +566,156 @@ string function UPCASE (string s)
   return s;
 }
 
-absorb_miss string function LPAD (string s, n)
+absorb_miss string function LPAD (string s, integer n)
      expression e;
+     expr_node node;
 {
-  if (n < 0 || n > MAX_STRING || (int) n != n)
-    return empty_string;
+  if (n < 0 || n > MAX_STRING)
+    {
+      if (n != INT_MIN)
+        {
+          msg_at (SE, expr_location (e, node),
+                  _("The length argument to LPAD must be between 0 and %d."),
+                  MAX_STRING);
+          msg_at (SN, expr_location (e, node->args[1]),
+                  _("The length argument is %d."), n);
+        }
+
+      return s;
+    }
   else if (s.length >= n)
     return s;
   else
     {
       struct substring t = alloc_string (e, n);
-      memset (t.string, ' ', n - s.length);
-      memcpy (&t.string[(int) n - s.length], s.string, s.length);
+      size_t pad = n - s.length;
+      memset (t.string, ' ', pad);
+      memcpy (&t.string[pad], s.string, s.length);
       return t;
     }
 }
 
-absorb_miss string function LPAD (string s, n, string c)
+absorb_miss string function LPAD (string s, integer n, string c)
      expression e;
+     expr_node node;
 {
-  if (n < 0 || n > MAX_STRING || (int) n != n || c.length != 1)
-    return empty_string;
+  if (n < 0 || n > MAX_STRING)
+    {
+      if (n != INT_MIN)
+        {
+          msg_at (SE, expr_location (e, node),
+                  _("The length argument to LPAD must be between 0 and %d."),
+                  MAX_STRING);
+          msg_at (SN, expr_location (e, node->args[1]),
+                  _("The length argument is %d."), n);
+        }
+
+      return s;
+    }
   else if (s.length >= n)
     return s;
+  else if (c.length == 0)
+    {
+      msg_at (SE, expr_location (e, node),
+              _("The padding argument to LPAD must not be an empty string."));
+      return s;
+    }
   else
     {
+      size_t n_pad = (n - s.length) / c.length;
+      if (!n_pad)
+        return s;
+
       struct substring t = alloc_string (e, n);
-      memset (t.string, c.string[0], n - s.length);
-      memcpy (&t.string[(int) n - s.length], s.string, s.length);
+      t.length = 0;
+      for (size_t i = 0; i < n_pad; i++)
+        {
+          memcpy (t.string + t.length, c.string, c.length);
+          t.length += c.length;
+        }
+      memcpy (t.string + t.length, s.string, s.length);
+      t.length += s.length;
       return t;
     }
 }
 
 string function REPLACE (string haystack, string needle, string replacement)
     expression e;
-  = replace_string (e, haystack, needle, replacement, DBL_MAX);
+  = replace_string (e, haystack, needle, replacement, INT_MAX);
 
 absorb_miss string function REPLACE (string haystack, string needle,
-                                     string replacement, n)
+                         string replacement, integer n)
     expression e;
   = replace_string (e, haystack, needle, replacement, n);
 
-absorb_miss string function RPAD (string s, n)
+absorb_miss string function RPAD (string s, integer n)
      expression e;
+     expr_node node;
 {
-  if (n < 0 || n > MAX_STRING || (int) n != n)
-    return empty_string;
+  if (n < 0 || n > MAX_STRING)
+    {
+      if (n != INT_MIN)
+        {
+          msg_at (SE, expr_location (e, node),
+                  _("The length argument to RPAD must be between 0 and %d."),
+                  MAX_STRING);
+          msg_at (SN, expr_location (e, node->args[1]),
+                  _("The length argument is %d."), n);
+        }
+
+      return s;
+    }
   else if (s.length >= n)
     return s;
   else
     {
       struct substring t = alloc_string (e, n);
+      size_t pad = n - s.length;
       memcpy (t.string, s.string, s.length);
-      memset (&t.string[s.length], ' ', n - s.length);
+      memset (t.string + s.length, ' ', pad);
       return t;
     }
 }
 
-absorb_miss string function RPAD (string s, n, string c)
+absorb_miss string function RPAD (string s, integer n, string c)
      expression e;
+     expr_node node;
 {
-  if (n < 0 || n > MAX_STRING || (int) n != n || c.length != 1)
-    return empty_string;
+  if (n < 0 || n > MAX_STRING)
+    {
+      if (n != INT_MIN)
+        {
+          msg_at (SE, expr_location (e, node),
+                  _("The length argument to RPAD must be between 0 and %d."),
+                  MAX_STRING);
+          msg_at (SN, expr_location (e, node->args[1]),
+                  _("The length argument is %d."), n);
+        }
+
+      return s;
+    }
   else if (s.length >= n)
     return s;
+  else if (c.length == 0)
+    {
+      msg_at (SE, expr_location (e, node),
+              _("The padding argument to RPAD must not be an empty string."));
+      return s;
+    }
   else
     {
+      size_t n_pad = (n - s.length) / c.length;
+      if (!n_pad)
+        return s;
+
       struct substring t = alloc_string (e, n);
       memcpy (t.string, s.string, s.length);
-      memset (&t.string[s.length], c.string[0], n - s.length);
+      t.length = s.length;
+      for (size_t i = 0; i < n_pad; i++)
+        {
+          memcpy (t.string + t.length, c.string, c.length);
+          t.length += c.length;
+        }
       return t;
     }
 }
@@ -559,17 +732,13 @@ string function LTRIM (string s)
 
 string function LTRIM (string s, string c)
 {
-  if (c.length == 1)
-    {
-      while (s.length > 0 && s.string[0] == c.string[0])
-        {
-          s.length--;
-          s.string++;
-        }
-      return s;
-    }
-  else
-    return empty_string;
+  if (c.length > 0)
+    while (s.length >= c.length && !memcmp (s.string, c.string, c.length))
+      {
+        s.length -= c.length;
+        s.string += c.length;
+      }
+  return s;
 }
 
 string function RTRIM (string s)
@@ -581,32 +750,31 @@ string function RTRIM (string s)
 
 string function RTRIM (string s, string c)
 {
-  if (c.length == 1)
-    {
-      while (s.length > 0 && s.string[s.length - 1] == c.string[0])
-        s.length--;
-      return s;
-    }
-  else
-    return empty_string;
+  if (c.length > 0)
+    while (s.length >= c.length
+           && !memcmp (&s.string[s.length - c.length], c.string, c.length))
+      s.length -= c.length;
+  return s;
 }
 
 function NUMBER (string s, ni_format f)
+  expression e;
+  expr_node n;
 {
-  union value out;
-  char *error;
-
   if (s.length > f->w)
     s.length = f->w;
-  error = data_in (s, C_ENCODING, f->type, settings_get_fmt_settings (),
-                   &out, 0, NULL);
+
+  union value out;
+  char *error = data_in (s, C_ENCODING, f->type, settings_get_fmt_settings (),
+                         &out, 0, NULL);
   if (error == NULL)
     data_in_imply_decimals (s, C_ENCODING, f->type, f->d,
                             settings_get_fmt_settings (), &out);
   else
     {
-      msg (SE, "Cannot parse `%.*s' as format %s: %s",
-           (int) s.length, s.string, fmt_name (f->type), error);
+      msg_at (SE, expr_location (e, n->args[0]),
+              _("Cannot parse \"%.*s\" as format %s: %s"),
+              (int) s.length, s.string, fmt_name (f->type), error);
       free (error);
     }
   return out.f;
@@ -629,10 +797,10 @@ absorb_miss string function STRING (x, no_format f)
   return dst;
 }
 
-absorb_miss string function STRUNC (string s, n)
+absorb_miss string function STRUNC (string s, integer n)
 {
-  if (n < 1 || n == SYSMIS)
-    return empty_string;
+  if (n < 1)
+    return n == INT_MIN ? s : empty_string;
 
   if (n < s.length)
     s.length = n;
@@ -641,27 +809,18 @@ absorb_miss string function STRUNC (string s, n)
   return s;
 }
 
-absorb_miss string function SUBSTR (string s, ofs)
-     expression e;
+absorb_miss string function SUBSTR (string s, integer ofs)
 {
-  if (ofs >= 1 && ofs <= s.length && (int) ofs == ofs)
-    return copy_string (e, &s.string[(int) ofs - 1], s.length - ofs + 1);
-  else
-    return empty_string;
+  return (ofs >= 1 && ofs <= s.length
+          ? ss_substr (s, ofs - 1, SIZE_MAX)
+          : empty_string);
 }
 
-absorb_miss string function SUBSTR (string s, ofs, cnt)
-     expression e;
+absorb_miss string function SUBSTR (string s, integer ofs, integer cnt)
 {
-  if (ofs >= 1 && ofs <= s.length && (int) ofs == ofs
-      && cnt >= 1 && cnt <= INT_MAX && (int) cnt == cnt)
-    {
-      int cnt_max = s.length - (int) ofs + 1;
-      return copy_string (e, &s.string[(int) ofs - 1],
-                          cnt <= cnt_max ? cnt : cnt_max);
-    }
-  else
-    return empty_string;
+  return (ofs >= 1 && cnt >= 1
+          ? ss_substr (s, ofs - 1, cnt)
+          : empty_string);
 }
 
 absorb_miss no_opt no_abbrev string function VALUELABEL (var v)
@@ -677,17 +836,63 @@ absorb_miss no_opt no_abbrev string function VALUELABEL (var v)
 
 // Artificial.
 operator SQUARE (x) = x * x;
-boolean operator NUM_TO_BOOLEAN (x)
+
+absorb_miss boolean operator OPERAND_TO_BOOLEAN (x, expr_node parent)
+  expression e;
+  expr_node n;
+{
+  if (x == 0. || x == 1. || x == SYSMIS)
+    return x;
+
+  switch (parent->n_args)
+    {
+    case 2:
+      msg_at (SE, expr_location (e, parent),
+              /* TRANSLATORS: There are exactly two operands. */
+              _("The operands of %s must have value 0 or 1."),
+              operations[parent->type].name);
+      break;
+
+    case 1:
+      msg_at (SE, expr_location (e, parent),
+              _("The operand of %s must have value 0 or 1."),
+              operations[parent->type].name);
+      break;
+
+    default:
+      NOT_REACHED ();
+    }
+
+  msg_at (SN, expr_location (e, n),
+          _("This operand with unexpected value %g will be treated as 0."), x);
+  return 0.;
+}
+
+absorb_miss boolean operator EXPR_TO_BOOLEAN (x)
+  expression e;
+  expr_node n;
 {
   if (x == 0. || x == 1. || x == SYSMIS)
     return x;
 
-  msg (SE, _("A logical expression was found to have a value other than 0 "
-             "(false), 1 (true), or the system-missing value.  The result "
-             "was forced to 0."));
+  msg_at (SE, expr_location (e, n),
+          _("This expression, which must be 0 or 1, evaluated to %g.  "
+            "It will be treated as 0."), x);
   return 0.;
 }
 
+operator NUM_TO_INTEGER (x)
+  expression e;
+  expr_node n;
+{
+  if (x == floor (x) && x > INT_MIN && x <= INT_MAX)
+    return x;
+
+  msg_at (SE, expr_location (e, n),
+          _("Treating unexpected non-integer value %g as missing."), x);
+  return SYSMIS;
+}
+
 operator BOOLEAN_TO_NUM (boolean x) = x;
 
 // Beta distribution.
@@ -959,59 +1164,60 @@ no_opt boolean function SYSMIS (num_var v)
 {
   return case_num (c, v) == SYSMIS;
 }
-no_opt boolean function VALUE (num_var v)
+no_opt function VALUE (num_var v)
      case c;
 {
   return case_num (c, v);
 }
+no_opt function VALUE (num_vec_elem v)
+{
+  return v;
+}
 
-no_opt operator VEC_ELEM_NUM (idx)
+// A numeric vector element used in a "normal" context, in which a user-missing
+// value becomes system-missing.
+absorb_miss no_opt operator VEC_ELEM_NUM (idx)
      vector v;
      case c;
+     expression e;
+     expr_node n;
 {
-  if (idx >= 1 && idx <= vector_get_n_vars (v))
+  const struct variable *var = expr_index_vector (e, n, v, idx);
+  if (var)
     {
-      const struct variable *var = vector_get_var (v, (size_t) idx - 1);
-      double value = case_num (c, var);
-      return !var_is_num_missing (var, value, MV_USER) ? value : SYSMIS;
-    }
-  else
-    {
-      if (idx == SYSMIS)
-        msg (SE, _("SYSMIS is not a valid index value for vector "
-                   "%s.  The result will be set to SYSMIS."),
-             vector_get_name (v));
-      else
-        msg (SE, _("%g is not a valid index value for vector %s.  "
-                   "The result will be set to SYSMIS."),
-             idx, vector_get_name (v));
-      return SYSMIS;
+      double d = case_num (c, var);
+      if (!var_is_num_missing (var, d, MV_USER))
+        return d;
     }
+  return SYSMIS;
+}
+
+// A numeric vector element used as the argument to the VALUE() function, in
+// which a user-missing value retains its value.
+//
+// All numeric vector elements are initially parsed this way.  In most contexts
+// they then get coerced into numbers.
+absorb_miss no_opt num_vec_elem operator VEC_ELEM_NUM_RAW (idx)
+     vector v;
+     case c;
+     expression e;
+     expr_node n;
+{
+  const struct variable *var = expr_index_vector (e, n, v, idx);
+  return var ? case_num (c, var) : SYSMIS;
 }
 
 absorb_miss no_opt string operator VEC_ELEM_STR (idx)
      expression e;
      vector v;
      case c;
+     expr_node n;
 {
-  if (idx >= 1 && idx <= vector_get_n_vars (v))
-    {
-      struct variable *var = vector_get_var (v, (size_t) idx - 1);
-      return copy_string (e, CHAR_CAST_BUG (char *, case_str (c, var)),
-                          var_get_width (var));
-    }
-  else
-    {
-      if (idx == SYSMIS)
-        msg (SE, _("SYSMIS is not a valid index value for vector "
-                   "%s.  The result will be set to the empty string."),
-             vector_get_name (v));
-      else
-        msg (SE, _("%g is not a valid index value for vector %s.  "
-                   "The result will be set to the empty string."),
-             idx, vector_get_name (v));
-      return empty_string;
-    }
+  const struct variable *var = expr_index_vector (e, n, v, idx);
+  return (var
+          ? copy_string (e, CHAR_CAST_BUG (char *, case_str (c, var)),
+                         var_get_width (var))
+          : empty_string);
 }
 
 // Terminals.
index ea631a975c4af3fd582523e74d235594f2399445..7d683d0181f55d5dcff4355eaadc0d4548d76c5f 100644 (file)
@@ -67,26 +67,34 @@ expr_optimize (struct expr_node *node, struct expression *e)
     }
 
   op = &operations[node->type];
+
+  struct expr_node *new;
   if (n_sysmis && (op->flags & OPF_ABSORB_MISS) == 0)
     {
       /* Most operations produce SYSMIS given any SYSMIS
          argument. */
       assert (op->returns == OP_number || op->returns == OP_boolean);
-      if (op->returns == OP_number)
-        return expr_allocate_number (e, SYSMIS);
-      else
-        return expr_allocate_boolean (e, SYSMIS);
+      new = (op->returns == OP_number
+             ? expr_allocate_number (e, SYSMIS)
+             : expr_allocate_boolean (e, SYSMIS));
     }
   else if (!n_nonconst && (op->flags & OPF_NONOPTIMIZABLE) == 0)
     {
       /* Evaluate constant expressions. */
-      return evaluate_tree (node, e);
+      new = evaluate_tree (node, e);
     }
   else
     {
       /* A few optimization possibilities are still left. */
-      return optimize_tree (node, e);
+      new = optimize_tree (node, e);
     }
+
+  if (new != node && !new->location)
+    {
+      const struct msg_location *loc = expr_location (e, node);
+      new->location = CONST_CAST (struct msg_location *, loc);
+    }
+  return new;
 }
 
 static int
@@ -135,38 +143,13 @@ optimize_tree (struct expr_node *n, struct expression *e)
     return n;
 }
 
-static double get_number_arg (struct expr_node *, size_t arg_idx);
-static double *get_number_args (struct expr_node *,
-                                 size_t arg_idx, size_t n_args,
-                                 struct expression *);
-static struct substring get_string_arg (struct expr_node *,
-                                           size_t arg_idx);
-static struct substring *get_string_args (struct expr_node *,
-                                             size_t arg_idx, size_t n_args,
-                                             struct expression *);
-static const struct fmt_spec *get_format_arg (struct expr_node *,
-                                              size_t arg_idx);
-
-static struct expr_node *
-evaluate_tree (struct expr_node *node, struct expression *e)
-{
-  switch (node->type)
-    {
-#include "optimize.inc"
-
-    default:
-      NOT_REACHED ();
-    }
-
-  NOT_REACHED ();
-}
-
 static double
 get_number_arg (struct expr_node *n, size_t arg_idx)
 {
   assert (arg_idx < n->n_args);
   assert (n->args[arg_idx]->type == OP_number
-          || n->args[arg_idx]->type == OP_boolean);
+          || n->args[arg_idx]->type == OP_boolean
+          || n->args[arg_idx]->type == OP_integer);
   return n->args[arg_idx]->number;
 }
 
@@ -209,6 +192,28 @@ get_format_arg (struct expr_node *n, size_t arg_idx)
           || n->args[arg_idx]->type == OP_no_format);
   return &n->args[arg_idx]->format;
 }
+
+static const struct expr_node *
+get_expr_node_arg (struct expr_node *n, size_t arg_idx)
+{
+  assert (arg_idx < n->n_args);
+  assert (n->args[arg_idx]->type == OP_expr_node);
+  return n->args[arg_idx]->expr_node;
+}
+
+static struct expr_node *
+evaluate_tree (struct expr_node *node, struct expression *e)
+{
+  switch (node->type)
+    {
+#include "optimize.inc"
+
+    default:
+      NOT_REACHED ();
+    }
+
+  NOT_REACHED ();
+}
 \f
 /* Expression flattening. */
 
@@ -290,6 +295,7 @@ flatten_atom (struct expr_node *n, struct expression *e)
     case OP_no_format:
     case OP_ni_format:
     case OP_pos_int:
+    case OP_expr_node:
       /* These are passed as aux data following the
          operation. */
       break;
@@ -334,6 +340,10 @@ flatten_composite (struct expr_node *n, struct expression *e)
           emit_integer (e, arg->integer);
           break;
 
+        case OP_expr_node:
+          allocate_aux (e, OP_expr_node)->expr_node = arg->expr_node;
+          break;
+
         default:
           /* Nothing to do. */
           break;
@@ -344,6 +354,8 @@ flatten_composite (struct expr_node *n, struct expression *e)
     emit_integer (e, n->n_args - op->n_args + 1);
   if (op->flags & OPF_MIN_VALID)
     emit_integer (e, n->min_valid);
+  if (op->flags & OPF_EXPR_NODE)
+    allocate_aux (e, OP_expr_node)->expr_node = n;
 }
 
 void
index 96b892b2efc75d811b717b227b8d45383fc0ed3d..0789fb294d39933fa1f54f2a4fc4fb92c7b72d95 100644 (file)
@@ -60,12 +60,22 @@ atom_type expr_node_returns (const struct expr_node *);
 static const char *atom_type_name (atom_type);
 static struct expression *finish_expression (struct expr_node *,
                                              struct expression *);
-static bool type_check (const struct expr_node *, enum val_type expected_type);
+static bool type_check (const struct expression *, const struct expr_node *,
+                        enum val_type expected_type);
 static struct expr_node *allocate_unary_variable (struct expression *,
                                                 const struct variable *);
 \f
 /* Public functions. */
 
+static struct expr_node *
+parse_expr (struct lexer *lexer, struct expression *e)
+{
+  struct expr_node *n = parse_or (lexer, e);
+  if (n && n->type == OP_VEC_ELEM_NUM_RAW)
+    n->type = OP_VEC_ELEM_NUM;
+  return n;
+}
+
 /* Parses an expression of the given TYPE.  If DS is nonnull then variables and
    vectors within it may be referenced within the expression; otherwise, the
    expression must not reference any variables or vectors.  Returns the new
@@ -76,8 +86,8 @@ expr_parse (struct lexer *lexer, struct dataset *ds, enum val_type type)
   assert (val_type_is_valid (type));
 
   struct expression *e = expr_create (ds);
-  struct expr_node *n = parse_or (lexer, e);
-  if (!n || !type_check (n, type))
+  struct expr_node *n = parse_expr (lexer, e);
+  if (!n || !type_check (e, n, type))
     {
       expr_free (e);
       return NULL;
@@ -91,7 +101,7 @@ struct expression *
 expr_parse_bool (struct lexer *lexer, struct dataset *ds)
 {
   struct expression *e = expr_create (ds);
-  struct expr_node *n = parse_or (lexer, e);
+  struct expr_node *n = parse_expr (lexer, e);
   if (!n)
     {
       expr_free (e);
@@ -100,10 +110,11 @@ expr_parse_bool (struct lexer *lexer, struct dataset *ds)
 
   atom_type actual_type = expr_node_returns (n);
   if (actual_type == OP_number)
-    n = expr_allocate_unary (e, OP_NUM_TO_BOOLEAN, n);
+    n = expr_allocate_unary (e, OP_EXPR_TO_BOOLEAN, n);
   else if (actual_type != OP_boolean)
     {
-      msg (SE, _("Type mismatch: expression has %s type, "
+      msg_at (SE, expr_location (e, n),
+              _("Type mismatch: expression has %s type, "
                  "but a boolean value is required here."),
            atom_type_name (actual_type));
       expr_free (e);
@@ -121,7 +132,7 @@ expr_parse_new_variable (struct lexer *lexer, struct dataset *ds,
                          const char *new_var_name)
 {
   struct expression *e = expr_create (ds);
-  struct expr_node *n = parse_or (lexer, e);
+  struct expr_node *n = parse_expr (lexer, e);
   if (!n)
     {
       expr_free (e);
@@ -158,7 +169,7 @@ expr_parse_any (struct lexer *lexer, struct dataset *ds, bool optimize)
   struct expression *e;
 
   e = expr_create (ds);
-  n = parse_or (lexer, e);
+  n = parse_expr (lexer, e);
   if (n == NULL)
     {
       expr_free (e);
@@ -195,6 +206,7 @@ atom_type_stack (atom_type type)
     {
     case OP_number:
     case OP_boolean:
+    case OP_num_vec_elem:
       return &on_number_stack;
 
     case OP_string:
@@ -208,6 +220,7 @@ atom_type_stack (atom_type type)
     case OP_integer:
     case OP_pos_int:
     case OP_vector:
+    case OP_expr_node:
       return &not_on_stack;
 
     default:
@@ -284,7 +297,8 @@ finish_expression (struct expr_node *n, struct expression *e)
    converted to type EXPECTED_TYPE, inserting a conversion at *N
    if necessary.  Returns true if successful, false on failure. */
 static bool
-type_check (const struct expr_node *n, enum val_type expected_type)
+type_check (const struct expression *e, const struct expr_node *n,
+            enum val_type expected_type)
 {
   atom_type actual_type = expr_node_returns (n);
 
@@ -293,8 +307,9 @@ type_check (const struct expr_node *n, enum val_type expected_type)
     case VAL_NUMERIC:
       if (actual_type != OP_number && actual_type != OP_boolean)
        {
-         msg (SE, _("Type mismatch: expression has %s type, "
-                     "but a numeric value is required here."),
+         msg_at (SE, expr_location (e, n),
+                  _("Type mismatch: expression has type '%s', "
+                     "but a numeric value is required."),
                atom_type_name (actual_type));
          return false;
        }
@@ -303,8 +318,9 @@ type_check (const struct expr_node *n, enum val_type expected_type)
     case VAL_STRING:
       if (actual_type != OP_string)
         {
-          msg (SE, _("Type mismatch: expression has %s type, "
-                     "but a string value is required here."),
+          msg_at (SE, expr_location (e, n),
+                  _("Type mismatch: expression has type '%s', "
+                     "but a string value is required."),
                atom_type_name (actual_type));
           return false;
         }
@@ -361,8 +377,8 @@ expr_location__ (struct expression *e,
 
 /* Returns the source code location corresponding to expression NODE, computing
    it lazily if needed. */
-static const struct msg_location *
-expr_location (struct expression *e, const struct expr_node *node_)
+const struct msg_location *
+expr_location (const struct expression *e_, const struct expr_node *node_)
 {
   struct expr_node *node = CONST_CAST (struct expr_node *, node_);
   if (!node)
@@ -370,6 +386,7 @@ expr_location (struct expression *e, const struct expr_node *node_)
 
   if (!node->location)
     {
+      struct expression *e = CONST_CAST (struct expression *, e_);
       const struct msg_location *min = NULL;
       const struct msg_location *max = NULL;
       expr_location__ (e, node, &min, &max);
@@ -432,6 +449,12 @@ type_coercion__ (struct expression *e, struct expr_node *node, size_t arg_idx,
             *argp = expr_allocate_unary (e, OP_BOOLEAN_TO_NUM, arg);
           return true;
         }
+      else if (actual_type == OP_num_vec_elem)
+        {
+          if (do_coercion)
+            arg->type = OP_VEC_ELEM_NUM;
+          return true;
+        }
       break;
 
     case OP_string:
@@ -443,12 +466,24 @@ type_coercion__ (struct expression *e, struct expr_node *node, size_t arg_idx,
         {
           /* Convert numeric to boolean. */
           if (do_coercion)
-            *argp = expr_allocate_unary (e, OP_NUM_TO_BOOLEAN, arg);
+            *argp = expr_allocate_binary (e, OP_OPERAND_TO_BOOLEAN, arg,
+                                          expr_allocate_expr_node (e, node));
+          return true;
+        }
+      break;
+
+    case OP_integer:
+      if (actual_type == OP_number)
+        {
+          /* Convert number to integer. */
+          if (do_coercion)
+            *argp = expr_allocate_unary (e, OP_NUM_TO_INTEGER, arg);
           return true;
         }
       break;
 
     case OP_format:
+      /* We never coerce to OP_format, only to OP_ni_format or OP_no_format. */
       NOT_REACHED ();
 
     case OP_ni_format:
@@ -536,19 +571,19 @@ is_coercible (const struct expr_node *node_, size_t arg_idx)
   return type_coercion__ (NULL, node, arg_idx, false);
 }
 
-/* How to parse an operator. */
+/* How to parse an operator.
+
+   Some operators support both numeric and string operators.  For those,
+   'num_op' and 'str_op' are both nonzero.  Otherwise, only one 'num_op' is
+   nonzero.  (PSPP doesn't have any string-only operators.) */
 struct operator
   {
     enum token_type token;      /* Operator token. */
-    operation_type type;        /* Operation. */
+    operation_type num_op;      /* Operation for numeric operands (or 0). */
+    operation_type str_op;      /* Operation for string operands (or 0). */
   };
 
-/* Attempts to match the current token against the tokens for the
-   OP_CNT operators in OPS[].  If successful, returns true
-   and, if OPERATOR is non-null, sets *OPERATOR to the operator.
-   On failure, returns false and, if OPERATOR is non-null, sets
-   *OPERATOR to a null pointer. */
-static const struct operator *
+static operation_type
 match_operator (struct lexer *lexer, const struct operator ops[], size_t n_ops,
                 const struct expr_node *lhs)
 {
@@ -556,21 +591,18 @@ match_operator (struct lexer *lexer, const struct operator ops[], size_t n_ops,
   for (const struct operator *op = ops; op < ops + n_ops; op++)
     if (lex_token (lexer) == op->token)
       {
-        bool op_is_numeric = operations[op->type].args[0] != OP_string;
-        if (op_is_numeric == lhs_is_numeric)
-          {
-            if (op->token != T_NEG_NUM)
-              lex_get (lexer);
-            return op;
-          }
+        if (op->token != T_NEG_NUM)
+          lex_get (lexer);
+
+        return op->str_op && !lhs_is_numeric ? op->str_op : op->num_op;
       }
-  return NULL;
+  return 0;
 }
 
 static const char *
-operator_name (const struct operator *op)
+operator_name (enum token_type token)
 {
-  return op->token == T_NEG_NUM ? "-" : token_type_to_string (op->token);
+  return token == T_NEG_NUM ? "-" : token_type_to_string (token);
 }
 
 static struct expr_node *
@@ -581,8 +613,9 @@ parse_binary_operators__ (struct lexer *lexer, struct expression *e,
 {
   for (int op_count = 0; ; op_count++)
     {
-      const struct operator *operator = match_operator (lexer, ops, n_ops, lhs);
-      if (!operator)
+      enum token_type token = lex_token (lexer);
+      operation_type optype = match_operator (lexer, ops, n_ops, lhs);
+      if (!optype)
         {
           if (op_count > 1 && chain_warning)
             msg_at (SW, expr_location (e, lhs), "%s", chain_warning);
@@ -594,39 +627,37 @@ parse_binary_operators__ (struct lexer *lexer, struct expression *e,
       if (!rhs)
         return NULL;
 
-      struct expr_node *node = expr_allocate_binary (e, operator->type,
-                                                     lhs, rhs);
-      bool lhs_ok = type_coercion (e, node, 0);
-      bool rhs_ok = type_coercion (e, node, 1);
-
-      if (!lhs_ok || !rhs_ok)
+      struct expr_node *node = expr_allocate_binary (e, optype, lhs, rhs);
+      if (!is_coercible (node, 0) || !is_coercible (node, 1))
         {
-          int n_matches = 0;
+          bool both = false;
           for (size_t i = 0; i < n_ops; i++)
-            if (ops[i].token == operator->token)
-              n_matches++;
+            if (ops[i].token == token)
+              both = ops[i].num_op && ops[i].str_op;
 
-          const char *name = operator_name (operator);
-          if (n_matches > 1)
+          const char *name = operator_name (token);
+          if (both)
             msg_at (SE, expr_location (e, node),
-                    _("The operands of %s must have the same type."), name);
+                    _("Both operands of %s must have the same type."), name);
           else if (operations[node->type].args[0] != OP_string)
             msg_at (SE, expr_location (e, node),
                     _("Both operands of %s must be numeric."), name);
           else
-            msg_at (SE, expr_location (e, node),
-                    _("Both operands of %s must be strings."), name);
+            NOT_REACHED ();
 
           msg_at (SN, expr_location (e, node->args[0]),
-                  _("The left-hand operand of %s has type '%s'."),
-                  name, atom_type_name (expr_node_returns (node->args[0])));
+                  _("This operand has type '%s'."),
+                  atom_type_name (expr_node_returns (node->args[0])));
           msg_at (SN, expr_location (e, node->args[1]),
-                  _("The right-hand operand of %s has type '%s'."),
-                  name, atom_type_name (expr_node_returns (node->args[1])));
+                  _("This operand has type '%s'."),
+                  atom_type_name (expr_node_returns (node->args[1])));
 
           return NULL;
         }
 
+      if (!type_coercion (e, node, 0) || !type_coercion (e, node, 1))
+        NOT_REACHED ();
+
       lhs = node;
     }
 }
@@ -659,14 +690,14 @@ parse_inverting_unary_operator (struct lexer *lexer, struct expression *e,
   if (!inner || !op_count)
     return inner;
 
-  struct expr_node *outer = expr_allocate_unary (e, op->type, inner);
+  struct expr_node *outer = expr_allocate_unary (e, op->num_op, inner);
   expr_add_location (lexer, e, start_ofs, outer);
 
   if (!type_coercion (e, outer, 0))
     {
       assert (operations[outer->type].args[0] != OP_string);
 
-      const char *name = operator_name (op);
+      const char *name = operator_name (op->token);
       msg_at (SE, expr_location (e, outer),
               _("The unary %s operator requires a numeric operand."), name);
 
@@ -684,7 +715,7 @@ parse_inverting_unary_operator (struct lexer *lexer, struct expression *e,
 static struct expr_node *
 parse_or (struct lexer *lexer, struct expression *e)
 {
-  static const struct operator op = { T_OR, OP_OR };
+  static const struct operator op = { .token = T_OR, .num_op = OP_OR };
   return parse_binary_operators (lexer, e, &op, 1, parse_and, NULL);
 }
 
@@ -692,7 +723,7 @@ parse_or (struct lexer *lexer, struct expression *e)
 static struct expr_node *
 parse_and (struct lexer *lexer, struct expression *e)
 {
-  static const struct operator op = { T_AND, OP_AND };
+  static const struct operator op = { .token = T_AND, .num_op = OP_AND };
 
   return parse_binary_operators (lexer, e, &op, 1, parse_not, NULL);
 }
@@ -701,7 +732,7 @@ parse_and (struct lexer *lexer, struct expression *e)
 static struct expr_node *
 parse_not (struct lexer *lexer, struct expression *e)
 {
-  static const struct operator op = { T_NOT, OP_NOT };
+  static const struct operator op = { .token = T_NOT, .num_op = OP_NOT };
   return parse_inverting_unary_operator (lexer, e, &op, parse_rel);
 }
 
@@ -714,28 +745,17 @@ parse_rel (struct lexer *lexer, struct expression *e)
       "not produce the mathematically expected result.  "
       "Use the AND logical operator to fix the problem "
       "(e.g. `a < b AND b < c').  "
-      "If chaining is really intended, parentheses will disable "
-      "this warning (e.g. `(a < b) < c'.)");
+      "To disable this warning, insert parentheses.");
 
   static const struct operator ops[] =
     {
-      /* Numeric operators. */
-      { T_EQUALS, OP_EQ },
-      { T_EQ, OP_EQ },
-      { T_GE, OP_GE },
-      { T_GT, OP_GT },
-      { T_LE, OP_LE },
-      { T_LT, OP_LT },
-      { T_NE, OP_NE },
-
-      /* String operators. */
-      { T_EQUALS, OP_EQ_STRING },
-      { T_EQ, OP_EQ_STRING },
-      { T_GE, OP_GE_STRING },
-      { T_GT, OP_GT_STRING },
-      { T_LE, OP_LE_STRING },
-      { T_LT, OP_LT_STRING },
-      { T_NE, OP_NE_STRING },
+      { .token = T_EQUALS, .num_op = OP_EQ, .str_op = OP_EQ_STRING },
+      { .token = T_EQ, .num_op = OP_EQ, .str_op = OP_EQ_STRING },
+      { .token = T_GE, .num_op = OP_GE, .str_op = OP_GE_STRING },
+      { .token = T_GT, .num_op = OP_GT, .str_op = OP_GT_STRING },
+      { .token = T_LE, .num_op = OP_LE, .str_op = OP_LE_STRING },
+      { .token = T_LT, .num_op = OP_LT, .str_op = OP_LT_STRING },
+      { .token = T_NE, .num_op = OP_NE, .str_op = OP_NE_STRING },
     };
 
   return parse_binary_operators (lexer, e, ops, sizeof ops / sizeof *ops,
@@ -748,9 +768,9 @@ parse_add (struct lexer *lexer, struct expression *e)
 {
   static const struct operator ops[] =
     {
-      { T_PLUS, OP_ADD },
-      { T_DASH, OP_SUB },
-      { T_NEG_NUM, OP_ADD },
+      { .token = T_PLUS, .num_op = OP_ADD },
+      { .token = T_DASH, .num_op = OP_SUB },
+      { .token = T_NEG_NUM, .num_op = OP_ADD },
     };
 
   return parse_binary_operators (lexer, e, ops, sizeof ops / sizeof *ops,
@@ -763,8 +783,8 @@ parse_mul (struct lexer *lexer, struct expression *e)
 {
   static const struct operator ops[] =
     {
-      { T_ASTERISK, OP_MUL },
-      { T_SLASH, OP_DIV },
+      { .token = T_ASTERISK, .num_op = OP_MUL },
+      { .token = T_SLASH, .num_op = OP_DIV },
     };
 
   return parse_binary_operators (lexer, e, ops, sizeof ops / sizeof *ops,
@@ -775,19 +795,18 @@ parse_mul (struct lexer *lexer, struct expression *e)
 static struct expr_node *
 parse_neg (struct lexer *lexer, struct expression *e)
 {
-  static const struct operator op = { T_DASH, OP_NEG };
+  static const struct operator op = { .token = T_DASH, .num_op = OP_NEG };
   return parse_inverting_unary_operator (lexer, e, &op, parse_exp);
 }
 
 static struct expr_node *
 parse_exp (struct lexer *lexer, struct expression *e)
 {
-  static const struct operator op = { T_EXP, OP_POW };
+  static const struct operator op = { .token = T_EXP, .num_op = OP_POW };
 
   const char *chain_warning =
-    _("The exponentiation operator (`**') is left-associative, "
-      "even though right-associative semantics are more useful.  "
-      "That is, `a**b**c' equals `(a**b)**c', not as `a**(b**c)'.  "
+    _("The exponentiation operator (`**') is left-associative: "
+      "`a**b**c' equals `(a**b)**c', not `a**(b**c)'.  "
       "To disable this warning, insert parentheses.");
 
   if (lex_token (lexer) != T_NEG_NUM || lex_next_token (lexer, 1) != T_EXP)
@@ -800,6 +819,7 @@ parse_exp (struct lexer *lexer, struct expression *e)
   int start_ofs = lex_ofs (lexer);
   struct expr_node *lhs = expr_allocate_number (e, -lex_tokval (lexer));
   lex_get (lexer);
+  expr_add_location (lexer, e, start_ofs, lhs);
 
   struct expr_node *node = parse_binary_operators__ (
     lexer, e, &op, 1, parse_primary, chain_warning, lhs);
@@ -811,6 +831,46 @@ parse_exp (struct lexer *lexer, struct expression *e)
   return node;
 }
 
+static double
+ymd_to_offset (int y, int m, int d)
+{
+  char *error;
+  double retval = calendar_gregorian_to_offset (
+    y, m, d, settings_get_fmt_settings (), &error);
+  if (error)
+    {
+      msg (SE, "%s", error);
+      free (error);
+    }
+  return retval;
+}
+
+static struct expr_node *
+expr_date (struct expression *e, int year_digits)
+{
+  static const char *months[12] =
+    {
+      "JAN", "FEB", "MAR", "APR", "MAY", "JUN",
+      "JUL", "AUG", "SEP", "OCT", "NOV", "DEC",
+    };
+
+  time_t last_proc_time = time_of_last_procedure (e->ds);
+  struct tm *time = localtime (&last_proc_time);
+
+  char *tmp = (year_digits == 2
+               ? xasprintf ("%02d-%s-%02d", time->tm_mday, months[time->tm_mon],
+                            time->tm_year % 100)
+               : xasprintf ("%02d-%s-%04d", time->tm_mday, months[time->tm_mon],
+                            time->tm_year + 1900));
+
+  struct substring s;
+  ss_alloc_substring_pool (&s, ss_cstr (tmp), e->expr_pool);
+
+  free (tmp);
+
+  return expr_allocate_string (e, s);
+}
+
 /* Parses system variables. */
 static struct expr_node *
 parse_sysvar (struct lexer *lexer, struct expression *e)
@@ -818,25 +878,9 @@ parse_sysvar (struct lexer *lexer, struct expression *e)
   if (lex_match_id (lexer, "$CASENUM"))
     return expr_allocate_nullary (e, OP_CASENUM);
   else if (lex_match_id (lexer, "$DATE"))
-    {
-      static const char *months[12] =
-        {
-          "JAN", "FEB", "MAR", "APR", "MAY", "JUN",
-          "JUL", "AUG", "SEP", "OCT", "NOV", "DEC",
-        };
-
-      time_t last_proc_time = time_of_last_procedure (e->ds);
-      struct tm *time;
-      char temp_buf[10];
-      struct substring s;
-
-      time = localtime (&last_proc_time);
-      sprintf (temp_buf, "%02d %s %02d", abs (time->tm_mday) % 100,
-               months[abs (time->tm_mon) % 12], abs (time->tm_year) % 100);
-
-      ss_alloc_substring (&s, ss_cstr (temp_buf));
-      return expr_allocate_string (e, s);
-    }
+    return expr_date (e, 2);
+  else if (lex_match_id (lexer, "$DATE11"))
+    return expr_date (e, 4);
   else if (lex_match_id (lexer, "$TRUE"))
     return expr_allocate_boolean (e, 1.0);
   else if (lex_match_id (lexer, "$FALSE"))
@@ -847,16 +891,15 @@ parse_sysvar (struct lexer *lexer, struct expression *e)
     {
       time_t time = time_of_last_procedure (e->ds);
       struct tm *tm = localtime (&time);
-      return expr_allocate_number (e, expr_ymd_to_ofs (tm->tm_year + 1900,
-                                                       tm->tm_mon + 1,
-                                                       tm->tm_mday));
+      return expr_allocate_number (e, ymd_to_offset (tm->tm_year + 1900,
+                                                     tm->tm_mon + 1,
+                                                     tm->tm_mday));
     }
   else if (lex_match_id (lexer, "$TIME"))
     {
       time_t time = time_of_last_procedure (e->ds);
       struct tm *tm = localtime (&time);
-      return expr_allocate_number (e,
-                                   expr_ymd_to_date (tm->tm_year + 1900,
+      return expr_allocate_number (e, ymd_to_offset (tm->tm_year + 1900,
                                                      tm->tm_mon + 1,
                                                      tm->tm_mday)
                                    + tm->tm_hour * 60 * 60.
@@ -949,23 +992,9 @@ parse_primary__ (struct lexer *lexer, struct expression *e)
 
     case T_LPAREN:
       {
-        /* Count number of left parentheses so that we can match them against
-           an equal number of right parentheses.  This defeats trivial attempts
-           to exhaust the stack with a lot of left parentheses.  (More
-           sophisticated attacks will still succeed.) */
-        size_t n = 0;
-        while (lex_match (lexer, T_LPAREN))
-          n++;
-
+        lex_get (lexer);
         struct expr_node *node = parse_or (lexer, e);
-       if (!node)
-          return NULL;
-
-        for (size_t i = 0; i < n; i++)
-          if (!lex_force_match (lexer, T_RPAREN))
-            return NULL;
-
-        return node;
+       return !node || !lex_force_match (lexer, T_RPAREN) ? NULL : node;
       }
 
     default:
@@ -1012,12 +1041,12 @@ parse_vector_element (struct lexer *lexer, struct expression *e)
     return NULL;
 
   operation_type type = (vector_get_type (vector) == VAL_NUMERIC
-                         ? OP_VEC_ELEM_NUM : OP_VEC_ELEM_STR);
+                         ? OP_VEC_ELEM_NUM_RAW : OP_VEC_ELEM_STR);
   struct expr_node *node = expr_allocate_binary (
     e, type, element, expr_allocate_vector (e, vector));
   expr_add_location (lexer, e, vector_start_ofs, node);
 
-  if (!type_coercion (e, node, 1))
+  if (!type_coercion (e, node, 0))
     {
       msg_at (SE, expr_location (e, node),
               _("A vector index must be numeric."));
@@ -1138,6 +1167,7 @@ match_function__ (struct expr_node *node, const struct operation *f)
       || node->n_args - (f->n_args - 1) < f->array_min_elems)
     return false;
 
+  node->type = f - operations;
   for (size_t i = 0; i < node->n_args; i++)
     if (!is_coercible (node, i))
       return false;
@@ -1156,7 +1186,8 @@ match_function (struct expr_node *node,
 }
 
 static bool
-validate_function_args (const struct operation *f, int n_args, int min_valid)
+validate_function_args (const struct expression *e, const struct expr_node *n,
+                        const struct operation *f, int n_args, int min_valid)
 {
   /* Count the function arguments that go into the trailing array (if any).  We
      know that there must be at least the minimum number because
@@ -1172,7 +1203,8 @@ validate_function_args (const struct operation *f, int n_args, int min_valid)
          here. */
       assert (f->array_granularity == 2);
       assert (n_args % 2 == 0);
-      msg (SE, _("%s must have an odd number of arguments."), f->prototype);
+      msg_at (SE, expr_location (e, n),
+              _("%s must have an odd number of arguments."), f->prototype);
       return false;
     }
 
@@ -1181,9 +1213,10 @@ validate_function_args (const struct operation *f, int n_args, int min_valid)
       if (f->array_min_elems == 0)
         {
           assert ((f->flags & OPF_MIN_VALID) == 0);
-          msg (SE, _("%s function cannot accept suffix .%d to specify the "
-                     "minimum number of valid arguments."),
-               f->prototype, min_valid);
+          msg_at (SE, expr_location (e, n),
+                  _("%s function cannot accept suffix .%d to specify the "
+                    "minimum number of valid arguments."),
+                  f->prototype, min_valid);
           return false;
         }
       else
@@ -1191,9 +1224,10 @@ validate_function_args (const struct operation *f, int n_args, int min_valid)
           assert (f->flags & OPF_MIN_VALID);
           if (min_valid > array_n_args)
             {
-              msg (SE, _("For %s with %d arguments, at most %d (not %d) may be "
-                         "required to be valid."),
-                   f->prototype, n_args, array_n_args, min_valid);
+              msg_at (SE, expr_location (e, n),
+                      _("For %s with %d arguments, at most %d (not %d) may be "
+                        "required to be valid."),
+                      f->prototype, n_args, array_n_args, min_valid);
               return false;
             }
         }
@@ -1216,47 +1250,73 @@ add_arg (struct expr_node ***args, size_t *n_args, size_t *allocated_args,
 
 static void
 put_invocation (struct string *s,
-                const char *func_name, struct expr_node **args, size_t n_args)
+                const char *func_name, struct expr_node *node)
 {
   size_t i;
 
   ds_put_format (s, "%s(", func_name);
-  for (i = 0; i < n_args; i++)
+  for (i = 0; i < node->n_args; i++)
     {
       if (i > 0)
         ds_put_cstr (s, ", ");
-      ds_put_cstr (s, operations[expr_node_returns (args[i])].prototype);
+      ds_put_cstr (s, operations[expr_node_returns (node->args[i])].prototype);
     }
   ds_put_byte (s, ')');
 }
 
 static void
-no_match (const char *func_name,
-          struct expr_node **args, size_t n_args,
-          const struct operation *first, const struct operation *last)
+no_match (struct expression *e, const char *func_name, struct expr_node *node,
+          const struct operation *ops, size_t n)
 {
   struct string s;
-  const struct operation *f;
 
   ds_init_empty (&s);
 
-  if (last - first == 1)
+  if (n == 1)
     {
-      ds_put_format (&s, _("Type mismatch invoking %s as "), first->prototype);
-      put_invocation (&s, func_name, args, n_args);
+      ds_put_format (&s, _("Type mismatch invoking %s as "), ops->prototype);
+      put_invocation (&s, func_name, node);
     }
   else
     {
       ds_put_cstr (&s, _("Function invocation "));
-      put_invocation (&s, func_name, args, n_args);
+      put_invocation (&s, func_name, node);
       ds_put_cstr (&s, _(" does not match any known function.  Candidates are:"));
 
-      for (f = first; f < last; f++)
-        ds_put_format (&s, "\n%s", f->prototype);
+      for (size_t i = 0; i < n; i++)
+        ds_put_format (&s, "\n%s", ops[i].prototype);
     }
   ds_put_byte (&s, '.');
 
-  msg (SE, "%s", ds_cstr (&s));
+  msg_at (SE, expr_location (e, node), "%s", ds_cstr (&s));
+
+  if (n == 1 && ops->n_args == node->n_args)
+    {
+      for (size_t i = 0; i < node->n_args; i++)
+        if (!is_coercible (node, i))
+          {
+            atom_type expected = ops->args[i];
+            atom_type actual = expr_node_returns (node->args[i]);
+            if ((expected == OP_ni_format || expected == OP_no_format)
+                && actual == OP_format)
+              {
+                const struct fmt_spec *f = &node->args[i]->format;
+                char *error = fmt_check__ (f, (ops->args[i] == OP_ni_format
+                                               ? FMT_FOR_INPUT : FMT_FOR_OUTPUT));
+                if (!error)
+                  error = fmt_check_type_compat__ (f, VAL_NUMERIC);
+                if (error)
+                  {
+                    msg_at (SN, expr_location (e, node->args[i]), "%s", error);
+                    free (error);
+                  }
+              }
+            else
+              msg_at (SN, expr_location (e, node->args[i]),
+                      _("This argument has type '%s' but '%s' is required."),
+                      atom_type_name (actual), atom_type_name (expected));
+          }
+      }
 
   ds_destroy (&s);
 }
@@ -1331,7 +1391,7 @@ parse_function (struct lexer *lexer, struct expression *e)
   const struct operation *f = match_function (n, first, last);
   if (!f)
     {
-      no_match (ds_cstr (&func_name), args, n_args, first, last);
+      no_match (e, ds_cstr (&func_name), n, first, last - first);
       goto fail;
     }
   n->type = f - operations;
@@ -1344,21 +1404,23 @@ parse_function (struct lexer *lexer, struct expression *e)
            arguments were coercible. */
         NOT_REACHED ();
       }
-  if (!validate_function_args (f, n_args, min_valid))
+  if (!validate_function_args (e, n, f, n_args, min_valid))
     goto fail;
 
   if ((f->flags & OPF_EXTENSION) && settings_get_syntax () == COMPATIBLE)
-    msg (SW, _("%s is a PSPP extension."), f->prototype);
+    msg_at (SW, expr_location (e, n),
+            _("%s is a PSPP extension."), f->prototype);
   if (f->flags & OPF_UNIMPLEMENTED)
     {
-      msg (SE, _("%s is not available in this version of PSPP."),
-           f->prototype);
+      msg_at (SE, expr_location (e, n),
+              _("%s is not available in this version of PSPP."), f->prototype);
       goto fail;
     }
   if ((f->flags & OPF_PERM_ONLY) &&
       proc_in_temporary_transformations (e->ds))
     {
-      msg (SE, _("%s may not appear after %s."), f->prototype, "TEMPORARY");
+      msg_at (SE, expr_location (e, n),
+              _("%s may not appear after %s."), f->prototype, "TEMPORARY");
       goto fail;
     }
 
@@ -1413,7 +1475,11 @@ static const char *
 atom_type_name (atom_type type)
 {
   assert (is_atom (type));
-  return operations[type].name;
+
+  /* The Boolean type is purely an internal concept that the documentation
+     doesn't mention, so it might confuse users if we talked about them in
+     diagnostics. */
+  return type == OP_boolean ? "number" : operations[type].name;
 }
 
 struct expr_node *
@@ -1527,6 +1593,15 @@ expr_allocate_format (struct expression *e, const struct fmt_spec *format)
   return n;
 }
 
+struct expr_node *
+expr_allocate_expr_node (struct expression *e,
+                         const struct expr_node *expr_node)
+{
+  struct expr_node *n = pool_alloc (e->expr_pool, sizeof *n);
+  *n = (struct expr_node) { .type = OP_expr_node, .expr_node = expr_node };
+  return n;
+}
+
 /* Allocates a unary composite node that represents the value of
    variable V in expression E. */
 static struct expr_node *
index e77ce4d941c852e7fe0e0cea7713a5af9cb513e4..235c2ecf207771ec0e8c6500f72cd965ebb7bac3 100644 (file)
@@ -33,36 +33,40 @@ enum operation_flags
        missing input values (although it is not obliged to do
        so).  Unless this bit is set, the operation's evaluation
        function will never be passed a missing argument. */
-    OPF_ABSORB_MISS = 004,
+    OPF_ABSORB_MISS = 1 << 0,
 
     /* If set, this operation's final operand is an array of one
        or more elements. */
-    OPF_ARRAY_OPERAND = 001,
+    OPF_ARRAY_OPERAND = 1 << 1,
 
     /* If set, the user can specify the minimum number of array
        elements that must be non-missing for the function result
        to be non-missing.  The operation must have an array
        operand and the array must contain `double's.  Both
        OPF_ABSORB_MISS and OPF_ARRAY_OPERAND must also be set. */
-    OPF_MIN_VALID = 002,
+    OPF_MIN_VALID = 1 << 2,
 
     /* If set, operation is non-optimizable in general.  Unless
        combined with OPF_ABSORB_MISS, missing input values are
        still assumed to yield missing results. */
-    OPF_NONOPTIMIZABLE = 010,
+    OPF_NONOPTIMIZABLE = 1 << 3,
 
     /* If set, this operation is not implemented. */
-    OPF_UNIMPLEMENTED = 020,
+    OPF_UNIMPLEMENTED = 1 << 4,
 
     /* If set, this operation is a PSPP extension. */
-    OPF_EXTENSION = 040,
+    OPF_EXTENSION = 1 << 5,
 
     /* If set, this operation may not occur after TEMPORARY.
        (Currently this applies only to LAG.) */
-    OPF_PERM_ONLY = 0100,
+    OPF_PERM_ONLY = 1 << 6,
 
     /* If set, this operation's name may not be abbreviated. */
-    OPF_NO_ABBREV = 0200
+    OPF_NO_ABBREV = 1 << 7,
+
+    /* If set, this operation needs the "struct expr_node *", for message
+       locations. */
+    OPF_EXPR_NODE = 1 << 8,
   };
 
 #define EXPR_ARG_MAX 4
@@ -113,6 +117,9 @@ struct expr_node
             struct expr_node **args; /* Arguments. */
             size_t min_valid;   /* Min valid array args to get valid result. */
           };
+
+        /* OP_exprnode. */
+        const struct expr_node *expr_node;
       };
   };
 
@@ -124,6 +131,7 @@ union operation_data
     const struct variable *variable;
     const struct vector *vector;
     struct fmt_spec *format;
+    const struct expr_node *expr_node;
     int integer;
   };
 
@@ -167,7 +175,12 @@ struct expr_node *expr_allocate_variable (struct expression *e,
                                         const struct variable *);
 struct expr_node *expr_allocate_format (struct expression *e,
                                  const struct fmt_spec *);
+struct expr_node *expr_allocate_expr_node (struct expression *,
+                                           const struct expr_node *);
 struct expr_node *expr_allocate_vector (struct expression *e,
                                       const struct vector *);
 
+const struct msg_location *expr_location (const struct expression *,
+                                          const struct expr_node *);
+
 #endif /* expressions/private.h */
index 605346ee1d3ec68c1fd8b0ec4fbbc80d9a124c9f..568f1eefd38cd596f7c230849056b7d7931c7acc 100644 (file)
@@ -102,98 +102,245 @@ DEBUG EVALUATE/DATE.DMY(1,1,99) = DATE.DMY(1,1,2099).
 DEBUG EVALUATE/DATE.DMY(1,1,100).
 ])
 
-AT_CHECK([pspp --testing-mode epoch.sps -o pspp.csv], [1], [dnl
-epoch.sps:11: error: DEBUG EVALUATE: Date 0100-1-1 is before the earliest acceptable date of 1582-10-15.
-epoch.sps:18: error: DEBUG EVALUATE: Date 0100-1-1 is before the earliest acceptable date of 1582-10-15.
-epoch.sps:27: error: DEBUG EVALUATE: Date 0100-1-1 is before the earliest acceptable date of 1582-10-15.
-epoch.sps:34: error: DEBUG EVALUATE: Date 0100-1-1 is before the earliest acceptable date of 1582-10-15.
-epoch.sps:43: error: DEBUG EVALUATE: Date 0100-1-1 is before the earliest acceptable date of 1582-10-15.
-epoch.sps:50: error: DEBUG EVALUATE: Date 0100-1-1 is before the earliest acceptable date of 1582-10-15.
-epoch.sps:59: error: DEBUG EVALUATE: Date 0100-1-1 is before the earliest acceptable date of 1582-10-15.
-epoch.sps:66: error: DEBUG EVALUATE: Date 0100-1-1 is before the earliest acceptable date of 1582-10-15.
-epoch.sps:75: error: DEBUG EVALUATE: Date 0100-1-1 is before the earliest acceptable date of 1582-10-15.
-epoch.sps:82: error: DEBUG EVALUATE: Date 0100-1-1 is before the earliest acceptable date of 1582-10-15.
-])
-AT_CHECK([sed '/^$/d' < pspp.csv], [0], [dnl
-true
-true
-true
-true
-true
-true
-epoch.sps:11: error: DEBUG EVALUATE: Date 0100-1-1 is before the earliest acceptable date of 1582-10-15.
-sysmis
-true
-true
-true
-true
-true
-true
-epoch.sps:18: error: DEBUG EVALUATE: Date 0100-1-1 is before the earliest acceptable date of 1582-10-15.
-sysmis
-true
-true
-true
-true
-true
-true
-epoch.sps:27: error: DEBUG EVALUATE: Date 0100-1-1 is before the earliest acceptable date of 1582-10-15.
-sysmis
-true
-true
-true
-true
-true
-true
-epoch.sps:34: error: DEBUG EVALUATE: Date 0100-1-1 is before the earliest acceptable date of 1582-10-15.
-sysmis
-true
-true
-true
-true
-true
-true
-epoch.sps:43: error: DEBUG EVALUATE: Date 0100-1-1 is before the earliest acceptable date of 1582-10-15.
-sysmis
-true
-true
-true
-true
-true
-true
-epoch.sps:50: error: DEBUG EVALUATE: Date 0100-1-1 is before the earliest acceptable date of 1582-10-15.
-sysmis
-true
-true
-true
-true
-true
-true
-epoch.sps:59: error: DEBUG EVALUATE: Date 0100-1-1 is before the earliest acceptable date of 1582-10-15.
-sysmis
-true
-true
-true
-true
-true
-true
-epoch.sps:66: error: DEBUG EVALUATE: Date 0100-1-1 is before the earliest acceptable date of 1582-10-15.
-sysmis
-true
-true
-true
-true
-true
-true
-epoch.sps:75: error: DEBUG EVALUATE: Date 0100-1-1 is before the earliest acceptable date of 1582-10-15.
-sysmis
-true
-true
-true
-true
-true
-true
-epoch.sps:82: error: DEBUG EVALUATE: Date 0100-1-1 is before the earliest acceptable date of 1582-10-15.
-sysmis
+AT_CHECK([pspp --testing-mode epoch.sps], [1], [dnl
+YRMODA(0,1,1) = YRMODA(1900,1,1) => true
+
+YRMODA(1,1,1) = YRMODA(1901,1,1) => true
+
+YRMODA(12,1,1) = YRMODA(1912,1,1) => true
+
+YRMODA(70,1,1) = YRMODA(1970,1,1) => true
+
+YRMODA(87,1,1) = YRMODA(1987,1,1) => true
+
+YRMODA(99,1,1) = YRMODA(1999,1,1) => true
+
+epoch.sps:11.16-11.30: error: DEBUG EVALUATE: Invalid arguments to YRMODA
+function.
+   11 | DEBUG EVALUATE/YRMODA(100,1,1).
+      |                ^~~~~~~~~~~~~~~
+
+epoch.sps:11.23-11.25: note: DEBUG EVALUATE: Date 0100-1-1 is before the
+earliest supported date 1582-10-15.
+   11 | DEBUG EVALUATE/YRMODA(100,1,1).
+      |                       ^~~
+
+YRMODA(100,1,1) => sysmis
+
+DATE.DMY(1,1,0) = DATE.DMY(1,1,1900) => true
+
+DATE.DMY(1,1,1) = DATE.DMY(1,1,1901) => true
+
+DATE.DMY(1,1,12) = DATE.DMY(1,1,1912) => true
+
+DATE.DMY(1,1,70) = DATE.DMY(1,1,1970) => true
+
+DATE.DMY(1,1,87) = DATE.DMY(1,1,1987) => true
+
+DATE.DMY(1,1,99) = DATE.DMY(1,1,1999) => true
+
+epoch.sps:18.16-18.32: error: DEBUG EVALUATE: Invalid arguments to DATE.DMY
+function.
+   18 | DEBUG EVALUATE/DATE.DMY(1,1,100).
+      |                ^~~~~~~~~~~~~~~~~
+
+epoch.sps:18.29-18.31: note: DEBUG EVALUATE: Date 0100-1-1 is before the
+earliest supported date 1582-10-15.
+   18 | DEBUG EVALUATE/DATE.DMY(1,1,100).
+      |                             ^~~
+
+DATE.DMY(1,1,100) => sysmis
+
+YRMODA(0,1,1) = YRMODA(1900,1,1) => true
+
+YRMODA(1,1,1) = YRMODA(1901,1,1) => true
+
+YRMODA(12,1,1) = YRMODA(1912,1,1) => true
+
+YRMODA(70,1,1) = YRMODA(1970,1,1) => true
+
+YRMODA(87,1,1) = YRMODA(1987,1,1) => true
+
+YRMODA(99,1,1) = YRMODA(1999,1,1) => true
+
+epoch.sps:27.16-27.30: error: DEBUG EVALUATE: Invalid arguments to YRMODA
+function.
+   27 | DEBUG EVALUATE/YRMODA(100,1,1).
+      |                ^~~~~~~~~~~~~~~
+
+epoch.sps:27.23-27.25: note: DEBUG EVALUATE: Date 0100-1-1 is before the
+earliest supported date 1582-10-15.
+   27 | DEBUG EVALUATE/YRMODA(100,1,1).
+      |                       ^~~
+
+YRMODA(100,1,1) => sysmis
+
+DATE.DMY(1,1,0) = DATE.DMY(1,1,2000) => true
+
+DATE.DMY(1,1,1) = DATE.DMY(1,1,1901) => true
+
+DATE.DMY(1,1,12) = DATE.DMY(1,1,1912) => true
+
+DATE.DMY(1,1,70) = DATE.DMY(1,1,1970) => true
+
+DATE.DMY(1,1,87) = DATE.DMY(1,1,1987) => true
+
+DATE.DMY(1,1,99) = DATE.DMY(1,1,1999) => true
+
+epoch.sps:34.16-34.32: error: DEBUG EVALUATE: Invalid arguments to DATE.DMY
+function.
+   34 | DEBUG EVALUATE/DATE.DMY(1,1,100).
+      |                ^~~~~~~~~~~~~~~~~
+
+epoch.sps:34.29-34.31: note: DEBUG EVALUATE: Date 0100-1-1 is before the
+earliest supported date 1582-10-15.
+   34 | DEBUG EVALUATE/DATE.DMY(1,1,100).
+      |                             ^~~
+
+DATE.DMY(1,1,100) => sysmis
+
+YRMODA(0,1,1) = YRMODA(1900,1,1) => true
+
+YRMODA(1,1,1) = YRMODA(1901,1,1) => true
+
+YRMODA(12,1,1) = YRMODA(1912,1,1) => true
+
+YRMODA(70,1,1) = YRMODA(1970,1,1) => true
+
+YRMODA(87,1,1) = YRMODA(1987,1,1) => true
+
+YRMODA(99,1,1) = YRMODA(1999,1,1) => true
+
+epoch.sps:43.16-43.30: error: DEBUG EVALUATE: Invalid arguments to YRMODA
+function.
+   43 | DEBUG EVALUATE/YRMODA(100,1,1).
+      |                ^~~~~~~~~~~~~~~
+
+epoch.sps:43.23-43.25: note: DEBUG EVALUATE: Date 0100-1-1 is before the
+earliest supported date 1582-10-15.
+   43 | DEBUG EVALUATE/YRMODA(100,1,1).
+      |                       ^~~
+
+YRMODA(100,1,1) => sysmis
+
+DATE.DMY(1,1,0) = DATE.DMY(1,1,2000) => true
+
+DATE.DMY(1,1,1) = DATE.DMY(1,1,2001) => true
+
+DATE.DMY(1,1,12) = DATE.DMY(1,1,1912) => true
+
+DATE.DMY(1,1,70) = DATE.DMY(1,1,1970) => true
+
+DATE.DMY(1,1,87) = DATE.DMY(1,1,1987) => true
+
+DATE.DMY(1,1,99) = DATE.DMY(1,1,1999) => true
+
+epoch.sps:50.16-50.32: error: DEBUG EVALUATE: Invalid arguments to DATE.DMY
+function.
+   50 | DEBUG EVALUATE/DATE.DMY(1,1,100).
+      |                ^~~~~~~~~~~~~~~~~
+
+epoch.sps:50.29-50.31: note: DEBUG EVALUATE: Date 0100-1-1 is before the
+earliest supported date 1582-10-15.
+   50 | DEBUG EVALUATE/DATE.DMY(1,1,100).
+      |                             ^~~
+
+DATE.DMY(1,1,100) => sysmis
+
+YRMODA(0,1,1) = YRMODA(1900,1,1) => true
+
+YRMODA(1,1,1) = YRMODA(1901,1,1) => true
+
+YRMODA(12,1,1) = YRMODA(1912,1,1) => true
+
+YRMODA(70,1,1) = YRMODA(1970,1,1) => true
+
+YRMODA(87,1,1) = YRMODA(1987,1,1) => true
+
+YRMODA(99,1,1) = YRMODA(1999,1,1) => true
+
+epoch.sps:59.16-59.30: error: DEBUG EVALUATE: Invalid arguments to YRMODA
+function.
+   59 | DEBUG EVALUATE/YRMODA(100,1,1).
+      |                ^~~~~~~~~~~~~~~
+
+epoch.sps:59.23-59.25: note: DEBUG EVALUATE: Date 0100-1-1 is before the
+earliest supported date 1582-10-15.
+   59 | DEBUG EVALUATE/YRMODA(100,1,1).
+      |                       ^~~
+
+YRMODA(100,1,1) => sysmis
+
+DATE.DMY(1,1,0) = DATE.DMY(1,1,2000) => true
+
+DATE.DMY(1,1,1) = DATE.DMY(1,1,2001) => true
+
+DATE.DMY(1,1,12) = DATE.DMY(1,1,1912) => true
+
+DATE.DMY(1,1,70) = DATE.DMY(1,1,1970) => true
+
+DATE.DMY(1,1,87) = DATE.DMY(1,1,1987) => true
+
+DATE.DMY(1,1,99) = DATE.DMY(1,1,1999) => true
+
+epoch.sps:66.16-66.32: error: DEBUG EVALUATE: Invalid arguments to DATE.DMY
+function.
+   66 | DEBUG EVALUATE/DATE.DMY(1,1,100).
+      |                ^~~~~~~~~~~~~~~~~
+
+epoch.sps:66.29-66.31: note: DEBUG EVALUATE: Date 0100-1-1 is before the
+earliest supported date 1582-10-15.
+   66 | DEBUG EVALUATE/DATE.DMY(1,1,100).
+      |                             ^~~
+
+DATE.DMY(1,1,100) => sysmis
+
+YRMODA(0,1,1) = YRMODA(1900,1,1) => true
+
+YRMODA(1,1,1) = YRMODA(1901,1,1) => true
+
+YRMODA(12,1,1) = YRMODA(1912,1,1) => true
+
+YRMODA(70,1,1) = YRMODA(1970,1,1) => true
+
+YRMODA(87,1,1) = YRMODA(1987,1,1) => true
+
+YRMODA(99,1,1) = YRMODA(1999,1,1) => true
+
+epoch.sps:75.16-75.30: error: DEBUG EVALUATE: Invalid arguments to YRMODA
+function.
+   75 | DEBUG EVALUATE/YRMODA(100,1,1).
+      |                ^~~~~~~~~~~~~~~
+
+epoch.sps:75.23-75.25: note: DEBUG EVALUATE: Date 0100-1-1 is before the
+earliest supported date 1582-10-15.
+   75 | DEBUG EVALUATE/YRMODA(100,1,1).
+      |                       ^~~
+
+YRMODA(100,1,1) => sysmis
+
+DATE.DMY(1,1,0) = DATE.DMY(1,1,2100) => true
+
+DATE.DMY(1,1,1) = DATE.DMY(1,1,2101) => true
+
+DATE.DMY(1,1,12) = DATE.DMY(1,1,2012) => true
+
+DATE.DMY(1,1,70) = DATE.DMY(1,1,2070) => true
+
+DATE.DMY(1,1,87) = DATE.DMY(1,1,2087) => true
+
+DATE.DMY(1,1,99) = DATE.DMY(1,1,2099) => true
+
+epoch.sps:82.16-82.32: error: DEBUG EVALUATE: Invalid arguments to DATE.DMY
+function.
+   82 | DEBUG EVALUATE/DATE.DMY(1,1,100).
+      |                ^~~~~~~~~~~~~~~~~
+
+epoch.sps:82.29-82.31: note: DEBUG EVALUATE: Date 0100-1-1 is before the
+earliest supported date 1582-10-15.
+   82 | DEBUG EVALUATE/DATE.DMY(1,1,100).
+      |                             ^~~
+
+DATE.DMY(1,1,100) => sysmis
 ])
 AT_CLEANUP
index cd574eb10f79eb8cea949a754d6b20ffc2947355..baf2266d98873621e89a466ccb61329d65b65e94 100644 (file)
@@ -13,1999 +13,7556 @@ dnl GNU General Public License for more details.
 dnl
 dnl You should have received a copy of the GNU General Public License
 dnl along with this program.  If not, see <http://www.gnu.org/licenses/>.
-m4_define([CHECK_EXPR_EVAL],
-  [AT_SETUP([expressions - $1])
-   AT_KEYWORDS([expression])
-   AT_DATA([evaluate.sps],
-     [set mxwarn 1000.
-set mxerr 1000.
-set epoch 1940.
-m4_foreach([check], [m4_shift($@)],
-                 [DEBUG EVALUATE NOOPT m4_argn(4, check)/[]m4_car(check).
-DEBUG EVALUATE m4_argn(4, check)/[]m4_car(check).
-])])
-   AT_CAPTURE_FILE([evaluate.sps])
-   m4_pushdef([i], [3])
-   AT_CHECK([pspp --testing-mode -O format=csv evaluate.sps],
-     [m4_if(m4_bregexp([m4_foreach([check], [m4_shift($@)], [m4_argn(3, check)])], [error:]), [-1], [0], [1])],
-     [stdout])
-   AT_DATA([expout], [m4_foreach([check], [m4_shift($@)],
-        [m4_define([i], m4_incr(i))dnl
-m4_if(m4_argn(3, check), [], [], [evaluate.sps:[]i[]: m4_argn(3, check)
-])dnl
-m4_argn(2, check)
-m4_define([i], m4_incr(i))dnl
-m4_if(m4_argn(3, check), [], [], [evaluate.sps:[]i[]: m4_argn(3, check)
-])dnl
-m4_argn(2, check)
-])])
-   AT_CHECK([[sed '
-# Transform "file:line.column:" into plain "file:line:",
-# because column numbers change between opt and noopt versions.
-s/\(evaluate.sps:[0-9]\{1,\}\)\.[0-9]\{1,\}:/\1:/
-
-# Remove leading or trailing quotes and un-double CSV quotes.
-s/^"//
-s/"$//
-s/""/"/g
-# "
-
-# Delete blank lines
-/^$/d' stdout]],
-     [0], [expout], [])
-   m4_popdef([i])
-   AT_CLEANUP])
+AT_BANNER([expressions])
+
+AT_SETUP([expressions - numeric syntax])
+AT_KEYWORDS([expression expressions evaluate])
+AT_DATA([evaluate-base.sps], [dnl
+SET EPOCH 1940.
+DEBUG EVALUATE SET opt.
+DEBUG EVALUATE /1e2.
+DEBUG EVALUATE /1e+2.
+DEBUG EVALUATE /1e-2.
+DEBUG EVALUATE /1e-99.
+])
+
+for opt in OPT NOOPT; do
+    AS_BOX([$opt])
+    sed "s/opt/$opt/" < evaluate-base.sps > evaluate.sps
+    AT_CHECK([pspp --testing-mode evaluate.sps], [0], [dnl
+1e2 => 100.00
+
+1e+2 => 100.00
+
+1e-2 => 0.01
+
+1e-99 => 0.00
+])
+done
+AT_CLEANUP
+
+AT_SETUP([expressions - coercion to and from Boolean])
+AT_KEYWORDS([expression expressions evaluate])
+AT_DATA([evaluate-base.sps], [dnl
+SET EPOCH 1940.
+DEBUG EVALUATE SET opt.
+DEBUG EVALUATE/0 AND 1.
+DEBUG EVALUATE/$true AND 1.
+DEBUG EVALUATE/1 OR $false.
+DEBUG EVALUATE/1 OR $sysmis.
+DEBUG EVALUATE/2 OR $sysmis.
+DEBUG EVALUATE/1 AND 3.
+])
+
+for opt in OPT NOOPT; do
+    AS_BOX([$opt])
+    sed "s/opt/$opt/" < evaluate-base.sps > evaluate.sps
+    AT_CHECK([pspp --testing-mode evaluate.sps], [1], [dnl
+0 AND 1 => false
+
+$true AND 1 => true
+
+1 OR $false => true
+
+1 OR $sysmis => true
+
+evaluate.sps:7.16-7.27: error: DEBUG EVALUATE: The operands of OR must have
+value 0 or 1.
+    7 | DEBUG EVALUATE/2 OR $sysmis.
+      |                ^~~~~~~~~~~~
+
+evaluate.sps:7.16: note: DEBUG EVALUATE: This operand with unexpected value 2
+will be treated as 0.
+    7 | DEBUG EVALUATE/2 OR $sysmis.
+      |                ^
+
+2 OR $sysmis => sysmis
+
+evaluate.sps:8.16-8.22: error: DEBUG EVALUATE: The operands of AND must have
+value 0 or 1.
+    8 | DEBUG EVALUATE/1 AND 3.
+      |                ^~~~~~~
+
+evaluate.sps:8.22: note: DEBUG EVALUATE: This operand with unexpected value 3
+will be treated as 0.
+    8 | DEBUG EVALUATE/1 AND 3.
+      |                      ^
+
+1 AND 3 => false
+])
+done
+AT_CLEANUP
+
+AT_SETUP([expressions - addition and subtraction])
+AT_KEYWORDS([expression expressions evaluate])
+AT_DATA([evaluate-base.sps], [dnl
+SET EPOCH 1940.
+DEBUG EVALUATE SET opt.
+DEBUG EVALUATE /1 + $true.
+DEBUG EVALUATE /$sysmis + 1.
+DEBUG EVALUATE /7676 + $sysmis.
+DEBUG EVALUATE /1 +3 - 2 +4 - 5.
+DEBUG EVALUATE /$true - 4/3.
+DEBUG EVALUATE /1 - 2.
+DEBUG EVALUATE /52 -23.
+
+DEBUG EVALUATE /('foo') + 5.
+DEBUG EVALUATE /('foo') + ('bar').   /* Concatenation requires CONCAT.
+DEBUG EVALUATE /'foo' + 'bar'.       /* Lexical concatenation succeeds.
+
+DEBUG EVALUATE /'string' - 1e10.
+DEBUG EVALUATE /9.5 - ''.
+
+DEBUG EVALUATE /F2.0 + 3.
+])
+
+for opt in OPT NOOPT; do
+    AS_BOX([$opt])
+    sed "s/opt/$opt/" < evaluate-base.sps > evaluate.sps
+    AT_CHECK([pspp --testing-mode evaluate.sps], [1], [dnl
+1 + $true => 2.00
+
+$sysmis + 1 => sysmis
+
+7676 + $sysmis => sysmis
+
+1 +3 - 2 +4 - 5 => 1.00
+
+$true - 4/3 => -0.33
+
+1 - 2 => -1.00
+
+52 -23 => 29.00
+
+evaluate.sps:11.18-11.27: error: DEBUG EVALUATE: Both operands of + must be
+numeric.
+   11 | DEBUG EVALUATE /('foo') + 5.
+      |                  ^~~~~~~~~~
+
+evaluate.sps:11.18-11.22: note: DEBUG EVALUATE: This operand has type 'string'.
+   11 | DEBUG EVALUATE /('foo') + 5.
+      |                  ^~~~~
+
+evaluate.sps:11.27: note: DEBUG EVALUATE: This operand has type 'number'.
+   11 | DEBUG EVALUATE /('foo') + 5.
+      |                           ^
+
+('foo') + 5 => error
+
+evaluate.sps:12.18-12.32: error: DEBUG EVALUATE: Both operands of + must be
+numeric.
+   12 | DEBUG EVALUATE /('foo') + ('bar').   /* Concatenation requires CONCAT.
+      |                  ^~~~~~~~~~~~~~~
+
+evaluate.sps:12.18-12.22: note: DEBUG EVALUATE: This operand has type 'string'.
+   12 | DEBUG EVALUATE /('foo') + ('bar').   /* Concatenation requires CONCAT.
+      |                  ^~~~~
+
+evaluate.sps:12.28-12.32: note: DEBUG EVALUATE: This operand has type 'string'.
+   12 | DEBUG EVALUATE /('foo') + ('bar').   /* Concatenation requires CONCAT.
+      |                            ^~~~~
+
+('foo') + ('bar') => error
+
+'foo' + 'bar' => "foobar"
+
+evaluate.sps:15.17-15.31: error: DEBUG EVALUATE: Both operands of - must be
+numeric.
+   15 | DEBUG EVALUATE /'string' - 1e10.
+      |                 ^~~~~~~~~~~~~~~
+
+evaluate.sps:15.17-15.24: note: DEBUG EVALUATE: This operand has type 'string'.
+   15 | DEBUG EVALUATE /'string' - 1e10.
+      |                 ^~~~~~~~
+
+evaluate.sps:15.26-15.31: note: DEBUG EVALUATE: This operand has type 'number'.
+   15 | DEBUG EVALUATE /'string' - 1e10.
+      |                          ^~~~~~
+
+'string' - 1e10 => error
+
+evaluate.sps:16.17-16.24: error: DEBUG EVALUATE: Both operands of - must be
+numeric.
+   16 | DEBUG EVALUATE /9.5 - ''.
+      |                 ^~~~~~~~
+
+evaluate.sps:16.17-16.19: note: DEBUG EVALUATE: This operand has type 'number'.
+   16 | DEBUG EVALUATE /9.5 - ''.
+      |                 ^~~
+
+evaluate.sps:16.23-16.24: note: DEBUG EVALUATE: This operand has type 'string'.
+   16 | DEBUG EVALUATE /9.5 - ''.
+      |                       ^~
+
+9.5 - '' => error
+
+evaluate.sps:18.17-18.24: error: DEBUG EVALUATE: Both operands of + must be
+numeric.
+   18 | DEBUG EVALUATE /F2.0 + 3.
+      |                 ^~~~~~~~
+
+evaluate.sps:18.17-18.20: note: DEBUG EVALUATE: This operand has type 'format'.
+   18 | DEBUG EVALUATE /F2.0 + 3.
+      |                 ^~~~
+
+evaluate.sps:18.24: note: DEBUG EVALUATE: This operand has type 'number'.
+   18 | DEBUG EVALUATE /F2.0 + 3.
+      |                        ^
+
+F2.0 + 3 => error
+])
+done
+AT_CLEANUP
+
+AT_SETUP([expressions - multiplication and division])
+AT_KEYWORDS([expression expressions evaluate])
+AT_DATA([evaluate-base.sps], [dnl
+SET EPOCH 1940.
+DEBUG EVALUATE SET opt.
+DEBUG EVALUATE /5 * 10.
+DEBUG EVALUATE /10 * $true.
+DEBUG EVALUATE /$true * 5.
+DEBUG EVALUATE /1.5 * $true.
+DEBUG EVALUATE /$sysmis * 15.
+DEBUG EVALUATE /8.5 / $sysmis.
+DEBUG EVALUATE /2 * 5 / 10.
+DEBUG EVALUATE /1 / 2.
+DEBUG EVALUATE /2 / 5.
+DEBUG EVALUATE /12 / 3 / 2.
+
+DEBUG EVALUATE /'x' * 1.
+DEBUG EVALUATE /2 / 'x'.
+])
+
+for opt in OPT NOOPT; do
+    AS_BOX([$opt])
+    sed "s/opt/$opt/" < evaluate-base.sps > evaluate.sps
+    AT_CHECK([pspp --testing-mode evaluate.sps], [1], [dnl
+5 * 10 => 50.00
+
+10 * $true => 10.00
+
+$true * 5 => 5.00
+
+1.5 * $true => 1.50
+
+$sysmis * 15 => sysmis
+
+8.5 / $sysmis => sysmis
+
+2 * 5 / 10 => 1.00
+
+1 / 2 => 0.50
+
+2 / 5 => 0.40
+
+12 / 3 / 2 => 2.00
+
+evaluate.sps:14.17-14.23: error: DEBUG EVALUATE: Both operands of * must be
+numeric.
+   14 | DEBUG EVALUATE /'x' * 1.
+      |                 ^~~~~~~
+
+evaluate.sps:14.17-14.19: note: DEBUG EVALUATE: This operand has type 'string'.
+   14 | DEBUG EVALUATE /'x' * 1.
+      |                 ^~~
+
+evaluate.sps:14.23: note: DEBUG EVALUATE: This operand has type 'number'.
+   14 | DEBUG EVALUATE /'x' * 1.
+      |                       ^
+
+'x' * 1 => error
+
+evaluate.sps:15.17-15.23: error: DEBUG EVALUATE: Both operands of / must be
+numeric.
+   15 | DEBUG EVALUATE /2 / 'x'.
+      |                 ^~~~~~~
+
+evaluate.sps:15.17: note: DEBUG EVALUATE: This operand has type 'number'.
+   15 | DEBUG EVALUATE /2 / 'x'.
+      |                 ^
+
+evaluate.sps:15.21-15.23: note: DEBUG EVALUATE: This operand has type 'string'.
+   15 | DEBUG EVALUATE /2 / 'x'.
+      |                     ^~~
+
+2 / 'x' => error
+])
+done
+AT_CLEANUP
+
+AT_SETUP([expressions - exponentiation])
+AT_KEYWORDS([expression expressions evaluate])
+AT_DATA([evaluate-base.sps], [dnl
+SET EPOCH 1940.
+DEBUG EVALUATE SET opt.
+DEBUG EVALUATE /2**8.
+DEBUG EVALUATE /(2**3)**4.
+DEBUG EVALUATE /2**3**4.
+DEBUG EVALUATE /-2**2.
+DEBUG EVALUATE /-2**-3**-4.
+DEBUG EVALUATE /-((2**-3)**-4).
+])
+
+for opt in OPT NOOPT; do
+    AS_BOX([$opt])
+    sed "s/opt/$opt/" < evaluate-base.sps > evaluate.sps
+    AT_CHECK([pspp --testing-mode evaluate.sps], [0], [dnl
+2**8 => 256.00
+
+(2**3)**4 => 4096.00
+
+evaluate.sps:5.17-5.23: warning: DEBUG EVALUATE: The exponentiation operator
+(`**') is left-associative: `a**b**c' equals `(a**b)**c', not `a**(b**c)'.  To
+disable this warning, insert parentheses.
+    5 | DEBUG EVALUATE /2**3**4.
+      |                 ^~~~~~~
+
+2**3**4 => 4096.00
+
+-2**2 => -4.00
+
+evaluate.sps:7.17-7.26: warning: DEBUG EVALUATE: The exponentiation operator
+(`**') is left-associative: `a**b**c' equals `(a**b)**c', not `a**(b**c)'.  To
+disable this warning, insert parentheses.
+    7 | DEBUG EVALUATE /-2**-3**-4.
+      |                 ^~~~~~~~~~
+
+-2**-3**-4 => -4096.00
+
+-((2**-3)**-4) => -4096.00
+])
+done
+AT_CLEANUP
+
+AT_SETUP([expressions - unary minus])
+AT_KEYWORDS([expression expressions evaluate])
+AT_DATA([evaluate-base.sps], [dnl
+SET EPOCH 1940.
+DEBUG EVALUATE SET opt.
+DEBUG EVALUATE /2+-3.
+DEBUG EVALUATE /2*-3.
+DEBUG EVALUATE /-3**2.
+DEBUG EVALUATE /(-3)**2.
+DEBUG EVALUATE /-(3**2).
+DEBUG EVALUATE /2**-1.
+DEBUG EVALUATE /0**0.
+DEBUG EVALUATE /0**-1.
+DEBUG EVALUATE /(-3)**1.5.
+])
+
+for opt in OPT NOOPT; do
+    sed "s/opt/$opt/" < evaluate-base.sps > evaluate.sps
+    AT_CHECK([pspp --testing-mode evaluate.sps], [0], [dnl
+2+-3 => -1.00
+
+2*-3 => -6.00
+
+-3**2 => -9.00
+
+(-3)**2 => 9.00
+
+-(3**2) => -9.00
+
+2**-1 => 0.50
+
+0**0 => sysmis
+
+0**-1 => sysmis
+
+(-3)**1.5 => sysmis
+])
+done
+AT_CLEANUP
+
+AT_SETUP([expressions - AND truth table])
+AT_KEYWORDS([expression expressions evaluate])
+AT_DATA([evaluate-base.sps], [dnl
+SET EPOCH 1940.
+DEBUG EVALUATE SET opt.
+DEBUG EVALUATE /$false AND $false.
+DEBUG EVALUATE /$false AND $true.
+DEBUG EVALUATE /$false AND $sysmis.
+DEBUG EVALUATE /$true AND $false.
+DEBUG EVALUATE /$true AND $true.
+DEBUG EVALUATE /$true AND $sysmis.
+DEBUG EVALUATE /$sysmis AND $false.
+DEBUG EVALUATE /$sysmis AND $true.
+DEBUG EVALUATE /$sysmis AND $sysmis.
+DEBUG EVALUATE /$false & $false.
+DEBUG EVALUATE /$false & $true.
+DEBUG EVALUATE /$false & $sysmis.
+DEBUG EVALUATE /$true & $false.
+DEBUG EVALUATE /$true & $true.
+DEBUG EVALUATE /$true & $sysmis.
+DEBUG EVALUATE /$sysmis & $false.
+DEBUG EVALUATE /$sysmis & $true.
+DEBUG EVALUATE /$sysmis & $sysmis.
+])
+
+for opt in OPT NOOPT; do
+    AS_BOX([$opt])
+    sed "s/opt/$opt/" < evaluate-base.sps > evaluate.sps
+    AT_CHECK([pspp --testing-mode evaluate.sps], [0], [dnl
+$false AND $false => false
+
+$false AND $true => false
+
+$false AND $sysmis => false
+
+$true AND $false => false
+
+$true AND $true => true
+
+$true AND $sysmis => sysmis
+
+$sysmis AND $false => false
+
+$sysmis AND $true => sysmis
+
+$sysmis AND $sysmis => sysmis
+
+$false & $false => false
+
+$false & $true => false
+
+$false & $sysmis => false
+
+$true & $false => false
+
+$true & $true => true
+
+$true & $sysmis => sysmis
+
+$sysmis & $false => false
+
+$sysmis & $true => sysmis
+
+$sysmis & $sysmis => sysmis
+])
+done
+AT_CLEANUP
+
+AT_SETUP([expressions - OR truth table])
+AT_KEYWORDS([expression expressions evaluate])
+AT_DATA([evaluate-base.sps], [dnl
+SET EPOCH 1940.
+DEBUG EVALUATE SET opt.
+DEBUG EVALUATE /$false OR $false.
+DEBUG EVALUATE /$false OR $true.
+DEBUG EVALUATE /$false OR $sysmis.
+DEBUG EVALUATE /$true OR $false.
+DEBUG EVALUATE /$true OR $true.
+DEBUG EVALUATE /$true OR $sysmis.
+DEBUG EVALUATE /$sysmis OR $false.
+DEBUG EVALUATE /$sysmis OR $true.
+DEBUG EVALUATE /$sysmis OR $sysmis.
+DEBUG EVALUATE /$false | $false.
+DEBUG EVALUATE /$false | $true.
+DEBUG EVALUATE /$false | $sysmis.
+DEBUG EVALUATE /$true | $false.
+DEBUG EVALUATE /$true | $true.
+DEBUG EVALUATE /$true | $sysmis.
+DEBUG EVALUATE /$sysmis | $false.
+DEBUG EVALUATE /$sysmis | $true.
+DEBUG EVALUATE /$sysmis | $sysmis.
+])
+
+for opt in OPT NOOPT; do
+    AS_BOX([$opt])
+    sed "s/opt/$opt/" < evaluate-base.sps > evaluate.sps
+    AT_CHECK([pspp --testing-mode evaluate.sps], [0], [dnl
+$false OR $false => false
+
+$false OR $true => true
+
+$false OR $sysmis => sysmis
+
+$true OR $false => true
+
+$true OR $true => true
+
+$true OR $sysmis => true
+
+$sysmis OR $false => sysmis
+
+$sysmis OR $true => true
+
+$sysmis OR $sysmis => sysmis
+
+$false | $false => false
+
+$false | $true => true
+
+$false | $sysmis => sysmis
+
+$true | $false => true
+
+$true | $true => true
+
+$true | $sysmis => true
+
+$sysmis | $false => sysmis
+
+$sysmis | $true => true
+
+$sysmis | $sysmis => sysmis
+])
+done
+AT_CLEANUP
+
+AT_SETUP([expressions - NOT truth table])
+AT_KEYWORDS([expression expressions evaluate])
+AT_DATA([evaluate-base.sps], [dnl
+SET EPOCH 1940.
+DEBUG EVALUATE SET opt.
+DEBUG EVALUATE /not $false.
+DEBUG EVALUATE /not 0.
+DEBUG EVALUATE /not 2.5.
+DEBUG EVALUATE /not $true.
+DEBUG EVALUATE /not 1.
+DEBUG EVALUATE /not $sysmis.
+DEBUG EVALUATE /~ $false.
+DEBUG EVALUATE /~ 0.
+DEBUG EVALUATE /~ 2.5.
+DEBUG EVALUATE /~ $true.
+DEBUG EVALUATE /~ 1.
+DEBUG EVALUATE /~ $sysmis.
+])
+
+for opt in OPT NOOPT; do
+    AS_BOX([$opt])
+    sed "s/opt/$opt/" < evaluate-base.sps > evaluate.sps
+    AT_CHECK([pspp --testing-mode evaluate.sps], [1], [dnl
+not $false => true
+
+not 0 => true
+
+evaluate.sps:5.17-5.23: error: DEBUG EVALUATE: The operand of NOT must have
+value 0 or 1.
+    5 | DEBUG EVALUATE /not 2.5.
+      |                 ^~~~~~~
+
+evaluate.sps:5.21-5.23: note: DEBUG EVALUATE: This operand with unexpected
+value 2.5 will be treated as 0.
+    5 | DEBUG EVALUATE /not 2.5.
+      |                     ^~~
+
+not 2.5 => true
+
+not $true => false
+
+not 1 => false
+
+not $sysmis => sysmis
+
+~ $false => true
+
+~ 0 => true
+
+evaluate.sps:11.17-11.21: error: DEBUG EVALUATE: The operand of NOT must have
+value 0 or 1.
+   11 | DEBUG EVALUATE /~ 2.5.
+      |                 ^~~~~
+
+evaluate.sps:11.19-11.21: note: DEBUG EVALUATE: This operand with unexpected
+value 2.5 will be treated as 0.
+   11 | DEBUG EVALUATE /~ 2.5.
+      |                   ^~~
+
+~ 2.5 => true
+
+~ $true => false
+
+~ 1 => false
+
+~ $sysmis => sysmis
+])
+done
+AT_CLEANUP
+
+AT_SETUP([expressions - = <= <])
+AT_KEYWORDS([expression expressions evaluate eq le lt])
+AT_DATA([evaluate-base.sps], [dnl
+SET EPOCH 1940.
+DEBUG EVALUATE SET opt.
+DEBUG EVALUATE /1 eq 1.
+DEBUG EVALUATE /1 = 1.
+DEBUG EVALUATE /1 eq 2.
+DEBUG EVALUATE /2 = 3.
+DEBUG EVALUATE /1 eq 'foobar'.
+DEBUG EVALUATE /'baz' = 10.
+DEBUG EVALUATE /'baz' = f8.2.
+DEBUG EVALUATE /'baz' = 'baz'.
+DEBUG EVALUATE /'quux' = 'bar'.
+DEBUG EVALUATE /'bar  ' = 'bar'.
+DEBUG EVALUATE /'asdf     ' = 'asdf  '.
+DEBUG EVALUATE /'asdfj     ' = 'asdf'.
+DEBUG EVALUATE /1 + 2 = 3.
+DEBUG EVALUATE /1 >= 2 = 2 ge 3.
+DEBUG EVALUATE /3 ne 2 != 1.
+DEBUG EVALUATE /3 > 2 > 1.
+
+DEBUG EVALUATE /1 <= 2.
+DEBUG EVALUATE /2.5 <= 1.5.
+DEBUG EVALUATE /1 le 2.
+DEBUG EVALUATE /2 <= 2.
+DEBUG EVALUATE /2 le 2.
+DEBUG EVALUATE /2 < = 2.
+DEBUG EVALUATE /1 <= 'foobar'.
+DEBUG EVALUATE /'baz' <= 10.
+DEBUG EVALUATE /'quux' <= 5.55.
+DEBUG EVALUATE /'0123' <= '0123'.
+DEBUG EVALUATE /'0123' <= '0124'.
+DEBUG EVALUATE /'0124' le '0123'.
+DEBUG EVALUATE /'0123  ' <= '0123'.
+DEBUG EVALUATE /'0123' le '0123  '.
+
+DEBUG EVALUATE /1 < 2.
+DEBUG EVALUATE /2.5 < 1.5.
+DEBUG EVALUATE /3.5 lt 4.
+DEBUG EVALUATE /4 lt 3.5
+DEBUG EVALUATE /1 lt 'foobar'.
+DEBUG EVALUATE /5 lt 'foobar'.
+DEBUG EVALUATE /'baz' < 10.
+DEBUG EVALUATE /'quux' < 5.55.
+DEBUG EVALUATE /'0123' lt '0123'.
+DEBUG EVALUATE /'0123' < '0124'.
+DEBUG EVALUATE /'0124' lt '0123'.
+DEBUG EVALUATE /'0123  ' < '0123'.
+DEBUG EVALUATE /'0123' lt '0123  '.
+])
+
+for opt in OPT NOOPT; do
+    AS_BOX([$opt])
+    sed "s/opt/$opt/" < evaluate-base.sps > evaluate.sps
+    AT_CHECK([pspp --testing-mode evaluate.sps], [1], [dnl
+1 eq 1 => true
+
+1 = 1 => true
+
+1 eq 2 => false
+
+2 = 3 => false
+
+evaluate.sps:7.17-7.29: error: DEBUG EVALUATE: Both operands of EQ must have
+the same type.
+    7 | DEBUG EVALUATE /1 eq 'foobar'.
+      |                 ^~~~~~~~~~~~~
+
+evaluate.sps:7.17: note: DEBUG EVALUATE: This operand has type 'number'.
+    7 | DEBUG EVALUATE /1 eq 'foobar'.
+      |                 ^
+
+evaluate.sps:7.22-7.29: note: DEBUG EVALUATE: This operand has type 'string'.
+    7 | DEBUG EVALUATE /1 eq 'foobar'.
+      |                      ^~~~~~~~
+
+1 eq 'foobar' => error
+
+evaluate.sps:8.17-8.26: error: DEBUG EVALUATE: Both operands of = must have the
+same type.
+    8 | DEBUG EVALUATE /'baz' = 10.
+      |                 ^~~~~~~~~~
+
+evaluate.sps:8.17-8.21: note: DEBUG EVALUATE: This operand has type 'string'.
+    8 | DEBUG EVALUATE /'baz' = 10.
+      |                 ^~~~~
+
+evaluate.sps:8.25-8.26: note: DEBUG EVALUATE: This operand has type 'number'.
+    8 | DEBUG EVALUATE /'baz' = 10.
+      |                         ^~
+
+'baz' = 10 => error
+
+evaluate.sps:9.17-9.28: error: DEBUG EVALUATE: Both operands of = must have the
+same type.
+    9 | DEBUG EVALUATE /'baz' = f8.2.
+      |                 ^~~~~~~~~~~~
+
+evaluate.sps:9.17-9.21: note: DEBUG EVALUATE: This operand has type 'string'.
+    9 | DEBUG EVALUATE /'baz' = f8.2.
+      |                 ^~~~~
+
+evaluate.sps:9.25-9.28: note: DEBUG EVALUATE: This operand has type 'format'.
+    9 | DEBUG EVALUATE /'baz' = f8.2.
+      |                         ^~~~
+
+'baz' = f8.2 => error
+
+'baz' = 'baz' => true
+
+'quux' = 'bar' => false
+
+'bar  ' = 'bar' => true
+
+'asdf     ' = 'asdf  ' => true
+
+'asdfj     ' = 'asdf' => false
+
+1 + 2 = 3 => true
+
+evaluate.sps:16.17-16.31: warning: DEBUG EVALUATE: Chaining relational
+operators (e.g. `a < b < c') will not produce the mathematically expected
+result.  Use the AND logical operator to fix the problem (e.g. `a < b AND b <
+c').  To disable this warning, insert parentheses.
+   16 | DEBUG EVALUATE /1 >= 2 = 2 ge 3.
+      |                 ^~~~~~~~~~~~~~~
+
+1 >= 2 = 2 ge 3 => false
+
+evaluate.sps:17.24: error: DEBUG EVALUATE: Syntax error at `!': expecting end
+of command.
+
+3 ne 2 != 1 => error
+
+evaluate.sps:18.17-18.25: warning: DEBUG EVALUATE: Chaining relational
+operators (e.g. `a < b < c') will not produce the mathematically expected
+result.  Use the AND logical operator to fix the problem (e.g. `a < b AND b <
+c').  To disable this warning, insert parentheses.
+   18 | DEBUG EVALUATE /3 > 2 > 1.
+      |                 ^~~~~~~~~
+
+3 > 2 > 1 => false
+
+1 <= 2 => true
+
+2.5 <= 1.5 => false
+
+1 le 2 => true
+
+2 <= 2 => true
+
+2 le 2 => true
+
+evaluate.sps:25.21: error: DEBUG EVALUATE: Syntax error at `='.
+
+2 < = 2 => error
+
+evaluate.sps:26.17-26.29: error: DEBUG EVALUATE: Both operands of <= must have
+the same type.
+   26 | DEBUG EVALUATE /1 <= 'foobar'.
+      |                 ^~~~~~~~~~~~~
+
+evaluate.sps:26.17: note: DEBUG EVALUATE: This operand has type 'number'.
+   26 | DEBUG EVALUATE /1 <= 'foobar'.
+      |                 ^
+
+evaluate.sps:26.22-26.29: note: DEBUG EVALUATE: This operand has type 'string'.
+   26 | DEBUG EVALUATE /1 <= 'foobar'.
+      |                      ^~~~~~~~
+
+1 <= 'foobar' => error
+
+evaluate.sps:27.17-27.27: error: DEBUG EVALUATE: Both operands of <= must have
+the same type.
+   27 | DEBUG EVALUATE /'baz' <= 10.
+      |                 ^~~~~~~~~~~
+
+evaluate.sps:27.17-27.21: note: DEBUG EVALUATE: This operand has type 'string'.
+   27 | DEBUG EVALUATE /'baz' <= 10.
+      |                 ^~~~~
+
+evaluate.sps:27.26-27.27: note: DEBUG EVALUATE: This operand has type 'number'.
+   27 | DEBUG EVALUATE /'baz' <= 10.
+      |                          ^~
+
+'baz' <= 10 => error
+
+evaluate.sps:28.17-28.30: error: DEBUG EVALUATE: Both operands of <= must have
+the same type.
+   28 | DEBUG EVALUATE /'quux' <= 5.55.
+      |                 ^~~~~~~~~~~~~~
+
+evaluate.sps:28.17-28.22: note: DEBUG EVALUATE: This operand has type 'string'.
+   28 | DEBUG EVALUATE /'quux' <= 5.55.
+      |                 ^~~~~~
+
+evaluate.sps:28.27-28.30: note: DEBUG EVALUATE: This operand has type 'number'.
+   28 | DEBUG EVALUATE /'quux' <= 5.55.
+      |                           ^~~~
+
+'quux' <= 5.55 => error
+
+'0123' <= '0123' => true
+
+'0123' <= '0124' => true
+
+'0124' le '0123' => false
+
+'0123  ' <= '0123' => true
+
+'0123' le '0123  ' => true
+
+1 < 2 => true
+
+2.5 < 1.5 => false
+
+3.5 lt 4 => true
+
+4 lt 3.5 => false
+
+evaluate.sps:39.17-39.29: error: DEBUG EVALUATE: Both operands of < must have
+the same type.
+   39 | DEBUG EVALUATE /1 lt 'foobar'.
+      |                 ^~~~~~~~~~~~~
+
+evaluate.sps:39.17: note: DEBUG EVALUATE: This operand has type 'number'.
+   39 | DEBUG EVALUATE /1 lt 'foobar'.
+      |                 ^
+
+evaluate.sps:39.22-39.29: note: DEBUG EVALUATE: This operand has type 'string'.
+   39 | DEBUG EVALUATE /1 lt 'foobar'.
+      |                      ^~~~~~~~
+
+1 lt 'foobar' => error
+
+evaluate.sps:40.17-40.29: error: DEBUG EVALUATE: Both operands of < must have
+the same type.
+   40 | DEBUG EVALUATE /5 lt 'foobar'.
+      |                 ^~~~~~~~~~~~~
+
+evaluate.sps:40.17: note: DEBUG EVALUATE: This operand has type 'number'.
+   40 | DEBUG EVALUATE /5 lt 'foobar'.
+      |                 ^
+
+evaluate.sps:40.22-40.29: note: DEBUG EVALUATE: This operand has type 'string'.
+   40 | DEBUG EVALUATE /5 lt 'foobar'.
+      |                      ^~~~~~~~
+
+5 lt 'foobar' => error
+
+evaluate.sps:41.17-41.26: error: DEBUG EVALUATE: Both operands of < must have
+the same type.
+   41 | DEBUG EVALUATE /'baz' < 10.
+      |                 ^~~~~~~~~~
+
+evaluate.sps:41.17-41.21: note: DEBUG EVALUATE: This operand has type 'string'.
+   41 | DEBUG EVALUATE /'baz' < 10.
+      |                 ^~~~~
+
+evaluate.sps:41.25-41.26: note: DEBUG EVALUATE: This operand has type 'number'.
+   41 | DEBUG EVALUATE /'baz' < 10.
+      |                         ^~
+
+'baz' < 10 => error
+
+evaluate.sps:42.17-42.29: error: DEBUG EVALUATE: Both operands of < must have
+the same type.
+   42 | DEBUG EVALUATE /'quux' < 5.55.
+      |                 ^~~~~~~~~~~~~
+
+evaluate.sps:42.17-42.22: note: DEBUG EVALUATE: This operand has type 'string'.
+   42 | DEBUG EVALUATE /'quux' < 5.55.
+      |                 ^~~~~~
+
+evaluate.sps:42.26-42.29: note: DEBUG EVALUATE: This operand has type 'number'.
+   42 | DEBUG EVALUATE /'quux' < 5.55.
+      |                          ^~~~
+
+'quux' < 5.55 => error
+
+'0123' lt '0123' => false
+
+'0123' < '0124' => true
+
+'0124' lt '0123' => false
+
+'0123  ' < '0123' => false
+
+'0123' lt '0123  ' => false
+])
+done
+AT_CLEANUP
+
+AT_SETUP([expressions - >= > <>])
+AT_KEYWORDS([expression expressions evaluate ge gt ne])
+AT_DATA([evaluate-base.sps], [dnl
+SET EPOCH 1940.
+DEBUG EVALUATE SET opt.
+DEBUG EVALUATE /1 >= 2.
+DEBUG EVALUATE /2.5 >= 1.5
+DEBUG EVALUATE /1 ge 2.
+DEBUG EVALUATE /2 >= 2.
+DEBUG EVALUATE /2 ge 2.
+DEBUG EVALUATE /2 > = 2.
+DEBUG EVALUATE /1 >= 'foobar'.
+DEBUG EVALUATE /5 ge 'foobar'.
+DEBUG EVALUATE /'baz' ge 10.
+DEBUG EVALUATE /'0123' ge '0123'.
+DEBUG EVALUATE /'0123' >= '0124'.
+DEBUG EVALUATE /'0124' >= '0123'.
+DEBUG EVALUATE /'0123  ' ge '0123'.
+DEBUG EVALUATE /'0123' >= '0123 '.
+
+DEBUG EVALUATE /1 > 2.
+DEBUG EVALUATE /2.5 > 1.5
+DEBUG EVALUATE /3.5 gt 4.
+DEBUG EVALUATE /4 gt 3.5
+DEBUG EVALUATE /1 gt 'foobar'.
+DEBUG EVALUATE /'baz' > 10.
+DEBUG EVALUATE /'0123' > '0123'.
+DEBUG EVALUATE /'0123' > '0124'.
+DEBUG EVALUATE /'0124' > '0123'.
+DEBUG EVALUATE /'0123   ' > '0123'.
+DEBUG EVALUATE /'0123    ' > '0123 '.
+
+DEBUG EVALUATE /1 ne 1.
+DEBUG EVALUATE /1 ~= 1.
+DEBUG EVALUATE /1 <> 2.
+DEBUG EVALUATE /2 ne 3.
+DEBUG EVALUATE /1 ~= 'foobar'.
+DEBUG EVALUATE /'baz' ne 10.
+DEBUG EVALUATE /'quux' ~= 5.55.
+DEBUG EVALUATE /'foobar' <> 'foobar'.
+DEBUG EVALUATE /'quux' ne 'bar'.
+DEBUG EVALUATE /'bar   ' <> 'bar'.
+DEBUG EVALUATE /'asdf       ' ~= "asdf   ".
+DEBUG EVALUATE /1 < > 1.
+DEBUG EVALUATE /1 ~ = 1.
+])
+
+for opt in OPT NOOPT; do
+    AS_BOX([$opt])
+    sed "s/opt/$opt/" < evaluate-base.sps > evaluate.sps
+    AT_CHECK([pspp --testing-mode evaluate.sps], [1], [dnl
+1 >= 2 => false
+
+2.5 >= 1.5 => true
+
+1 ge 2 => false
+
+2 >= 2 => true
+
+2 ge 2 => true
+
+evaluate.sps:8.21: error: DEBUG EVALUATE: Syntax error at `='.
+
+2 > = 2 => error
+
+evaluate.sps:9.17-9.29: error: DEBUG EVALUATE: Both operands of >= must have
+the same type.
+    9 | DEBUG EVALUATE /1 >= 'foobar'.
+      |                 ^~~~~~~~~~~~~
+
+evaluate.sps:9.17: note: DEBUG EVALUATE: This operand has type 'number'.
+    9 | DEBUG EVALUATE /1 >= 'foobar'.
+      |                 ^
+
+evaluate.sps:9.22-9.29: note: DEBUG EVALUATE: This operand has type 'string'.
+    9 | DEBUG EVALUATE /1 >= 'foobar'.
+      |                      ^~~~~~~~
+
+1 >= 'foobar' => error
+
+evaluate.sps:10.17-10.29: error: DEBUG EVALUATE: Both operands of >= must have
+the same type.
+   10 | DEBUG EVALUATE /5 ge 'foobar'.
+      |                 ^~~~~~~~~~~~~
+
+evaluate.sps:10.17: note: DEBUG EVALUATE: This operand has type 'number'.
+   10 | DEBUG EVALUATE /5 ge 'foobar'.
+      |                 ^
+
+evaluate.sps:10.22-10.29: note: DEBUG EVALUATE: This operand has type 'string'.
+   10 | DEBUG EVALUATE /5 ge 'foobar'.
+      |                      ^~~~~~~~
+
+5 ge 'foobar' => error
+
+evaluate.sps:11.17-11.27: error: DEBUG EVALUATE: Both operands of >= must have
+the same type.
+   11 | DEBUG EVALUATE /'baz' ge 10.
+      |                 ^~~~~~~~~~~
+
+evaluate.sps:11.17-11.21: note: DEBUG EVALUATE: This operand has type 'string'.
+   11 | DEBUG EVALUATE /'baz' ge 10.
+      |                 ^~~~~
+
+evaluate.sps:11.26-11.27: note: DEBUG EVALUATE: This operand has type 'number'.
+   11 | DEBUG EVALUATE /'baz' ge 10.
+      |                          ^~
+
+'baz' ge 10 => error
+
+'0123' ge '0123' => true
+
+'0123' >= '0124' => false
+
+'0124' >= '0123' => true
+
+'0123  ' ge '0123' => true
+
+'0123' >= '0123 ' => true
+
+1 > 2 => false
+
+2.5 > 1.5 => true
+
+3.5 gt 4 => false
+
+4 gt 3.5 => true
+
+evaluate.sps:22.17-22.29: error: DEBUG EVALUATE: Both operands of > must have
+the same type.
+   22 | DEBUG EVALUATE /1 gt 'foobar'.
+      |                 ^~~~~~~~~~~~~
+
+evaluate.sps:22.17: note: DEBUG EVALUATE: This operand has type 'number'.
+   22 | DEBUG EVALUATE /1 gt 'foobar'.
+      |                 ^
+
+evaluate.sps:22.22-22.29: note: DEBUG EVALUATE: This operand has type 'string'.
+   22 | DEBUG EVALUATE /1 gt 'foobar'.
+      |                      ^~~~~~~~
+
+1 gt 'foobar' => error
+
+evaluate.sps:23.17-23.26: error: DEBUG EVALUATE: Both operands of > must have
+the same type.
+   23 | DEBUG EVALUATE /'baz' > 10.
+      |                 ^~~~~~~~~~
+
+evaluate.sps:23.17-23.21: note: DEBUG EVALUATE: This operand has type 'string'.
+   23 | DEBUG EVALUATE /'baz' > 10.
+      |                 ^~~~~
+
+evaluate.sps:23.25-23.26: note: DEBUG EVALUATE: This operand has type 'number'.
+   23 | DEBUG EVALUATE /'baz' > 10.
+      |                         ^~
+
+'baz' > 10 => error
+
+'0123' > '0123' => false
+
+'0123' > '0124' => false
+
+'0124' > '0123' => true
+
+'0123   ' > '0123' => false
+
+'0123    ' > '0123 ' => false
+
+1 ne 1 => false
+
+1 ~= 1 => false
+
+1 <> 2 => true
+
+2 ne 3 => true
+
+evaluate.sps:34.17-34.29: error: DEBUG EVALUATE: Both operands of ~= must have
+the same type.
+   34 | DEBUG EVALUATE /1 ~= 'foobar'.
+      |                 ^~~~~~~~~~~~~
+
+evaluate.sps:34.17: note: DEBUG EVALUATE: This operand has type 'number'.
+   34 | DEBUG EVALUATE /1 ~= 'foobar'.
+      |                 ^
+
+evaluate.sps:34.22-34.29: note: DEBUG EVALUATE: This operand has type 'string'.
+   34 | DEBUG EVALUATE /1 ~= 'foobar'.
+      |                      ^~~~~~~~
+
+1 ~= 'foobar' => error
+
+evaluate.sps:35.17-35.27: error: DEBUG EVALUATE: Both operands of ~= must have
+the same type.
+   35 | DEBUG EVALUATE /'baz' ne 10.
+      |                 ^~~~~~~~~~~
+
+evaluate.sps:35.17-35.21: note: DEBUG EVALUATE: This operand has type 'string'.
+   35 | DEBUG EVALUATE /'baz' ne 10.
+      |                 ^~~~~
+
+evaluate.sps:35.26-35.27: note: DEBUG EVALUATE: This operand has type 'number'.
+   35 | DEBUG EVALUATE /'baz' ne 10.
+      |                          ^~
+
+'baz' ne 10 => error
+
+evaluate.sps:36.17-36.30: error: DEBUG EVALUATE: Both operands of ~= must have
+the same type.
+   36 | DEBUG EVALUATE /'quux' ~= 5.55.
+      |                 ^~~~~~~~~~~~~~
+
+evaluate.sps:36.17-36.22: note: DEBUG EVALUATE: This operand has type 'string'.
+   36 | DEBUG EVALUATE /'quux' ~= 5.55.
+      |                 ^~~~~~
+
+evaluate.sps:36.27-36.30: note: DEBUG EVALUATE: This operand has type 'number'.
+   36 | DEBUG EVALUATE /'quux' ~= 5.55.
+      |                           ^~~~
+
+'quux' ~= 5.55 => error
+
+'foobar' <> 'foobar' => false
+
+'quux' ne 'bar' => true
+
+'bar   ' <> 'bar' => false
+
+'asdf       ' ~= "asdf   " => false
+
+evaluate.sps:41.21: error: DEBUG EVALUATE: Syntax error at `>'.
+
+1 < > 1 => error
+
+evaluate.sps:42.19: error: DEBUG EVALUATE: Syntax error at `~': expecting end
+of command.
+
+1 ~ = 1 => error
+])
+done
+AT_CLEANUP
+
+AT_SETUP([expressions - EXP LG10 LN SQRT ABS MOD MOD10 RND TRUNC])
+AT_KEYWORDS([expression expressions evaluate])
+AT_DATA([evaluate-base.sps], [dnl
+SET EPOCH 1940.
+DEBUG EVALUATE SET opt.
+DEBUG EVALUATE /EXP(10).
+DEBUG EVALUATE /EXP('x').
+
+DEBUG EVALUATE /LG10(500).
+DEBUG EVALUATE /LG10('x').
+
+DEBUG EVALUATE /SQRT(500).
+DEBUG EVALUATE /SQRT(-1).
+
+DEBUG EVALUATE /ABS(-10.5).
+DEBUG EVALUATE /ABS(55.79).
+DEBUG EVALUATE /ABS(0).
+DEBUG EVALUATE /ABS(-0).
+
+DEBUG EVALUATE /MOD(55.5, 2).
+DEBUG EVALUATE /MOD(-55.5, 2).
+DEBUG EVALUATE /MOD(55.5, -2).
+DEBUG EVALUATE /MOD(-55.5, -2).
+DEBUG EVALUATE /MOD('a', 2).
+DEBUG EVALUATE /MOD(2, 'a').
+DEBUG EVALUATE /MOD('a', 'b').
+
+DEBUG EVALUATE /MOD10(55.5).
+DEBUG EVALUATE /MOD10(-55.5).
+
+DEBUG EVALUATE /RND(5.4).
+DEBUG EVALUATE /RND(5.6).
+DEBUG EVALUATE /RND(-5.4).
+DEBUG EVALUATE /RND(-5.6).
+DEBUG EVALUATE /RND(5.56, .1).
+DEBUG EVALUATE /RND(-5.56, .1)
+DEBUG EVALUATE /RND(.5).
+DEBUG EVALUATE /RND(.5 - 2**-53).
+DEBUG EVALUATE /RND(.5 - 2**-52).
+DEBUG EVALUATE /RND(.5 - 2**-51).
+DEBUG EVALUATE /RND(.5 - 2**-45).
+DEBUG EVALUATE /RND(.5 - 2**-45, 1, 10).
+DEBUG EVALUATE /RND('x').
+
+DEBUG EVALUATE /TRUNC(1.2).
+DEBUG EVALUATE /TRUNC(1.9).
+DEBUG EVALUATE /TRUNC(-1.2).
+DEBUG EVALUATE /TRUNC(-1.9).
+DEBUG EVALUATE /TRUNC(5.06, .1).
+DEBUG EVALUATE /TRUNC(-5.06, .1).
+DEBUG EVALUATE /TRUNC(1).
+DEBUG EVALUATE /TRUNC(1 - 2**-53).
+DEBUG EVALUATE /TRUNC(1 - 2**-52).
+DEBUG EVALUATE /TRUNC(1 - 2**-51).
+DEBUG EVALUATE /TRUNC(1 - 2**-45).
+DEBUG EVALUATE /TRUNC(1 - 2**-45, 1, 10).
+DEBUG EVALUATE /TRUNC('x').
+])
+
+for opt in OPT NOOPT; do
+    AS_BOX([$opt])
+    sed "s/opt/$opt/" < evaluate-base.sps > evaluate.sps
+    AT_CHECK([pspp --testing-mode evaluate.sps], [1], [dnl
+EXP(10) => 22026.47
+
+evaluate.sps:4.17-4.24: error: DEBUG EVALUATE: Type mismatch invoking
+EXP(number) as EXP(string).
+    4 | DEBUG EVALUATE /EXP('x').
+      |                 ^~~~~~~~
+
+evaluate.sps:4.21-4.23: note: DEBUG EVALUATE: This argument has type 'string'
+but 'number' is required.
+    4 | DEBUG EVALUATE /EXP('x').
+      |                     ^~~
+
+EXP('x') => error
+
+LG10(500) => 2.70
+
+evaluate.sps:7.17-7.25: error: DEBUG EVALUATE: Type mismatch invoking
+LG10(number) as LG10(string).
+    7 | DEBUG EVALUATE /LG10('x').
+      |                 ^~~~~~~~~
+
+evaluate.sps:7.22-7.24: note: DEBUG EVALUATE: This argument has type 'string'
+but 'number' is required.
+    7 | DEBUG EVALUATE /LG10('x').
+      |                      ^~~
+
+LG10('x') => error
+
+SQRT(500) => 22.36
+
+SQRT(-1) => sysmis
+
+ABS(-10.5) => 10.50
+
+ABS(55.79) => 55.79
+
+ABS(0) => 0.00
+
+ABS(-0) => 0.00
+
+MOD(55.5, 2) => 1.50
+
+MOD(-55.5, 2) => -1.50
+
+MOD(55.5, -2) => 1.50
+
+MOD(-55.5, -2) => -1.50
+
+evaluate.sps:21.17-21.27: error: DEBUG EVALUATE: Type mismatch invoking
+MOD(number, number) as MOD(string, number).
+   21 | DEBUG EVALUATE /MOD('a', 2).
+      |                 ^~~~~~~~~~~
+
+evaluate.sps:21.21-21.23: note: DEBUG EVALUATE: This argument has type 'string'
+but 'number' is required.
+   21 | DEBUG EVALUATE /MOD('a', 2).
+      |                     ^~~
+
+MOD('a', 2) => error
+
+evaluate.sps:22.17-22.27: error: DEBUG EVALUATE: Type mismatch invoking
+MOD(number, number) as MOD(number, string).
+   22 | DEBUG EVALUATE /MOD(2, 'a').
+      |                 ^~~~~~~~~~~
+
+evaluate.sps:22.24-22.26: note: DEBUG EVALUATE: This argument has type 'string'
+but 'number' is required.
+   22 | DEBUG EVALUATE /MOD(2, 'a').
+      |                        ^~~
+
+MOD(2, 'a') => error
+
+evaluate.sps:23.17-23.29: error: DEBUG EVALUATE: Type mismatch invoking
+MOD(number, number) as MOD(string, string).
+   23 | DEBUG EVALUATE /MOD('a', 'b').
+      |                 ^~~~~~~~~~~~~
+
+evaluate.sps:23.21-23.23: note: DEBUG EVALUATE: This argument has type 'string'
+but 'number' is required.
+   23 | DEBUG EVALUATE /MOD('a', 'b').
+      |                     ^~~
+
+evaluate.sps:23.26-23.28: note: DEBUG EVALUATE: This argument has type 'string'
+but 'number' is required.
+   23 | DEBUG EVALUATE /MOD('a', 'b').
+      |                          ^~~
+
+MOD('a', 'b') => error
+
+MOD10(55.5) => 5.50
+
+MOD10(-55.5) => -5.50
+
+RND(5.4) => 5.00
+
+RND(5.6) => 6.00
+
+RND(-5.4) => -5.00
+
+RND(-5.6) => -6.00
+
+RND(5.56, .1) => 5.60
+
+RND(-5.56, .1) => -5.60
+
+RND(.5) => 1.00
+
+RND(.5 - 2**-53) => 1.00
+
+RND(.5 - 2**-52) => 1.00
+
+RND(.5 - 2**-51) => 1.00
+
+RND(.5 - 2**-45) => 0.00
+
+RND(.5 - 2**-45, 1, 10) => 1.00
+
+evaluate.sps:40.17-40.24: error: DEBUG EVALUATE: Function invocation
+RND(string) does not match any known function.  Candidates are:
+RND(number)
+RND(number, number)
+RND(number, number, number).
+   40 | DEBUG EVALUATE /RND('x').
+      |                 ^~~~~~~~
+
+RND('x') => error
+
+TRUNC(1.2) => 1.00
+
+TRUNC(1.9) => 1.00
+
+TRUNC(-1.2) => -1.00
+
+TRUNC(-1.9) => -1.00
+
+TRUNC(5.06, .1) => 5.00
+
+TRUNC(-5.06, .1) => -5.00
+
+TRUNC(1) => 1.00
+
+TRUNC(1 - 2**-53) => 1.00
+
+TRUNC(1 - 2**-52) => 1.00
+
+TRUNC(1 - 2**-51) => 1.00
+
+TRUNC(1 - 2**-45) => 0.00
+
+TRUNC(1 - 2**-45, 1, 10) => 1.00
+
+evaluate.sps:54.17-54.26: error: DEBUG EVALUATE: Function invocation
+TRUNC(string) does not match any known function.  Candidates are:
+TRUNC(number)
+TRUNC(number, number)
+TRUNC(number, number, number).
+   54 | DEBUG EVALUATE /TRUNC('x').
+      |                 ^~~~~~~~~~
+
+TRUNC('x') => error
+])
+done
+AT_CLEANUP
+
+AT_SETUP([expressions - ACOS ARSIN ARTAN COS SIN TAN])
+AT_KEYWORDS([expression expressions evaluate])
+AT_DATA([evaluate-base.sps], [dnl
+SET EPOCH 1940.
+DEBUG EVALUATE SET opt.
+DEBUG EVALUATE /ACOS(.5) / 3.14159 * 180.
+DEBUG EVALUATE /ARCOS(.75) / 3.14159 * 180.
+DEBUG EVALUATE /ARCOS(-.5) / 3.14159 * 180.
+DEBUG EVALUATE /ACOS(-.75) / 3.14159 * 180.
+DEBUG EVALUATE /ACOS(-1) / 3.14159 * 180.
+DEBUG EVALUATE /ARCOS(1) / 3.14159 * 180.
+DEBUG EVALUATE /ACOS(-1.01) / 3.14159 * 180.
+DEBUG EVALUATE /ARCOS(1.01) / 3.14159 * 180.
+DEBUG EVALUATE /ACOS('x') / 3.14159 * 180.
+
+DEBUG EVALUATE /ASIN(.5) / 3.14159 * 180.
+DEBUG EVALUATE /ARSIN(.25) / 3.14159 * 180.
+DEBUG EVALUATE /ARSIN(-.5) / 3.14159 * 180.
+DEBUG EVALUATE /ASIN(-.25) / 3.14159 * 180.
+DEBUG EVALUATE /ASIN(-1.01) / 3.14159 * 180.
+DEBUG EVALUATE /ARSIN(1.01) / 3.14159 * 180.
+DEBUG EVALUATE /ASIN('x') / 3.14159 * 180.
+
+DEBUG EVALUATE /ATAN(1) / 3.14159 * 180.
+DEBUG EVALUATE /ARTAN(10) / 3.14159 * 180.
+DEBUG EVALUATE /ARTAN(-1) / 3.14159 * 180.
+DEBUG EVALUATE /ATAN(-10) / 3.14159 * 180.
+DEBUG EVALUATE /ATAN('x') / 3.14159 * 180.
+
+DEBUG EVALUATE /COS(60 / 180 * 3.14159).
+DEBUG EVALUATE /COS(45 / 180 * 3.14159).
+DEBUG EVALUATE /COS(30 / 180 * 3.14159).
+DEBUG EVALUATE /COS(15 / 180 * 3.14159).
+DEBUG EVALUATE /COS(-60 / 180 * 3.14159).
+DEBUG EVALUATE /COS(-45 / 180 * 3.14159).
+DEBUG EVALUATE /COS(-30 / 180 * 3.14159).
+DEBUG EVALUATE /COS(-15 / 180 * 3.14159).
+DEBUG EVALUATE /COS(123 / 180 * 3.14159).
+DEBUG EVALUATE /COS(321 / 180 * 3.14159).
+DEBUG EVALUATE /COS('x').
+
+DEBUG EVALUATE /SIN(60 / 180 * 3.14159).
+DEBUG EVALUATE /SIN(45 / 180 * 3.14159).
+DEBUG EVALUATE /SIN(30 / 180 * 3.14159).
+DEBUG EVALUATE /SIN(15 / 180 * 3.14159).
+DEBUG EVALUATE /SIN(-60 / 180 * 3.14159).
+DEBUG EVALUATE /SIN(-45 / 180 * 3.14159).
+DEBUG EVALUATE /SIN(-30 / 180 * 3.14159).
+DEBUG EVALUATE /SIN(-15 / 180 * 3.14159).
+DEBUG EVALUATE /SIN(123 / 180 * 3.14159).
+DEBUG EVALUATE /SIN(321 / 180 * 3.14159).
+DEBUG EVALUATE /SIN('x').
+
+DEBUG EVALUATE /TAN(60 / 180 * 3.14159).
+DEBUG EVALUATE /TAN(45 / 180 * 3.14159).
+DEBUG EVALUATE /TAN(30 / 180 * 3.14159).
+DEBUG EVALUATE /TAN(15 / 180 * 3.14159).
+DEBUG EVALUATE /TAN(-60 / 180 * 3.14159).
+DEBUG EVALUATE /TAN(-45 / 180 * 3.14159).
+DEBUG EVALUATE /TAN(-30 / 180 * 3.14159).
+DEBUG EVALUATE /TAN(-15 / 180 * 3.14159).
+DEBUG EVALUATE /TAN(123 / 180 * 3.14159).
+DEBUG EVALUATE /TAN(321 / 180 * 3.14159).
+DEBUG EVALUATE /TAN('x').
+])
+
+for opt in OPT NOOPT; do
+    AS_BOX([$opt])
+    sed "s/opt/$opt/" < evaluate-base.sps > evaluate.sps
+    AT_CHECK([pspp --testing-mode evaluate.sps], [1], [dnl
+ACOS(.5) / 3.14159 * 180 => 60.00
+
+ARCOS(.75) / 3.14159 * 180 => 41.41
+
+ARCOS(-.5) / 3.14159 * 180 => 120.00
+
+ACOS(-.75) / 3.14159 * 180 => 138.59
+
+ACOS(-1) / 3.14159 * 180 => 180.00
+
+ARCOS(1) / 3.14159 * 180 => 0.00
+
+ACOS(-1.01) / 3.14159 * 180 => sysmis
+
+ARCOS(1.01) / 3.14159 * 180 => sysmis
+
+evaluate.sps:11.17-11.25: error: DEBUG EVALUATE: Type mismatch invoking
+ACOS(number) as ACOS(string).
+   11 | DEBUG EVALUATE /ACOS('x') / 3.14159 * 180.
+      |                 ^~~~~~~~~
+
+evaluate.sps:11.22-11.24: note: DEBUG EVALUATE: This argument has type 'string'
+but 'number' is required.
+   11 | DEBUG EVALUATE /ACOS('x') / 3.14159 * 180.
+      |                      ^~~
+
+ACOS('x') / 3.14159 * 180 => error
+
+ASIN(.5) / 3.14159 * 180 => 30.00
+
+ARSIN(.25) / 3.14159 * 180 => 14.48
+
+ARSIN(-.5) / 3.14159 * 180 => -30.00
+
+ASIN(-.25) / 3.14159 * 180 => -14.48
+
+ASIN(-1.01) / 3.14159 * 180 => sysmis
+
+ARSIN(1.01) / 3.14159 * 180 => sysmis
+
+evaluate.sps:19.17-19.25: error: DEBUG EVALUATE: Type mismatch invoking
+ASIN(number) as ASIN(string).
+   19 | DEBUG EVALUATE /ASIN('x') / 3.14159 * 180.
+      |                 ^~~~~~~~~
+
+evaluate.sps:19.22-19.24: note: DEBUG EVALUATE: This argument has type 'string'
+but 'number' is required.
+   19 | DEBUG EVALUATE /ASIN('x') / 3.14159 * 180.
+      |                      ^~~
+
+ASIN('x') / 3.14159 * 180 => error
+
+ATAN(1) / 3.14159 * 180 => 45.00
+
+ARTAN(10) / 3.14159 * 180 => 84.29
+
+ARTAN(-1) / 3.14159 * 180 => -45.00
+
+ATAN(-10) / 3.14159 * 180 => -84.29
+
+evaluate.sps:25.17-25.25: error: DEBUG EVALUATE: Type mismatch invoking
+ATAN(number) as ATAN(string).
+   25 | DEBUG EVALUATE /ATAN('x') / 3.14159 * 180.
+      |                 ^~~~~~~~~
+
+evaluate.sps:25.22-25.24: note: DEBUG EVALUATE: This argument has type 'string'
+but 'number' is required.
+   25 | DEBUG EVALUATE /ATAN('x') / 3.14159 * 180.
+      |                      ^~~
+
+ATAN('x') / 3.14159 * 180 => error
+
+COS(60 / 180 * 3.14159) => 0.50
+
+COS(45 / 180 * 3.14159) => 0.71
+
+COS(30 / 180 * 3.14159) => 0.87
+
+COS(15 / 180 * 3.14159) => 0.97
+
+COS(-60 / 180 * 3.14159) => 0.50
+
+COS(-45 / 180 * 3.14159) => 0.71
+
+COS(-30 / 180 * 3.14159) => 0.87
+
+COS(-15 / 180 * 3.14159) => 0.97
+
+COS(123 / 180 * 3.14159) => -0.54
+
+COS(321 / 180 * 3.14159) => 0.78
+
+evaluate.sps:37.17-37.24: error: DEBUG EVALUATE: Type mismatch invoking
+COS(number) as COS(string).
+   37 | DEBUG EVALUATE /COS('x').
+      |                 ^~~~~~~~
+
+evaluate.sps:37.21-37.23: note: DEBUG EVALUATE: This argument has type 'string'
+but 'number' is required.
+   37 | DEBUG EVALUATE /COS('x').
+      |                     ^~~
+
+COS('x') => error
+
+SIN(60 / 180 * 3.14159) => 0.87
+
+SIN(45 / 180 * 3.14159) => 0.71
+
+SIN(30 / 180 * 3.14159) => 0.50
+
+SIN(15 / 180 * 3.14159) => 0.26
+
+SIN(-60 / 180 * 3.14159) => -0.87
+
+SIN(-45 / 180 * 3.14159) => -0.71
+
+SIN(-30 / 180 * 3.14159) => -0.50
+
+SIN(-15 / 180 * 3.14159) => -0.26
+
+SIN(123 / 180 * 3.14159) => 0.84
+
+SIN(321 / 180 * 3.14159) => -0.63
+
+evaluate.sps:49.17-49.24: error: DEBUG EVALUATE: Type mismatch invoking
+SIN(number) as SIN(string).
+   49 | DEBUG EVALUATE /SIN('x').
+      |                 ^~~~~~~~
+
+evaluate.sps:49.21-49.23: note: DEBUG EVALUATE: This argument has type 'string'
+but 'number' is required.
+   49 | DEBUG EVALUATE /SIN('x').
+      |                     ^~~
+
+SIN('x') => error
+
+TAN(60 / 180 * 3.14159) => 1.73
+
+TAN(45 / 180 * 3.14159) => 1.00
+
+TAN(30 / 180 * 3.14159) => 0.58
+
+TAN(15 / 180 * 3.14159) => 0.27
+
+TAN(-60 / 180 * 3.14159) => -1.73
+
+TAN(-45 / 180 * 3.14159) => -1.00
+
+TAN(-30 / 180 * 3.14159) => -0.58
+
+TAN(-15 / 180 * 3.14159) => -0.27
+
+TAN(123 / 180 * 3.14159) => -1.54
+
+TAN(321 / 180 * 3.14159) => -0.81
+
+evaluate.sps:61.17-61.24: error: DEBUG EVALUATE: Type mismatch invoking
+TAN(number) as TAN(string).
+   61 | DEBUG EVALUATE /TAN('x').
+      |                 ^~~~~~~~
+
+evaluate.sps:61.21-61.23: note: DEBUG EVALUATE: This argument has type 'string'
+but 'number' is required.
+   61 | DEBUG EVALUATE /TAN('x').
+      |                     ^~~
+
+TAN('x') => error
+])
+done
+AT_CLEANUP
+
+AT_SETUP([expressions - vector indexing])
+AT_KEYWORDS([expression expressions evaluate])
+AT_DATA([evaluate-base.sps], [dnl
+SET EPOCH 1940.
+DEBUG EVALUATE SET opt.
+
+DEBUG EVALUATE (a=11)(b=22)(c=33) VECTOR/v(1).
+DEBUG EVALUATE (a=11)(b=22)(c=33) VECTOR/v(2).
+DEBUG EVALUATE (a=11)(b=22)(c=33) VECTOR/v(3).
+DEBUG EVALUATE (a=11)(b=22)(c=33) VECTOR/v($sysmis).
+DEBUG EVALUATE (a=11)(b=22)(c=33) VECTOR/v(0).
+DEBUG EVALUATE (a=11)(b=22)(c=33) VECTOR/v(4).
+
+DEBUG EVALUATE (a='abc')(b='def')(c='ghi') VECTOR/v(1).
+DEBUG EVALUATE (a='abc')(b='def')(c='ghi') VECTOR/v(2).
+DEBUG EVALUATE (a='abc')(b='def')(c='ghi') VECTOR/v(3).
+DEBUG EVALUATE (a='abc')(b='def')(c='ghi') VECTOR/v($sysmis).
+DEBUG EVALUATE (a='abc')(b='def')(c='ghi') VECTOR/v(0).
+DEBUG EVALUATE (a='abc')(b='def')(c='ghi') VECTOR/v(4).
+
+])
+
+for opt in OPT NOOPT; do
+    AS_BOX([$opt])
+    sed "s/opt/$opt/" < evaluate-base.sps > evaluate.sps
+    AT_CHECK([pspp --testing-mode evaluate.sps], [1], [dnl
+v(1) => 11.00
+
+v(2) => 22.00
+
+v(3) => 33.00
+
+evaluate.sps:7.42-7.51: error: DEBUG EVALUATE: Index outside valid range 1 to
+3, inclusive, for vector V.  The value will be treated as system-missing.
+    7 | DEBUG EVALUATE (a=11)(b=22)(c=33) VECTOR/v($sysmis).
+      |                                          ^~~~~~~~~~
+
+evaluate.sps:7.44-7.50: note: DEBUG EVALUATE: The index is system-missing.
+    7 | DEBUG EVALUATE (a=11)(b=22)(c=33) VECTOR/v($sysmis).
+      |                                            ^~~~~~~
+
+v($sysmis) => sysmis
+
+evaluate.sps:8.42-8.45: error: DEBUG EVALUATE: Index outside valid range 1 to
+3, inclusive, for vector V.  The value will be treated as system-missing.
+    8 | DEBUG EVALUATE (a=11)(b=22)(c=33) VECTOR/v(0).
+      |                                          ^~~~
+
+evaluate.sps:8.44: note: DEBUG EVALUATE: The index has value 0.
+    8 | DEBUG EVALUATE (a=11)(b=22)(c=33) VECTOR/v(0).
+      |                                            ^
+
+v(0) => sysmis
+
+evaluate.sps:9.42-9.45: error: DEBUG EVALUATE: Index outside valid range 1 to
+3, inclusive, for vector V.  The value will be treated as system-missing.
+    9 | DEBUG EVALUATE (a=11)(b=22)(c=33) VECTOR/v(4).
+      |                                          ^~~~
+
+evaluate.sps:9.44: note: DEBUG EVALUATE: The index has value 4.
+    9 | DEBUG EVALUATE (a=11)(b=22)(c=33) VECTOR/v(4).
+      |                                            ^
+
+v(4) => sysmis
+
+v(1) => "abc"
+
+v(2) => "def"
+
+v(3) => "ghi"
+
+evaluate.sps:14.51-14.60: error: DEBUG EVALUATE: Index outside valid range 1 to
+3, inclusive, for vector V.  The value will be treated as system-missing.
+   14 | DEBUG EVALUATE (a='abc')(b='def')(c='ghi') VECTOR/v($sysmis).
+      |                                                   ^~~~~~~~~~
+
+evaluate.sps:14.53-14.59: note: DEBUG EVALUATE: The index is system-missing.
+   14 | DEBUG EVALUATE (a='abc')(b='def')(c='ghi') VECTOR/v($sysmis).
+      |                                                     ^~~~~~~
+
+v($sysmis) => ""
+
+evaluate.sps:15.51-15.54: error: DEBUG EVALUATE: Index outside valid range 1 to
+3, inclusive, for vector V.  The value will be treated as system-missing.
+   15 | DEBUG EVALUATE (a='abc')(b='def')(c='ghi') VECTOR/v(0).
+      |                                                   ^~~~
+
+evaluate.sps:15.53: note: DEBUG EVALUATE: The index has value 0.
+   15 | DEBUG EVALUATE (a='abc')(b='def')(c='ghi') VECTOR/v(0).
+      |                                                     ^
+
+v(0) => ""
+
+evaluate.sps:16.51-16.54: error: DEBUG EVALUATE: Index outside valid range 1 to
+3, inclusive, for vector V.  The value will be treated as system-missing.
+   16 | DEBUG EVALUATE (a='abc')(b='def')(c='ghi') VECTOR/v(4).
+      |                                                   ^~~~
+
+evaluate.sps:16.53: note: DEBUG EVALUATE: The index has value 4.
+   16 | DEBUG EVALUATE (a='abc')(b='def')(c='ghi') VECTOR/v(4).
+      |                                                     ^
+
+v(4) => ""
+])
+done
+AT_CLEANUP
+
+AT_SETUP([expressions - MISSING NMISS NVALID SYSMIS])
+AT_KEYWORDS([expression expressions evaluate])
+AT_DATA([evaluate-base.sps], [dnl
+SET EPOCH 1940.
+DEBUG EVALUATE SET opt.
+DEBUG EVALUATE /MISSING(10).
+DEBUG EVALUATE /MISSING($SYSMIS).
+DEBUG EVALUATE /MISSING(ASIN(1.01)).
+DEBUG EVALUATE /MISSING(ASIN(.5)).
+DEBUG EVALUATE /MISSING('    ').
+
+DEBUG EVALUATE (x=5)/x.
+DEBUG EVALUATE (x=5 MISSING)/x.
+DEBUG EVALUATE (x=5 MISSING)/x + 1.
+DEBUG EVALUATE (x=SYSMIS)/x.
+
+DEBUG EVALUATE (x=5) VECTOR/v(1).
+DEBUG EVALUATE (x=5 MISSING) VECTOR/v(1).
+DEBUG EVALUATE (x=5 MISSING) VECTOR/v(1) + 1.
+DEBUG EVALUATE (x=SYSMIS) VECTOR/v(1).
+
+DEBUG EVALUATE (x=5)/VALUE(x).
+DEBUG EVALUATE (x=5 MISSING)/VALUE(x).
+DEBUG EVALUATE (x=SYSMIS)/VALUE(x).
+
+DEBUG EVALUATE (x=5) VECTOR/VALUE(v(1)).
+DEBUG EVALUATE (x=5 MISSING) VECTOR/VALUE(v(1)).
+DEBUG EVALUATE (x=SYSMIS) VECTOR/VALUE(v(1)).
+
+DEBUG EVALUATE (x=5)/MISSING(x).
+DEBUG EVALUATE (x=5 MISSING)/MISSING(x).
+DEBUG EVALUATE (x=SYSMIS)/MISSING(x).
+
+DEBUG EVALUATE (x=5)/SYSMIS(x).
+DEBUG EVALUATE (x=5 MISSING)/SYSMIS(x).
+DEBUG EVALUATE (x=SYSMIS)/SYSMIS(x).
+
+DEBUG EVALUATE /NMISS($sysmis).
+DEBUG EVALUATE /NMISS(0).
+DEBUG EVALUATE /NMISS($sysmis, $sysmis, $sysmis).
+DEBUG EVALUATE /NMISS(1, 2, 3, 4).
+DEBUG EVALUATE /NMISS(1, $sysmis, $sysmis, 2, 2, $sysmis, $sysmis, 3, 4).
+DEBUG EVALUATE (a=1 MISSING) (b=2) (c=3 MISSING) (d=4)/NMISS(a, b, c, d).
+
+DEBUG EVALUATE /NVALID($sysmis).
+DEBUG EVALUATE /NVALID(0).
+DEBUG EVALUATE /NVALID($sysmis, $sysmis, $sysmis).
+DEBUG EVALUATE /NVALID(1, 2, 3, 4).
+DEBUG EVALUATE /NVALID(1, $sysmis, $sysmis, 2, 2, $sysmis, $sysmis, 3, 4).
+DEBUG EVALUATE (a=1 MISSING) (b=2) (c=3 MISSING) (d=4)/NVALID(a, b, c, d).
+
+DEBUG EVALUATE /SYSMIS(1).
+DEBUG EVALUATE /SYSMIS($SYSMIS).
+DEBUG EVALUATE /SYSMIS(1 + $SYSMIS).
+])
+
+for opt in OPT NOOPT; do
+    AS_BOX([$opt])
+    sed "s/opt/$opt/" < evaluate-base.sps > evaluate.sps
+    AT_CHECK([pspp --testing-mode evaluate.sps], [1], [dnl
+MISSING(10) => false
+
+MISSING($SYSMIS) => true
+
+MISSING(ASIN(1.01)) => true
+
+MISSING(ASIN(.5)) => false
+
+evaluate.sps:7.17-7.31: error: DEBUG EVALUATE: Type mismatch invoking
+MISSING(number) as MISSING(string).
+    7 | DEBUG EVALUATE /MISSING('    ').
+      |                 ^~~~~~~~~~~~~~~
+
+evaluate.sps:7.25-7.30: note: DEBUG EVALUATE: This argument has type 'string'
+but 'number' is required.
+    7 | DEBUG EVALUATE /MISSING('    ').
+      |                         ^~~~~~
+
+MISSING('    ') => error
+
+x => 5.00
+
+x => sysmis
+
+x + 1 => sysmis
+
+x => sysmis
+
+v(1) => 5.00
+
+v(1) => sysmis
+
+v(1) + 1 => sysmis
+
+v(1) => sysmis
+
+VALUE(x) => 5.00
+
+VALUE(x) => 5.00
+
+VALUE(x) => sysmis
+
+VALUE(v(1)) => 5.00
+
+VALUE(v(1)) => 5.00
+
+VALUE(v(1)) => sysmis
+
+MISSING(x) => false
+
+MISSING(x) => true
+
+MISSING(x) => true
+
+SYSMIS(x) => false
+
+SYSMIS(x) => false
+
+SYSMIS(x) => true
+
+NMISS($sysmis) => 1.00
+
+NMISS(0) => 0.00
+
+NMISS($sysmis, $sysmis, $sysmis) => 3.00
+
+NMISS(1, 2, 3, 4) => 0.00
+
+NMISS(1, $sysmis, $sysmis, 2, 2, $sysmis, $sysmis, 3, 4) => 4.00
+
+NMISS(a, b, c, d) => 2.00
+
+NVALID($sysmis) => 0.00
+
+NVALID(0) => 1.00
+
+NVALID($sysmis, $sysmis, $sysmis) => 0.00
+
+NVALID(1, 2, 3, 4) => 4.00
+
+NVALID(1, $sysmis, $sysmis, 2, 2, $sysmis, $sysmis, 3, 4) => 5.00
+
+NVALID(a, b, c, d) => 2.00
+
+SYSMIS(1) => false
+
+SYSMIS($SYSMIS) => true
+
+SYSMIS(1 + $SYSMIS) => true
+])
+done
+AT_CLEANUP
+
+AT_SETUP([expressions - ANY])
+AT_KEYWORDS([expression expressions evaluate])
+AT_DATA([evaluate-base.sps], [dnl
+SET EPOCH 1940.
+DEBUG EVALUATE SET opt.
+
+DEBUG EVALUATE /any(1, 1, 2, 3).
+DEBUG EVALUATE /any(1, $true, 2, 3).
+DEBUG EVALUATE /any(1, $false, 2, 3).
+DEBUG EVALUATE /any(2, 1, 2, 3).
+DEBUG EVALUATE /any(3, 1, 2, 3).
+DEBUG EVALUATE /any(5, 1, 2, 3).
+DEBUG EVALUATE /any(1, 1, 1, 1).
+DEBUG EVALUATE /any($sysmis, 1, 1, 1).
+DEBUG EVALUATE /any($sysmis, 1, $sysmis, 3).
+DEBUG EVALUATE /any(1, 1, $sysmis, $sysmis).
+DEBUG EVALUATE /any(1, $sysmis, $sysmis, $sysmis).
+DEBUG EVALUATE /any($sysmis, $sysmis, $sysmis, $sysmis).
+
+DEBUG EVALUATE /any(1).
+DEBUG EVALUATE /any('1', 2, 3, 4).
+DEBUG EVALUATE /any(1, '2', 3, 4).
+DEBUG EVALUATE /any(1, 2, '3', 4).
+DEBUG EVALUATE /any(1, 2, 3, '4').
+
+DEBUG EVALUATE /any('', 'a', '', 'c').
+DEBUG EVALUATE /any('a', 'a', 'b', 'c').
+DEBUG EVALUATE /any('b', 'a', 'b', 'c').
+DEBUG EVALUATE /any('c', 'a', 'b', 'c').
+DEBUG EVALUATE /any('e', 'a', 'b', 'c').
+DEBUG EVALUATE /any('a', 'a', 'a', 'a').
+DEBUG EVALUATE /any('', 'a', 'a', 'a').
+DEBUG EVALUATE /any('a', '', '', '').
+DEBUG EVALUATE /any('a').
+
+DEBUG EVALUATE /any('a', 'a  ', 'b', 'c').
+DEBUG EVALUATE /any('b   ', 'a', 'b', 'c').
+DEBUG EVALUATE /any('c   ', 'a', 'b', 'c     ').
+DEBUG EVALUATE /any(a10, 'b', 'c', 'd').
+DEBUG EVALUATE /any('a', b, 'c', 'd').
+DEBUG EVALUATE /any('a', 'b', c, 'd').
+DEBUG EVALUATE /any('a', 'b', 'c', d).
+])
+
+for opt in OPT NOOPT; do
+    AS_BOX([$opt])
+    sed "s/opt/$opt/" < evaluate-base.sps > evaluate.sps
+    AT_CHECK([pspp --testing-mode evaluate.sps], [1],
+[[any(1, 1, 2, 3) => true
+
+any(1, $true, 2, 3) => true
+
+any(1, $false, 2, 3) => false
+
+any(2, 1, 2, 3) => true
+
+any(3, 1, 2, 3) => true
+
+any(5, 1, 2, 3) => false
+
+any(1, 1, 1, 1) => true
+
+any($sysmis, 1, 1, 1) => sysmis
+
+any($sysmis, 1, $sysmis, 3) => sysmis
+
+any(1, 1, $sysmis, $sysmis) => true
+
+any(1, $sysmis, $sysmis, $sysmis) => sysmis
+
+any($sysmis, $sysmis, $sysmis, $sysmis) => sysmis
+
+evaluate.sps:17.17-17.22: error: DEBUG EVALUATE: Function invocation
+any(number) does not match any known function.  Candidates are:
+ANY(number, number[, number]...)
+ANY(string, string[, string]...).
+   17 | DEBUG EVALUATE /any(1).
+      |                 ^~~~~~
+
+any(1) => error
+
+evaluate.sps:18.17-18.33: error: DEBUG EVALUATE: Function invocation
+any(string, number, number, number) does not match any known function.
+Candidates are:
+ANY(number, number[, number]...)
+ANY(string, string[, string]...).
+   18 | DEBUG EVALUATE /any('1', 2, 3, 4).
+      |                 ^~~~~~~~~~~~~~~~~
+
+any('1', 2, 3, 4) => error
+
+evaluate.sps:19.17-19.33: error: DEBUG EVALUATE: Function invocation
+any(number, string, number, number) does not match any known function.
+Candidates are:
+ANY(number, number[, number]...)
+ANY(string, string[, string]...).
+   19 | DEBUG EVALUATE /any(1, '2', 3, 4).
+      |                 ^~~~~~~~~~~~~~~~~
+
+any(1, '2', 3, 4) => error
+
+evaluate.sps:20.17-20.33: error: DEBUG EVALUATE: Function invocation
+any(number, number, string, number) does not match any known function.
+Candidates are:
+ANY(number, number[, number]...)
+ANY(string, string[, string]...).
+   20 | DEBUG EVALUATE /any(1, 2, '3', 4).
+      |                 ^~~~~~~~~~~~~~~~~
+
+any(1, 2, '3', 4) => error
+
+evaluate.sps:21.17-21.33: error: DEBUG EVALUATE: Function invocation
+any(number, number, number, string) does not match any known function.
+Candidates are:
+ANY(number, number[, number]...)
+ANY(string, string[, string]...).
+   21 | DEBUG EVALUATE /any(1, 2, 3, '4').
+      |                 ^~~~~~~~~~~~~~~~~
+
+any(1, 2, 3, '4') => error
+
+any('', 'a', '', 'c') => true
+
+any('a', 'a', 'b', 'c') => true
+
+any('b', 'a', 'b', 'c') => true
+
+any('c', 'a', 'b', 'c') => true
+
+any('e', 'a', 'b', 'c') => false
+
+any('a', 'a', 'a', 'a') => true
+
+any('', 'a', 'a', 'a') => false
+
+any('a', '', '', '') => false
+
+evaluate.sps:31.17-31.24: error: DEBUG EVALUATE: Function invocation
+any(string) does not match any known function.  Candidates are:
+ANY(number, number[, number]...)
+ANY(string, string[, string]...).
+   31 | DEBUG EVALUATE /any('a').
+      |                 ^~~~~~~~
+
+any('a') => error
+
+any('a', 'a  ', 'b', 'c') => true
+
+any('b   ', 'a', 'b', 'c') => true
+
+any('c   ', 'a', 'b', 'c     ') => true
+
+evaluate.sps:36.17-36.39: error: DEBUG EVALUATE: Function invocation
+any(format, string, string, string) does not match any known function.
+Candidates are:
+ANY(number, number[, number]...)
+ANY(string, string[, string]...).
+   36 | DEBUG EVALUATE /any(a10, 'b', 'c', 'd').
+      |                 ^~~~~~~~~~~~~~~~~~~~~~~
+
+any(a10, 'b', 'c', 'd') => error
+
+evaluate.sps:37: error: DEBUG EVALUATE: Unknown identifier b.
+
+any('a', b, 'c', 'd') => error
+
+evaluate.sps:38: error: DEBUG EVALUATE: Unknown identifier c.
+
+any('a', 'b', c, 'd') => error
+
+evaluate.sps:39: error: DEBUG EVALUATE: Unknown identifier d.
+
+any('a', 'b', 'c', d) => error
+]])
+done
+AT_CLEANUP
+
+AT_SETUP([expressions - RANGE])
+AT_KEYWORDS([expression expressions evaluate])
+AT_DATA([evaluate-base.sps], [dnl
+SET EPOCH 1940.
+DEBUG EVALUATE SET opt.
+
+DEBUG EVALUATE /range(5, 1, 10).
+DEBUG EVALUATE /range(1, 1, 10).
+DEBUG EVALUATE /range(10, 1, 10).
+DEBUG EVALUATE /range(-1, 1, 10).
+DEBUG EVALUATE /range(12, 1, 10).
+
+DEBUG EVALUATE /range(5, $sysmis, 6, 1, 10).
+DEBUG EVALUATE /range(5, $sysmis, $sysmis, 1, 10).
+DEBUG EVALUATE /range(1, 1, 10, 6, $sysmis).
+DEBUG EVALUATE /range(1, 1, 10, $sysmis, $sysmis).
+
+DEBUG EVALUATE /range(5, 5, 10, 3, 7).
+DEBUG EVALUATE /range(5, 10, 5, 3, 7).
+DEBUG EVALUATE /range(5, 3, 7, 5, 10).
+DEBUG EVALUATE /range(5, 3, 7, 10, 5).
+
+DEBUG EVALUATE /range($sysmis, 1, 10).
+DEBUG EVALUATE /range(5, 1, $sysmis).
+DEBUG EVALUATE /range(5, $sysmis, 10).
+DEBUG EVALUATE /range(1, 1, $sysmis).
+DEBUG EVALUATE /range(10, $sysmis, 10).
+DEBUG EVALUATE /range(5, 10, 5).
+DEBUG EVALUATE /range($sysmis, $sysmis, 10).
+DEBUG EVALUATE /range($sysmis, 1, $sysmis).
+DEBUG EVALUATE /range($sysmis, $sysmis, $sysmis).
+
+DEBUG EVALUATE /range(0, 1, 8, 10, 18).
+DEBUG EVALUATE /range(1, 1, 8, 10, 18).
+DEBUG EVALUATE /range(6, 1, 8, 10, 18).
+DEBUG EVALUATE /range(8, 1, 8, 10, 18).
+DEBUG EVALUATE /range(9, 1, 8, 10, 18).
+DEBUG EVALUATE /range(10, 1, 8, 10, 18).
+DEBUG EVALUATE /range(13, 1, 8, 10, 18).
+DEBUG EVALUATE /range(16, 1, 8, 10, 18).
+DEBUG EVALUATE /range(18, 1, 8, 10, 18).
+DEBUG EVALUATE /range(20, 1, 8, 10, 18).
+DEBUG EVALUATE /range(1).
+DEBUG EVALUATE /range(1, 2).
+DEBUG EVALUATE /range(1, 2, 3, 4).
+DEBUG EVALUATE /range(1, 2, 3, 4, 5, 6).
+DEBUG EVALUATE /range('1', 2, 3).
+DEBUG EVALUATE /range(1, '2', 3).
+DEBUG EVALUATE /range(1, 2, '3').
+
+DEBUG EVALUATE /range('123', '111', '888').
+DEBUG EVALUATE /range('111', '111', '888').
+DEBUG EVALUATE /range('888', '111', '888').
+DEBUG EVALUATE /range('110', '111', '888').
+DEBUG EVALUATE /range('889', '111', '888').
+DEBUG EVALUATE /range('000', '111', '888').
+DEBUG EVALUATE /range('999', '111', '888').
+
+DEBUG EVALUATE /range('123   ', '111', '888').
+DEBUG EVALUATE /range('123', '111   ', '888').
+DEBUG EVALUATE /range('123', '111', '888   ').
+DEBUG EVALUATE /range('123', '111    ', '888   ').
+
+DEBUG EVALUATE /range('00', '01', '08', '10', '18').
+DEBUG EVALUATE /range('01', '01', '08', '10', '18').
+DEBUG EVALUATE /range('06', '01', '08', '10', '18').
+DEBUG EVALUATE /range('08', '01', '08', '10', '18').
+DEBUG EVALUATE /range('09', '01', '08', '10', '18').
+DEBUG EVALUATE /range('10', '01', '08', '10', '18').
+DEBUG EVALUATE /range('15', '01', '08', '10', '18').
+DEBUG EVALUATE /range('18', '01', '08', '10', '18').
+DEBUG EVALUATE /range('19', '01', '08', '10', '18').
+
+DEBUG EVALUATE /range('07', '01', '08', '18', '10').
+DEBUG EVALUATE /range('12', '08', '01', '10', '18').
+
+DEBUG EVALUATE /range('1').
+DEBUG EVALUATE /range('1', '2').
+DEBUG EVALUATE /range('1', '2', '3', '4').
+DEBUG EVALUATE /range('1', '2', '3', '4', '5', '6').
+DEBUG EVALUATE /range(1, '2', '3').
+DEBUG EVALUATE /range('1', 2, '3').
+DEBUG EVALUATE /range('1', '2', 3).
+])
+
+for opt in OPT NOOPT; do
+    AS_BOX([$opt])
+    sed "s/opt/$opt/" < evaluate-base.sps > evaluate.sps
+    AT_CHECK([pspp --testing-mode evaluate.sps], [1],
+[[range(5, 1, 10) => true
+
+range(1, 1, 10) => true
+
+range(10, 1, 10) => true
+
+range(-1, 1, 10) => false
+
+range(12, 1, 10) => false
+
+range(5, $sysmis, 6, 1, 10) => true
+
+range(5, $sysmis, $sysmis, 1, 10) => true
+
+range(1, 1, 10, 6, $sysmis) => true
+
+range(1, 1, 10, $sysmis, $sysmis) => true
+
+range(5, 5, 10, 3, 7) => true
+
+range(5, 10, 5, 3, 7) => sysmis
+
+range(5, 3, 7, 5, 10) => true
+
+range(5, 3, 7, 10, 5) => sysmis
+
+range($sysmis, 1, 10) => sysmis
+
+range(5, 1, $sysmis) => sysmis
+
+range(5, $sysmis, 10) => sysmis
+
+range(1, 1, $sysmis) => sysmis
+
+range(10, $sysmis, 10) => sysmis
+
+range(5, 10, 5) => sysmis
+
+range($sysmis, $sysmis, 10) => sysmis
+
+range($sysmis, 1, $sysmis) => sysmis
+
+range($sysmis, $sysmis, $sysmis) => sysmis
+
+range(0, 1, 8, 10, 18) => false
+
+range(1, 1, 8, 10, 18) => true
+
+range(6, 1, 8, 10, 18) => true
+
+range(8, 1, 8, 10, 18) => true
+
+range(9, 1, 8, 10, 18) => false
+
+range(10, 1, 8, 10, 18) => true
+
+range(13, 1, 8, 10, 18) => true
+
+range(16, 1, 8, 10, 18) => true
+
+range(18, 1, 8, 10, 18) => true
+
+range(20, 1, 8, 10, 18) => false
+
+evaluate.sps:40.17-40.24: error: DEBUG EVALUATE: Function invocation
+range(number) does not match any known function.  Candidates are:
+RANGE(number, number, number[, number, number]...)
+RANGE(string, string, string[, string, string]...).
+   40 | DEBUG EVALUATE /range(1).
+      |                 ^~~~~~~~
+
+range(1) => error
+
+evaluate.sps:41.17-41.27: error: DEBUG EVALUATE: RANGE(number, number, number[,
+number, number]...) must have an odd number of arguments.
+   41 | DEBUG EVALUATE /range(1, 2).
+      |                 ^~~~~~~~~~~
+
+range(1, 2) => error
+
+evaluate.sps:42.17-42.33: error: DEBUG EVALUATE: RANGE(number, number, number[,
+number, number]...) must have an odd number of arguments.
+   42 | DEBUG EVALUATE /range(1, 2, 3, 4).
+      |                 ^~~~~~~~~~~~~~~~~
+
+range(1, 2, 3, 4) => error
+
+evaluate.sps:43.17-43.39: error: DEBUG EVALUATE: RANGE(number, number, number[,
+number, number]...) must have an odd number of arguments.
+   43 | DEBUG EVALUATE /range(1, 2, 3, 4, 5, 6).
+      |                 ^~~~~~~~~~~~~~~~~~~~~~~
+
+range(1, 2, 3, 4, 5, 6) => error
+
+evaluate.sps:44.17-44.32: error: DEBUG EVALUATE: Function invocation
+range(string, number, number) does not match any known function.  Candidates
+are:
+RANGE(number, number, number[, number, number]...)
+RANGE(string, string, string[, string, string]...).
+   44 | DEBUG EVALUATE /range('1', 2, 3).
+      |                 ^~~~~~~~~~~~~~~~
+
+range('1', 2, 3) => error
+
+evaluate.sps:45.17-45.32: error: DEBUG EVALUATE: Function invocation
+range(number, string, number) does not match any known function.  Candidates
+are:
+RANGE(number, number, number[, number, number]...)
+RANGE(string, string, string[, string, string]...).
+   45 | DEBUG EVALUATE /range(1, '2', 3).
+      |                 ^~~~~~~~~~~~~~~~
+
+range(1, '2', 3) => error
+
+evaluate.sps:46.17-46.32: error: DEBUG EVALUATE: Function invocation
+range(number, number, string) does not match any known function.  Candidates
+are:
+RANGE(number, number, number[, number, number]...)
+RANGE(string, string, string[, string, string]...).
+   46 | DEBUG EVALUATE /range(1, 2, '3').
+      |                 ^~~~~~~~~~~~~~~~
+
+range(1, 2, '3') => error
+
+range('123', '111', '888') => true
+
+range('111', '111', '888') => true
+
+range('888', '111', '888') => true
+
+range('110', '111', '888') => false
+
+range('889', '111', '888') => false
+
+range('000', '111', '888') => false
+
+range('999', '111', '888') => false
+
+range('123   ', '111', '888') => true
+
+range('123', '111   ', '888') => true
+
+range('123', '111', '888   ') => true
+
+range('123', '111    ', '888   ') => true
+
+range('00', '01', '08', '10', '18') => false
+
+range('01', '01', '08', '10', '18') => true
+
+range('06', '01', '08', '10', '18') => true
+
+range('08', '01', '08', '10', '18') => true
+
+range('09', '01', '08', '10', '18') => false
+
+range('10', '01', '08', '10', '18') => true
+
+range('15', '01', '08', '10', '18') => true
+
+range('18', '01', '08', '10', '18') => true
+
+range('19', '01', '08', '10', '18') => false
+
+range('07', '01', '08', '18', '10') => sysmis
+
+range('12', '08', '01', '10', '18') => sysmis
+
+evaluate.sps:74.17-74.26: error: DEBUG EVALUATE: Function invocation
+range(string) does not match any known function.  Candidates are:
+RANGE(number, number, number[, number, number]...)
+RANGE(string, string, string[, string, string]...).
+   74 | DEBUG EVALUATE /range('1').
+      |                 ^~~~~~~~~~
+
+range('1') => error
+
+evaluate.sps:75.17-75.31: error: DEBUG EVALUATE: RANGE(string, string, string[,
+string, string]...) must have an odd number of arguments.
+   75 | DEBUG EVALUATE /range('1', '2').
+      |                 ^~~~~~~~~~~~~~~
+
+range('1', '2') => error
+
+evaluate.sps:76.17-76.41: error: DEBUG EVALUATE: RANGE(string, string, string[,
+string, string]...) must have an odd number of arguments.
+   76 | DEBUG EVALUATE /range('1', '2', '3', '4').
+      |                 ^~~~~~~~~~~~~~~~~~~~~~~~~
+
+range('1', '2', '3', '4') => error
+
+evaluate.sps:77.17-77.51: error: DEBUG EVALUATE: RANGE(string, string, string[,
+string, string]...) must have an odd number of arguments.
+   77 | DEBUG EVALUATE /range('1', '2', '3', '4', '5', '6').
+      |                 ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+range('1', '2', '3', '4', '5', '6') => error
+
+evaluate.sps:78.17-78.34: error: DEBUG EVALUATE: Function invocation
+range(number, string, string) does not match any known function.  Candidates
+are:
+RANGE(number, number, number[, number, number]...)
+RANGE(string, string, string[, string, string]...).
+   78 | DEBUG EVALUATE /range(1, '2', '3').
+      |                 ^~~~~~~~~~~~~~~~~~
+
+range(1, '2', '3') => error
+
+evaluate.sps:79.17-79.34: error: DEBUG EVALUATE: Function invocation
+range(string, number, string) does not match any known function.  Candidates
+are:
+RANGE(number, number, number[, number, number]...)
+RANGE(string, string, string[, string, string]...).
+   79 | DEBUG EVALUATE /range('1', 2, '3').
+      |                 ^~~~~~~~~~~~~~~~~~
+
+range('1', 2, '3') => error
+
+evaluate.sps:80.17-80.34: error: DEBUG EVALUATE: Function invocation
+range(string, string, number) does not match any known function.  Candidates
+are:
+RANGE(number, number, number[, number, number]...)
+RANGE(string, string, string[, string, string]...).
+   80 | DEBUG EVALUATE /range('1', '2', 3).
+      |                 ^~~~~~~~~~~~~~~~~~
+
+range('1', '2', 3) => error
+]])
+done
+AT_CLEANUP
+
+AT_SETUP([expressions - MAX MIN])
+AT_KEYWORDS([expression expressions evaluate])
+AT_DATA([evaluate-base.sps], [dnl
+SET EPOCH 1940.
+DEBUG EVALUATE SET opt.
+DEBUG EVALUATE /max(1, 2, 3, 4, 5).
+DEBUG EVALUATE /max(1, $sysmis, 2, 3, $sysmis, 4, 5).
+DEBUG EVALUATE /max(1, 2).
+DEBUG EVALUATE /max().
+DEBUG EVALUATE /max(1).
+DEBUG EVALUATE /max(1, $sysmis).
+DEBUG EVALUATE /max(1, 2, 3, $sysmis).
+DEBUG EVALUATE /max.4(1, 2, 3, $sysmis).
+DEBUG EVALUATE /max.4(1, 2, 3).
+
+DEBUG EVALUATE /max("2", "3", "5", "1", "4").
+DEBUG EVALUATE /max("1", "2").
+DEBUG EVALUATE /max("1").
+
+DEBUG EVALUATE /min(1, 2, 3, 4, 5).
+DEBUG EVALUATE /min(1, $sysmis, 2, 3, $sysmis, 4, 5).
+DEBUG EVALUATE /min(1, 2).
+DEBUG EVALUATE /min().
+DEBUG EVALUATE /min(1).
+DEBUG EVALUATE /min(1, $sysmis).
+DEBUG EVALUATE /min(1, 2, 3, $sysmis).
+DEBUG EVALUATE /min.4(1, 2, 3, $sysmis).
+DEBUG EVALUATE /min.4(1, 2, 3).
+
+DEBUG EVALUATE /min("2", "3", "5", "1", "4").
+DEBUG EVALUATE /min("1", "2").
+DEBUG EVALUATE /min("1").
+])
+
+for opt in OPT NOOPT; do
+    AS_BOX([$opt])
+    sed "s/opt/$opt/" < evaluate-base.sps > evaluate.sps
+    AT_CHECK([pspp --testing-mode evaluate.sps], [1],
+[[max(1, 2, 3, 4, 5) => 5.00
+
+max(1, $sysmis, 2, 3, $sysmis, 4, 5) => 5.00
+
+max(1, 2) => 2.00
+
+evaluate.sps:6.17-6.20: error: DEBUG EVALUATE: Function invocation max() does
+not match any known function.  Candidates are:
+MAX(number[, number]...)
+MAX(string[, string]...).
+    6 | DEBUG EVALUATE /max().
+      |                 ^~~~
+
+max() => error
+
+max(1) => 1.00
+
+max(1, $sysmis) => 1.00
+
+max(1, 2, 3, $sysmis) => 3.00
+
+max.4(1, 2, 3, $sysmis) => sysmis
+
+evaluate.sps:11.17-11.30: error: DEBUG EVALUATE: For MAX(number[, number]...)
+with 3 arguments, at most 3 (not 4) may be required to be valid.
+   11 | DEBUG EVALUATE /max.4(1, 2, 3).
+      |                 ^~~~~~~~~~~~~~
+
+max.4(1, 2, 3) => error
+
+max("2", "3", "5", "1", "4") => "5"
+
+max("1", "2") => "2"
+
+max("1") => "1"
+
+min(1, 2, 3, 4, 5) => 1.00
+
+min(1, $sysmis, 2, 3, $sysmis, 4, 5) => 1.00
+
+min(1, 2) => 1.00
+
+evaluate.sps:20.17-20.20: error: DEBUG EVALUATE: Function invocation min() does
+not match any known function.  Candidates are:
+MIN(number[, number]...)
+MIN(string[, string]...).
+   20 | DEBUG EVALUATE /min().
+      |                 ^~~~
+
+min() => error
+
+min(1) => 1.00
+
+min(1, $sysmis) => 1.00
+
+min(1, 2, 3, $sysmis) => 1.00
+
+min.4(1, 2, 3, $sysmis) => sysmis
+
+evaluate.sps:25.17-25.30: error: DEBUG EVALUATE: For MIN(number[, number]...)
+with 3 arguments, at most 3 (not 4) may be required to be valid.
+   25 | DEBUG EVALUATE /min.4(1, 2, 3).
+      |                 ^~~~~~~~~~~~~~
+
+min.4(1, 2, 3) => error
+
+min("2", "3", "5", "1", "4") => "1"
+
+min("1", "2") => "1"
+
+min("1") => "1"
+]])
+done
+AT_CLEANUP
+
+AT_SETUP([expressions - CFVAR MEAN MEDIAN])
+AT_KEYWORDS([expression expressions evaluate])
+AT_DATA([evaluate-base.sps], [dnl
+SET EPOCH 1940.
+DEBUG EVALUATE SET opt.
+
+DEBUG EVALUATE /cfvar(1, 2, 3, 4, 5).
+DEBUG EVALUATE /cfvar(1, $sysmis, 2, 3, $sysmis, 4, 5).
+DEBUG EVALUATE /cfvar(1, 2).
+DEBUG EVALUATE /cfvar(1).
+DEBUG EVALUATE /cfvar(1, $sysmis).
+DEBUG EVALUATE /cfvar(1, 2, 3, $sysmis).
+DEBUG EVALUATE /cfvar.4(1, 2, 3, $sysmis).
+DEBUG EVALUATE /cfvar.4(1, 2, 3).
+DEBUG EVALUATE /cfvar('x').
+DEBUG EVALUATE /cfvar('x', 1, 2, 3).
+
+DEBUG EVALUATE /mean(1, 2, 3, 4, 5).
+DEBUG EVALUATE /mean(1, $sysmis, 2, 3, $sysmis, 4, 5).
+DEBUG EVALUATE /mean(1, 2).
+DEBUG EVALUATE /mean().
+DEBUG EVALUATE /mean(1).
+DEBUG EVALUATE /mean(1, $sysmis).
+DEBUG EVALUATE /mean(1, 2, 3, $sysmis).
+DEBUG EVALUATE /mean.4(1, 2, 3, $sysmis).
+DEBUG EVALUATE /mean.4(1, 2, 3).
+
+DEBUG EVALUATE /median(1, 2, 3, 4, 5).
+DEBUG EVALUATE /median(2, 3, 4, 5, 1).
+DEBUG EVALUATE /median(2, 3, 4, 1, 5).
+DEBUG EVALUATE /median(2, 1, 4, 5, 3).
+DEBUG EVALUATE /median(1, 2, 3, 4).
+DEBUG EVALUATE /median(2, 3, 1, 4).
+DEBUG EVALUATE /median(2, 3, 4, 1).
+DEBUG EVALUATE /median(2, 1, 4, 3).
+DEBUG EVALUATE /median(1, $sysmis, 3, 4, 5).
+DEBUG EVALUATE /median(2, 3, 4, 5, $sysmis, 1).
+DEBUG EVALUATE /median($sysmis, $sysmis, $sysmis, 2, 3, 4, 1, 5).
+DEBUG EVALUATE /median(1, 2, 3).
+DEBUG EVALUATE /median(1).
+DEBUG EVALUATE /median(1, 2).
+DEBUG EVALUATE /median(1, 2, $sysmis).
+DEBUG EVALUATE /median(1, $sysmis, $sysmis).
+DEBUG EVALUATE /median($sysmis, $sysmis, $sysmis).
+DEBUG EVALUATE /median.3(1, 2, $sysmis).
+DEBUG EVALUATE /median.2(1, $sysmis).
+])
+
+for opt in OPT NOOPT; do
+    AS_BOX([$opt])
+    sed "s/opt/$opt/" < evaluate-base.sps > evaluate.sps
+    AT_CHECK([pspp --testing-mode evaluate.sps], [1],
+[[cfvar(1, 2, 3, 4, 5) => 0.53
+
+cfvar(1, $sysmis, 2, 3, $sysmis, 4, 5) => 0.53
+
+cfvar(1, 2) => 0.47
+
+evaluate.sps:7.17-7.24: error: DEBUG EVALUATE: Type mismatch invoking
+CFVAR(number, number[, number]...) as cfvar(number).
+    7 | DEBUG EVALUATE /cfvar(1).
+      |                 ^~~~~~~~
+
+cfvar(1) => error
+
+cfvar(1, $sysmis) => sysmis
+
+cfvar(1, 2, 3, $sysmis) => 0.50
+
+cfvar.4(1, 2, 3, $sysmis) => sysmis
+
+evaluate.sps:11.17-11.32: error: DEBUG EVALUATE: For CFVAR(number, number[,
+number]...) with 3 arguments, at most 3 (not 4) may be required to be valid.
+   11 | DEBUG EVALUATE /cfvar.4(1, 2, 3).
+      |                 ^~~~~~~~~~~~~~~~
+
+cfvar.4(1, 2, 3) => error
+
+evaluate.sps:12.17-12.26: error: DEBUG EVALUATE: Type mismatch invoking
+CFVAR(number, number[, number]...) as cfvar(string).
+   12 | DEBUG EVALUATE /cfvar('x').
+      |                 ^~~~~~~~~~
+
+evaluate.sps:12.23-12.25: note: DEBUG EVALUATE: This argument has type 'string'
+but 'number' is required.
+   12 | DEBUG EVALUATE /cfvar('x').
+      |                       ^~~
+
+cfvar('x') => error
+
+evaluate.sps:13.17-13.35: error: DEBUG EVALUATE: Type mismatch invoking
+CFVAR(number, number[, number]...) as cfvar(string, number, number, number).
+   13 | DEBUG EVALUATE /cfvar('x', 1, 2, 3).
+      |                 ^~~~~~~~~~~~~~~~~~~
+
+cfvar('x', 1, 2, 3) => error
+
+mean(1, 2, 3, 4, 5) => 3.00
+
+mean(1, $sysmis, 2, 3, $sysmis, 4, 5) => 3.00
+
+mean(1, 2) => 1.50
+
+evaluate.sps:18.17-18.21: error: DEBUG EVALUATE: Type mismatch invoking
+MEAN(number[, number]...) as mean().
+   18 | DEBUG EVALUATE /mean().
+      |                 ^~~~~
+
+mean() => error
+
+mean(1) => 1.00
+
+mean(1, $sysmis) => 1.00
+
+mean(1, 2, 3, $sysmis) => 2.00
+
+mean.4(1, 2, 3, $sysmis) => sysmis
+
+evaluate.sps:23.17-23.31: error: DEBUG EVALUATE: For MEAN(number[, number]...)
+with 3 arguments, at most 3 (not 4) may be required to be valid.
+   23 | DEBUG EVALUATE /mean.4(1, 2, 3).
+      |                 ^~~~~~~~~~~~~~~
+
+mean.4(1, 2, 3) => error
+
+median(1, 2, 3, 4, 5) => 3.00
+
+median(2, 3, 4, 5, 1) => 3.00
+
+median(2, 3, 4, 1, 5) => 3.00
+
+median(2, 1, 4, 5, 3) => 3.00
+
+median(1, 2, 3, 4) => 2.50
+
+median(2, 3, 1, 4) => 2.50
+
+median(2, 3, 4, 1) => 2.50
+
+median(2, 1, 4, 3) => 2.50
+
+median(1, $sysmis, 3, 4, 5) => 3.50
+
+median(2, 3, 4, 5, $sysmis, 1) => 3.00
+
+median($sysmis, $sysmis, $sysmis, 2, 3, 4, 1, 5) => 3.00
+
+median(1, 2, 3) => 2.00
+
+median(1) => 1.00
+
+median(1, 2) => 1.50
+
+median(1, 2, $sysmis) => 1.50
+
+median(1, $sysmis, $sysmis) => 1.00
+
+median($sysmis, $sysmis, $sysmis) => sysmis
+
+median.3(1, 2, $sysmis) => sysmis
+
+median.2(1, $sysmis) => sysmis
+]])
+done
+AT_CLEANUP
+
+AT_SETUP([expressions - SD SUM VARIANCE])
+AT_KEYWORDS([expression expressions evaluate])
+AT_DATA([evaluate-base.sps], [dnl
+SET EPOCH 1940.
+DEBUG EVALUATE SET opt.
+
+DEBUG EVALUATE /sd(1, 2, 3, 4, 5).
+DEBUG EVALUATE /sd(1, $sysmis, 2, 3, $sysmis, 4, 5).
+DEBUG EVALUATE /sd(1, 2).
+DEBUG EVALUATE /sd(1).
+DEBUG EVALUATE /sd(1, $sysmis).
+DEBUG EVALUATE /sd(1, 2, 3, $sysmis).
+DEBUG EVALUATE /sd.4(1, 2, 3, $sysmis).
+DEBUG EVALUATE /sd.4(1, 2, 3).
+DEBUG EVALUATE /sd('x').
+DEBUG EVALUATE /sd('x', 1, 2, 3).
+DEBUG EVALUATE (a=1)(b=2)(c=3)(d=4)(e=5)(f=6)(g=7)/sd(a TO c).
+
+DEBUG EVALUATE /sum(1, 2, 3, 4, 5).
+DEBUG EVALUATE /sum(1, $sysmis, 2, 3, $sysmis, 4, 5).
+DEBUG EVALUATE /sum(1, 2).
+DEBUG EVALUATE /sum().
+DEBUG EVALUATE /sum(1).
+DEBUG EVALUATE /sum(1, $sysmis).
+DEBUG EVALUATE /sum(1, 2, 3, $sysmis).
+DEBUG EVALUATE /sum.4(1, 2, 3, $sysmis).
+DEBUG EVALUATE /sum.4(1, 2, 3).
+DEBUG EVALUATE (a=1)(b=2)(c=3)(d=4)(e=5)(f=6)(g=7)/sum(b TO F).
+
+DEBUG EVALUATE /variance(1, 2, 3, 4, 5).
+DEBUG EVALUATE /variance(1, $sysmis, 2, 3, $sysmis, 4, 5).
+DEBUG EVALUATE /variance(1, 2).
+DEBUG EVALUATE /variance(1).
+DEBUG EVALUATE /variance(1, $sysmis).
+DEBUG EVALUATE /variance(1, 2, 3, $sysmis).
+DEBUG EVALUATE /variance.4(1, 2, 3, $sysmis).
+DEBUG EVALUATE /variance.4(1, 2, 3).
+DEBUG EVALUATE /variance('x').
+DEBUG EVALUATE /variance('x', 1, 2, 3).
+DEBUG EVALUATE (a=1)(b=2)(c=3)(d=4)(e=5)(f=6)(g=7)/variance(a TO e).
+])
+
+for opt in OPT NOOPT; do
+    AS_BOX([$opt])
+    sed "s/opt/$opt/" < evaluate-base.sps > evaluate.sps
+    AT_CHECK([pspp --testing-mode evaluate.sps], [1],
+[[sd(1, 2, 3, 4, 5) => 1.58
+
+sd(1, $sysmis, 2, 3, $sysmis, 4, 5) => 1.58
+
+sd(1, 2) => 0.71
+
+evaluate.sps:7.17-7.21: error: DEBUG EVALUATE: Type mismatch invoking
+SD(number, number[, number]...) as sd(number).
+    7 | DEBUG EVALUATE /sd(1).
+      |                 ^~~~~
+
+sd(1) => error
+
+sd(1, $sysmis) => sysmis
+
+sd(1, 2, 3, $sysmis) => 1.00
+
+sd.4(1, 2, 3, $sysmis) => sysmis
+
+evaluate.sps:11.17-11.29: error: DEBUG EVALUATE: For SD(number, number[,
+number]...) with 3 arguments, at most 3 (not 4) may be required to be valid.
+   11 | DEBUG EVALUATE /sd.4(1, 2, 3).
+      |                 ^~~~~~~~~~~~~
+
+sd.4(1, 2, 3) => error
+
+evaluate.sps:12.17-12.23: error: DEBUG EVALUATE: Type mismatch invoking
+SD(number, number[, number]...) as sd(string).
+   12 | DEBUG EVALUATE /sd('x').
+      |                 ^~~~~~~
+
+evaluate.sps:12.20-12.22: note: DEBUG EVALUATE: This argument has type 'string'
+but 'number' is required.
+   12 | DEBUG EVALUATE /sd('x').
+      |                    ^~~
+
+sd('x') => error
+
+evaluate.sps:13.17-13.32: error: DEBUG EVALUATE: Type mismatch invoking
+SD(number, number[, number]...) as sd(string, number, number, number).
+   13 | DEBUG EVALUATE /sd('x', 1, 2, 3).
+      |                 ^~~~~~~~~~~~~~~~
+
+sd('x', 1, 2, 3) => error
+
+sd(a TO c) => 1.00
+
+sum(1, 2, 3, 4, 5) => 15.00
+
+sum(1, $sysmis, 2, 3, $sysmis, 4, 5) => 15.00
+
+sum(1, 2) => 3.00
+
+evaluate.sps:19.17-19.20: error: DEBUG EVALUATE: Type mismatch invoking
+SUM(number[, number]...) as sum().
+   19 | DEBUG EVALUATE /sum().
+      |                 ^~~~
+
+sum() => error
+
+sum(1) => 1.00
+
+sum(1, $sysmis) => 1.00
+
+sum(1, 2, 3, $sysmis) => 6.00
+
+sum.4(1, 2, 3, $sysmis) => sysmis
+
+evaluate.sps:24.17-24.30: error: DEBUG EVALUATE: For SUM(number[, number]...)
+with 3 arguments, at most 3 (not 4) may be required to be valid.
+   24 | DEBUG EVALUATE /sum.4(1, 2, 3).
+      |                 ^~~~~~~~~~~~~~
+
+sum.4(1, 2, 3) => error
+
+sum(b TO F) => 20.00
+
+variance(1, 2, 3, 4, 5) => 2.50
+
+variance(1, $sysmis, 2, 3, $sysmis, 4, 5) => 2.50
+
+variance(1, 2) => 0.50
+
+evaluate.sps:30.17-30.27: error: DEBUG EVALUATE: Type mismatch invoking
+VARIANCE(number, number[, number]...) as variance(number).
+   30 | DEBUG EVALUATE /variance(1).
+      |                 ^~~~~~~~~~~
+
+variance(1) => error
+
+variance(1, $sysmis) => sysmis
+
+variance(1, 2, 3, $sysmis) => 1.00
+
+variance.4(1, 2, 3, $sysmis) => sysmis
+
+evaluate.sps:34.17-34.35: error: DEBUG EVALUATE: For VARIANCE(number, number[,
+number]...) with 3 arguments, at most 3 (not 4) may be required to be valid.
+   34 | DEBUG EVALUATE /variance.4(1, 2, 3).
+      |                 ^~~~~~~~~~~~~~~~~~~
+
+variance.4(1, 2, 3) => error
+
+evaluate.sps:35.17-35.29: error: DEBUG EVALUATE: Type mismatch invoking
+VARIANCE(number, number[, number]...) as variance(string).
+   35 | DEBUG EVALUATE /variance('x').
+      |                 ^~~~~~~~~~~~~
+
+evaluate.sps:35.26-35.28: note: DEBUG EVALUATE: This argument has type 'string'
+but 'number' is required.
+   35 | DEBUG EVALUATE /variance('x').
+      |                          ^~~
+
+variance('x') => error
+
+evaluate.sps:36.17-36.38: error: DEBUG EVALUATE: Type mismatch invoking
+VARIANCE(number, number[, number]...) as variance(string, number, number,
+number).
+   36 | DEBUG EVALUATE /variance('x', 1, 2, 3).
+      |                 ^~~~~~~~~~~~~~~~~~~~~~
+
+variance('x', 1, 2, 3) => error
+
+variance(a TO e) => 2.50
+]])
+done
+AT_CLEANUP
+
+AT_SETUP([expressions - CONCAT])
+AT_KEYWORDS([expression expressions evaluate])
+AT_DATA([evaluate-base.sps], [dnl
+SET EPOCH 1940.
+DEBUG EVALUATE SET opt.
+
+DEBUG EVALUATE /concat('').
+DEBUG EVALUATE /concat('a', 'b').
+DEBUG EVALUATE /concat('a', 'b', 'c', 'd', 'e', 'f', 'g', 'h').
+DEBUG EVALUATE /concat('abcdefgh', 'ijklmnopq').
+DEBUG EVALUATE /concat('a', 1).
+DEBUG EVALUATE /concat(1, 2).
+])
+
+for opt in OPT NOOPT; do
+    AS_BOX([$opt])
+    sed "s/opt/$opt/" < evaluate-base.sps > evaluate.sps
+    AT_CHECK([pspp --testing-mode evaluate.sps], [1],
+[[concat('') => ""
+
+concat('a', 'b') => "ab"
+
+concat('a', 'b', 'c', 'd', 'e', 'f', 'g', 'h') => "abcdefgh"
+
+concat('abcdefgh', 'ijklmnopq') => "abcdefghijklmnopq"
+
+evaluate.sps:8.17-8.30: error: DEBUG EVALUATE: Type mismatch invoking
+CONCAT(string[, string]...) as concat(string, number).
+    8 | DEBUG EVALUATE /concat('a', 1).
+      |                 ^~~~~~~~~~~~~~
+
+concat('a', 1) => error
+
+evaluate.sps:9.17-9.28: error: DEBUG EVALUATE: Type mismatch invoking
+CONCAT(string[, string]...) as concat(number, number).
+    9 | DEBUG EVALUATE /concat(1, 2).
+      |                 ^~~~~~~~~~~~
+
+concat(1, 2) => error
+]])
+done
+AT_CLEANUP
+
+AT_SETUP([expressions - INDEX])
+AT_KEYWORDS([expression expressions evaluate])
+AT_DATA([evaluate-base.sps], [dnl
+SET EPOCH 1940.
+DEBUG EVALUATE SET opt.
+
+DEBUG EVALUATE /index('abcbcde', 'bc').
+DEBUG EVALUATE /index('abcbcde', 'bcd').
+DEBUG EVALUATE /index('abcbcde', 'bcbc').
+DEBUG EVALUATE /index('abcdefgh', 'abc').
+DEBUG EVALUATE /index('abcdefgh', 'bcd').
+DEBUG EVALUATE /index('abcdefgh', 'cde').
+DEBUG EVALUATE /index('abcdefgh', 'def').
+DEBUG EVALUATE /index('abcdefgh', 'efg').
+DEBUG EVALUATE /index('abcdefgh', 'fgh').
+DEBUG EVALUATE /index('abcdefgh', 'fghi').
+DEBUG EVALUATE /index('abcdefgh', 'x').
+DEBUG EVALUATE /index('abcdefgh', 'abch').
+DEBUG EVALUATE /index('banana', 'na').
+DEBUG EVALUATE /index('banana', 'ana').
+DEBUG EVALUATE /index('', 'x').
+DEBUG EVALUATE /index('x', '').
+DEBUG EVALUATE /index('', '').
+DEBUG EVALUATE /index('abcdefgh', '').
+DEBUG EVALUATE /index('abcdefgh', 'alkjsfdjlskalkjfa').
+
+DEBUG EVALUATE /index('abcbcde', 'bc', 1).
+DEBUG EVALUATE /index('abcbcde', 'dc', 1).
+DEBUG EVALUATE /index('abcbcde', 'abc', 1).
+DEBUG EVALUATE /index('abcbcde', 'bc', 2).
+DEBUG EVALUATE /index('abcbcde', 'dc', 2).
+DEBUG EVALUATE /index('abcbcde', 'abc', 1).
+DEBUG EVALUATE /index('abcbcde', 'bccb', 2).
+DEBUG EVALUATE /index('abcbcde', 'bcbc', 2).
+DEBUG EVALUATE /index('abcbcde', 'bcbc', $sysmis).
+DEBUG EVALUATE /index('abcbcde', 'bcbc', 3).
+DEBUG EVALUATE /index('abcbcde', '', 1).
+DEBUG EVALUATE /index('', '', 1).
+])
+
+for opt in OPT NOOPT; do
+    AS_BOX([$opt])
+    sed "s/opt/$opt/" < evaluate-base.sps > evaluate.sps
+    AT_CHECK([pspp --testing-mode evaluate.sps], [1], [dnl
+index('abcbcde', 'bc') => 2.00
+
+index('abcbcde', 'bcd') => 4.00
+
+index('abcbcde', 'bcbc') => 2.00
+
+index('abcdefgh', 'abc') => 1.00
+
+index('abcdefgh', 'bcd') => 2.00
+
+index('abcdefgh', 'cde') => 3.00
+
+index('abcdefgh', 'def') => 4.00
+
+index('abcdefgh', 'efg') => 5.00
+
+index('abcdefgh', 'fgh') => 6.00
+
+index('abcdefgh', 'fghi') => 0.00
+
+index('abcdefgh', 'x') => 0.00
+
+index('abcdefgh', 'abch') => 0.00
+
+index('banana', 'na') => 3.00
+
+index('banana', 'ana') => 2.00
+
+index('', 'x') => 0.00
+
+index('x', '') => 1.00
+
+index('', '') => 1.00
+
+index('abcdefgh', '') => 1.00
+
+index('abcdefgh', 'alkjsfdjlskalkjfa') => 0.00
+
+index('abcbcde', 'bc', 1) => 2.00
+
+index('abcbcde', 'dc', 1) => 3.00
+
+index('abcbcde', 'abc', 1) => 1.00
+
+index('abcbcde', 'bc', 2) => 2.00
+
+index('abcbcde', 'dc', 2) => 0.00
+
+index('abcbcde', 'abc', 1) => 1.00
+
+index('abcbcde', 'bccb', 2) => 2.00
+
+index('abcbcde', 'bcbc', 2) => 2.00
+
+index('abcbcde', 'bcbc', $sysmis) => sysmis
+
+evaluate.sps:33.17-33.43: error: DEBUG EVALUATE: INDEX needle length argument
+must evenly divide the length of the needles argument.
+   33 | DEBUG EVALUATE /index('abcbcde', 'bcbc', 3).
+      |                 ^~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+evaluate.sps:33.34-33.39: note: DEBUG EVALUATE: The needles argument has length
+4.
+   33 | DEBUG EVALUATE /index('abcbcde', 'bcbc', 3).
+      |                                  ^~~~~~
+
+evaluate.sps:33.42: note: DEBUG EVALUATE: The needle length argument has value
+3.
+   33 | DEBUG EVALUATE /index('abcbcde', 'bcbc', 3).
+      |                                          ^
+
+index('abcbcde', 'bcbc', 3) => sysmis
+
+index('abcbcde', '', 1) => 0.00
+
+index('', '', 1) => 0.00
+])
+done
+AT_CLEANUP
+
+AT_SETUP([expressions - RINDEX])
+AT_KEYWORDS([expression expressions evaluate])
+AT_DATA([evaluate-base.sps], [dnl
+SET EPOCH 1940.
+DEBUG EVALUATE SET opt.
+
+DEBUG EVALUATE /rindex('abcbcde', 'bc').
+DEBUG EVALUATE /rindex('abcbcde', 'bcd').
+DEBUG EVALUATE /rindex('abcbcde', 'bcbc').
+DEBUG EVALUATE /rindex('abcdefgh', 'abc').
+DEBUG EVALUATE /rindex('abcdefgh', 'bcd').
+DEBUG EVALUATE /rindex('abcdefgh', 'cde').
+DEBUG EVALUATE /rindex('abcdefgh', 'def').
+DEBUG EVALUATE /rindex('abcdefgh', 'efg').
+DEBUG EVALUATE /rindex('abcdefgh', 'fgh').
+DEBUG EVALUATE /rindex('abcdefgh', 'fghi').
+DEBUG EVALUATE /rindex('abcdefgh', 'x').
+DEBUG EVALUATE /rindex('abcdefgh', 'abch').
+DEBUG EVALUATE /rindex('banana', 'na').
+DEBUG EVALUATE /rindex('banana', 'ana').
+DEBUG EVALUATE /rindex('', 'x').
+DEBUG EVALUATE /rindex('x', '').
+DEBUG EVALUATE /rindex('', '').
+DEBUG EVALUATE /rindex('abcdefgh', '').
+DEBUG EVALUATE /rindex('abcdefgh', 'alkjsfdjlskalkjfa').
+
+DEBUG EVALUATE /rindex('abcbcde', 'bc', 1).
+DEBUG EVALUATE /rindex('abcbcde', 'dc', 1).
+DEBUG EVALUATE /rindex('abcbcde', 'abc', 1).
+DEBUG EVALUATE /rindex('abcbcde', 'bc', 2).
+DEBUG EVALUATE /rindex('abcbcde', 'dc', 2).
+DEBUG EVALUATE /rindex('abcbcde', 'abc', 1).
+DEBUG EVALUATE /rindex('abcbcde', 'bccb', 2).
+DEBUG EVALUATE /rindex('abcbcde', 'bcbc', 2).
+DEBUG EVALUATE /rindex('abcbcde', 'bcbc', 0).
+DEBUG EVALUATE /rindex('abcbcde', 'bcbc', $sysmis).
+DEBUG EVALUATE /rindex('abcbcde', 'bcbcg', 2).
+DEBUG EVALUATE /rindex('abcbcde', 'bcbcg', $sysmis).
+DEBUG EVALUATE /rindex('abcbcde', 'bcbcg', 'x').
+DEBUG EVALUATE /rindex(1, 'bcdfkjl', 2).
+DEBUG EVALUATE /rindex('aksj', 2, 2).
+DEBUG EVALUATE /rindex(1, 2, 3).
+DEBUG EVALUATE /rindex(1, 2, '3').
+DEBUG EVALUATE /rindex('abcbcde', '', 1).
+DEBUG EVALUATE /rindex('', '', 1).
+])
+
+for opt in OPT NOOPT; do
+    AS_BOX([$opt])
+    sed "s/opt/$opt/" < evaluate-base.sps > evaluate.sps
+    AT_CHECK([pspp --testing-mode evaluate.sps], [1], [dnl
+rindex('abcbcde', 'bc') => 4.00
+
+rindex('abcbcde', 'bcd') => 4.00
+
+rindex('abcbcde', 'bcbc') => 2.00
+
+rindex('abcdefgh', 'abc') => 1.00
+
+rindex('abcdefgh', 'bcd') => 2.00
+
+rindex('abcdefgh', 'cde') => 3.00
+
+rindex('abcdefgh', 'def') => 4.00
+
+rindex('abcdefgh', 'efg') => 5.00
+
+rindex('abcdefgh', 'fgh') => 6.00
+
+rindex('abcdefgh', 'fghi') => 0.00
+
+rindex('abcdefgh', 'x') => 0.00
+
+rindex('abcdefgh', 'abch') => 0.00
+
+rindex('banana', 'na') => 5.00
+
+rindex('banana', 'ana') => 4.00
+
+rindex('', 'x') => 0.00
+
+rindex('x', '') => 2.00
+
+rindex('', '') => 1.00
+
+rindex('abcdefgh', '') => 9.00
+
+rindex('abcdefgh', 'alkjsfdjlskalkjfa') => 0.00
+
+rindex('abcbcde', 'bc', 1) => 5.00
+
+rindex('abcbcde', 'dc', 1) => 6.00
+
+rindex('abcbcde', 'abc', 1) => 5.00
+
+rindex('abcbcde', 'bc', 2) => 4.00
+
+rindex('abcbcde', 'dc', 2) => 0.00
+
+rindex('abcbcde', 'abc', 1) => 5.00
+
+rindex('abcbcde', 'bccb', 2) => 4.00
+
+rindex('abcbcde', 'bcbc', 2) => 4.00
+
+evaluate.sps:32.17-32.44: error: DEBUG EVALUATE: RINDEX needle length argument
+must evenly divide the length of the needles argument.
+   32 | DEBUG EVALUATE /rindex('abcbcde', 'bcbc', 0).
+      |                 ^~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+evaluate.sps:32.35-32.40: note: DEBUG EVALUATE: The needles argument has length
+4.
+   32 | DEBUG EVALUATE /rindex('abcbcde', 'bcbc', 0).
+      |                                   ^~~~~~
+
+evaluate.sps:32.43: note: DEBUG EVALUATE: The needle length argument has value
+0.
+   32 | DEBUG EVALUATE /rindex('abcbcde', 'bcbc', 0).
+      |                                           ^
+
+rindex('abcbcde', 'bcbc', 0) => sysmis
+
+rindex('abcbcde', 'bcbc', $sysmis) => sysmis
+
+evaluate.sps:34.17-34.45: error: DEBUG EVALUATE: RINDEX needle length argument
+must evenly divide the length of the needles argument.
+   34 | DEBUG EVALUATE /rindex('abcbcde', 'bcbcg', 2).
+      |                 ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+evaluate.sps:34.35-34.41: note: DEBUG EVALUATE: The needles argument has length
+5.
+   34 | DEBUG EVALUATE /rindex('abcbcde', 'bcbcg', 2).
+      |                                   ^~~~~~~
+
+evaluate.sps:34.44: note: DEBUG EVALUATE: The needle length argument has value
+2.
+   34 | DEBUG EVALUATE /rindex('abcbcde', 'bcbcg', 2).
+      |                                            ^
+
+rindex('abcbcde', 'bcbcg', 2) => sysmis
+
+rindex('abcbcde', 'bcbcg', $sysmis) => sysmis
+
+evaluate.sps:36.17-36.47: error: DEBUG EVALUATE: Function invocation
+rindex(string, string, string) does not match any known function.  Candidates
+are:
+RINDEX(string, string)
+RINDEX(string, string, integer).
+   36 | DEBUG EVALUATE /rindex('abcbcde', 'bcbcg', 'x').
+      |                 ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+rindex('abcbcde', 'bcbcg', 'x') => error
+
+evaluate.sps:37.17-37.39: error: DEBUG EVALUATE: Function invocation
+rindex(number, string, number) does not match any known function.  Candidates
+are:
+RINDEX(string, string)
+RINDEX(string, string, integer).
+   37 | DEBUG EVALUATE /rindex(1, 'bcdfkjl', 2).
+      |                 ^~~~~~~~~~~~~~~~~~~~~~~
+
+rindex(1, 'bcdfkjl', 2) => error
+
+evaluate.sps:38.17-38.36: error: DEBUG EVALUATE: Function invocation
+rindex(string, number, number) does not match any known function.  Candidates
+are:
+RINDEX(string, string)
+RINDEX(string, string, integer).
+   38 | DEBUG EVALUATE /rindex('aksj', 2, 2).
+      |                 ^~~~~~~~~~~~~~~~~~~~
+
+rindex('aksj', 2, 2) => error
+
+evaluate.sps:39.17-39.31: error: DEBUG EVALUATE: Function invocation
+rindex(number, number, number) does not match any known function.  Candidates
+are:
+RINDEX(string, string)
+RINDEX(string, string, integer).
+   39 | DEBUG EVALUATE /rindex(1, 2, 3).
+      |                 ^~~~~~~~~~~~~~~
+
+rindex(1, 2, 3) => error
+
+evaluate.sps:40.17-40.33: error: DEBUG EVALUATE: Function invocation
+rindex(number, number, string) does not match any known function.  Candidates
+are:
+RINDEX(string, string)
+RINDEX(string, string, integer).
+   40 | DEBUG EVALUATE /rindex(1, 2, '3').
+      |                 ^~~~~~~~~~~~~~~~~
+
+rindex(1, 2, '3') => error
+
+rindex('abcbcde', '', 1) => 0.00
+
+rindex('', '', 1) => 0.00
+])
+done
+AT_CLEANUP
+
+AT_SETUP([expressions - LENGTH])
+AT_KEYWORDS([expression expressions evaluate])
+AT_DATA([evaluate-base.sps], [dnl
+SET EPOCH 1940.
+DEBUG EVALUATE SET opt.
+
+DEBUG EVALUATE /length('').
+DEBUG EVALUATE /length('a').
+DEBUG EVALUATE /length('xy').
+DEBUG EVALUATE /length('adsf    ').
+DEBUG EVALUATE /length('abcdefghijkl').
+DEBUG EVALUATE /length(0).
+DEBUG EVALUATE /length($sysmis).
+])
+
+for opt in OPT NOOPT; do
+    AS_BOX([$opt])
+    sed "s/opt/$opt/" < evaluate-base.sps > evaluate.sps
+    AT_CHECK([pspp --testing-mode evaluate.sps], [1], [dnl
+length('') => 0.00
+
+length('a') => 1.00
+
+length('xy') => 2.00
+
+length('adsf    ') => 8.00
+
+length('abcdefghijkl') => 12.00
+
+evaluate.sps:9.17-9.25: error: DEBUG EVALUATE: Type mismatch invoking
+LENGTH(string) as length(number).
+    9 | DEBUG EVALUATE /length(0).
+      |                 ^~~~~~~~~
+
+evaluate.sps:9.24: note: DEBUG EVALUATE: This argument has type 'number' but
+'string' is required.
+    9 | DEBUG EVALUATE /length(0).
+      |                        ^
+
+length(0) => error
+
+evaluate.sps:10.17-10.31: error: DEBUG EVALUATE: Type mismatch invoking
+LENGTH(string) as length(number).
+   10 | DEBUG EVALUATE /length($sysmis).
+      |                 ^~~~~~~~~~~~~~~
+
+evaluate.sps:10.24-10.30: note: DEBUG EVALUATE: This argument has type 'number'
+but 'string' is required.
+   10 | DEBUG EVALUATE /length($sysmis).
+      |                        ^~~~~~~
+
+length($sysmis) => error
+])
+done
+AT_CLEANUP
+
+AT_SETUP([expressions - LOWER])
+AT_KEYWORDS([expression expressions evaluate])
+AT_DATA([evaluate-base.sps], [dnl
+SET EPOCH 1940.
+DEBUG EVALUATE SET opt.
+
+DEBUG EVALUATE /lower('ABCDEFGHIJKLMNOPQRSTUVWXYZ!@%&*089').
+DEBUG EVALUATE /lower('').
+DEBUG EVALUATE /lower(1).
+])
+
+for opt in OPT NOOPT; do
+    AS_BOX([$opt])
+    sed "s/opt/$opt/" < evaluate-base.sps > evaluate.sps
+    AT_CHECK([pspp --testing-mode evaluate.sps], [1], [dnl
+lower('ABCDEFGHIJKLMNOPQRSTUVWXYZ!@%&*089') => "abcdefghijklmnopqrstuvwxyz!@
+%&*089"
+
+lower('') => ""
+
+evaluate.sps:6.17-6.24: error: DEBUG EVALUATE: Type mismatch invoking
+LOWER(string) as lower(number).
+    6 | DEBUG EVALUATE /lower(1).
+      |                 ^~~~~~~~
+
+evaluate.sps:6.23: note: DEBUG EVALUATE: This argument has type 'number' but
+'string' is required.
+    6 | DEBUG EVALUATE /lower(1).
+      |                       ^
+
+lower(1) => error
+])
+done
+AT_CLEANUP
+
+AT_SETUP([expressions - REPLACE])
+AT_KEYWORDS([expression expressions evaluate])
+AT_DATA([evaluate-base.sps], [dnl
+SET EPOCH 1940.
+DEBUG EVALUATE SET opt.
+DEBUG EVALUATE /replace('banana', 'an', 'AN').
+DEBUG EVALUATE /replace('banana', 'an', 'a').
+DEBUG EVALUATE /replace('banana', 'an', '').
+DEBUG EVALUATE /replace('banana', 'na', '').
+DEBUG EVALUATE /replace('banana', 'ba', 'BA').
+DEBUG EVALUATE /replace('banana', 'na', 'xyzzy').
+DEBUG EVALUATE /replace('banana', 'an', 'xyzzy', 1).
+DEBUG EVALUATE /replace('banana', 'an', 'xyzzy', 1.5).
+DEBUG EVALUATE /replace('banana', 'bananana', 'xyzzy').
+DEBUG EVALUATE /replace('banana', '', 'xyzzy').
+DEBUG EVALUATE /replace('banana', 'ba', '', 0).
+DEBUG EVALUATE /replace('banana', 'ba', '', -1).
+DEBUG EVALUATE /replace('banana', 'ba', '', $sysmis).
+])
+
+for opt in OPT NOOPT; do
+    AS_BOX([$opt])
+    sed "s/opt/$opt/" < evaluate-base.sps > evaluate.sps
+    AT_CHECK([pspp --testing-mode evaluate.sps], [1], [dnl
+replace('banana', 'an', 'AN') => "bANANa"
+
+replace('banana', 'an', 'a') => "baaa"
+
+replace('banana', 'an', '') => "ba"
+
+replace('banana', 'na', '') => "ba"
+
+replace('banana', 'ba', 'BA') => "BAnana"
+
+replace('banana', 'na', 'xyzzy') => "baxyzzyxyzzy"
+
+replace('banana', 'an', 'xyzzy', 1) => "bxyzzyana"
+
+evaluate.sps:10.50-10.52: error: DEBUG EVALUATE: Treating unexpected non-
+integer value 1.5 as missing.
+   10 | DEBUG EVALUATE /replace('banana', 'an', 'xyzzy', 1.5).
+      |                                                  ^~~
+
+replace('banana', 'an', 'xyzzy', 1.5) => "banana"
+
+replace('banana', 'bananana', 'xyzzy') => "banana"
+
+replace('banana', '', 'xyzzy') => "banana"
+
+replace('banana', 'ba', '', 0) => "banana"
+
+replace('banana', 'ba', '', -1) => "banana"
+
+replace('banana', 'ba', '', $sysmis) => "banana"
+])
+done
+AT_CLEANUP
+
+AT_SETUP([expressions - LPAD])
+AT_KEYWORDS([expression expressions evaluate])
+AT_DATA([evaluate-base.sps], [dnl
+SET EPOCH 1940.
+DEBUG EVALUATE SET opt.
+
+DEBUG EVALUATE /lpad('abc', 0).
+DEBUG EVALUATE /lpad('abc', 2).
+DEBUG EVALUATE /lpad('abc', 3).
+DEBUG EVALUATE /lpad('abc', 10).
+
+DEBUG EVALUATE /lpad('abc', -1).
+DEBUG EVALUATE /lpad('abc', 32768).
+DEBUG EVALUATE /lpad('abc', $sysmis).
+
+DEBUG EVALUATE /lpad('abc', -1, '*').
+DEBUG EVALUATE /lpad('abc', 0, '*').
+DEBUG EVALUATE /lpad('abc', 2, '*').
+DEBUG EVALUATE /lpad('abc', 3, '*').
+DEBUG EVALUATE /lpad('abc', 10, '*').
+
+DEBUG EVALUATE /lpad('abc', 5, 'de').
+DEBUG EVALUATE /lpad('abc', 6, 'de').
+DEBUG EVALUATE /lpad('abc', 7, 'de').
+DEBUG EVALUATE /lpad('abc', 8, 'de').
+DEBUG EVALUATE /lpad('abc', 9, 'de').
+
+DEBUG EVALUATE /lpad('abc', 32768, '*').
+DEBUG EVALUATE /lpad('abc', $sysmis, '*').
+DEBUG EVALUATE /lpad('abc', $sysmis, '').
+DEBUG EVALUATE /lpad('abc', $sysmis, 'xy').
+DEBUG EVALUATE /lpad(0, 10).
+DEBUG EVALUATE /lpad('abc', 'def').
+DEBUG EVALUATE /lpad(0, 10, ' ').
+DEBUG EVALUATE /lpad('abc', 'def', ' ').
+DEBUG EVALUATE /lpad('x', 5, 0).
+DEBUG EVALUATE /lpad('x', 5, 2).
+])
+
+for opt in OPT NOOPT; do
+    AS_BOX([$opt])
+    sed "s/opt/$opt/" < evaluate-base.sps > evaluate.sps
+    AT_CHECK([pspp --testing-mode evaluate.sps], [1], [dnl
+lpad('abc', 0) => "abc"
+
+lpad('abc', 2) => "abc"
+
+lpad('abc', 3) => "abc"
+
+lpad('abc', 10) => "       abc"
+
+evaluate.sps:9.17-9.31: error: DEBUG EVALUATE: The length argument to LPAD must
+be between 0 and 32767.
+    9 | DEBUG EVALUATE /lpad('abc', -1).
+      |                 ^~~~~~~~~~~~~~~
+
+evaluate.sps:9.29-9.30: note: DEBUG EVALUATE: The length argument is -1.
+    9 | DEBUG EVALUATE /lpad('abc', -1).
+      |                             ^~
+
+lpad('abc', -1) => "abc"
+
+evaluate.sps:10.17-10.34: error: DEBUG EVALUATE: The length argument to LPAD
+must be between 0 and 32767.
+   10 | DEBUG EVALUATE /lpad('abc', 32768).
+      |                 ^~~~~~~~~~~~~~~~~~
+
+evaluate.sps:10.29-10.33: note: DEBUG EVALUATE: The length argument is 32768.
+   10 | DEBUG EVALUATE /lpad('abc', 32768).
+      |                             ^~~~~
+
+lpad('abc', 32768) => "abc"
+
+lpad('abc', $sysmis) => "abc"
+
+evaluate.sps:13.17-13.36: error: DEBUG EVALUATE: The length argument to LPAD
+must be between 0 and 32767.
+   13 | DEBUG EVALUATE /lpad('abc', -1, '*').
+      |                 ^~~~~~~~~~~~~~~~~~~~
+
+evaluate.sps:13.29-13.30: note: DEBUG EVALUATE: The length argument is -1.
+   13 | DEBUG EVALUATE /lpad('abc', -1, '*').
+      |                             ^~
+
+lpad('abc', -1, '*') => "abc"
+
+lpad('abc', 0, '*') => "abc"
+
+lpad('abc', 2, '*') => "abc"
+
+lpad('abc', 3, '*') => "abc"
+
+lpad('abc', 10, '*') => "*******abc"
+
+lpad('abc', 5, 'de') => "deabc"
+
+lpad('abc', 6, 'de') => "deabc"
+
+lpad('abc', 7, 'de') => "dedeabc"
+
+lpad('abc', 8, 'de') => "dedeabc"
+
+lpad('abc', 9, 'de') => "dededeabc"
+
+evaluate.sps:25.17-25.39: error: DEBUG EVALUATE: The length argument to LPAD
+must be between 0 and 32767.
+   25 | DEBUG EVALUATE /lpad('abc', 32768, '*').
+      |                 ^~~~~~~~~~~~~~~~~~~~~~~
+
+evaluate.sps:25.29-25.33: note: DEBUG EVALUATE: The length argument is 32768.
+   25 | DEBUG EVALUATE /lpad('abc', 32768, '*').
+      |                             ^~~~~
+
+lpad('abc', 32768, '*') => "abc"
+
+lpad('abc', $sysmis, '*') => "abc"
+
+lpad('abc', $sysmis, '') => "abc"
+
+lpad('abc', $sysmis, 'xy') => "abc"
+
+evaluate.sps:29.17-29.27: error: DEBUG EVALUATE: Function invocation
+lpad(number, number) does not match any known function.  Candidates are:
+LPAD(string, integer)
+LPAD(string, integer, string).
+   29 | DEBUG EVALUATE /lpad(0, 10).
+      |                 ^~~~~~~~~~~
+
+lpad(0, 10) => error
+
+evaluate.sps:30.17-30.34: error: DEBUG EVALUATE: Function invocation
+lpad(string, string) does not match any known function.  Candidates are:
+LPAD(string, integer)
+LPAD(string, integer, string).
+   30 | DEBUG EVALUATE /lpad('abc', 'def').
+      |                 ^~~~~~~~~~~~~~~~~~
+
+lpad('abc', 'def') => error
+
+evaluate.sps:31.17-31.32: error: DEBUG EVALUATE: Function invocation
+lpad(number, number, string) does not match any known function.  Candidates
+are:
+LPAD(string, integer)
+LPAD(string, integer, string).
+   31 | DEBUG EVALUATE /lpad(0, 10, ' ').
+      |                 ^~~~~~~~~~~~~~~~
+
+lpad(0, 10, ' ') => error
+
+evaluate.sps:32.17-32.39: error: DEBUG EVALUATE: Function invocation
+lpad(string, string, string) does not match any known function.  Candidates
+are:
+LPAD(string, integer)
+LPAD(string, integer, string).
+   32 | DEBUG EVALUATE /lpad('abc', 'def', ' ').
+      |                 ^~~~~~~~~~~~~~~~~~~~~~~
+
+lpad('abc', 'def', ' ') => error
+
+evaluate.sps:33.17-33.31: error: DEBUG EVALUATE: Function invocation
+lpad(string, number, number) does not match any known function.  Candidates
+are:
+LPAD(string, integer)
+LPAD(string, integer, string).
+   33 | DEBUG EVALUATE /lpad('x', 5, 0).
+      |                 ^~~~~~~~~~~~~~~
+
+lpad('x', 5, 0) => error
+
+evaluate.sps:34.17-34.31: error: DEBUG EVALUATE: Function invocation
+lpad(string, number, number) does not match any known function.  Candidates
+are:
+LPAD(string, integer)
+LPAD(string, integer, string).
+   34 | DEBUG EVALUATE /lpad('x', 5, 2).
+      |                 ^~~~~~~~~~~~~~~
+
+lpad('x', 5, 2) => error
+])
+done
+AT_CLEANUP
+
+AT_SETUP([expressions - NUMBER])
+AT_KEYWORDS([expression expressions evaluate])
+AT_DATA([evaluate-base.sps], [dnl
+SET EPOCH 1940.
+DEBUG EVALUATE SET opt.
+DEBUG EVALUATE /number("123", f3.0).
+DEBUG EVALUATE /number(" 123", f3.0).
+DEBUG EVALUATE /number("123", f3.1).
+DEBUG EVALUATE /number("   ", f3.1).
+DEBUG EVALUATE /number("123", a8).
+dnl CCA is not an input format:
+DEBUG EVALUATE /number("123", cca1.2).
+])
+
+for opt in OPT NOOPT; do
+    AS_BOX([$opt])
+    sed "s/opt/$opt/" < evaluate-base.sps > evaluate.sps
+    AT_CHECK([pspp --testing-mode evaluate.sps], [1], [dnl
+number("123", f3.0) => 123.00
+
+number(" 123", f3.0) => 12.00
+
+number("123", f3.1) => 12.30
+
+number("   ", f3.1) => sysmis
+
+evaluate.sps:7.17-7.33: error: DEBUG EVALUATE: Type mismatch invoking
+NUMBER(string, num_input_format) as number(string, format).
+    7 | DEBUG EVALUATE /number("123", a8).
+      |                 ^~~~~~~~~~~~~~~~~
+
+evaluate.sps:7.31-7.32: note: DEBUG EVALUATE: Numeric variables are not
+compatible with string format A8.
+    7 | DEBUG EVALUATE /number("123", a8).
+      |                               ^~
+
+number("123", a8) => error
+
+evaluate.sps:8.17-8.37: error: DEBUG EVALUATE: Type mismatch invoking
+NUMBER(string, num_input_format) as number(string, format).
+    8 | DEBUG EVALUATE /number("123", cca1.2).
+      |                 ^~~~~~~~~~~~~~~~~~~~~
+
+evaluate.sps:8.31-8.36: note: DEBUG EVALUATE: Format CCA1.2 may not be used for
+input.
+    8 | DEBUG EVALUATE /number("123", cca1.2).
+      |                               ^~~~~~
+
+number("123", cca1.2) => error
+])
+done
+AT_CLEANUP
+
+AT_SETUP([expressions - LTRIM])
+AT_KEYWORDS([expression expressions evaluate])
+AT_DATA([evaluate-base.sps], [dnl
+SET EPOCH 1940.
+DEBUG EVALUATE SET opt.
+DEBUG EVALUATE /ltrim('   abc').
+DEBUG EVALUATE /rtrim('   abc   ').
+DEBUG EVALUATE /ltrim('abc').
+* The following string contains a tab.
+DEBUG EVALUATE /ltrim('        abc').
+DEBUG EVALUATE /ltrim('    ').
+DEBUG EVALUATE /ltrim('').
+DEBUG EVALUATE /ltrim(8).
+
+DEBUG EVALUATE /ltrim('***abc', '*').
+DEBUG EVALUATE /ltrim('abc', '*').
+DEBUG EVALUATE /ltrim('*abc', '*').
+DEBUG EVALUATE /ltrim('', '*').
+
+DEBUG EVALUATE /ltrim('abc', 'xy').
+DEBUG EVALUATE /ltrim('xyabc', 'xy').
+DEBUG EVALUATE /ltrim('xyxyabc', 'xy').
+
+DEBUG EVALUATE /ltrim(8, '*').
+DEBUG EVALUATE /ltrim(' x', 8).
+DEBUG EVALUATE /ltrim(8, 9).
+])
+
+for opt in OPT NOOPT; do
+    AS_BOX([$opt])
+    sed "s/opt/$opt/" < evaluate-base.sps > evaluate.sps
+    AT_CHECK([pspp --testing-mode evaluate.sps], [1], [dnl
+ltrim('   abc') => "abc"
+
+rtrim('   abc   ') => "   abc"
+
+ltrim('abc') => "abc"
+
+ltrim('        abc') => "      abc"
+
+ltrim('    ') => ""
+
+ltrim('') => ""
+
+evaluate.sps:10.17-10.24: error: DEBUG EVALUATE: Function invocation
+ltrim(number) does not match any known function.  Candidates are:
+LTRIM(string)
+LTRIM(string, string).
+   10 | DEBUG EVALUATE /ltrim(8).
+      |                 ^~~~~~~~
+
+ltrim(8) => error
+
+ltrim('***abc', '*') => "abc"
+
+ltrim('abc', '*') => "abc"
+
+ltrim('*abc', '*') => "abc"
+
+ltrim('', '*') => ""
+
+ltrim('abc', 'xy') => "abc"
+
+ltrim('xyabc', 'xy') => "abc"
+
+ltrim('xyxyabc', 'xy') => "abc"
+
+evaluate.sps:21.17-21.29: error: DEBUG EVALUATE: Function invocation
+ltrim(number, string) does not match any known function.  Candidates are:
+LTRIM(string)
+LTRIM(string, string).
+   21 | DEBUG EVALUATE /ltrim(8, '*').
+      |                 ^~~~~~~~~~~~~
+
+ltrim(8, '*') => error
+
+evaluate.sps:22.17-22.30: error: DEBUG EVALUATE: Function invocation
+ltrim(string, number) does not match any known function.  Candidates are:
+LTRIM(string)
+LTRIM(string, string).
+   22 | DEBUG EVALUATE /ltrim(' x', 8).
+      |                 ^~~~~~~~~~~~~~
+
+ltrim(' x', 8) => error
+
+evaluate.sps:23.17-23.27: error: DEBUG EVALUATE: Function invocation
+ltrim(number, number) does not match any known function.  Candidates are:
+LTRIM(string)
+LTRIM(string, string).
+   23 | DEBUG EVALUATE /ltrim(8, 9).
+      |                 ^~~~~~~~~~~
+
+ltrim(8, 9) => error
+])
+done
+AT_CLEANUP
+
+AT_SETUP([expressions - RPAD])
+AT_KEYWORDS([expression expressions evaluate])
+AT_DATA([evaluate-base.sps], [dnl
+SET EPOCH 1940.
+DEBUG EVALUATE SET opt.
+
+DEBUG EVALUATE /rpad('abc', 0).
+DEBUG EVALUATE /rpad('abc', 2).
+DEBUG EVALUATE /rpad('abc', 3).
+DEBUG EVALUATE /rpad('abc', 10).
+
+DEBUG EVALUATE /rpad('abc', -1).
+DEBUG EVALUATE /rpad('abc', 32768).
+DEBUG EVALUATE /rpad('abc', $sysmis).
+
+DEBUG EVALUATE /rpad('abc', -1, '*').
+DEBUG EVALUATE /rpad('abc', 0, '*').
+DEBUG EVALUATE /rpad('abc', 2, '*').
+DEBUG EVALUATE /rpad('abc', 3, '*').
+DEBUG EVALUATE /rpad('abc', 10, '*').
+
+DEBUG EVALUATE /rpad('abc', 5, 'de').
+DEBUG EVALUATE /rpad('abc', 6, 'de').
+DEBUG EVALUATE /rpad('abc', 7, 'de').
+DEBUG EVALUATE /rpad('abc', 8, 'de').
+DEBUG EVALUATE /rpad('abc', 9, 'de').
+
+DEBUG EVALUATE /rpad('abc', 32768, '*').
+DEBUG EVALUATE /rpad('abc', $sysmis, '*').
+DEBUG EVALUATE /rpad('abc', $sysmis, '').
+DEBUG EVALUATE /rpad('abc', $sysmis, 'xy').
+DEBUG EVALUATE /rpad(0, 10).
+DEBUG EVALUATE /rpad('abc', 'def').
+DEBUG EVALUATE /rpad(0, 10, ' ').
+DEBUG EVALUATE /rpad('abc', 'def', ' ').
+DEBUG EVALUATE /rpad('x', 5, 0).
+DEBUG EVALUATE /rpad('x', 5, 2).
+])
+
+for opt in OPT NOOPT; do
+    AS_BOX([$opt])
+    sed "s/opt/$opt/" < evaluate-base.sps > evaluate.sps
+    AT_CHECK([pspp --testing-mode evaluate.sps], [1], [dnl
+rpad('abc', 0) => "abc"
+
+rpad('abc', 2) => "abc"
+
+rpad('abc', 3) => "abc"
+
+rpad('abc', 10) => "abc       "
+
+evaluate.sps:9.17-9.31: error: DEBUG EVALUATE: The length argument to RPAD must
+be between 0 and 32767.
+    9 | DEBUG EVALUATE /rpad('abc', -1).
+      |                 ^~~~~~~~~~~~~~~
+
+evaluate.sps:9.29-9.30: note: DEBUG EVALUATE: The length argument is -1.
+    9 | DEBUG EVALUATE /rpad('abc', -1).
+      |                             ^~
+
+rpad('abc', -1) => "abc"
+
+evaluate.sps:10.17-10.34: error: DEBUG EVALUATE: The length argument to RPAD
+must be between 0 and 32767.
+   10 | DEBUG EVALUATE /rpad('abc', 32768).
+      |                 ^~~~~~~~~~~~~~~~~~
+
+evaluate.sps:10.29-10.33: note: DEBUG EVALUATE: The length argument is 32768.
+   10 | DEBUG EVALUATE /rpad('abc', 32768).
+      |                             ^~~~~
+
+rpad('abc', 32768) => "abc"
+
+rpad('abc', $sysmis) => "abc"
+
+evaluate.sps:13.17-13.36: error: DEBUG EVALUATE: The length argument to RPAD
+must be between 0 and 32767.
+   13 | DEBUG EVALUATE /rpad('abc', -1, '*').
+      |                 ^~~~~~~~~~~~~~~~~~~~
+
+evaluate.sps:13.29-13.30: note: DEBUG EVALUATE: The length argument is -1.
+   13 | DEBUG EVALUATE /rpad('abc', -1, '*').
+      |                             ^~
+
+rpad('abc', -1, '*') => "abc"
+
+rpad('abc', 0, '*') => "abc"
+
+rpad('abc', 2, '*') => "abc"
+
+rpad('abc', 3, '*') => "abc"
+
+rpad('abc', 10, '*') => "abc*******"
+
+rpad('abc', 5, 'de') => "abcde"
+
+rpad('abc', 6, 'de') => "abcde"
+
+rpad('abc', 7, 'de') => "abcdede"
+
+rpad('abc', 8, 'de') => "abcdede"
+
+rpad('abc', 9, 'de') => "abcdedede"
+
+evaluate.sps:25.17-25.39: error: DEBUG EVALUATE: The length argument to RPAD
+must be between 0 and 32767.
+   25 | DEBUG EVALUATE /rpad('abc', 32768, '*').
+      |                 ^~~~~~~~~~~~~~~~~~~~~~~
+
+evaluate.sps:25.29-25.33: note: DEBUG EVALUATE: The length argument is 32768.
+   25 | DEBUG EVALUATE /rpad('abc', 32768, '*').
+      |                             ^~~~~
+
+rpad('abc', 32768, '*') => "abc"
+
+rpad('abc', $sysmis, '*') => "abc"
+
+rpad('abc', $sysmis, '') => "abc"
+
+rpad('abc', $sysmis, 'xy') => "abc"
+
+evaluate.sps:29.17-29.27: error: DEBUG EVALUATE: Function invocation
+rpad(number, number) does not match any known function.  Candidates are:
+RPAD(string, integer)
+RPAD(string, integer, string).
+   29 | DEBUG EVALUATE /rpad(0, 10).
+      |                 ^~~~~~~~~~~
+
+rpad(0, 10) => error
+
+evaluate.sps:30.17-30.34: error: DEBUG EVALUATE: Function invocation
+rpad(string, string) does not match any known function.  Candidates are:
+RPAD(string, integer)
+RPAD(string, integer, string).
+   30 | DEBUG EVALUATE /rpad('abc', 'def').
+      |                 ^~~~~~~~~~~~~~~~~~
+
+rpad('abc', 'def') => error
+
+evaluate.sps:31.17-31.32: error: DEBUG EVALUATE: Function invocation
+rpad(number, number, string) does not match any known function.  Candidates
+are:
+RPAD(string, integer)
+RPAD(string, integer, string).
+   31 | DEBUG EVALUATE /rpad(0, 10, ' ').
+      |                 ^~~~~~~~~~~~~~~~
+
+rpad(0, 10, ' ') => error
+
+evaluate.sps:32.17-32.39: error: DEBUG EVALUATE: Function invocation
+rpad(string, string, string) does not match any known function.  Candidates
+are:
+RPAD(string, integer)
+RPAD(string, integer, string).
+   32 | DEBUG EVALUATE /rpad('abc', 'def', ' ').
+      |                 ^~~~~~~~~~~~~~~~~~~~~~~
+
+rpad('abc', 'def', ' ') => error
+
+evaluate.sps:33.17-33.31: error: DEBUG EVALUATE: Function invocation
+rpad(string, number, number) does not match any known function.  Candidates
+are:
+RPAD(string, integer)
+RPAD(string, integer, string).
+   33 | DEBUG EVALUATE /rpad('x', 5, 0).
+      |                 ^~~~~~~~~~~~~~~
+
+rpad('x', 5, 0) => error
+
+evaluate.sps:34.17-34.31: error: DEBUG EVALUATE: Function invocation
+rpad(string, number, number) does not match any known function.  Candidates
+are:
+RPAD(string, integer)
+RPAD(string, integer, string).
+   34 | DEBUG EVALUATE /rpad('x', 5, 2).
+      |                 ^~~~~~~~~~~~~~~
+
+rpad('x', 5, 2) => error
+])
+done
+AT_CLEANUP
+
+AT_SETUP([expressions - RTRIM])
+AT_KEYWORDS([expression expressions evaluate])
+AT_DATA([evaluate-base.sps], [dnl
+SET EPOCH 1940.
+DEBUG EVALUATE SET opt.
+DEBUG EVALUATE /rtrim('abc   ').
+DEBUG EVALUATE /rtrim('   abc   ').
+DEBUG EVALUATE /rtrim('abc').
+* The following string contains a tab.
+DEBUG EVALUATE /rtrim('abc     ').
+DEBUG EVALUATE /rtrim('    ').
+DEBUG EVALUATE /rtrim('').
+DEBUG EVALUATE /rtrim(8).
+
+DEBUG EVALUATE /rtrim('abc***', '*').
+DEBUG EVALUATE /rtrim('abc', '*').
+DEBUG EVALUATE /rtrim('abc*', '*').
+DEBUG EVALUATE /rtrim('', '*').
+
+DEBUG EVALUATE /rtrim('abc', 'xy').
+DEBUG EVALUATE /rtrim('abcxy', 'xy').
+DEBUG EVALUATE /rtrim('abcxyxy', 'xy').
+
+DEBUG EVALUATE /rtrim(8, '*').
+DEBUG EVALUATE /rtrim('x ', 8).
+DEBUG EVALUATE /rtrim(8, 9).
+])
+
+for opt in OPT NOOPT; do
+    AS_BOX([$opt])
+    sed "s/opt/$opt/" < evaluate-base.sps > evaluate.sps
+    AT_CHECK([pspp --testing-mode evaluate.sps], [1], [dnl
+rtrim('abc   ') => "abc"
+
+rtrim('   abc   ') => "   abc"
+
+rtrim('abc') => "abc"
+
+rtrim('abc     ') => "abc      "
+
+rtrim('    ') => ""
+
+rtrim('') => ""
+
+evaluate.sps:10.17-10.24: error: DEBUG EVALUATE: Function invocation
+rtrim(number) does not match any known function.  Candidates are:
+RTRIM(string)
+RTRIM(string, string).
+   10 | DEBUG EVALUATE /rtrim(8).
+      |                 ^~~~~~~~
+
+rtrim(8) => error
+
+rtrim('abc***', '*') => "abc"
+
+rtrim('abc', '*') => "abc"
+
+rtrim('abc*', '*') => "abc"
+
+rtrim('', '*') => ""
+
+rtrim('abc', 'xy') => "abc"
+
+rtrim('abcxy', 'xy') => "abc"
+
+rtrim('abcxyxy', 'xy') => "abc"
+
+evaluate.sps:21.17-21.29: error: DEBUG EVALUATE: Function invocation
+rtrim(number, string) does not match any known function.  Candidates are:
+RTRIM(string)
+RTRIM(string, string).
+   21 | DEBUG EVALUATE /rtrim(8, '*').
+      |                 ^~~~~~~~~~~~~
+
+rtrim(8, '*') => error
+
+evaluate.sps:22.17-22.30: error: DEBUG EVALUATE: Function invocation
+rtrim(string, number) does not match any known function.  Candidates are:
+RTRIM(string)
+RTRIM(string, string).
+   22 | DEBUG EVALUATE /rtrim('x ', 8).
+      |                 ^~~~~~~~~~~~~~
+
+rtrim('x ', 8) => error
+
+evaluate.sps:23.17-23.27: error: DEBUG EVALUATE: Function invocation
+rtrim(number, number) does not match any known function.  Candidates are:
+RTRIM(string)
+RTRIM(string, string).
+   23 | DEBUG EVALUATE /rtrim(8, 9).
+      |                 ^~~~~~~~~~~
+
+rtrim(8, 9) => error
+])
+done
+AT_CLEANUP
+
+AT_SETUP([expressions - STRING])
+AT_KEYWORDS([expression expressions evaluate])
+AT_DATA([evaluate-base.sps], [dnl
+SET EPOCH 1940.
+DEBUG EVALUATE SET opt.
+DEBUG EVALUATE /string(123.56, f5.1).
+DEBUG EVALUATE /string($sysmis, f5.1).
+DEBUG EVALUATE /string("abc", A5).
+dnl E has a minimum width of 6 on output:
+DEBUG EVALUATE /string(123, e1).
+DEBUG EVALUATE /string(123, e6.0).
+])
+
+for opt in OPT NOOPT; do
+    AS_BOX([$opt])
+    sed "s/opt/$opt/" < evaluate-base.sps > evaluate.sps
+    AT_CHECK([pspp --testing-mode evaluate.sps], [1], [dnl
+string(123.56, f5.1) => "123.6"
+
+string($sysmis, f5.1) => "   . "
+
+evaluate.sps:5.17-5.33: error: DEBUG EVALUATE: Type mismatch invoking
+STRING(number, num_output_format) as string(string, format).
+    5 | DEBUG EVALUATE /string("abc", A5).
+      |                 ^~~~~~~~~~~~~~~~~
+
+evaluate.sps:5.24-5.28: note: DEBUG EVALUATE: This argument has type 'string'
+but 'number' is required.
+    5 | DEBUG EVALUATE /string("abc", A5).
+      |                        ^~~~~
+
+evaluate.sps:5.31-5.32: note: DEBUG EVALUATE: Numeric variables are not
+compatible with string format A5.
+    5 | DEBUG EVALUATE /string("abc", A5).
+      |                               ^~
+
+string("abc", A5) => error
+
+evaluate.sps:6.17-6.31: error: DEBUG EVALUATE: Type mismatch invoking
+STRING(number, num_output_format) as string(number, format).
+    6 | DEBUG EVALUATE /string(123, e1).
+      |                 ^~~~~~~~~~~~~~~
+
+evaluate.sps:6.29-6.30: note: DEBUG EVALUATE: Output format E1.0 specifies
+width 1, but E requires a width between 6 and 40.
+    6 | DEBUG EVALUATE /string(123, e1).
+      |                             ^~
+
+string(123, e1) => error
+
+string(123, e6.0) => "1E+002"
+])
+done
+AT_CLEANUP
+
+AT_SETUP([expressions - STRUNC])
+AT_KEYWORDS([expression expressions evaluate])
+AT_DATA([evaluate-base.sps], [dnl
+SET EPOCH 1940.
+DEBUG EVALUATE SET opt.
+DEBUG EVALUATE /strunc('a c   ', 9).
+DEBUG EVALUATE /strunc('a c   ', 7).
+DEBUG EVALUATE /strunc('a c   ', 6).
+DEBUG EVALUATE /strunc('a c   ', 5).
+DEBUG EVALUATE /strunc('a c   ', 4).
+DEBUG EVALUATE /strunc('a c   ', 3).
+DEBUG EVALUATE /strunc('a c   ', 2).
+DEBUG EVALUATE /strunc('a c   ', 1).
+DEBUG EVALUATE /strunc('a c   ', 0).
+
+DEBUG EVALUATE /strunc('a c   ', 0.75).
+DEBUG EVALUATE /strunc('a c   ', -1).
+DEBUG EVALUATE /strunc('a c   ', $sysmis).
+
+DEBUG EVALUATE /strunc('  abc  ', 9).
+DEBUG EVALUATE /strunc('  abc  ', 8).
+DEBUG EVALUATE /strunc('  abc  ', 7).
+DEBUG EVALUATE /strunc('  abc  ', 6).
+DEBUG EVALUATE /strunc('  abc  ', 5).
+DEBUG EVALUATE /strunc('  abc  ', 4).
+DEBUG EVALUATE /strunc('  abc  ', 3).
+DEBUG EVALUATE /strunc('  abc  ', 2).
+DEBUG EVALUATE /strunc('  abc  ', 1).
+
+DEBUG EVALUATE /strunc('  abc  ', 1.5).
+DEBUG EVALUATE /strunc('  abc  ', -1).
+DEBUG EVALUATE /strunc('  abc  ', $sysmis).
+])
+
+for opt in OPT NOOPT; do
+    AS_BOX([$opt])
+    sed "s/opt/$opt/" < evaluate-base.sps > evaluate.sps
+    AT_CHECK([pspp --testing-mode evaluate.sps], [1], [dnl
+strunc('a c   ', 9) => "a c"
+
+strunc('a c   ', 7) => "a c"
+
+strunc('a c   ', 6) => "a c"
+
+strunc('a c   ', 5) => "a c"
+
+strunc('a c   ', 4) => "a c"
+
+strunc('a c   ', 3) => "a c"
+
+strunc('a c   ', 2) => "a"
+
+strunc('a c   ', 1) => "a"
+
+strunc('a c   ', 0) => ""
+
+evaluate.sps:13.34-13.37: error: DEBUG EVALUATE: Treating unexpected non-
+integer value 0.75 as missing.
+   13 | DEBUG EVALUATE /strunc('a c   ', 0.75).
+      |                                  ^~~~
+
+strunc('a c   ', 0.75) => "a c   "
+
+strunc('a c   ', -1) => ""
+
+strunc('a c   ', $sysmis) => "a c   "
+
+strunc('  abc  ', 9) => "  abc"
+
+strunc('  abc  ', 8) => "  abc"
+
+strunc('  abc  ', 7) => "  abc"
+
+strunc('  abc  ', 6) => "  abc"
+
+strunc('  abc  ', 5) => "  abc"
+
+strunc('  abc  ', 4) => "  ab"
+
+strunc('  abc  ', 3) => "  a"
+
+strunc('  abc  ', 2) => ""
+
+strunc('  abc  ', 1) => ""
+
+evaluate.sps:27.35-27.37: error: DEBUG EVALUATE: Treating unexpected non-
+integer value 1.5 as missing.
+   27 | DEBUG EVALUATE /strunc('  abc  ', 1.5).
+      |                                   ^~~
+
+strunc('  abc  ', 1.5) => "  abc  "
+
+strunc('  abc  ', -1) => ""
+
+strunc('  abc  ', $sysmis) => "  abc  "
+])
+done
+AT_CLEANUP
+
+AT_SETUP([expressions - SUBSTR])
+AT_KEYWORDS([expression expressions evaluate])
+AT_DATA([evaluate-base.sps], [dnl
+DEBUG EVALUATE SET opt.
+DEBUG EVALUATE /substr('abcdefgh', 1).
+DEBUG EVALUATE /substr('abcdefgh', 3).
+DEBUG EVALUATE /substr('abcdefgh', 5).
+DEBUG EVALUATE /substr('abcdefgh', 6).
+DEBUG EVALUATE /substr('abcdefgh', 7).
+DEBUG EVALUATE /substr('abcdefgh', 8).
+DEBUG EVALUATE /substr('abcdefgh', 9).
+DEBUG EVALUATE /substr('abcdefgh', 10).
+DEBUG EVALUATE /substr('abcdefgh', 20).
+DEBUG EVALUATE /substr('abcdefgh', $sysmis).
+DEBUG EVALUATE /substr('abcdefgh', -5).
+DEBUG EVALUATE /substr('abcdefgh', 0).
+DEBUG EVALUATE /substr(0, 10).
+DEBUG EVALUATE /substr('abcd', 'abc').
+DEBUG EVALUATE /substr(0, 'abc').
+
+DEBUG EVALUATE /substr('abcdefgh', 0, 0).
+DEBUG EVALUATE /substr('abcdefgh', 3, 0).
+DEBUG EVALUATE /substr('abcdefgh', 5, 0).
+DEBUG EVALUATE /substr('abcdefgh', 9, 0).
+DEBUG EVALUATE /substr('abcdefgh', 0, 1).
+DEBUG EVALUATE /substr('abcdefgh', 0, 5).
+DEBUG EVALUATE /substr('abcdefgh', 1, 8).
+DEBUG EVALUATE /substr('abcdefgh', 1, 10).
+DEBUG EVALUATE /substr('abcdefgh', 1, 20).
+DEBUG EVALUATE /substr('abcdefgh', 3, 4).
+DEBUG EVALUATE /substr('abcdefgh', 5, 2).
+DEBUG EVALUATE /substr('abcdefgh', 6, 1).
+DEBUG EVALUATE /substr('abcdefgh', 7, 10).
+DEBUG EVALUATE /substr('abcdefgh', 8, 1).
+DEBUG EVALUATE /substr('abcdefgh', 8, 2).
+DEBUG EVALUATE /substr('abcdefgh', 9, 11).
+DEBUG EVALUATE /substr('abcdefgh', 10, 52).
+DEBUG EVALUATE /substr('abcdefgh', 20, 1).
+DEBUG EVALUATE /substr('abcdefgh', $sysmis, 2).
+DEBUG EVALUATE /substr('abcdefgh', 9, $sysmis).
+DEBUG EVALUATE /substr('abcdefgh', $sysmis, $sysmis).
+DEBUG EVALUATE /substr('abc', 1, 'x').
+DEBUG EVALUATE /substr(0, 10, 1).
+DEBUG EVALUATE /substr(0, 10, 'x').
+DEBUG EVALUATE /substr('abcd', 'abc', 0).
+DEBUG EVALUATE /substr('abcd', 'abc', 'j').
+DEBUG EVALUATE /substr(0, 'abc', 4).
+DEBUG EVALUATE /substr(0, 'abc', 'k').
+])
+
+for opt in OPT NOOPT; do
+    AS_BOX([$opt])
+    sed "s/opt/$opt/" < evaluate-base.sps > evaluate.sps
+    AT_CHECK([pspp --testing-mode evaluate.sps], [1], [dnl
+substr('abcdefgh', 1) => "abcdefgh"
+
+substr('abcdefgh', 3) => "cdefgh"
+
+substr('abcdefgh', 5) => "efgh"
+
+substr('abcdefgh', 6) => "fgh"
+
+substr('abcdefgh', 7) => "gh"
+
+substr('abcdefgh', 8) => "h"
+
+substr('abcdefgh', 9) => ""
+
+substr('abcdefgh', 10) => ""
+
+substr('abcdefgh', 20) => ""
+
+substr('abcdefgh', $sysmis) => ""
+
+substr('abcdefgh', -5) => ""
+
+substr('abcdefgh', 0) => ""
+
+evaluate.sps:14.17-14.29: error: DEBUG EVALUATE: Function invocation
+substr(number, number) does not match any known function.  Candidates are:
+SUBSTR(string, integer)
+SUBSTR(string, integer, integer).
+   14 | DEBUG EVALUATE /substr(0, 10).
+      |                 ^~~~~~~~~~~~~
+
+substr(0, 10) => error
+
+evaluate.sps:15.17-15.37: error: DEBUG EVALUATE: Function invocation
+substr(string, string) does not match any known function.  Candidates are:
+SUBSTR(string, integer)
+SUBSTR(string, integer, integer).
+   15 | DEBUG EVALUATE /substr('abcd', 'abc').
+      |                 ^~~~~~~~~~~~~~~~~~~~~
+
+substr('abcd', 'abc') => error
+
+evaluate.sps:16.17-16.32: error: DEBUG EVALUATE: Function invocation
+substr(number, string) does not match any known function.  Candidates are:
+SUBSTR(string, integer)
+SUBSTR(string, integer, integer).
+   16 | DEBUG EVALUATE /substr(0, 'abc').
+      |                 ^~~~~~~~~~~~~~~~
+
+substr(0, 'abc') => error
+
+substr('abcdefgh', 0, 0) => ""
+
+substr('abcdefgh', 3, 0) => ""
+
+substr('abcdefgh', 5, 0) => ""
+
+substr('abcdefgh', 9, 0) => ""
+
+substr('abcdefgh', 0, 1) => ""
+
+substr('abcdefgh', 0, 5) => ""
+
+substr('abcdefgh', 1, 8) => "abcdefgh"
+
+substr('abcdefgh', 1, 10) => "abcdefgh"
+
+substr('abcdefgh', 1, 20) => "abcdefgh"
+
+substr('abcdefgh', 3, 4) => "cdef"
+
+substr('abcdefgh', 5, 2) => "ef"
+
+substr('abcdefgh', 6, 1) => "f"
+
+substr('abcdefgh', 7, 10) => "gh"
+
+substr('abcdefgh', 8, 1) => "h"
+
+substr('abcdefgh', 8, 2) => "h"
+
+substr('abcdefgh', 9, 11) => ""
+
+substr('abcdefgh', 10, 52) => ""
+
+substr('abcdefgh', 20, 1) => ""
+
+substr('abcdefgh', $sysmis, 2) => ""
+
+substr('abcdefgh', 9, $sysmis) => ""
+
+substr('abcdefgh', $sysmis, $sysmis) => ""
+
+evaluate.sps:39.17-39.37: error: DEBUG EVALUATE: Function invocation
+substr(string, number, string) does not match any known function.  Candidates
+are:
+SUBSTR(string, integer)
+SUBSTR(string, integer, integer).
+   39 | DEBUG EVALUATE /substr('abc', 1, 'x').
+      |                 ^~~~~~~~~~~~~~~~~~~~~
+
+substr('abc', 1, 'x') => error
+
+evaluate.sps:40.17-40.32: error: DEBUG EVALUATE: Function invocation
+substr(number, number, number) does not match any known function.  Candidates
+are:
+SUBSTR(string, integer)
+SUBSTR(string, integer, integer).
+   40 | DEBUG EVALUATE /substr(0, 10, 1).
+      |                 ^~~~~~~~~~~~~~~~
+
+substr(0, 10, 1) => error
+
+evaluate.sps:41.17-41.34: error: DEBUG EVALUATE: Function invocation
+substr(number, number, string) does not match any known function.  Candidates
+are:
+SUBSTR(string, integer)
+SUBSTR(string, integer, integer).
+   41 | DEBUG EVALUATE /substr(0, 10, 'x').
+      |                 ^~~~~~~~~~~~~~~~~~
+
+substr(0, 10, 'x') => error
+
+evaluate.sps:42.17-42.40: error: DEBUG EVALUATE: Function invocation
+substr(string, string, number) does not match any known function.  Candidates
+are:
+SUBSTR(string, integer)
+SUBSTR(string, integer, integer).
+   42 | DEBUG EVALUATE /substr('abcd', 'abc', 0).
+      |                 ^~~~~~~~~~~~~~~~~~~~~~~~
+
+substr('abcd', 'abc', 0) => error
+
+evaluate.sps:43.17-43.42: error: DEBUG EVALUATE: Function invocation
+substr(string, string, string) does not match any known function.  Candidates
+are:
+SUBSTR(string, integer)
+SUBSTR(string, integer, integer).
+   43 | DEBUG EVALUATE /substr('abcd', 'abc', 'j').
+      |                 ^~~~~~~~~~~~~~~~~~~~~~~~~~
+
+substr('abcd', 'abc', 'j') => error
+
+evaluate.sps:44.17-44.35: error: DEBUG EVALUATE: Function invocation
+substr(number, string, number) does not match any known function.  Candidates
+are:
+SUBSTR(string, integer)
+SUBSTR(string, integer, integer).
+   44 | DEBUG EVALUATE /substr(0, 'abc', 4).
+      |                 ^~~~~~~~~~~~~~~~~~~
+
+substr(0, 'abc', 4) => error
+
+evaluate.sps:45.17-45.37: error: DEBUG EVALUATE: Function invocation
+substr(number, string, string) does not match any known function.  Candidates
+are:
+SUBSTR(string, integer)
+SUBSTR(string, integer, integer).
+   45 | DEBUG EVALUATE /substr(0, 'abc', 'k').
+      |                 ^~~~~~~~~~~~~~~~~~~~~
+
+substr(0, 'abc', 'k') => error
+])
+done
+AT_CLEANUP
+
+AT_SETUP([expressions - UPCASE])
+AT_KEYWORDS([expression expressions evaluate])
+AT_DATA([evaluate-base.sps], [dnl
+DEBUG EVALUATE SET opt.
+DEBUG EVALUATE /upcase('abcdefghijklmnopqrstuvwxyz!@%&*089').
+DEBUG EVALUATE /upcase('').
+DEBUG EVALUATE /upcase(1).
+])
+
+for opt in OPT NOOPT; do
+    AS_BOX([$opt])
+    sed "s/opt/$opt/" < evaluate-base.sps > evaluate.sps
+    AT_CHECK([pspp --testing-mode evaluate.sps], [1], [dnl
+upcase('abcdefghijklmnopqrstuvwxyz!@%&*089') => "ABCDEFGHIJKLMNOPQRSTUVWXYZ!@
+%&*089"
+
+upcase('') => ""
+
+evaluate.sps:4.17-4.25: error: DEBUG EVALUATE: Type mismatch invoking
+UPCASE(string) as upcase(number).
+    4 | DEBUG EVALUATE /upcase(1).
+      |                 ^~~~~~~~~
+
+evaluate.sps:4.24: note: DEBUG EVALUATE: This argument has type 'number' but
+'string' is required.
+    4 | DEBUG EVALUATE /upcase(1).
+      |                        ^
+
+upcase(1) => error
+])
+done
+AT_CLEANUP
+
+AT_SETUP([expressions - TIME.DAYS])
+AT_KEYWORDS([expression expressions evaluate])
+AT_DATA([evaluate-base.sps], [dnl
+SET EPOCH 1940.
+DEBUG EVALUATE SET opt.
+DEBUG EVALUATE /time.days(1).
+DEBUG EVALUATE /time.days(-1).
+DEBUG EVALUATE /time.days(0.5).
+DEBUG EVALUATE /time.days('x').
+DEBUG EVALUATE /time.days($sysmis).
+])
+
+for opt in OPT NOOPT; do
+    AS_BOX([$opt])
+    sed "s/opt/$opt/" < evaluate-base.sps > evaluate.sps
+    AT_CHECK([pspp --testing-mode evaluate.sps], [1], [dnl
+time.days(1) => 86400.00
+
+time.days(-1) => -86400.00
+
+time.days(0.5) => 43200.00
+
+evaluate.sps:6.17-6.30: error: DEBUG EVALUATE: Type mismatch invoking TIME.
+DAYS(number) as time.days(string).
+    6 | DEBUG EVALUATE /time.days('x').
+      |                 ^~~~~~~~~~~~~~
+
+evaluate.sps:6.27-6.29: note: DEBUG EVALUATE: This argument has type 'string'
+but 'number' is required.
+    6 | DEBUG EVALUATE /time.days('x').
+      |                           ^~~
+
+time.days('x') => error
+
+time.days($sysmis) => sysmis
+])
+done
+AT_CLEANUP
+
+AT_SETUP([expressions - TIME.HMS])
+AT_KEYWORDS([expression expressions evaluate])
+AT_DATA([evaluate-base.sps], [dnl
+SET EPOCH 1940.
+DEBUG EVALUATE SET opt.
+DEBUG EVALUATE /time.hms(4,50,38).
+DEBUG EVALUATE /time.hms(12,31,35).
+DEBUG EVALUATE /time.hms(12,47,53).
+DEBUG EVALUATE /time.hms(1,26,0).
+DEBUG EVALUATE /time.hms(20,58,11).
+DEBUG EVALUATE /time.hms(7,36,5).
+DEBUG EVALUATE /time.hms(15,43,49).
+DEBUG EVALUATE /time.hms(4,25,9).
+DEBUG EVALUATE /time.hms(6,49,27).
+DEBUG EVALUATE /time.hms(2,57,52).
+DEBUG EVALUATE /time.hms(16,45,44).
+DEBUG EVALUATE /time.hms(21,30,57).
+DEBUG EVALUATE /time.hms(22,30,4).
+DEBUG EVALUATE /time.hms(1,56,51).
+DEBUG EVALUATE /time.hms(5, 6, 7).
+DEBUG EVALUATE /time.hms(5, 6, 0).
+DEBUG EVALUATE /time.hms(5, 0, 7).
+DEBUG EVALUATE /time.hms(0, 6, 7).
+DEBUG EVALUATE /time.hms(-5, 6, -7).
+DEBUG EVALUATE /time.hms(-5, 5, -7).
+DEBUG EVALUATE /time.hms($sysmis, 6, 7).
+DEBUG EVALUATE /time.hms(5, $sysmis, 7).
+DEBUG EVALUATE /time.hms(5, $sysmis, 7).
+DEBUG EVALUATE /time.hms($sysmis, $sysmis, 7).
+DEBUG EVALUATE /time.hms(5, $sysmis, $sysmis).
+DEBUG EVALUATE /time.hms($sysmis, $sysmis, 7).
+DEBUG EVALUATE /time.hms($sysmis, $sysmis, $sysmis).
+])
+
+for opt in OPT NOOPT; do
+    AS_BOX([$opt])
+    sed "s/opt/$opt/" < evaluate-base.sps > evaluate.sps
+    AT_CHECK([pspp --testing-mode evaluate.sps], [0], [dnl
+time.hms(4,50,38) => 17438.00
+
+time.hms(12,31,35) => 45095.00
+
+time.hms(12,47,53) => 46073.00
+
+time.hms(1,26,0) => 5160.00
+
+time.hms(20,58,11) => 75491.00
+
+time.hms(7,36,5) => 27365.00
+
+time.hms(15,43,49) => 56629.00
+
+time.hms(4,25,9) => 15909.00
+
+time.hms(6,49,27) => 24567.00
+
+time.hms(2,57,52) => 10672.00
+
+time.hms(16,45,44) => 60344.00
+
+time.hms(21,30,57) => 77457.00
+
+time.hms(22,30,4) => 81004.00
+
+time.hms(1,56,51) => 7011.00
+
+time.hms(5, 6, 7) => 18367.00
+
+time.hms(5, 6, 0) => 18360.00
+
+time.hms(5, 0, 7) => 18007.00
+
+time.hms(0, 6, 7) => 367.00
+
+evaluate.sps:21.17-21.35: warning: DEBUG EVALUATE: TIME.HMS cannot accept a mix
+of positive and negative arguments.
+   21 | DEBUG EVALUATE /time.hms(-5, 6, -7).
+      |                 ^~~~~~~~~~~~~~~~~~~
+
+evaluate.sps:21.26-21.27: note: DEBUG EVALUATE: This argument has negative
+value -5.
+   21 | DEBUG EVALUATE /time.hms(-5, 6, -7).
+      |                          ^~
+
+evaluate.sps:21.30: note: DEBUG EVALUATE: This argument has positive value 6.
+   21 | DEBUG EVALUATE /time.hms(-5, 6, -7).
+      |                              ^
+
+evaluate.sps:21.33-21.34: note: DEBUG EVALUATE: This argument has negative
+value -7.
+   21 | DEBUG EVALUATE /time.hms(-5, 6, -7).
+      |                                 ^~
+
+time.hms(-5, 6, -7) => sysmis
+
+evaluate.sps:22.17-22.35: warning: DEBUG EVALUATE: TIME.HMS cannot accept a mix
+of positive and negative arguments.
+   22 | DEBUG EVALUATE /time.hms(-5, 5, -7).
+      |                 ^~~~~~~~~~~~~~~~~~~
+
+evaluate.sps:22.26-22.27: note: DEBUG EVALUATE: This argument has negative
+value -5.
+   22 | DEBUG EVALUATE /time.hms(-5, 5, -7).
+      |                          ^~
+
+evaluate.sps:22.30: note: DEBUG EVALUATE: This argument has positive value 5.
+   22 | DEBUG EVALUATE /time.hms(-5, 5, -7).
+      |                              ^
+
+evaluate.sps:22.33-22.34: note: DEBUG EVALUATE: This argument has negative
+value -7.
+   22 | DEBUG EVALUATE /time.hms(-5, 5, -7).
+      |                                 ^~
+
+time.hms(-5, 5, -7) => sysmis
+
+time.hms($sysmis, 6, 7) => sysmis
+
+time.hms(5, $sysmis, 7) => sysmis
+
+time.hms(5, $sysmis, 7) => sysmis
+
+time.hms($sysmis, $sysmis, 7) => sysmis
+
+time.hms(5, $sysmis, $sysmis) => sysmis
+
+time.hms($sysmis, $sysmis, 7) => sysmis
+
+time.hms($sysmis, $sysmis, $sysmis) => sysmis
+])
+done
+AT_CLEANUP
+
+AT_SETUP([expressions - CTIME.*])
+AT_KEYWORDS([expression expressions evaluate ctime])
+AT_DATA([evaluate-base.sps], [dnl
+SET EPOCH 1940.
+DEBUG EVALUATE SET opt.
+DEBUG EVALUATE /ctime.days(106272).
+DEBUG EVALUATE /ctime.hours(106272).
+DEBUG EVALUATE /ctime.minutes(106272).
+DEBUG EVALUATE /ctime.seconds(106272).
+DEBUG EVALUATE /ctime.days(-106272).
+DEBUG EVALUATE /ctime.hours(-106272).
+DEBUG EVALUATE /ctime.minutes(-106272).
+DEBUG EVALUATE /ctime.seconds(-106272).
+DEBUG EVALUATE /ctime.days($sysmis).
+DEBUG EVALUATE /ctime.hours($sysmis).
+DEBUG EVALUATE /ctime.minutes($sysmis).
+DEBUG EVALUATE /ctime.seconds($sysmis).
+DEBUG EVALUATE /ctime.days('a').
+DEBUG EVALUATE /ctime.hours('b').
+DEBUG EVALUATE /ctime.minutes('c').
+DEBUG EVALUATE /ctime.seconds('d').
+
+DEBUG EVALUATE /ctime.days(date.dmy(15,10,1582)).
+DEBUG EVALUATE /ctime.days(date.dmy(6,9,1719)).
+DEBUG EVALUATE /ctime.days(date.dmy(24,1,1583)).
+DEBUG EVALUATE /ctime.days(date.dmy(14,12,1585)).
+DEBUG EVALUATE /ctime.days(date.dmy(26,11,1621)).
+DEBUG EVALUATE /ctime.days(date.dmy(25,12,1821)).
+DEBUG EVALUATE /ctime.days(date.dmy(3,12,1882)).
+DEBUG EVALUATE /ctime.days(date.dmy(6,4,2002)).
+DEBUG EVALUATE /ctime.days(date.dmy(19,12,1999)).
+DEBUG EVALUATE /ctime.days(date.dmy(1,10,1978)).
+DEBUG EVALUATE /ctime.days(date.dmy(0,10,1978)).
+DEBUG EVALUATE /ctime.days(date.dmy(32,10,1978)).
+DEBUG EVALUATE /ctime.days(date.dmy(31,0,1978)).
+DEBUG EVALUATE /ctime.days(date.dmy(31,13,1978)).
+DEBUG EVALUATE /ctime.days(date.dmy($sysmis,10,1978)).
+DEBUG EVALUATE /ctime.days(date.dmy(31,$sysmis,1978)).
+DEBUG EVALUATE /ctime.days(date.dmy(31,10,$sysmis)).
+DEBUG EVALUATE /ctime.days(date.dmy($sysmis,$sysmis,1978)).
+DEBUG EVALUATE /ctime.days(date.dmy(31,$sysmis,$sysmis)).
+DEBUG EVALUATE /ctime.days(date.dmy($sysmis,10,$sysmis)).
+DEBUG EVALUATE /ctime.days(date.dmy($sysmis,$sysmis,$sysmis)).
+])
+
+for opt in OPT NOOPT; do
+    AS_BOX([$opt])
+    sed "s/opt/$opt/" < evaluate-base.sps > evaluate.sps
+    AT_CHECK([pspp --testing-mode evaluate.sps], [1], [dnl
+ctime.days(106272) => 1.23
+
+ctime.hours(106272) => 29.52
+
+ctime.minutes(106272) => 1771.20
+
+ctime.seconds(106272) => 106272.00
+
+ctime.days(-106272) => -1.23
+
+ctime.hours(-106272) => -29.52
+
+ctime.minutes(-106272) => -1771.20
+
+ctime.seconds(-106272) => -106272.00
+
+ctime.days($sysmis) => sysmis
+
+ctime.hours($sysmis) => sysmis
+
+ctime.minutes($sysmis) => sysmis
+
+ctime.seconds($sysmis) => sysmis
+
+evaluate.sps:15.17-15.31: error: DEBUG EVALUATE: Type mismatch invoking CTIME.
+DAYS(number) as ctime.days(string).
+   15 | DEBUG EVALUATE /ctime.days('a').
+      |                 ^~~~~~~~~~~~~~~
+
+evaluate.sps:15.28-15.30: note: DEBUG EVALUATE: This argument has type 'string'
+but 'number' is required.
+   15 | DEBUG EVALUATE /ctime.days('a').
+      |                            ^~~
+
+ctime.days('a') => error
+
+evaluate.sps:16.17-16.32: error: DEBUG EVALUATE: Type mismatch invoking CTIME.
+HOURS(number) as ctime.hours(string).
+   16 | DEBUG EVALUATE /ctime.hours('b').
+      |                 ^~~~~~~~~~~~~~~~
+
+evaluate.sps:16.29-16.31: note: DEBUG EVALUATE: This argument has type 'string'
+but 'number' is required.
+   16 | DEBUG EVALUATE /ctime.hours('b').
+      |                             ^~~
+
+ctime.hours('b') => error
+
+evaluate.sps:17.17-17.34: error: DEBUG EVALUATE: Type mismatch invoking CTIME.
+MINUTES(number) as ctime.minutes(string).
+   17 | DEBUG EVALUATE /ctime.minutes('c').
+      |                 ^~~~~~~~~~~~~~~~~~
+
+evaluate.sps:17.31-17.33: note: DEBUG EVALUATE: This argument has type 'string'
+but 'number' is required.
+   17 | DEBUG EVALUATE /ctime.minutes('c').
+      |                               ^~~
+
+ctime.minutes('c') => error
+
+evaluate.sps:18.17-18.34: error: DEBUG EVALUATE: Type mismatch invoking CTIME.
+SECONDS(number) as ctime.seconds(string).
+   18 | DEBUG EVALUATE /ctime.seconds('d').
+      |                 ^~~~~~~~~~~~~~~~~~
+
+evaluate.sps:18.31-18.33: note: DEBUG EVALUATE: This argument has type 'string'
+but 'number' is required.
+   18 | DEBUG EVALUATE /ctime.seconds('d').
+      |                               ^~~
+
+ctime.seconds('d') => error
+
+ctime.days(date.dmy(15,10,1582)) => 1.00
+
+ctime.days(date.dmy(6,9,1719)) => 50000.00
+
+ctime.days(date.dmy(24,1,1583)) => 102.00
+
+ctime.days(date.dmy(14,12,1585)) => 1157.00
+
+ctime.days(date.dmy(26,11,1621)) => 14288.00
+
+ctime.days(date.dmy(25,12,1821)) => 87365.00
+
+ctime.days(date.dmy(3,12,1882)) => 109623.00
+
+ctime.days(date.dmy(6,4,2002)) => 153211.00
+
+ctime.days(date.dmy(19,12,1999)) => 152372.00
+
+ctime.days(date.dmy(1,10,1978)) => 144623.00
+
+ctime.days(date.dmy(0,10,1978)) => 144622.00
+
+evaluate.sps:31.28-31.47: error: DEBUG EVALUATE: Invalid arguments to DATE.DMY
+function.
+   31 | DEBUG EVALUATE /ctime.days(date.dmy(32,10,1978)).
+      |                            ^~~~~~~~~~~~~~~~~~~~
+
+evaluate.sps:31.37-31.38: note: DEBUG EVALUATE: Day 32 is not in the acceptable
+range of 0 to 31.
+   31 | DEBUG EVALUATE /ctime.days(date.dmy(32,10,1978)).
+      |                                     ^~
+
+ctime.days(date.dmy(32,10,1978)) => sysmis
+
+ctime.days(date.dmy(31,0,1978)) => 144349.00
+
+ctime.days(date.dmy(31,13,1978)) => 144745.00
+
+ctime.days(date.dmy($sysmis,10,1978)) => sysmis
+
+ctime.days(date.dmy(31,$sysmis,1978)) => sysmis
+
+ctime.days(date.dmy(31,10,$sysmis)) => sysmis
+
+ctime.days(date.dmy($sysmis,$sysmis,1978)) => sysmis
+
+ctime.days(date.dmy(31,$sysmis,$sysmis)) => sysmis
+
+ctime.days(date.dmy($sysmis,10,$sysmis)) => sysmis
+
+ctime.days(date.dmy($sysmis,$sysmis,$sysmis)) => sysmis
+])
+done
+AT_CLEANUP
+
+AT_SETUP([expressions - DATE.DMY])
+AT_KEYWORDS([expression expressions evaluate])
+AT_DATA([evaluate-base.sps], [dnl
+SET EPOCH 1940.
+DEBUG EVALUATE SET opt.
+DEBUG EVALUATE /date.dmy('a',1,2).
+DEBUG EVALUATE /date.dmy(1,'a',2).
+DEBUG EVALUATE /date.dmy(1,2,'a').
+])
+
+for opt in OPT NOOPT; do
+    AS_BOX([$opt])
+    sed "s/opt/$opt/" < evaluate-base.sps > evaluate.sps
+    AT_CHECK([pspp --testing-mode evaluate.sps], [1], [dnl
+evaluate.sps:3.17-3.33: error: DEBUG EVALUATE: Type mismatch invoking DATE.
+DMY(integer, integer, integer) as date.dmy(string, number, number).
+    3 | DEBUG EVALUATE /date.dmy('a',1,2).
+      |                 ^~~~~~~~~~~~~~~~~
+
+evaluate.sps:3.26-3.28: note: DEBUG EVALUATE: This argument has type 'string'
+but 'integer' is required.
+    3 | DEBUG EVALUATE /date.dmy('a',1,2).
+      |                          ^~~
+
+date.dmy('a',1,2) => error
+
+evaluate.sps:4.17-4.33: error: DEBUG EVALUATE: Type mismatch invoking DATE.
+DMY(integer, integer, integer) as date.dmy(number, string, number).
+    4 | DEBUG EVALUATE /date.dmy(1,'a',2).
+      |                 ^~~~~~~~~~~~~~~~~
+
+evaluate.sps:4.28-4.30: note: DEBUG EVALUATE: This argument has type 'string'
+but 'integer' is required.
+    4 | DEBUG EVALUATE /date.dmy(1,'a',2).
+      |                            ^~~
+
+date.dmy(1,'a',2) => error
+
+evaluate.sps:5.17-5.33: error: DEBUG EVALUATE: Type mismatch invoking DATE.
+DMY(integer, integer, integer) as date.dmy(number, number, string).
+    5 | DEBUG EVALUATE /date.dmy(1,2,'a').
+      |                 ^~~~~~~~~~~~~~~~~
+
+evaluate.sps:5.30-5.32: note: DEBUG EVALUATE: This argument has type 'string'
+but 'integer' is required.
+    5 | DEBUG EVALUATE /date.dmy(1,2,'a').
+      |                              ^~~
+
+date.dmy(1,2,'a') => error
+])
+done
+AT_CLEANUP
+
+AT_SETUP([expressions - YRMODA])
+AT_KEYWORDS([expression expressions evaluate])
+AT_DATA([evaluate-base.sps], [dnl
+SET EPOCH 1940.
+DEBUG EVALUATE SET opt.
+dnl FIXME: check out-of-range and nearly out-of-range values
+DEBUG EVALUATE /yrmoda(1582,10,15).
+DEBUG EVALUATE /yrmoda(1719,9,6).
+DEBUG EVALUATE /yrmoda(1583,1,24).
+DEBUG EVALUATE /yrmoda(1585,12,14).
+DEBUG EVALUATE /yrmoda(1621,11,26).
+DEBUG EVALUATE /yrmoda(1821,12,25).
+DEBUG EVALUATE /yrmoda(1882,12,3).
+DEBUG EVALUATE /yrmoda(2002,4,6).
+DEBUG EVALUATE /yrmoda(1999,12,19).
+DEBUG EVALUATE /yrmoda(1978,10,1).
+DEBUG EVALUATE /yrmoda(1978,10,0).
+DEBUG EVALUATE /yrmoda(1978,10,32).
+DEBUG EVALUATE /yrmoda(1978,0,31).
+DEBUG EVALUATE /yrmoda(1978,13,31).
+DEBUG EVALUATE /yrmoda(1978,10,$sysmis).
+DEBUG EVALUATE /yrmoda(1978,$sysmis,31).
+DEBUG EVALUATE /yrmoda($sysmis,10,31).
+DEBUG EVALUATE /yrmoda(1978,$sysmis,$sysmis).
+DEBUG EVALUATE /yrmoda($sysmis,$sysmis,31).
+DEBUG EVALUATE /yrmoda($sysmis,10,$sysmis).
+DEBUG EVALUATE /yrmoda($sysmis,$sysmis,$sysmis).
+DEBUG EVALUATE /yrmoda('a',1,2).
+DEBUG EVALUATE /yrmoda(1,'a',2).
+DEBUG EVALUATE /yrmoda(1,2,'a').
+])
+
+for opt in OPT NOOPT; do
+    AS_BOX([$opt])
+    sed "s/opt/$opt/" < evaluate-base.sps > evaluate.sps
+    AT_CHECK([pspp --testing-mode evaluate.sps], [1], [dnl
+yrmoda(1582,10,15) => 1.00
+
+yrmoda(1719,9,6) => 50000.00
+
+yrmoda(1583,1,24) => 102.00
+
+yrmoda(1585,12,14) => 1157.00
+
+yrmoda(1621,11,26) => 14288.00
+
+yrmoda(1821,12,25) => 87365.00
+
+yrmoda(1882,12,3) => 109623.00
+
+yrmoda(2002,4,6) => 153211.00
+
+yrmoda(1999,12,19) => 152372.00
+
+yrmoda(1978,10,1) => 144623.00
+
+yrmoda(1978,10,0) => 144622.00
+
+evaluate.sps:14.17-14.34: error: DEBUG EVALUATE: Invalid arguments to YRMODA
+function.
+   14 | DEBUG EVALUATE /yrmoda(1978,10,32).
+      |                 ^~~~~~~~~~~~~~~~~~
+
+evaluate.sps:14.32-14.33: note: DEBUG EVALUATE: Day 32 is not in the acceptable
+range of 0 to 31.
+   14 | DEBUG EVALUATE /yrmoda(1978,10,32).
+      |                                ^~
+
+yrmoda(1978,10,32) => sysmis
+
+yrmoda(1978,0,31) => 144349.00
+
+yrmoda(1978,13,31) => 144745.00
+
+yrmoda(1978,10,$sysmis) => sysmis
+
+yrmoda(1978,$sysmis,31) => sysmis
+
+yrmoda($sysmis,10,31) => sysmis
+
+yrmoda(1978,$sysmis,$sysmis) => sysmis
+
+yrmoda($sysmis,$sysmis,31) => sysmis
+
+yrmoda($sysmis,10,$sysmis) => sysmis
+
+yrmoda($sysmis,$sysmis,$sysmis) => sysmis
+
+evaluate.sps:24.17-24.31: error: DEBUG EVALUATE: Type mismatch invoking
+YRMODA(integer, integer, integer) as yrmoda(string, number, number).
+   24 | DEBUG EVALUATE /yrmoda('a',1,2).
+      |                 ^~~~~~~~~~~~~~~
+
+evaluate.sps:24.24-24.26: note: DEBUG EVALUATE: This argument has type 'string'
+but 'integer' is required.
+   24 | DEBUG EVALUATE /yrmoda('a',1,2).
+      |                        ^~~
+
+yrmoda('a',1,2) => error
+
+evaluate.sps:25.17-25.31: error: DEBUG EVALUATE: Type mismatch invoking
+YRMODA(integer, integer, integer) as yrmoda(number, string, number).
+   25 | DEBUG EVALUATE /yrmoda(1,'a',2).
+      |                 ^~~~~~~~~~~~~~~
+
+evaluate.sps:25.26-25.28: note: DEBUG EVALUATE: This argument has type 'string'
+but 'integer' is required.
+   25 | DEBUG EVALUATE /yrmoda(1,'a',2).
+      |                          ^~~
+
+yrmoda(1,'a',2) => error
+
+evaluate.sps:26.17-26.31: error: DEBUG EVALUATE: Type mismatch invoking
+YRMODA(integer, integer, integer) as yrmoda(number, number, string).
+   26 | DEBUG EVALUATE /yrmoda(1,2,'a').
+      |                 ^~~~~~~~~~~~~~~
+
+evaluate.sps:26.28-26.30: note: DEBUG EVALUATE: This argument has type 'string'
+but 'integer' is required.
+   26 | DEBUG EVALUATE /yrmoda(1,2,'a').
+      |                            ^~~
+
+yrmoda(1,2,'a') => error
+])
+done
+AT_CLEANUP
+
+AT_SETUP([expressions - DATE.MDY])
+AT_KEYWORDS([expression expressions evaluate])
+AT_DATA([evaluate-base.sps], [dnl
+SET EPOCH 1940.
+DEBUG EVALUATE SET opt.
+
+dnl FIXME: check out-of-range and nearly out-of-range values
+DEBUG EVALUATE /ctime.days(date.mdy(6,10,1648)) + 577735.
+DEBUG EVALUATE /ctime.days(date.mdy(6,30,1680)) + 577735.
+DEBUG EVALUATE /ctime.days(date.mdy(7,24,1716)) + 577735.
+DEBUG EVALUATE /ctime.days(date.mdy(6,19,1768)) + 577735.
+DEBUG EVALUATE /ctime.days(date.mdy(8,2,1819)) + 577735.
+DEBUG EVALUATE /ctime.days(date.mdy(3,27,1839)) + 577735.
+DEBUG EVALUATE /ctime.days(date.mdy(4,19,1903)) + 577735.
+DEBUG EVALUATE /ctime.days(date.mdy(8,25,1929)) + 577735.
+DEBUG EVALUATE /ctime.days(date.mdy(9,29,1941)) + 577735.
+DEBUG EVALUATE /ctime.days(date.mdy(4,19,1943)) + 577735.
+DEBUG EVALUATE /ctime.days(date.mdy(10,7,1943)) + 577735.
+DEBUG EVALUATE /ctime.days(date.mdy(3,17,1992)) + 577735.
+DEBUG EVALUATE /ctime.days(date.mdy(2,25,1996)) + 577735.
+DEBUG EVALUATE /ctime.days(date.mdy(11,10,2038)) + 577735.
+DEBUG EVALUATE /ctime.days(date.mdy(7,18,2094)) + 577735.
+
+dnl FIXME: check out-of-range and nearly out-of-range values
+DEBUG EVALUATE /ctime.days(date.mdy(10,15,1582)).
+DEBUG EVALUATE /ctime.days(date.mdy(9,6,1719)).
+DEBUG EVALUATE /ctime.days(date.mdy(1,24,1583)).
+DEBUG EVALUATE /ctime.days(date.mdy(12,14,1585)).
+DEBUG EVALUATE /ctime.days(date.mdy(11,26,1621)).
+DEBUG EVALUATE /ctime.days(date.mdy(12,25,1821)).
+DEBUG EVALUATE /ctime.days(date.mdy(12,3,1882)).
+DEBUG EVALUATE /ctime.days(date.mdy(4,6,2002)).
+DEBUG EVALUATE /ctime.days(date.mdy(12,19,1999)).
+DEBUG EVALUATE /ctime.days(date.mdy(10,1,1978)).
+DEBUG EVALUATE /ctime.days(date.mdy(10,0,1978)).
+DEBUG EVALUATE /ctime.days(date.mdy(10,32,1978)).
+DEBUG EVALUATE /ctime.days(date.mdy(0,31,1978)).
+DEBUG EVALUATE /ctime.days(date.mdy(13,31,1978)).
+DEBUG EVALUATE /ctime.days(date.mdy(10,$sysmis,1978)).
+DEBUG EVALUATE /ctime.days(date.mdy($sysmis,31,1978)).
+DEBUG EVALUATE /ctime.days(date.mdy(10,31,$sysmis)).
+DEBUG EVALUATE /ctime.days(date.mdy($sysmis,$sysmis,1978)).
+DEBUG EVALUATE /ctime.days(date.mdy($sysmis,31,$sysmis)).
+DEBUG EVALUATE /ctime.days(date.mdy(10,$sysmis,$sysmis)).
+DEBUG EVALUATE /ctime.days(date.mdy($sysmis,$sysmis,$sysmis)).
+DEBUG EVALUATE /date.mdy('a',1,2).
+DEBUG EVALUATE /date.mdy(1,'a',2).
+DEBUG EVALUATE /date.mdy(1,2,'a').
+DEBUG EVALUATE /ctime.days(date.mdy(0,0,0)).
+DEBUG EVALUATE /ctime.days(date.mdy(0,0,999)).
+DEBUG EVALUATE /date.mdy(1,1,1582).
+DEBUG EVALUATE /date.mdy(10,14,1582).
+DEBUG EVALUATE /date.mdy(10,15,1582).
+])
+
+for opt in OPT NOOPT; do
+    AS_BOX([$opt])
+    sed "s/opt/$opt/" < evaluate-base.sps > evaluate.sps
+    AT_CHECK([pspp --testing-mode evaluate.sps], [1], [dnl
+ctime.days(date.mdy(6,10,1648)) + 577735 => 601716.00
+
+ctime.days(date.mdy(6,30,1680)) + 577735 => 613424.00
+
+ctime.days(date.mdy(7,24,1716)) + 577735 => 626596.00
+
+ctime.days(date.mdy(6,19,1768)) + 577735 => 645554.00
+
+ctime.days(date.mdy(8,2,1819)) + 577735 => 664224.00
+
+ctime.days(date.mdy(3,27,1839)) + 577735 => 671401.00
+
+ctime.days(date.mdy(4,19,1903)) + 577735 => 694799.00
+
+ctime.days(date.mdy(8,25,1929)) + 577735 => 704424.00
+
+ctime.days(date.mdy(9,29,1941)) + 577735 => 708842.00
+
+ctime.days(date.mdy(4,19,1943)) + 577735 => 709409.00
+
+ctime.days(date.mdy(10,7,1943)) + 577735 => 709580.00
+
+ctime.days(date.mdy(3,17,1992)) + 577735 => 727274.00
+
+ctime.days(date.mdy(2,25,1996)) + 577735 => 728714.00
+
+ctime.days(date.mdy(11,10,2038)) + 577735 => 744313.00
+
+ctime.days(date.mdy(7,18,2094)) + 577735 => 764652.00
+
+ctime.days(date.mdy(10,15,1582)) => 1.00
+
+ctime.days(date.mdy(9,6,1719)) => 50000.00
+
+ctime.days(date.mdy(1,24,1583)) => 102.00
+
+ctime.days(date.mdy(12,14,1585)) => 1157.00
+
+ctime.days(date.mdy(11,26,1621)) => 14288.00
+
+ctime.days(date.mdy(12,25,1821)) => 87365.00
+
+ctime.days(date.mdy(12,3,1882)) => 109623.00
+
+ctime.days(date.mdy(4,6,2002)) => 153211.00
+
+ctime.days(date.mdy(12,19,1999)) => 152372.00
+
+ctime.days(date.mdy(10,1,1978)) => 144623.00
+
+ctime.days(date.mdy(10,0,1978)) => 144622.00
+
+evaluate.sps:31.28-31.47: error: DEBUG EVALUATE: Invalid arguments to DATE.MDY
+function.
+   31 | DEBUG EVALUATE /ctime.days(date.mdy(10,32,1978)).
+      |                            ^~~~~~~~~~~~~~~~~~~~
+
+evaluate.sps:31.40-31.41: note: DEBUG EVALUATE: Day 32 is not in the acceptable
+range of 0 to 31.
+   31 | DEBUG EVALUATE /ctime.days(date.mdy(10,32,1978)).
+      |                                        ^~
+
+ctime.days(date.mdy(10,32,1978)) => sysmis
+
+ctime.days(date.mdy(0,31,1978)) => 144349.00
+
+ctime.days(date.mdy(13,31,1978)) => 144745.00
+
+ctime.days(date.mdy(10,$sysmis,1978)) => sysmis
+
+ctime.days(date.mdy($sysmis,31,1978)) => sysmis
+
+ctime.days(date.mdy(10,31,$sysmis)) => sysmis
+
+ctime.days(date.mdy($sysmis,$sysmis,1978)) => sysmis
+
+ctime.days(date.mdy($sysmis,31,$sysmis)) => sysmis
+
+ctime.days(date.mdy(10,$sysmis,$sysmis)) => sysmis
+
+ctime.days(date.mdy($sysmis,$sysmis,$sysmis)) => sysmis
+
+evaluate.sps:41.17-41.33: error: DEBUG EVALUATE: Type mismatch invoking DATE.
+MDY(integer, integer, integer) as date.mdy(string, number, number).
+   41 | DEBUG EVALUATE /date.mdy('a',1,2).
+      |                 ^~~~~~~~~~~~~~~~~
+
+evaluate.sps:41.26-41.28: note: DEBUG EVALUATE: This argument has type 'string'
+but 'integer' is required.
+   41 | DEBUG EVALUATE /date.mdy('a',1,2).
+      |                          ^~~
+
+date.mdy('a',1,2) => error
+
+evaluate.sps:42.17-42.33: error: DEBUG EVALUATE: Type mismatch invoking DATE.
+MDY(integer, integer, integer) as date.mdy(number, string, number).
+   42 | DEBUG EVALUATE /date.mdy(1,'a',2).
+      |                 ^~~~~~~~~~~~~~~~~
+
+evaluate.sps:42.28-42.30: note: DEBUG EVALUATE: This argument has type 'string'
+but 'integer' is required.
+   42 | DEBUG EVALUATE /date.mdy(1,'a',2).
+      |                            ^~~
+
+date.mdy(1,'a',2) => error
+
+evaluate.sps:43.17-43.33: error: DEBUG EVALUATE: Type mismatch invoking DATE.
+MDY(integer, integer, integer) as date.mdy(number, number, string).
+   43 | DEBUG EVALUATE /date.mdy(1,2,'a').
+      |                 ^~~~~~~~~~~~~~~~~
+
+evaluate.sps:43.30-43.32: note: DEBUG EVALUATE: This argument has type 'string'
+but 'integer' is required.
+   43 | DEBUG EVALUATE /date.mdy(1,2,'a').
+      |                              ^~~
+
+date.mdy(1,2,'a') => error
+
+ctime.days(date.mdy(0,0,0)) => 152353.00
+
+evaluate.sps:45.28-45.44: error: DEBUG EVALUATE: Invalid arguments to DATE.MDY
+function.
+   45 | DEBUG EVALUATE /ctime.days(date.mdy(0,0,999)).
+      |                            ^~~~~~~~~~~~~~~~~
+
+evaluate.sps:45.41-45.43: note: DEBUG EVALUATE: Date 0998-12-0 is before the
+earliest supported date 1582-10-15.
+   45 | DEBUG EVALUATE /ctime.days(date.mdy(0,0,999)).
+      |                                         ^~~
+
+ctime.days(date.mdy(0,0,999)) => sysmis
+
+evaluate.sps:46.17-46.34: error: DEBUG EVALUATE: Invalid arguments to DATE.MDY
+function.
+   46 | DEBUG EVALUATE /date.mdy(1,1,1582).
+      |                 ^~~~~~~~~~~~~~~~~~
+
+evaluate.sps:46.17-46.34: note: DEBUG EVALUATE: Date 1582-1-1 is before the
+earliest supported date 1582-10-15.
+   46 | DEBUG EVALUATE /date.mdy(1,1,1582).
+      |                 ^~~~~~~~~~~~~~~~~~
+
+date.mdy(1,1,1582) => sysmis
+
+evaluate.sps:47.17-47.36: error: DEBUG EVALUATE: Invalid arguments to DATE.MDY
+function.
+   47 | DEBUG EVALUATE /date.mdy(10,14,1582).
+      |                 ^~~~~~~~~~~~~~~~~~~~
+
+evaluate.sps:47.17-47.36: note: DEBUG EVALUATE: Date 1582-10-14 is before the
+earliest supported date 1582-10-15.
+   47 | DEBUG EVALUATE /date.mdy(10,14,1582).
+      |                 ^~~~~~~~~~~~~~~~~~~~
+
+date.mdy(10,14,1582) => sysmis
+
+date.mdy(10,15,1582) => 86400.00
+])
+done
+AT_CLEANUP
+
+AT_SETUP([expressions - DATE.MOYR])
+AT_KEYWORDS([expression expressions evaluate])
+AT_DATA([evaluate-base.sps], [dnl
+SET EPOCH 1940.
+DEBUG EVALUATE SET opt.
+DEBUG EVALUATE /ctime.days(date.moyr(1,2000)).
+DEBUG EVALUATE /ctime.days(date.moyr(2,2000)).
+DEBUG EVALUATE /ctime.days(date.moyr(3,2000)).
+DEBUG EVALUATE /ctime.days(date.moyr(4,2000)).
+DEBUG EVALUATE /ctime.days(date.moyr(5,2000)).
+DEBUG EVALUATE /ctime.days(date.moyr(13,2000)).
+DEBUG EVALUATE /ctime.days(date.moyr(14,2000)).
+DEBUG EVALUATE /ctime.days(date.moyr($sysmis,2000)).
+DEBUG EVALUATE /ctime.days(date.moyr(1,$sysmis)).
+DEBUG EVALUATE /ctime.days(date.moyr($sysmis,$sysmis)).
+DEBUG EVALUATE /date.moyr('a',2000).
+DEBUG EVALUATE /date.moyr(5,'a').
+DEBUG EVALUATE /date.moyr('a','b').
+])
+
+for opt in OPT NOOPT; do
+    AS_BOX([$opt])
+    sed "s/opt/$opt/" < evaluate-base.sps > evaluate.sps
+    AT_CHECK([pspp --testing-mode evaluate.sps], [1], [dnl
+ctime.days(date.moyr(1,2000)) => 152385.00
+
+ctime.days(date.moyr(2,2000)) => 152416.00
+
+ctime.days(date.moyr(3,2000)) => 152445.00
+
+ctime.days(date.moyr(4,2000)) => 152476.00
+
+ctime.days(date.moyr(5,2000)) => 152506.00
+
+ctime.days(date.moyr(13,2000)) => 152751.00
+
+evaluate.sps:9.28-9.45: error: DEBUG EVALUATE: Invalid arguments to DATE.MOYR
+function.
+    9 | DEBUG EVALUATE /ctime.days(date.moyr(14,2000)).
+      |                            ^~~~~~~~~~~~~~~~~~
+
+evaluate.sps:9.38-9.39: note: DEBUG EVALUATE: Month 14 is not in the acceptable
+range of 0 to 13.
+    9 | DEBUG EVALUATE /ctime.days(date.moyr(14,2000)).
+      |                                      ^~
+
+ctime.days(date.moyr(14,2000)) => sysmis
+
+ctime.days(date.moyr($sysmis,2000)) => sysmis
+
+ctime.days(date.moyr(1,$sysmis)) => sysmis
+
+ctime.days(date.moyr($sysmis,$sysmis)) => sysmis
+
+evaluate.sps:13.17-13.35: error: DEBUG EVALUATE: Type mismatch invoking DATE.
+MOYR(integer, integer) as date.moyr(string, number).
+   13 | DEBUG EVALUATE /date.moyr('a',2000).
+      |                 ^~~~~~~~~~~~~~~~~~~
+
+evaluate.sps:13.27-13.29: note: DEBUG EVALUATE: This argument has type 'string'
+but 'integer' is required.
+   13 | DEBUG EVALUATE /date.moyr('a',2000).
+      |                           ^~~
+
+date.moyr('a',2000) => error
+
+evaluate.sps:14.17-14.32: error: DEBUG EVALUATE: Type mismatch invoking DATE.
+MOYR(integer, integer) as date.moyr(number, string).
+   14 | DEBUG EVALUATE /date.moyr(5,'a').
+      |                 ^~~~~~~~~~~~~~~~
+
+evaluate.sps:14.29-14.31: note: DEBUG EVALUATE: This argument has type 'string'
+but 'integer' is required.
+   14 | DEBUG EVALUATE /date.moyr(5,'a').
+      |                             ^~~
+
+date.moyr(5,'a') => error
+
+evaluate.sps:15.17-15.34: error: DEBUG EVALUATE: Type mismatch invoking DATE.
+MOYR(integer, integer) as date.moyr(string, string).
+   15 | DEBUG EVALUATE /date.moyr('a','b').
+      |                 ^~~~~~~~~~~~~~~~~~
+
+evaluate.sps:15.27-15.29: note: DEBUG EVALUATE: This argument has type 'string'
+but 'integer' is required.
+   15 | DEBUG EVALUATE /date.moyr('a','b').
+      |                           ^~~
+
+evaluate.sps:15.31-15.33: note: DEBUG EVALUATE: This argument has type 'string'
+but 'integer' is required.
+   15 | DEBUG EVALUATE /date.moyr('a','b').
+      |                               ^~~
+
+date.moyr('a','b') => error
+])
+done
+AT_CLEANUP
+
+AT_SETUP([expressions - DATE.QYR])
+AT_KEYWORDS([expression expressions evaluate])
+AT_DATA([evaluate-base.sps], [dnl
+SET EPOCH 1940.
+DEBUG EVALUATE SET opt.
+DEBUG EVALUATE /ctime.days(date.qyr(1,2000)).
+DEBUG EVALUATE /ctime.days(date.qyr(2,2000)).
+DEBUG EVALUATE /ctime.days(date.qyr(5,2000)).
+DEBUG EVALUATE /ctime.days(date.qyr(6,2000)).
+DEBUG EVALUATE /ctime.days(date.qyr($sysmis,2000)).
+DEBUG EVALUATE /ctime.days(date.qyr(1,$sysmis)).
+DEBUG EVALUATE /ctime.days(date.qyr($sysmis,$sysmis)).
+DEBUG EVALUATE /date.qyr('a',2000).
+DEBUG EVALUATE /date.qyr(5,'a').
+DEBUG EVALUATE /date.qyr('a','b').
+])
+
+for opt in OPT NOOPT; do
+    AS_BOX([$opt])
+    sed "s/opt/$opt/" < evaluate-base.sps > evaluate.sps
+    AT_CHECK([pspp --testing-mode evaluate.sps], [1], [dnl
+ctime.days(date.qyr(1,2000)) => 152385.00
+
+ctime.days(date.qyr(2,2000)) => 152476.00
+
+evaluate.sps:5.37: warning: DEBUG EVALUATE: Argument 1 to DATE.QYR must be 1,
+2, 3, or 4 (not 5).
+    5 | DEBUG EVALUATE /ctime.days(date.qyr(5,2000)).
+      |                                     ^
+
+ctime.days(date.qyr(5,2000)) => sysmis
+
+evaluate.sps:6.37: warning: DEBUG EVALUATE: Argument 1 to DATE.QYR must be 1,
+2, 3, or 4 (not 6).
+    6 | DEBUG EVALUATE /ctime.days(date.qyr(6,2000)).
+      |                                     ^
+
+ctime.days(date.qyr(6,2000)) => sysmis
+
+ctime.days(date.qyr($sysmis,2000)) => sysmis
+
+ctime.days(date.qyr(1,$sysmis)) => sysmis
+
+ctime.days(date.qyr($sysmis,$sysmis)) => sysmis
+
+evaluate.sps:10.17-10.34: error: DEBUG EVALUATE: Type mismatch invoking DATE.
+QYR(integer, integer) as date.qyr(string, number).
+   10 | DEBUG EVALUATE /date.qyr('a',2000).
+      |                 ^~~~~~~~~~~~~~~~~~
+
+evaluate.sps:10.26-10.28: note: DEBUG EVALUATE: This argument has type 'string'
+but 'integer' is required.
+   10 | DEBUG EVALUATE /date.qyr('a',2000).
+      |                          ^~~
+
+date.qyr('a',2000) => error
+
+evaluate.sps:11.17-11.31: error: DEBUG EVALUATE: Type mismatch invoking DATE.
+QYR(integer, integer) as date.qyr(number, string).
+   11 | DEBUG EVALUATE /date.qyr(5,'a').
+      |                 ^~~~~~~~~~~~~~~
+
+evaluate.sps:11.28-11.30: note: DEBUG EVALUATE: This argument has type 'string'
+but 'integer' is required.
+   11 | DEBUG EVALUATE /date.qyr(5,'a').
+      |                            ^~~
+
+date.qyr(5,'a') => error
+
+evaluate.sps:12.17-12.33: error: DEBUG EVALUATE: Type mismatch invoking DATE.
+QYR(integer, integer) as date.qyr(string, string).
+   12 | DEBUG EVALUATE /date.qyr('a','b').
+      |                 ^~~~~~~~~~~~~~~~~
+
+evaluate.sps:12.26-12.28: note: DEBUG EVALUATE: This argument has type 'string'
+but 'integer' is required.
+   12 | DEBUG EVALUATE /date.qyr('a','b').
+      |                          ^~~
+
+evaluate.sps:12.30-12.32: note: DEBUG EVALUATE: This argument has type 'string'
+but 'integer' is required.
+   12 | DEBUG EVALUATE /date.qyr('a','b').
+      |                              ^~~
+
+date.qyr('a','b') => error
+])
+done
+AT_CLEANUP
+
+AT_SETUP([expressions - DATE.WKYR])
+AT_KEYWORDS([expression expressions evaluate])
+AT_DATA([evaluate-base.sps], [dnl
+SET EPOCH 1940.
+DEBUG EVALUATE SET opt.
+DEBUG EVALUATE /ctime.days(date.wkyr(1,2000)).
+DEBUG EVALUATE /ctime.days(date.wkyr(15,1999)).
+DEBUG EVALUATE /ctime.days(date.wkyr(36,1999)).
+DEBUG EVALUATE /ctime.days(date.wkyr(54,1999)).
+DEBUG EVALUATE /ctime.days(date.wkyr($sysmis,1999)).
+DEBUG EVALUATE /ctime.days(date.wkyr(1,$sysmis)).
+DEBUG EVALUATE /ctime.days(date.wkyr($sysmis,$sysmis)).
+DEBUG EVALUATE /date.wkyr('a',1999).
+DEBUG EVALUATE /date.wkyr(5,'a').
+DEBUG EVALUATE /date.wkyr('a','b').
+])
+
+for opt in OPT NOOPT; do
+    AS_BOX([$opt])
+    sed "s/opt/$opt/" < evaluate-base.sps > evaluate.sps
+    AT_CHECK([pspp --testing-mode evaluate.sps], [1], [dnl
+ctime.days(date.wkyr(1,2000)) => 152385.00
+
+ctime.days(date.wkyr(15,1999)) => 152118.00
+
+ctime.days(date.wkyr(36,1999)) => 152265.00
+
+evaluate.sps:6.38-6.39: error: DEBUG EVALUATE: The week argument to DATE.WKYR
+is outside the acceptable range of 1 to 53.  The result will be system-missing.
+    6 | DEBUG EVALUATE /ctime.days(date.wkyr(54,1999)).
+      |                                      ^~
+
+ctime.days(date.wkyr(54,1999)) => sysmis
+
+ctime.days(date.wkyr($sysmis,1999)) => sysmis
+
+ctime.days(date.wkyr(1,$sysmis)) => sysmis
+
+ctime.days(date.wkyr($sysmis,$sysmis)) => sysmis
+
+evaluate.sps:10.17-10.35: error: DEBUG EVALUATE: Type mismatch invoking DATE.
+WKYR(integer, integer) as date.wkyr(string, number).
+   10 | DEBUG EVALUATE /date.wkyr('a',1999).
+      |                 ^~~~~~~~~~~~~~~~~~~
+
+evaluate.sps:10.27-10.29: note: DEBUG EVALUATE: This argument has type 'string'
+but 'integer' is required.
+   10 | DEBUG EVALUATE /date.wkyr('a',1999).
+      |                           ^~~
+
+date.wkyr('a',1999) => error
+
+evaluate.sps:11.17-11.32: error: DEBUG EVALUATE: Type mismatch invoking DATE.
+WKYR(integer, integer) as date.wkyr(number, string).
+   11 | DEBUG EVALUATE /date.wkyr(5,'a').
+      |                 ^~~~~~~~~~~~~~~~
+
+evaluate.sps:11.29-11.31: note: DEBUG EVALUATE: This argument has type 'string'
+but 'integer' is required.
+   11 | DEBUG EVALUATE /date.wkyr(5,'a').
+      |                             ^~~
+
+date.wkyr(5,'a') => error
+
+evaluate.sps:12.17-12.34: error: DEBUG EVALUATE: Type mismatch invoking DATE.
+WKYR(integer, integer) as date.wkyr(string, string).
+   12 | DEBUG EVALUATE /date.wkyr('a','b').
+      |                 ^~~~~~~~~~~~~~~~~~
+
+evaluate.sps:12.27-12.29: note: DEBUG EVALUATE: This argument has type 'string'
+but 'integer' is required.
+   12 | DEBUG EVALUATE /date.wkyr('a','b').
+      |                           ^~~
+
+evaluate.sps:12.31-12.33: note: DEBUG EVALUATE: This argument has type 'string'
+but 'integer' is required.
+   12 | DEBUG EVALUATE /date.wkyr('a','b').
+      |                               ^~~
+
+date.wkyr('a','b') => error
+])
+done
+AT_CLEANUP
+
+AT_SETUP([expressions - DATE.YRDAY])
+AT_KEYWORDS([expression expressions evaluate])
+AT_DATA([evaluate-base.sps], [dnl
+SET EPOCH 1940.
+DEBUG EVALUATE SET opt.
+DEBUG EVALUATE /ctime.days(date.yrday(2000,1)).
+DEBUG EVALUATE /ctime.days(date.yrday(2000,100)).
+DEBUG EVALUATE /ctime.days(date.yrday(2000,253)).
+DEBUG EVALUATE /ctime.days(date.yrday(2000,500)).
+DEBUG EVALUATE /ctime.days(date.yrday(2000,-100)).
+DEBUG EVALUATE /ctime.days(date.yrday(1999,$sysmis)).
+DEBUG EVALUATE /ctime.days(date.yrday($sysmis,1)).
+DEBUG EVALUATE /ctime.days(date.yrday($sysmis,$sysmis)).
+DEBUG EVALUATE /date.yrday(1999,'a').
+DEBUG EVALUATE /date.yrday('a',5).
+DEBUG EVALUATE /date.yrday('a','b').
+])
+
+for opt in OPT NOOPT; do
+    AS_BOX([$opt])
+    sed "s/opt/$opt/" < evaluate-base.sps > evaluate.sps
+    AT_CHECK([pspp --testing-mode evaluate.sps], [1], [dnl
+ctime.days(date.yrday(2000,1)) => 152385.00
+
+ctime.days(date.yrday(2000,100)) => 152484.00
+
+ctime.days(date.yrday(2000,253)) => 152637.00
+
+evaluate.sps:6.44-6.46: error: DEBUG EVALUATE: The value 500 as day argument to
+DATE.YRDAY is outside the acceptable range of 1 to 366.  The result will be
+system-missing.
+    6 | DEBUG EVALUATE /ctime.days(date.yrday(2000,500)).
+      |                                            ^~~
+
+ctime.days(date.yrday(2000,500)) => sysmis
+
+evaluate.sps:7.44-7.47: error: DEBUG EVALUATE: The value -100 as day argument
+to DATE.YRDAY is outside the acceptable range of 1 to 366.  The result will be
+system-missing.
+    7 | DEBUG EVALUATE /ctime.days(date.yrday(2000,-100)).
+      |                                            ^~~~
+
+ctime.days(date.yrday(2000,-100)) => sysmis
+
+ctime.days(date.yrday(1999,$sysmis)) => sysmis
+
+ctime.days(date.yrday($sysmis,1)) => sysmis
+
+ctime.days(date.yrday($sysmis,$sysmis)) => sysmis
+
+evaluate.sps:11.17-11.36: error: DEBUG EVALUATE: Type mismatch invoking DATE.
+YRDAY(integer, integer) as date.yrday(number, string).
+   11 | DEBUG EVALUATE /date.yrday(1999,'a').
+      |                 ^~~~~~~~~~~~~~~~~~~~
+
+evaluate.sps:11.33-11.35: note: DEBUG EVALUATE: This argument has type 'string'
+but 'integer' is required.
+   11 | DEBUG EVALUATE /date.yrday(1999,'a').
+      |                                 ^~~
+
+date.yrday(1999,'a') => error
+
+evaluate.sps:12.17-12.33: error: DEBUG EVALUATE: Type mismatch invoking DATE.
+YRDAY(integer, integer) as date.yrday(string, number).
+   12 | DEBUG EVALUATE /date.yrday('a',5).
+      |                 ^~~~~~~~~~~~~~~~~
+
+evaluate.sps:12.28-12.30: note: DEBUG EVALUATE: This argument has type 'string'
+but 'integer' is required.
+   12 | DEBUG EVALUATE /date.yrday('a',5).
+      |                            ^~~
+
+date.yrday('a',5) => error
+
+evaluate.sps:13.17-13.35: error: DEBUG EVALUATE: Type mismatch invoking DATE.
+YRDAY(integer, integer) as date.yrday(string, string).
+   13 | DEBUG EVALUATE /date.yrday('a','b').
+      |                 ^~~~~~~~~~~~~~~~~~~
+
+evaluate.sps:13.28-13.30: note: DEBUG EVALUATE: This argument has type 'string'
+but 'integer' is required.
+   13 | DEBUG EVALUATE /date.yrday('a','b').
+      |                            ^~~
+
+evaluate.sps:13.32-13.34: note: DEBUG EVALUATE: This argument has type 'string'
+but 'integer' is required.
+   13 | DEBUG EVALUATE /date.yrday('a','b').
+      |                                ^~~
+
+date.yrday('a','b') => error
+])
+done
+AT_CLEANUP
+
+AT_SETUP([expressions - XDATE.DATE])
+AT_KEYWORDS([expression expressions evaluate])
+AT_DATA([evaluate-base.sps], [dnl
+SET EPOCH 1940.
+DEBUG EVALUATE SET opt.
+DEBUG EVALUATE /xdate.date(date.mdy(6,10,1648) + time.hms(0,0,0)) / 86400.
+DEBUG EVALUATE /xdate.date(date.mdy(6,30,1680) + time.hms(4,50,38)) / 86400.
+DEBUG EVALUATE /xdate.date(date.mdy(7,24,1716) + time.hms(12,31,35)) / 86400.
+DEBUG EVALUATE /xdate.date(date.mdy(6,19,1768) + time.hms(12,47,53)) / 86400.
+DEBUG EVALUATE /xdate.date(date.mdy(8,2,1819) + time.hms(1,26,0)) / 86400.
+DEBUG EVALUATE /xdate.date(date.mdy(3,27,1839) + time.hms(20,58,11)) / 86400.
+DEBUG EVALUATE /xdate.date(date.mdy(4,19,1903) + time.hms(7,36,5)) / 86400.
+DEBUG EVALUATE /xdate.date(date.mdy(8,25,1929) + time.hms(15,43,49)) / 86400.
+DEBUG EVALUATE /xdate.date(date.mdy(9,29,1941) + time.hms(4,25,9)) / 86400.
+DEBUG EVALUATE /xdate.date(date.mdy(4,19,1943) + time.hms(6,49,27)) / 86400.
+DEBUG EVALUATE /xdate.date(date.mdy(10,7,1943) + time.hms(2,57,52)) / 86400.
+DEBUG EVALUATE /xdate.date(date.mdy(3,17,1992) + time.hms(16,45,44)) / 86400.
+DEBUG EVALUATE /xdate.date(date.mdy(2,25,1996) + time.hms(21,30,57)) / 86400.
+DEBUG EVALUATE /xdate.date(date.mdy(9,29,1941) + time.hms(4,25,9)) / 86400.
+DEBUG EVALUATE /xdate.date(date.mdy(4,19,43) + time.hms(6,49,27)) / 86400.
+DEBUG EVALUATE /xdate.date(date.mdy(10,7,43) + time.hms(2,57,52)) / 86400.
+DEBUG EVALUATE /xdate.date(date.mdy(3,17,92) + time.hms(16,45,44)) / 86400.
+DEBUG EVALUATE /xdate.date(date.mdy(2,25,96) + time.hms(21,30,57)) / 86400.
+DEBUG EVALUATE /xdate.date(date.mdy(11,10,2038) + time.hms(22,30,4)) / 86400.
+DEBUG EVALUATE /xdate.date(date.mdy(7,18,2094) + time.hms(1,56,51)) / 86400.
+DEBUG EVALUATE /xdate.date(123.4).
+DEBUG EVALUATE /xdate.date('').
+])
+
+for opt in OPT NOOPT; do
+    AS_BOX([$opt])
+    sed "s/opt/$opt/" < evaluate-base.sps > evaluate.sps
+    AT_CHECK([pspp --testing-mode evaluate.sps], [1], [dnl
+xdate.date(date.mdy(6,10,1648) + time.hms(0,0,0)) / 86400 => 23981.00
+
+xdate.date(date.mdy(6,30,1680) + time.hms(4,50,38)) / 86400 => 35689.00
+
+xdate.date(date.mdy(7,24,1716) + time.hms(12,31,35)) / 86400 => 48861.00
+
+xdate.date(date.mdy(6,19,1768) + time.hms(12,47,53)) / 86400 => 67819.00
+
+xdate.date(date.mdy(8,2,1819) + time.hms(1,26,0)) / 86400 => 86489.00
+
+xdate.date(date.mdy(3,27,1839) + time.hms(20,58,11)) / 86400 => 93666.00
+
+xdate.date(date.mdy(4,19,1903) + time.hms(7,36,5)) / 86400 => 117064.00
+
+xdate.date(date.mdy(8,25,1929) + time.hms(15,43,49)) / 86400 => 126689.00
+
+xdate.date(date.mdy(9,29,1941) + time.hms(4,25,9)) / 86400 => 131107.00
+
+xdate.date(date.mdy(4,19,1943) + time.hms(6,49,27)) / 86400 => 131674.00
+
+xdate.date(date.mdy(10,7,1943) + time.hms(2,57,52)) / 86400 => 131845.00
+
+xdate.date(date.mdy(3,17,1992) + time.hms(16,45,44)) / 86400 => 149539.00
+
+xdate.date(date.mdy(2,25,1996) + time.hms(21,30,57)) / 86400 => 150979.00
+
+xdate.date(date.mdy(9,29,1941) + time.hms(4,25,9)) / 86400 => 131107.00
+
+xdate.date(date.mdy(4,19,43) + time.hms(6,49,27)) / 86400 => 131674.00
+
+xdate.date(date.mdy(10,7,43) + time.hms(2,57,52)) / 86400 => 131845.00
+
+xdate.date(date.mdy(3,17,92) + time.hms(16,45,44)) / 86400 => 149539.00
+
+xdate.date(date.mdy(2,25,96) + time.hms(21,30,57)) / 86400 => 150979.00
+
+xdate.date(date.mdy(11,10,2038) + time.hms(22,30,4)) / 86400 => 166578.00
+
+xdate.date(date.mdy(7,18,2094) + time.hms(1,56,51)) / 86400 => 186917.00
+
+xdate.date(123.4) => 0.00
+
+evaluate.sps:24.17-24.30: error: DEBUG EVALUATE: Type mismatch invoking XDATE.
+DATE(number) as xdate.date(string).
+   24 | DEBUG EVALUATE /xdate.date('').
+      |                 ^~~~~~~~~~~~~~
+
+evaluate.sps:24.28-24.29: note: DEBUG EVALUATE: This argument has type 'string'
+but 'number' is required.
+   24 | DEBUG EVALUATE /xdate.date('').
+      |                            ^~
+
+xdate.date('') => error
+])
+done
+AT_CLEANUP
+
+AT_SETUP([expressions - XDATE.HOUR])
+AT_KEYWORDS([expression expressions evaluate])
+AT_DATA([evaluate-base.sps], [dnl
+SET EPOCH 1940.
+DEBUG EVALUATE SET opt.
+DEBUG EVALUATE /xdate.hour(date.mdy(6,10,1648) + time.hms(0,0,0)).
+DEBUG EVALUATE /xdate.hour(date.mdy(6,30,1680) + time.hms(4,50,38)).
+DEBUG EVALUATE /xdate.hour(date.mdy(7,24,1716) + time.hms(12,31,35)).
+DEBUG EVALUATE /xdate.hour(date.mdy(6,19,1768) + time.hms(12,47,53)).
+DEBUG EVALUATE /xdate.hour(date.mdy(8,2,1819) + time.hms(1,26,0)).
+DEBUG EVALUATE /xdate.hour(date.mdy(3,27,1839) + time.hms(20,58,11)).
+DEBUG EVALUATE /xdate.hour(date.mdy(4,19,1903) + time.hms(7,36,5)).
+DEBUG EVALUATE /xdate.hour(date.mdy(8,25,1929) + time.hms(15,43,49)).
+DEBUG EVALUATE /xdate.hour(date.mdy(9,29,1941) + time.hms(4,25,9)).
+DEBUG EVALUATE /xdate.hour(date.mdy(4,19,1943) + time.hms(6,49,27)).
+DEBUG EVALUATE /xdate.hour(date.mdy(10,7,1943) + time.hms(2,57,52)).
+DEBUG EVALUATE /xdate.hour(date.mdy(3,17,1992) + time.hms(16,45,44)).
+DEBUG EVALUATE /xdate.hour(date.mdy(2,25,1996) + time.hms(21,30,57)).
+DEBUG EVALUATE /xdate.hour(date.mdy(9,29,1941) + time.hms(4,25,9)).
+DEBUG EVALUATE /xdate.hour(date.mdy(4,19,43) + time.hms(6,49,27)).
+DEBUG EVALUATE /xdate.hour(date.mdy(10,7,43) + time.hms(2,57,52)).
+DEBUG EVALUATE /xdate.hour(date.mdy(3,17,92) + time.hms(16,45,44)).
+DEBUG EVALUATE /xdate.hour(date.mdy(2,25,96) + time.hms(21,30,57)).
+DEBUG EVALUATE /xdate.hour(date.mdy(11,10,2038) + time.hms(22,30,4)).
+DEBUG EVALUATE /xdate.hour(date.mdy(7,18,2094) + time.hms(1,56,51)).
+DEBUG EVALUATE /xdate.hour(-1).
+DEBUG EVALUATE /xdate.hour(1).
+DEBUG EVALUATE /xdate.hour($sysmis).
+DEBUG EVALUATE /xdate.hour('').
+])
+
+for opt in OPT NOOPT; do
+    AS_BOX([$opt])
+    sed "s/opt/$opt/" < evaluate-base.sps > evaluate.sps
+    AT_CHECK([pspp --testing-mode evaluate.sps], [1], [dnl
+xdate.hour(date.mdy(6,10,1648) + time.hms(0,0,0)) => 0.00
+
+xdate.hour(date.mdy(6,30,1680) + time.hms(4,50,38)) => 4.00
+
+xdate.hour(date.mdy(7,24,1716) + time.hms(12,31,35)) => 12.00
+
+xdate.hour(date.mdy(6,19,1768) + time.hms(12,47,53)) => 12.00
+
+xdate.hour(date.mdy(8,2,1819) + time.hms(1,26,0)) => 1.00
+
+xdate.hour(date.mdy(3,27,1839) + time.hms(20,58,11)) => 20.00
+
+xdate.hour(date.mdy(4,19,1903) + time.hms(7,36,5)) => 7.00
+
+xdate.hour(date.mdy(8,25,1929) + time.hms(15,43,49)) => 15.00
+
+xdate.hour(date.mdy(9,29,1941) + time.hms(4,25,9)) => 4.00
+
+xdate.hour(date.mdy(4,19,1943) + time.hms(6,49,27)) => 6.00
+
+xdate.hour(date.mdy(10,7,1943) + time.hms(2,57,52)) => 2.00
+
+xdate.hour(date.mdy(3,17,1992) + time.hms(16,45,44)) => 16.00
+
+xdate.hour(date.mdy(2,25,1996) + time.hms(21,30,57)) => 21.00
+
+xdate.hour(date.mdy(9,29,1941) + time.hms(4,25,9)) => 4.00
+
+xdate.hour(date.mdy(4,19,43) + time.hms(6,49,27)) => 6.00
+
+xdate.hour(date.mdy(10,7,43) + time.hms(2,57,52)) => 2.00
+
+xdate.hour(date.mdy(3,17,92) + time.hms(16,45,44)) => 16.00
+
+xdate.hour(date.mdy(2,25,96) + time.hms(21,30,57)) => 21.00
+
+xdate.hour(date.mdy(11,10,2038) + time.hms(22,30,4)) => 22.00
+
+xdate.hour(date.mdy(7,18,2094) + time.hms(1,56,51)) => 1.00
+
+xdate.hour(-1) => -1.00
+
+xdate.hour(1) => 0.00
+
+xdate.hour($sysmis) => sysmis
+
+evaluate.sps:26.17-26.30: error: DEBUG EVALUATE: Type mismatch invoking XDATE.
+HOUR(number) as xdate.hour(string).
+   26 | DEBUG EVALUATE /xdate.hour('').
+      |                 ^~~~~~~~~~~~~~
+
+evaluate.sps:26.28-26.29: note: DEBUG EVALUATE: This argument has type 'string'
+but 'number' is required.
+   26 | DEBUG EVALUATE /xdate.hour('').
+      |                            ^~
+
+xdate.hour('') => error
+])
+done
+AT_CLEANUP
+
+AT_SETUP([expressions - XDATE.JDAY])
+AT_KEYWORDS([expression expressions evaluate])
+AT_DATA([evaluate-base.sps], [dnl
+SET EPOCH 1940.
+DEBUG EVALUATE SET opt.
+DEBUG EVALUATE /xdate.jday(date.mdy(6,10,1648) + time.hms(0,0,0)).
+DEBUG EVALUATE /xdate.jday(date.mdy(6,30,1680) + time.hms(4,50,38)).
+DEBUG EVALUATE /xdate.jday(date.mdy(7,24,1716) + time.hms(12,31,35)).
+DEBUG EVALUATE /xdate.jday(date.mdy(6,19,1768) + time.hms(12,47,53)).
+DEBUG EVALUATE /xdate.jday(date.mdy(8,2,1819) + time.hms(1,26,0)).
+DEBUG EVALUATE /xdate.jday(date.mdy(3,27,1839) + time.hms(20,58,11)).
+DEBUG EVALUATE /xdate.jday(date.mdy(4,19,1903) + time.hms(7,36,5)).
+DEBUG EVALUATE /xdate.jday(date.mdy(8,25,1929) + time.hms(15,43,49)).
+DEBUG EVALUATE /xdate.jday(date.mdy(9,29,1941) + time.hms(4,25,9)).
+DEBUG EVALUATE /xdate.jday(date.mdy(4,19,1943) + time.hms(6,49,27)).
+DEBUG EVALUATE /xdate.jday(date.mdy(10,7,1943) + time.hms(2,57,52)).
+DEBUG EVALUATE /xdate.jday(date.mdy(3,17,1992) + time.hms(16,45,44)).
+DEBUG EVALUATE /xdate.jday(date.mdy(2,25,1996) + time.hms(21,30,57)).
+DEBUG EVALUATE /xdate.jday(date.mdy(9,29,1941) + time.hms(4,25,9)).
+DEBUG EVALUATE /xdate.jday(date.mdy(4,19,43) + time.hms(6,49,27)).
+DEBUG EVALUATE /xdate.jday(date.mdy(10,7,43) + time.hms(2,57,52)).
+DEBUG EVALUATE /xdate.jday(date.mdy(3,17,92) + time.hms(16,45,44)).
+DEBUG EVALUATE /xdate.jday(date.mdy(2,25,96) + time.hms(21,30,57)).
+DEBUG EVALUATE /xdate.jday(date.mdy(11,10,2038) + time.hms(22,30,4)).
+DEBUG EVALUATE /xdate.jday(date.mdy(7,18,2094) + time.hms(1,56,51)).
+DEBUG EVALUATE /xdate.jday(0).
+DEBUG EVALUATE /xdate.jday(1).
+DEBUG EVALUATE /xdate.jday(86400).
+])
+
+for opt in OPT NOOPT; do
+    AS_BOX([$opt])
+    sed "s/opt/$opt/" < evaluate-base.sps > evaluate.sps
+    AT_CHECK([pspp --testing-mode evaluate.sps], [0], [dnl
+xdate.jday(date.mdy(6,10,1648) + time.hms(0,0,0)) => 162.00
+
+xdate.jday(date.mdy(6,30,1680) + time.hms(4,50,38)) => 182.00
+
+xdate.jday(date.mdy(7,24,1716) + time.hms(12,31,35)) => 206.00
+
+xdate.jday(date.mdy(6,19,1768) + time.hms(12,47,53)) => 171.00
+
+xdate.jday(date.mdy(8,2,1819) + time.hms(1,26,0)) => 214.00
+
+xdate.jday(date.mdy(3,27,1839) + time.hms(20,58,11)) => 86.00
+
+xdate.jday(date.mdy(4,19,1903) + time.hms(7,36,5)) => 109.00
+
+xdate.jday(date.mdy(8,25,1929) + time.hms(15,43,49)) => 237.00
+
+xdate.jday(date.mdy(9,29,1941) + time.hms(4,25,9)) => 272.00
+
+xdate.jday(date.mdy(4,19,1943) + time.hms(6,49,27)) => 109.00
+
+xdate.jday(date.mdy(10,7,1943) + time.hms(2,57,52)) => 280.00
+
+xdate.jday(date.mdy(3,17,1992) + time.hms(16,45,44)) => 77.00
+
+xdate.jday(date.mdy(2,25,1996) + time.hms(21,30,57)) => 56.00
+
+xdate.jday(date.mdy(9,29,1941) + time.hms(4,25,9)) => 272.00
+
+xdate.jday(date.mdy(4,19,43) + time.hms(6,49,27)) => 109.00
+
+xdate.jday(date.mdy(10,7,43) + time.hms(2,57,52)) => 280.00
+
+xdate.jday(date.mdy(3,17,92) + time.hms(16,45,44)) => 77.00
+
+xdate.jday(date.mdy(2,25,96) + time.hms(21,30,57)) => 56.00
+
+xdate.jday(date.mdy(11,10,2038) + time.hms(22,30,4)) => 314.00
+
+xdate.jday(date.mdy(7,18,2094) + time.hms(1,56,51)) => 199.00
+
+xdate.jday(0) => sysmis
+
+xdate.jday(1) => sysmis
+
+xdate.jday(86400) => 288.00
+])
+done
+AT_CLEANUP
+
+AT_SETUP([expressions - XDATE.MDAY])
+AT_KEYWORDS([expression expressions evaluate])
+AT_DATA([evaluate-base.sps], [dnl
+SET EPOCH 1940.
+DEBUG EVALUATE SET opt.
+DEBUG EVALUATE /xdate.mday(date.mdy(6,10,1648) + time.hms(0,0,0)).
+DEBUG EVALUATE /xdate.mday(date.mdy(6,30,1680) + time.hms(4,50,38)).
+DEBUG EVALUATE /xdate.mday(date.mdy(7,24,1716) + time.hms(12,31,35)).
+DEBUG EVALUATE /xdate.mday(date.mdy(6,19,1768) + time.hms(12,47,53)).
+DEBUG EVALUATE /xdate.mday(date.mdy(8,2,1819) + time.hms(1,26,0)).
+DEBUG EVALUATE /xdate.mday(date.mdy(3,27,1839) + time.hms(20,58,11)).
+DEBUG EVALUATE /xdate.mday(date.mdy(4,19,1903) + time.hms(7,36,5)).
+DEBUG EVALUATE /xdate.mday(date.mdy(8,25,1929) + time.hms(15,43,49)).
+DEBUG EVALUATE /xdate.mday(date.mdy(9,29,1941) + time.hms(4,25,9)).
+DEBUG EVALUATE /xdate.mday(date.mdy(4,19,1943) + time.hms(6,49,27)).
+DEBUG EVALUATE /xdate.mday(date.mdy(10,7,1943) + time.hms(2,57,52)).
+DEBUG EVALUATE /xdate.mday(date.mdy(3,17,1992) + time.hms(16,45,44)).
+DEBUG EVALUATE /xdate.mday(date.mdy(2,25,1996) + time.hms(21,30,57)).
+DEBUG EVALUATE /xdate.mday(date.mdy(9,29,1941) + time.hms(4,25,9)).
+DEBUG EVALUATE /xdate.mday(date.mdy(4,19,43) + time.hms(6,49,27)).
+DEBUG EVALUATE /xdate.mday(date.mdy(10,7,43) + time.hms(2,57,52)).
+DEBUG EVALUATE /xdate.mday(date.mdy(3,17,92) + time.hms(16,45,44)).
+DEBUG EVALUATE /xdate.mday(date.mdy(2,25,96) + time.hms(21,30,57)).
+DEBUG EVALUATE /xdate.mday(date.mdy(11,10,2038) + time.hms(22,30,4)).
+DEBUG EVALUATE /xdate.mday(date.mdy(7,18,2094) + time.hms(1,56,51)).
+])
+
+for opt in OPT NOOPT; do
+    AS_BOX([$opt])
+    sed "s/opt/$opt/" < evaluate-base.sps > evaluate.sps
+    AT_CHECK([pspp --testing-mode evaluate.sps], [0], [dnl
+xdate.mday(date.mdy(6,10,1648) + time.hms(0,0,0)) => 10.00
+
+xdate.mday(date.mdy(6,30,1680) + time.hms(4,50,38)) => 30.00
+
+xdate.mday(date.mdy(7,24,1716) + time.hms(12,31,35)) => 24.00
+
+xdate.mday(date.mdy(6,19,1768) + time.hms(12,47,53)) => 19.00
+
+xdate.mday(date.mdy(8,2,1819) + time.hms(1,26,0)) => 2.00
+
+xdate.mday(date.mdy(3,27,1839) + time.hms(20,58,11)) => 27.00
+
+xdate.mday(date.mdy(4,19,1903) + time.hms(7,36,5)) => 19.00
+
+xdate.mday(date.mdy(8,25,1929) + time.hms(15,43,49)) => 25.00
+
+xdate.mday(date.mdy(9,29,1941) + time.hms(4,25,9)) => 29.00
+
+xdate.mday(date.mdy(4,19,1943) + time.hms(6,49,27)) => 19.00
+
+xdate.mday(date.mdy(10,7,1943) + time.hms(2,57,52)) => 7.00
+
+xdate.mday(date.mdy(3,17,1992) + time.hms(16,45,44)) => 17.00
+
+xdate.mday(date.mdy(2,25,1996) + time.hms(21,30,57)) => 25.00
+
+xdate.mday(date.mdy(9,29,1941) + time.hms(4,25,9)) => 29.00
+
+xdate.mday(date.mdy(4,19,43) + time.hms(6,49,27)) => 19.00
+
+xdate.mday(date.mdy(10,7,43) + time.hms(2,57,52)) => 7.00
+
+xdate.mday(date.mdy(3,17,92) + time.hms(16,45,44)) => 17.00
+
+xdate.mday(date.mdy(2,25,96) + time.hms(21,30,57)) => 25.00
+
+xdate.mday(date.mdy(11,10,2038) + time.hms(22,30,4)) => 10.00
+
+xdate.mday(date.mdy(7,18,2094) + time.hms(1,56,51)) => 18.00
+])
+done
+AT_CLEANUP
+
+AT_SETUP([expressions - XDATE.MINUTE])
+AT_KEYWORDS([expression expressions evaluate])
+AT_DATA([evaluate-base.sps], [dnl
+SET EPOCH 1940.
+DEBUG EVALUATE SET opt.
+DEBUG EVALUATE /xdate.minute(date.mdy(6,10,1648) + time.hms(0,0,0)).
+DEBUG EVALUATE /xdate.minute(date.mdy(6,30,1680) + time.hms(4,50,38)).
+DEBUG EVALUATE /xdate.minute(date.mdy(7,24,1716) + time.hms(12,31,35)).
+DEBUG EVALUATE /xdate.minute(date.mdy(6,19,1768) + time.hms(12,47,53)).
+DEBUG EVALUATE /xdate.minute(date.mdy(8,2,1819) + time.hms(1,26,0)).
+DEBUG EVALUATE /xdate.minute(date.mdy(3,27,1839) + time.hms(20,58,11)).
+DEBUG EVALUATE /xdate.minute(date.mdy(4,19,1903) + time.hms(7,36,5)).
+DEBUG EVALUATE /xdate.minute(date.mdy(8,25,1929) + time.hms(15,43,49)).
+DEBUG EVALUATE /xdate.minute(date.mdy(9,29,1941) + time.hms(4,25,9)).
+DEBUG EVALUATE /xdate.minute(date.mdy(4,19,1943) + time.hms(6,49,27)).
+DEBUG EVALUATE /xdate.minute(date.mdy(10,7,1943) + time.hms(2,57,52)).
+DEBUG EVALUATE /xdate.minute(date.mdy(3,17,1992) + time.hms(16,45,44)).
+DEBUG EVALUATE /xdate.minute(date.mdy(2,25,1996) + time.hms(21,30,57)).
+DEBUG EVALUATE /xdate.minute(date.mdy(9,29,1941) + time.hms(4,25,9)).
+DEBUG EVALUATE /xdate.minute(date.mdy(4,19,43) + time.hms(6,49,27)).
+DEBUG EVALUATE /xdate.minute(date.mdy(10,7,43) + time.hms(2,57,52)).
+DEBUG EVALUATE /xdate.minute(date.mdy(3,17,92) + time.hms(16,45,44)).
+DEBUG EVALUATE /xdate.minute(date.mdy(2,25,96) + time.hms(21,30,57)).
+DEBUG EVALUATE /xdate.minute(date.mdy(11,10,2038) + time.hms(22,30,4)).
+DEBUG EVALUATE /xdate.minute(date.mdy(7,18,2094) + time.hms(1,56,51)).
+])
+
+for opt in OPT NOOPT; do
+    AS_BOX([$opt])
+    sed "s/opt/$opt/" < evaluate-base.sps > evaluate.sps
+    AT_CHECK([pspp --testing-mode evaluate.sps], [0], [dnl
+xdate.minute(date.mdy(6,10,1648) + time.hms(0,0,0)) => 0.00
+
+xdate.minute(date.mdy(6,30,1680) + time.hms(4,50,38)) => 50.00
+
+xdate.minute(date.mdy(7,24,1716) + time.hms(12,31,35)) => 31.00
+
+xdate.minute(date.mdy(6,19,1768) + time.hms(12,47,53)) => 47.00
+
+xdate.minute(date.mdy(8,2,1819) + time.hms(1,26,0)) => 26.00
+
+xdate.minute(date.mdy(3,27,1839) + time.hms(20,58,11)) => 58.00
+
+xdate.minute(date.mdy(4,19,1903) + time.hms(7,36,5)) => 36.00
+
+xdate.minute(date.mdy(8,25,1929) + time.hms(15,43,49)) => 43.00
+
+xdate.minute(date.mdy(9,29,1941) + time.hms(4,25,9)) => 25.00
+
+xdate.minute(date.mdy(4,19,1943) + time.hms(6,49,27)) => 49.00
+
+xdate.minute(date.mdy(10,7,1943) + time.hms(2,57,52)) => 57.00
+
+xdate.minute(date.mdy(3,17,1992) + time.hms(16,45,44)) => 45.00
+
+xdate.minute(date.mdy(2,25,1996) + time.hms(21,30,57)) => 30.00
+
+xdate.minute(date.mdy(9,29,1941) + time.hms(4,25,9)) => 25.00
+
+xdate.minute(date.mdy(4,19,43) + time.hms(6,49,27)) => 49.00
+
+xdate.minute(date.mdy(10,7,43) + time.hms(2,57,52)) => 57.00
+
+xdate.minute(date.mdy(3,17,92) + time.hms(16,45,44)) => 45.00
+
+xdate.minute(date.mdy(2,25,96) + time.hms(21,30,57)) => 30.00
+
+xdate.minute(date.mdy(11,10,2038) + time.hms(22,30,4)) => 30.00
+
+xdate.minute(date.mdy(7,18,2094) + time.hms(1,56,51)) => 56.00
+])
+done
+AT_CLEANUP
+
+AT_SETUP([expressions - XDATE.MONTH])
+AT_KEYWORDS([expression expressions evaluate])
+AT_DATA([evaluate-base.sps], [dnl
+SET EPOCH 1940.
+DEBUG EVALUATE SET opt.
+DEBUG EVALUATE /xdate.month(date.mdy(6,10,1648) + time.hms(0,0,0)).
+DEBUG EVALUATE /xdate.month(date.mdy(6,30,1680) + time.hms(4,50,38)).
+DEBUG EVALUATE /xdate.month(date.mdy(7,24,1716) + time.hms(12,31,35)).
+DEBUG EVALUATE /xdate.month(date.mdy(6,19,1768) + time.hms(12,47,53)).
+DEBUG EVALUATE /xdate.month(date.mdy(8,2,1819) + time.hms(1,26,0)).
+DEBUG EVALUATE /xdate.month(date.mdy(3,27,1839) + time.hms(20,58,11)).
+DEBUG EVALUATE /xdate.month(date.mdy(4,19,1903) + time.hms(7,36,5)).
+DEBUG EVALUATE /xdate.month(date.mdy(8,25,1929) + time.hms(15,43,49)).
+DEBUG EVALUATE /xdate.month(date.mdy(9,29,1941) + time.hms(4,25,9)).
+DEBUG EVALUATE /xdate.month(date.mdy(4,19,1943) + time.hms(6,49,27)).
+DEBUG EVALUATE /xdate.month(date.mdy(10,7,1943) + time.hms(2,57,52)).
+DEBUG EVALUATE /xdate.month(date.mdy(3,17,1992) + time.hms(16,45,44)).
+DEBUG EVALUATE /xdate.month(date.mdy(2,25,1996) + time.hms(21,30,57)).
+DEBUG EVALUATE /xdate.month(date.mdy(9,29,1941) + time.hms(4,25,9)).
+DEBUG EVALUATE /xdate.month(date.mdy(4,19,43) + time.hms(6,49,27)).
+DEBUG EVALUATE /xdate.month(date.mdy(10,7,43) + time.hms(2,57,52)).
+DEBUG EVALUATE /xdate.month(date.mdy(3,17,92) + time.hms(16,45,44)).
+DEBUG EVALUATE /xdate.month(date.mdy(2,25,96) + time.hms(21,30,57)).
+DEBUG EVALUATE /xdate.month(date.mdy(11,10,2038) + time.hms(22,30,4)).
+DEBUG EVALUATE /xdate.month(date.mdy(7,18,2094) + time.hms(1,56,51)).
+])
+
+for opt in OPT NOOPT; do
+    AS_BOX([$opt])
+    sed "s/opt/$opt/" < evaluate-base.sps > evaluate.sps
+    AT_CHECK([pspp --testing-mode evaluate.sps], [0], [dnl
+xdate.month(date.mdy(6,10,1648) + time.hms(0,0,0)) => 6.00
+
+xdate.month(date.mdy(6,30,1680) + time.hms(4,50,38)) => 6.00
+
+xdate.month(date.mdy(7,24,1716) + time.hms(12,31,35)) => 7.00
+
+xdate.month(date.mdy(6,19,1768) + time.hms(12,47,53)) => 6.00
+
+xdate.month(date.mdy(8,2,1819) + time.hms(1,26,0)) => 8.00
+
+xdate.month(date.mdy(3,27,1839) + time.hms(20,58,11)) => 3.00
+
+xdate.month(date.mdy(4,19,1903) + time.hms(7,36,5)) => 4.00
+
+xdate.month(date.mdy(8,25,1929) + time.hms(15,43,49)) => 8.00
+
+xdate.month(date.mdy(9,29,1941) + time.hms(4,25,9)) => 9.00
+
+xdate.month(date.mdy(4,19,1943) + time.hms(6,49,27)) => 4.00
+
+xdate.month(date.mdy(10,7,1943) + time.hms(2,57,52)) => 10.00
+
+xdate.month(date.mdy(3,17,1992) + time.hms(16,45,44)) => 3.00
+
+xdate.month(date.mdy(2,25,1996) + time.hms(21,30,57)) => 2.00
+
+xdate.month(date.mdy(9,29,1941) + time.hms(4,25,9)) => 9.00
+
+xdate.month(date.mdy(4,19,43) + time.hms(6,49,27)) => 4.00
+
+xdate.month(date.mdy(10,7,43) + time.hms(2,57,52)) => 10.00
+
+xdate.month(date.mdy(3,17,92) + time.hms(16,45,44)) => 3.00
+
+xdate.month(date.mdy(2,25,96) + time.hms(21,30,57)) => 2.00
+
+xdate.month(date.mdy(11,10,2038) + time.hms(22,30,4)) => 11.00
+
+xdate.month(date.mdy(7,18,2094) + time.hms(1,56,51)) => 7.00
+])
+done
+AT_CLEANUP
+
+AT_SETUP([expressions - XDATE.QUARTER])
+AT_KEYWORDS([expression expressions evaluate])
+AT_DATA([evaluate-base.sps], [dnl
+SET EPOCH 1940.
+DEBUG EVALUATE SET opt.
+DEBUG EVALUATE /xdate.quarter(date.mdy(6,10,1648) + time.hms(0,0,0)).
+DEBUG EVALUATE /xdate.quarter(date.mdy(6,30,1680) + time.hms(4,50,38)).
+DEBUG EVALUATE /xdate.quarter(date.mdy(7,24,1716) + time.hms(12,31,35)).
+DEBUG EVALUATE /xdate.quarter(date.mdy(6,19,1768) + time.hms(12,47,53)).
+DEBUG EVALUATE /xdate.quarter(date.mdy(8,2,1819) + time.hms(1,26,0)).
+DEBUG EVALUATE /xdate.quarter(date.mdy(3,27,1839) + time.hms(20,58,11)).
+DEBUG EVALUATE /xdate.quarter(date.mdy(4,19,1903) + time.hms(7,36,5)).
+DEBUG EVALUATE /xdate.quarter(date.mdy(8,25,1929) + time.hms(15,43,49)).
+DEBUG EVALUATE /xdate.quarter(date.mdy(9,29,1941) + time.hms(4,25,9)).
+DEBUG EVALUATE /xdate.quarter(date.mdy(4,19,1943) + time.hms(6,49,27)).
+DEBUG EVALUATE /xdate.quarter(date.mdy(10,7,1943) + time.hms(2,57,52)).
+DEBUG EVALUATE /xdate.quarter(date.mdy(3,17,1992) + time.hms(16,45,44)).
+DEBUG EVALUATE /xdate.quarter(date.mdy(2,25,1996) + time.hms(21,30,57)).
+DEBUG EVALUATE /xdate.quarter(date.mdy(9,29,1941) + time.hms(4,25,9)).
+DEBUG EVALUATE /xdate.quarter(date.mdy(4,19,43) + time.hms(6,49,27)).
+DEBUG EVALUATE /xdate.quarter(date.mdy(10,7,43) + time.hms(2,57,52)).
+DEBUG EVALUATE /xdate.quarter(date.mdy(3,17,92) + time.hms(16,45,44)).
+DEBUG EVALUATE /xdate.quarter(date.mdy(2,25,96) + time.hms(21,30,57)).
+DEBUG EVALUATE /xdate.quarter(date.mdy(11,10,2038) + time.hms(22,30,4)).
+DEBUG EVALUATE /xdate.quarter(date.mdy(7,18,2094) + time.hms(1,56,51)).
+])
+
+for opt in OPT NOOPT; do
+    AS_BOX([$opt])
+    sed "s/opt/$opt/" < evaluate-base.sps > evaluate.sps
+    AT_CHECK([pspp --testing-mode evaluate.sps], [0], [dnl
+xdate.quarter(date.mdy(6,10,1648) + time.hms(0,0,0)) => 2.00
+
+xdate.quarter(date.mdy(6,30,1680) + time.hms(4,50,38)) => 2.00
+
+xdate.quarter(date.mdy(7,24,1716) + time.hms(12,31,35)) => 3.00
+
+xdate.quarter(date.mdy(6,19,1768) + time.hms(12,47,53)) => 2.00
+
+xdate.quarter(date.mdy(8,2,1819) + time.hms(1,26,0)) => 3.00
+
+xdate.quarter(date.mdy(3,27,1839) + time.hms(20,58,11)) => 1.00
+
+xdate.quarter(date.mdy(4,19,1903) + time.hms(7,36,5)) => 2.00
+
+xdate.quarter(date.mdy(8,25,1929) + time.hms(15,43,49)) => 3.00
+
+xdate.quarter(date.mdy(9,29,1941) + time.hms(4,25,9)) => 3.00
+
+xdate.quarter(date.mdy(4,19,1943) + time.hms(6,49,27)) => 2.00
+
+xdate.quarter(date.mdy(10,7,1943) + time.hms(2,57,52)) => 4.00
+
+xdate.quarter(date.mdy(3,17,1992) + time.hms(16,45,44)) => 1.00
+
+xdate.quarter(date.mdy(2,25,1996) + time.hms(21,30,57)) => 1.00
+
+xdate.quarter(date.mdy(9,29,1941) + time.hms(4,25,9)) => 3.00
+
+xdate.quarter(date.mdy(4,19,43) + time.hms(6,49,27)) => 2.00
+
+xdate.quarter(date.mdy(10,7,43) + time.hms(2,57,52)) => 4.00
+
+xdate.quarter(date.mdy(3,17,92) + time.hms(16,45,44)) => 1.00
+
+xdate.quarter(date.mdy(2,25,96) + time.hms(21,30,57)) => 1.00
+
+xdate.quarter(date.mdy(11,10,2038) + time.hms(22,30,4)) => 4.00
+
+xdate.quarter(date.mdy(7,18,2094) + time.hms(1,56,51)) => 3.00
+])
+done
+AT_CLEANUP
+
+AT_SETUP([expressions - XDATE.SECOND])
+AT_KEYWORDS([expression expressions evaluate])
+AT_DATA([evaluate-base.sps], [dnl
+SET EPOCH 1940.
+DEBUG EVALUATE SET opt.
+DEBUG EVALUATE /xdate.second(date.mdy(6,10,1648) + time.hms(0,0,0)).
+DEBUG EVALUATE /xdate.second(date.mdy(6,30,1680) + time.hms(4,50,38)).
+DEBUG EVALUATE /xdate.second(date.mdy(7,24,1716) + time.hms(12,31,35)).
+DEBUG EVALUATE /xdate.second(date.mdy(6,19,1768) + time.hms(12,47,53)).
+DEBUG EVALUATE /xdate.second(date.mdy(8,2,1819) + time.hms(1,26,0)).
+DEBUG EVALUATE /xdate.second(date.mdy(3,27,1839) + time.hms(20,58,11)).
+DEBUG EVALUATE /xdate.second(date.mdy(4,19,1903) + time.hms(7,36,5)).
+DEBUG EVALUATE /xdate.second(date.mdy(8,25,1929) + time.hms(15,43,49)).
+DEBUG EVALUATE /xdate.second(date.mdy(9,29,1941) + time.hms(4,25,9)).
+DEBUG EVALUATE /xdate.second(date.mdy(4,19,1943) + time.hms(6,49,27)).
+DEBUG EVALUATE /xdate.second(date.mdy(10,7,1943) + time.hms(2,57,52)).
+DEBUG EVALUATE /xdate.second(date.mdy(3,17,1992) + time.hms(16,45,44)).
+DEBUG EVALUATE /xdate.second(date.mdy(2,25,1996) + time.hms(21,30,57)).
+DEBUG EVALUATE /xdate.second(date.mdy(9,29,1941) + time.hms(4,25,9)).
+DEBUG EVALUATE /xdate.second(date.mdy(4,19,43) + time.hms(6,49,27)).
+DEBUG EVALUATE /xdate.second(date.mdy(10,7,43) + time.hms(2,57,52)).
+DEBUG EVALUATE /xdate.second(date.mdy(3,17,92) + time.hms(16,45,44)).
+DEBUG EVALUATE /xdate.second(date.mdy(2,25,96) + time.hms(21,30,57)).
+DEBUG EVALUATE /xdate.second(date.mdy(11,10,2038) + time.hms(22,30,4)).
+DEBUG EVALUATE /xdate.second(date.mdy(7,18,2094) + time.hms(1,56,51)).
+])
+
+for opt in OPT NOOPT; do
+    AS_BOX([$opt])
+    sed "s/opt/$opt/" < evaluate-base.sps > evaluate.sps
+    AT_CHECK([pspp --testing-mode evaluate.sps], [0], [dnl
+xdate.second(date.mdy(6,10,1648) + time.hms(0,0,0)) => 0.00
+
+xdate.second(date.mdy(6,30,1680) + time.hms(4,50,38)) => 38.00
+
+xdate.second(date.mdy(7,24,1716) + time.hms(12,31,35)) => 35.00
+
+xdate.second(date.mdy(6,19,1768) + time.hms(12,47,53)) => 53.00
+
+xdate.second(date.mdy(8,2,1819) + time.hms(1,26,0)) => 0.00
+
+xdate.second(date.mdy(3,27,1839) + time.hms(20,58,11)) => 11.00
+
+xdate.second(date.mdy(4,19,1903) + time.hms(7,36,5)) => 5.00
+
+xdate.second(date.mdy(8,25,1929) + time.hms(15,43,49)) => 49.00
+
+xdate.second(date.mdy(9,29,1941) + time.hms(4,25,9)) => 9.00
+
+xdate.second(date.mdy(4,19,1943) + time.hms(6,49,27)) => 27.00
+
+xdate.second(date.mdy(10,7,1943) + time.hms(2,57,52)) => 52.00
+
+xdate.second(date.mdy(3,17,1992) + time.hms(16,45,44)) => 44.00
+
+xdate.second(date.mdy(2,25,1996) + time.hms(21,30,57)) => 57.00
+
+xdate.second(date.mdy(9,29,1941) + time.hms(4,25,9)) => 9.00
+
+xdate.second(date.mdy(4,19,43) + time.hms(6,49,27)) => 27.00
+
+xdate.second(date.mdy(10,7,43) + time.hms(2,57,52)) => 52.00
+
+xdate.second(date.mdy(3,17,92) + time.hms(16,45,44)) => 44.00
+
+xdate.second(date.mdy(2,25,96) + time.hms(21,30,57)) => 57.00
+
+xdate.second(date.mdy(11,10,2038) + time.hms(22,30,4)) => 4.00
+
+xdate.second(date.mdy(7,18,2094) + time.hms(1,56,51)) => 51.00
+])
+done
+AT_CLEANUP
+
+AT_SETUP([expressions - XDATE.TDAY])
+AT_KEYWORDS([expression expressions evaluate])
+AT_DATA([evaluate-base.sps], [dnl
+SET EPOCH 1940.
+DEBUG EVALUATE SET opt.
+DEBUG EVALUATE /xdate.tday(date.mdy(6,10,1648) + time.hms(0,0,0)).
+DEBUG EVALUATE /xdate.tday(date.mdy(6,30,1680) + time.hms(4,50,38)).
+DEBUG EVALUATE /xdate.tday(date.mdy(7,24,1716) + time.hms(12,31,35)).
+DEBUG EVALUATE /xdate.tday(date.mdy(6,19,1768) + time.hms(12,47,53)).
+DEBUG EVALUATE /xdate.tday(date.mdy(8,2,1819) + time.hms(1,26,0)).
+DEBUG EVALUATE /xdate.tday(date.mdy(3,27,1839) + time.hms(20,58,11)).
+DEBUG EVALUATE /xdate.tday(date.mdy(4,19,1903) + time.hms(7,36,5)).
+DEBUG EVALUATE /xdate.tday(date.mdy(8,25,1929) + time.hms(15,43,49)).
+DEBUG EVALUATE /xdate.tday(date.mdy(9,29,1941) + time.hms(4,25,9)).
+DEBUG EVALUATE /xdate.tday(date.mdy(4,19,1943) + time.hms(6,49,27)).
+DEBUG EVALUATE /xdate.tday(date.mdy(10,7,1943) + time.hms(2,57,52)).
+DEBUG EVALUATE /xdate.tday(date.mdy(3,17,1992) + time.hms(16,45,44)).
+DEBUG EVALUATE /xdate.tday(date.mdy(2,25,1996) + time.hms(21,30,57)).
+DEBUG EVALUATE /xdate.tday(date.mdy(9,29,1941) + time.hms(4,25,9)).
+DEBUG EVALUATE /xdate.tday(date.mdy(4,19,43) + time.hms(6,49,27)).
+DEBUG EVALUATE /xdate.tday(date.mdy(10,7,43) + time.hms(2,57,52)).
+DEBUG EVALUATE /xdate.tday(date.mdy(3,17,92) + time.hms(16,45,44)).
+DEBUG EVALUATE /xdate.tday(date.mdy(2,25,96) + time.hms(21,30,57)).
+DEBUG EVALUATE /xdate.tday(date.mdy(11,10,2038) + time.hms(22,30,4)).
+DEBUG EVALUATE /xdate.tday(date.mdy(7,18,2094) + time.hms(1,56,51)).
+])
+
+for opt in OPT NOOPT; do
+    AS_BOX([$opt])
+    sed "s/opt/$opt/" < evaluate-base.sps > evaluate.sps
+    AT_CHECK([pspp --testing-mode evaluate.sps], [0], [dnl
+xdate.tday(date.mdy(6,10,1648) + time.hms(0,0,0)) => 23981.00
+
+xdate.tday(date.mdy(6,30,1680) + time.hms(4,50,38)) => 35689.00
+
+xdate.tday(date.mdy(7,24,1716) + time.hms(12,31,35)) => 48861.00
+
+xdate.tday(date.mdy(6,19,1768) + time.hms(12,47,53)) => 67819.00
+
+xdate.tday(date.mdy(8,2,1819) + time.hms(1,26,0)) => 86489.00
+
+xdate.tday(date.mdy(3,27,1839) + time.hms(20,58,11)) => 93666.00
+
+xdate.tday(date.mdy(4,19,1903) + time.hms(7,36,5)) => 117064.00
+
+xdate.tday(date.mdy(8,25,1929) + time.hms(15,43,49)) => 126689.00
+
+xdate.tday(date.mdy(9,29,1941) + time.hms(4,25,9)) => 131107.00
+
+xdate.tday(date.mdy(4,19,1943) + time.hms(6,49,27)) => 131674.00
+
+xdate.tday(date.mdy(10,7,1943) + time.hms(2,57,52)) => 131845.00
+
+xdate.tday(date.mdy(3,17,1992) + time.hms(16,45,44)) => 149539.00
+
+xdate.tday(date.mdy(2,25,1996) + time.hms(21,30,57)) => 150979.00
+
+xdate.tday(date.mdy(9,29,1941) + time.hms(4,25,9)) => 131107.00
+
+xdate.tday(date.mdy(4,19,43) + time.hms(6,49,27)) => 131674.00
+
+xdate.tday(date.mdy(10,7,43) + time.hms(2,57,52)) => 131845.00
+
+xdate.tday(date.mdy(3,17,92) + time.hms(16,45,44)) => 149539.00
+
+xdate.tday(date.mdy(2,25,96) + time.hms(21,30,57)) => 150979.00
+
+xdate.tday(date.mdy(11,10,2038) + time.hms(22,30,4)) => 166578.00
+
+xdate.tday(date.mdy(7,18,2094) + time.hms(1,56,51)) => 186917.00
+])
+done
+AT_CLEANUP
+
+AT_SETUP([expressions - XDATE.TIME])
+AT_KEYWORDS([expression expressions evaluate])
+AT_DATA([evaluate-base.sps], [dnl
+SET EPOCH 1940.
+DEBUG EVALUATE SET opt.
+DEBUG EVALUATE /xdate.time(date.mdy(6,10,1648) + time.hms(0,0,0)).
+DEBUG EVALUATE /xdate.time(date.mdy(6,30,1680) + time.hms(4,50,38)).
+DEBUG EVALUATE /xdate.time(date.mdy(7,24,1716) + time.hms(12,31,35)).
+DEBUG EVALUATE /xdate.time(date.mdy(6,19,1768) + time.hms(12,47,53)).
+DEBUG EVALUATE /xdate.time(date.mdy(8,2,1819) + time.hms(1,26,0)).
+DEBUG EVALUATE /xdate.time(date.mdy(3,27,1839) + time.hms(20,58,11)).
+DEBUG EVALUATE /xdate.time(date.mdy(4,19,1903) + time.hms(7,36,5)).
+DEBUG EVALUATE /xdate.time(date.mdy(8,25,1929) + time.hms(15,43,49)).
+DEBUG EVALUATE /xdate.time(date.mdy(9,29,1941) + time.hms(4,25,9)).
+DEBUG EVALUATE /xdate.time(date.mdy(4,19,1943) + time.hms(6,49,27)).
+DEBUG EVALUATE /xdate.time(date.mdy(10,7,1943) + time.hms(2,57,52)).
+DEBUG EVALUATE /xdate.time(date.mdy(3,17,1992) + time.hms(16,45,44)).
+DEBUG EVALUATE /xdate.time(date.mdy(2,25,1996) + time.hms(21,30,57)).
+DEBUG EVALUATE /xdate.time(date.mdy(9,29,1941) + time.hms(4,25,9)).
+DEBUG EVALUATE /xdate.time(date.mdy(4,19,43) + time.hms(6,49,27)).
+DEBUG EVALUATE /xdate.time(date.mdy(10,7,43) + time.hms(2,57,52)).
+DEBUG EVALUATE /xdate.time(date.mdy(3,17,92) + time.hms(16,45,44)).
+DEBUG EVALUATE /xdate.time(date.mdy(2,25,96) + time.hms(21,30,57)).
+DEBUG EVALUATE /xdate.time(date.mdy(11,10,2038) + time.hms(22,30,4)).
+DEBUG EVALUATE /xdate.time(date.mdy(7,18,2094) + time.hms(1,56,51)).
+])
+
+for opt in OPT NOOPT; do
+    AS_BOX([$opt])
+    sed "s/opt/$opt/" < evaluate-base.sps > evaluate.sps
+    AT_CHECK([pspp --testing-mode evaluate.sps], [0], [dnl
+xdate.time(date.mdy(6,10,1648) + time.hms(0,0,0)) => 0.00
+
+xdate.time(date.mdy(6,30,1680) + time.hms(4,50,38)) => 17438.00
+
+xdate.time(date.mdy(7,24,1716) + time.hms(12,31,35)) => 45095.00
+
+xdate.time(date.mdy(6,19,1768) + time.hms(12,47,53)) => 46073.00
+
+xdate.time(date.mdy(8,2,1819) + time.hms(1,26,0)) => 5160.00
+
+xdate.time(date.mdy(3,27,1839) + time.hms(20,58,11)) => 75491.00
+
+xdate.time(date.mdy(4,19,1903) + time.hms(7,36,5)) => 27365.00
+
+xdate.time(date.mdy(8,25,1929) + time.hms(15,43,49)) => 56629.00
+
+xdate.time(date.mdy(9,29,1941) + time.hms(4,25,9)) => 15909.00
+
+xdate.time(date.mdy(4,19,1943) + time.hms(6,49,27)) => 24567.00
+
+xdate.time(date.mdy(10,7,1943) + time.hms(2,57,52)) => 10672.00
+
+xdate.time(date.mdy(3,17,1992) + time.hms(16,45,44)) => 60344.00
+
+xdate.time(date.mdy(2,25,1996) + time.hms(21,30,57)) => 77457.00
+
+xdate.time(date.mdy(9,29,1941) + time.hms(4,25,9)) => 15909.00
+
+xdate.time(date.mdy(4,19,43) + time.hms(6,49,27)) => 24567.00
+
+xdate.time(date.mdy(10,7,43) + time.hms(2,57,52)) => 10672.00
+
+xdate.time(date.mdy(3,17,92) + time.hms(16,45,44)) => 60344.00
+
+xdate.time(date.mdy(2,25,96) + time.hms(21,30,57)) => 77457.00
+
+xdate.time(date.mdy(11,10,2038) + time.hms(22,30,4)) => 81004.00
+
+xdate.time(date.mdy(7,18,2094) + time.hms(1,56,51)) => 7011.00
+])
+done
+AT_CLEANUP
+
+AT_SETUP([expressions - XDATE.WEEK])
+AT_KEYWORDS([expression expressions evaluate])
+AT_DATA([evaluate-base.sps], [dnl
+SET EPOCH 1940.
+DEBUG EVALUATE SET opt.
+DEBUG EVALUATE /xdate.week(date.mdy(6,10,1648) + time.hms(0,0,0)).
+DEBUG EVALUATE /xdate.week(date.mdy(6,30,1680) + time.hms(4,50,38)).
+DEBUG EVALUATE /xdate.week(date.mdy(7,24,1716) + time.hms(12,31,35)).
+DEBUG EVALUATE /xdate.week(date.mdy(6,19,1768) + time.hms(12,47,53)).
+DEBUG EVALUATE /xdate.week(date.mdy(8,2,1819) + time.hms(1,26,0)).
+DEBUG EVALUATE /xdate.week(date.mdy(3,27,1839) + time.hms(20,58,11)).
+DEBUG EVALUATE /xdate.week(date.mdy(4,19,1903) + time.hms(7,36,5)).
+DEBUG EVALUATE /xdate.week(date.mdy(8,25,1929) + time.hms(15,43,49)).
+DEBUG EVALUATE /xdate.week(date.mdy(9,29,1941) + time.hms(4,25,9)).
+DEBUG EVALUATE /xdate.week(date.mdy(4,19,1943) + time.hms(6,49,27)).
+DEBUG EVALUATE /xdate.week(date.mdy(10,7,1943) + time.hms(2,57,52)).
+DEBUG EVALUATE /xdate.week(date.mdy(3,17,1992) + time.hms(16,45,44)).
+DEBUG EVALUATE /xdate.week(date.mdy(2,25,1996) + time.hms(21,30,57)).
+DEBUG EVALUATE /xdate.week(date.mdy(9,29,1941) + time.hms(4,25,9)).
+DEBUG EVALUATE /xdate.week(date.mdy(4,19,43) + time.hms(6,49,27)).
+DEBUG EVALUATE /xdate.week(date.mdy(10,7,43) + time.hms(2,57,52)).
+DEBUG EVALUATE /xdate.week(date.mdy(3,17,92) + time.hms(16,45,44)).
+DEBUG EVALUATE /xdate.week(date.mdy(2,25,96) + time.hms(21,30,57)).
+DEBUG EVALUATE /xdate.week(date.mdy(11,10,2038) + time.hms(22,30,4)).
+DEBUG EVALUATE /xdate.week(date.mdy(7,18,2094) + time.hms(1,56,51)).
+])
+
+for opt in OPT NOOPT; do
+    AS_BOX([$opt])
+    sed "s/opt/$opt/" < evaluate-base.sps > evaluate.sps
+    AT_CHECK([pspp --testing-mode evaluate.sps], [0], [dnl
+xdate.week(date.mdy(6,10,1648) + time.hms(0,0,0)) => 24.00
+
+xdate.week(date.mdy(6,30,1680) + time.hms(4,50,38)) => 26.00
+
+xdate.week(date.mdy(7,24,1716) + time.hms(12,31,35)) => 30.00
+
+xdate.week(date.mdy(6,19,1768) + time.hms(12,47,53)) => 25.00
+
+xdate.week(date.mdy(8,2,1819) + time.hms(1,26,0)) => 31.00
+
+xdate.week(date.mdy(3,27,1839) + time.hms(20,58,11)) => 13.00
+
+xdate.week(date.mdy(4,19,1903) + time.hms(7,36,5)) => 16.00
+
+xdate.week(date.mdy(8,25,1929) + time.hms(15,43,49)) => 34.00
+
+xdate.week(date.mdy(9,29,1941) + time.hms(4,25,9)) => 39.00
+
+xdate.week(date.mdy(4,19,1943) + time.hms(6,49,27)) => 16.00
+
+xdate.week(date.mdy(10,7,1943) + time.hms(2,57,52)) => 40.00
+
+xdate.week(date.mdy(3,17,1992) + time.hms(16,45,44)) => 11.00
+
+xdate.week(date.mdy(2,25,1996) + time.hms(21,30,57)) => 8.00
+
+xdate.week(date.mdy(9,29,1941) + time.hms(4,25,9)) => 39.00
+
+xdate.week(date.mdy(4,19,43) + time.hms(6,49,27)) => 16.00
+
+xdate.week(date.mdy(10,7,43) + time.hms(2,57,52)) => 40.00
+
+xdate.week(date.mdy(3,17,92) + time.hms(16,45,44)) => 11.00
+
+xdate.week(date.mdy(2,25,96) + time.hms(21,30,57)) => 8.00
+
+xdate.week(date.mdy(11,10,2038) + time.hms(22,30,4)) => 45.00
+
+xdate.week(date.mdy(7,18,2094) + time.hms(1,56,51)) => 29.00
+])
+done
+AT_CLEANUP
+
+AT_SETUP([expressions - XDATE.WKDAY])
+AT_KEYWORDS([expression expressions evaluate])
+AT_DATA([evaluate-base.sps], [dnl
+SET EPOCH 1940.
+DEBUG EVALUATE SET opt.
+DEBUG EVALUATE /xdate.wkday(date.mdy(6,10,1648)).
+DEBUG EVALUATE /xdate.wkday(date.mdy(6,30,1680)).
+DEBUG EVALUATE /xdate.wkday(date.mdy(7,24,1716)).
+DEBUG EVALUATE /xdate.wkday(date.mdy(6,19,1768)).
+DEBUG EVALUATE /xdate.wkday(date.mdy(8,2,1819)).
+DEBUG EVALUATE /xdate.wkday(date.mdy(3,27,1839)).
+DEBUG EVALUATE /xdate.wkday(date.mdy(4,19,1903)).
+DEBUG EVALUATE /xdate.wkday(date.mdy(8,25,1929)).
+DEBUG EVALUATE /xdate.wkday(date.mdy(9,29,1941)).
+DEBUG EVALUATE /xdate.wkday(date.mdy(4,19,1943)).
+DEBUG EVALUATE /xdate.wkday(date.mdy(10,7,1943)).
+DEBUG EVALUATE /xdate.wkday(date.mdy(3,17,1992)).
+DEBUG EVALUATE /xdate.wkday(date.mdy(2,25,1996)).
+DEBUG EVALUATE /xdate.wkday(date.mdy(9,29,1941)).
+DEBUG EVALUATE /xdate.wkday(date.mdy(4,19,43)).
+DEBUG EVALUATE /xdate.wkday(date.mdy(10,7,43)).
+DEBUG EVALUATE /xdate.wkday(date.mdy(3,17,92)).
+DEBUG EVALUATE /xdate.wkday(date.mdy(2,25,96)).
+DEBUG EVALUATE /xdate.wkday(date.mdy(11,10,2038)).
+DEBUG EVALUATE /xdate.wkday(date.mdy(7,18,2094)).
+])
+
+for opt in OPT NOOPT; do
+    AS_BOX([$opt])
+    sed "s/opt/$opt/" < evaluate-base.sps > evaluate.sps
+    AT_CHECK([pspp --testing-mode evaluate.sps], [0], [dnl
+xdate.wkday(date.mdy(6,10,1648)) => 4.00
+
+xdate.wkday(date.mdy(6,30,1680)) => 1.00
+
+xdate.wkday(date.mdy(7,24,1716)) => 6.00
+
+xdate.wkday(date.mdy(6,19,1768)) => 1.00
+
+xdate.wkday(date.mdy(8,2,1819)) => 2.00
+
+xdate.wkday(date.mdy(3,27,1839)) => 4.00
+
+xdate.wkday(date.mdy(4,19,1903)) => 1.00
+
+xdate.wkday(date.mdy(8,25,1929)) => 1.00
+
+xdate.wkday(date.mdy(9,29,1941)) => 2.00
+
+xdate.wkday(date.mdy(4,19,1943)) => 2.00
+
+xdate.wkday(date.mdy(10,7,1943)) => 5.00
+
+xdate.wkday(date.mdy(3,17,1992)) => 3.00
+
+xdate.wkday(date.mdy(2,25,1996)) => 1.00
+
+xdate.wkday(date.mdy(9,29,1941)) => 2.00
+
+xdate.wkday(date.mdy(4,19,43)) => 2.00
+
+xdate.wkday(date.mdy(10,7,43)) => 5.00
+
+xdate.wkday(date.mdy(3,17,92)) => 3.00
+
+xdate.wkday(date.mdy(2,25,96)) => 1.00
+
+xdate.wkday(date.mdy(11,10,2038)) => 4.00
+
+xdate.wkday(date.mdy(7,18,2094)) => 1.00
+])
+done
+AT_CLEANUP
+
+AT_SETUP([expressions - XDATE.YEAR])
+AT_KEYWORDS([expression expressions evaluate])
+AT_DATA([evaluate-base.sps], [dnl
+SET EPOCH 1940.
+DEBUG EVALUATE SET opt.
+DEBUG EVALUATE /xdate.year(date.mdy(6,10,1648) + time.hms(0,0,0)).
+DEBUG EVALUATE /xdate.year(date.mdy(6,30,1680) + time.hms(4,50,38)).
+DEBUG EVALUATE /xdate.year(date.mdy(7,24,1716) + time.hms(12,31,35)).
+DEBUG EVALUATE /xdate.year(date.mdy(6,19,1768) + time.hms(12,47,53)).
+DEBUG EVALUATE /xdate.year(date.mdy(8,2,1819) + time.hms(1,26,0)).
+DEBUG EVALUATE /xdate.year(date.mdy(3,27,1839) + time.hms(20,58,11)).
+DEBUG EVALUATE /xdate.year(date.mdy(4,19,1903) + time.hms(7,36,5)).
+DEBUG EVALUATE /xdate.year(date.mdy(8,25,1929) + time.hms(15,43,49)).
+DEBUG EVALUATE /xdate.year(date.mdy(9,29,1941) + time.hms(4,25,9)).
+DEBUG EVALUATE /xdate.year(date.mdy(4,19,1943) + time.hms(6,49,27)).
+DEBUG EVALUATE /xdate.year(date.mdy(10,7,1943) + time.hms(2,57,52)).
+DEBUG EVALUATE /xdate.year(date.mdy(3,17,1992) + time.hms(16,45,44)).
+DEBUG EVALUATE /xdate.year(date.mdy(2,25,1996) + time.hms(21,30,57)).
+DEBUG EVALUATE /xdate.year(date.mdy(9,29,1941) + time.hms(4,25,9)).
+DEBUG EVALUATE /xdate.year(date.mdy(4,19,43) + time.hms(6,49,27)).
+DEBUG EVALUATE /xdate.year(date.mdy(10,7,43) + time.hms(2,57,52)).
+DEBUG EVALUATE /xdate.year(date.mdy(3,17,92) + time.hms(16,45,44)).
+DEBUG EVALUATE /xdate.year(date.mdy(2,25,96) + time.hms(21,30,57)).
+DEBUG EVALUATE /xdate.year(date.mdy(11,10,2038) + time.hms(22,30,4)).
+DEBUG EVALUATE /xdate.year(date.mdy(7,18,2094) + time.hms(1,56,51)).
+])
+
+for opt in OPT NOOPT; do
+    AS_BOX([$opt])
+    sed "s/opt/$opt/" < evaluate-base.sps > evaluate.sps
+    AT_CHECK([pspp --testing-mode evaluate.sps], [0], [dnl
+xdate.year(date.mdy(6,10,1648) + time.hms(0,0,0)) => 1648.00
+
+xdate.year(date.mdy(6,30,1680) + time.hms(4,50,38)) => 1680.00
+
+xdate.year(date.mdy(7,24,1716) + time.hms(12,31,35)) => 1716.00
+
+xdate.year(date.mdy(6,19,1768) + time.hms(12,47,53)) => 1768.00
+
+xdate.year(date.mdy(8,2,1819) + time.hms(1,26,0)) => 1819.00
+
+xdate.year(date.mdy(3,27,1839) + time.hms(20,58,11)) => 1839.00
+
+xdate.year(date.mdy(4,19,1903) + time.hms(7,36,5)) => 1903.00
+
+xdate.year(date.mdy(8,25,1929) + time.hms(15,43,49)) => 1929.00
+
+xdate.year(date.mdy(9,29,1941) + time.hms(4,25,9)) => 1941.00
+
+xdate.year(date.mdy(4,19,1943) + time.hms(6,49,27)) => 1943.00
+
+xdate.year(date.mdy(10,7,1943) + time.hms(2,57,52)) => 1943.00
+
+xdate.year(date.mdy(3,17,1992) + time.hms(16,45,44)) => 1992.00
+
+xdate.year(date.mdy(2,25,1996) + time.hms(21,30,57)) => 1996.00
+
+xdate.year(date.mdy(9,29,1941) + time.hms(4,25,9)) => 1941.00
+
+xdate.year(date.mdy(4,19,43) + time.hms(6,49,27)) => 1943.00
+
+xdate.year(date.mdy(10,7,43) + time.hms(2,57,52)) => 1943.00
+
+xdate.year(date.mdy(3,17,92) + time.hms(16,45,44)) => 1992.00
+
+xdate.year(date.mdy(2,25,96) + time.hms(21,30,57)) => 1996.00
+
+xdate.year(date.mdy(11,10,2038) + time.hms(22,30,4)) => 2038.00
+
+xdate.year(date.mdy(7,18,2094) + time.hms(1,56,51)) => 2094.00
+])
+done
+AT_CLEANUP
+
+AT_SETUP([expressions - DATEDIFF years])
+AT_KEYWORDS([expression expressions evaluate])
+AT_DATA([evaluate-base.sps], [dnl
+SET EPOCH 1940.
+DEBUG EVALUATE SET opt.
+
+DEBUG EVALUATE /datediff(date.mdy(6,10,1648), date.mdy(6,30,1680), 'years').
+DEBUG EVALUATE /datediff(date.mdy(6,30,1680), date.mdy(7,24,1716), 'years').
+DEBUG EVALUATE /datediff(date.mdy(7,24,1716), date.mdy(6,19,1768), 'years').
+DEBUG EVALUATE /datediff(date.mdy(6,19,1768), date.mdy(8,2,1819), 'years').
+DEBUG EVALUATE /datediff(date.mdy(8,2,1819), date.mdy(3,27,1839), 'years').
+DEBUG EVALUATE /datediff(date.mdy(3,27,1839), date.mdy(4,19,1903), 'years').
+DEBUG EVALUATE /datediff(date.mdy(4,19,1903), date.mdy(8,25,1929), 'years').
+DEBUG EVALUATE /datediff(date.mdy(8,25,1929), date.mdy(9,29,1941), 'years').
+DEBUG EVALUATE /datediff(date.mdy(9,29,1941), date.mdy(4,19,1943), 'years').
+DEBUG EVALUATE /datediff(date.mdy(4,19,1943), date.mdy(10,7,1943), 'years').
+DEBUG EVALUATE /datediff(date.mdy(10,7,1943), date.mdy(3,17,1992), 'years').
+DEBUG EVALUATE /datediff(date.mdy(3,17,1992), date.mdy(2,25,1996), 'years').
+DEBUG EVALUATE /datediff(date.mdy(9,29,1941), date.mdy(2,25,1996), 'years').
+DEBUG EVALUATE /datediff(date.mdy(9,29,1941), date.mdy(4,19,43), 'years').
+DEBUG EVALUATE /datediff(date.mdy(4,19,43), date.mdy(10,7,43), 'years').
+DEBUG EVALUATE /datediff(date.mdy(10,7,43), date.mdy(3,17,92), 'years').
+DEBUG EVALUATE /datediff(date.mdy(3,17,92), date.mdy(2,25,96), 'years').
+DEBUG EVALUATE /datediff(date.mdy(2,25,96), date.mdy(11,10,2038), 'years').
+DEBUG EVALUATE /datediff(date.mdy(11,10,2038), date.mdy(7,18,2094), 'years').
+DEBUG EVALUATE /datediff(date.mdy(2,29,1900), date.mdy(2,29,1904), 'years').
+DEBUG EVALUATE /datediff(date.mdy(2,29,1904), date.mdy(2,29,1908), 'years').
+DEBUG EVALUATE /datediff(date.mdy(2,29,1900), date.mdy(2,28,1903), 'years').
+
+DEBUG EVALUATE /datediff(date.mdy(6,30,1680), date.mdy(6,10,1648), 'years').
+DEBUG EVALUATE /datediff(date.mdy(7,24,1716), date.mdy(6,30,1680), 'years').
+DEBUG EVALUATE /datediff(date.mdy(6,19,1768), date.mdy(7,24,1716), 'years').
+DEBUG EVALUATE /datediff(date.mdy(8,2,1819), date.mdy(6,19,1768), 'years').
+DEBUG EVALUATE /datediff(date.mdy(3,27,1839), date.mdy(8,2,1819), 'years').
+DEBUG EVALUATE /datediff(date.mdy(4,19,1903), date.mdy(3,27,1839), 'years').
+DEBUG EVALUATE /datediff(date.mdy(8,25,1929), date.mdy(4,19,1903), 'years').
+DEBUG EVALUATE /datediff(date.mdy(9,29,1941), date.mdy(8,25,1929), 'years').
+DEBUG EVALUATE /datediff(date.mdy(4,19,1943), date.mdy(9,29,1941), 'years').
+DEBUG EVALUATE /datediff(date.mdy(10,7,1943), date.mdy(4,19,1943), 'years').
+DEBUG EVALUATE /datediff(date.mdy(3,17,1992), date.mdy(10,7,1943), 'years').
+DEBUG EVALUATE /datediff(date.mdy(2,25,1996), date.mdy(3,17,1992), 'years').
+DEBUG EVALUATE /datediff(date.mdy(2,25,1996), date.mdy(9,29,1941), 'years').
+DEBUG EVALUATE /datediff(date.mdy(4,19,43), date.mdy(9,29,1941), 'years').
+DEBUG EVALUATE /datediff(date.mdy(10,7,43), date.mdy(4,19,43), 'years').
+DEBUG EVALUATE /datediff(date.mdy(3,17,92), date.mdy(10,7,43), 'years').
+DEBUG EVALUATE /datediff(date.mdy(2,25,96), date.mdy(3,17,92), 'years').
+DEBUG EVALUATE /datediff(date.mdy(11,10,2038), date.mdy(2,25,96), 'years').
+DEBUG EVALUATE /datediff(date.mdy(7,18,2094), date.mdy(11,10,2038), 'years').
+DEBUG EVALUATE /datediff(date.mdy(2,29,1904), date.mdy(2,29,1900), 'years').
+DEBUG EVALUATE /datediff(date.mdy(2,29,1908), date.mdy(2,29,1904), 'years').
+DEBUG EVALUATE /datediff(date.mdy(2,28,1903), date.mdy(2,29,1900), 'years').
+])
+
+for opt in OPT NOOPT; do
+    AS_BOX([$opt])
+    sed "s/opt/$opt/" < evaluate-base.sps > evaluate.sps
+    AT_CHECK([pspp --testing-mode evaluate.sps], [0], [dnl
+datediff(date.mdy(6,10,1648), date.mdy(6,30,1680), 'years') => -32.00
+
+datediff(date.mdy(6,30,1680), date.mdy(7,24,1716), 'years') => -36.00
+
+datediff(date.mdy(7,24,1716), date.mdy(6,19,1768), 'years') => -51.00
+
+datediff(date.mdy(6,19,1768), date.mdy(8,2,1819), 'years') => -51.00
+
+datediff(date.mdy(8,2,1819), date.mdy(3,27,1839), 'years') => -19.00
+
+datediff(date.mdy(3,27,1839), date.mdy(4,19,1903), 'years') => -64.00
+
+datediff(date.mdy(4,19,1903), date.mdy(8,25,1929), 'years') => -26.00
+
+datediff(date.mdy(8,25,1929), date.mdy(9,29,1941), 'years') => -12.00
+
+datediff(date.mdy(9,29,1941), date.mdy(4,19,1943), 'years') => -1.00
+
+datediff(date.mdy(4,19,1943), date.mdy(10,7,1943), 'years') => 0.00
+
+datediff(date.mdy(10,7,1943), date.mdy(3,17,1992), 'years') => -48.00
+
+datediff(date.mdy(3,17,1992), date.mdy(2,25,1996), 'years') => -3.00
+
+datediff(date.mdy(9,29,1941), date.mdy(2,25,1996), 'years') => -54.00
+
+datediff(date.mdy(9,29,1941), date.mdy(4,19,43), 'years') => -1.00
+
+datediff(date.mdy(4,19,43), date.mdy(10,7,43), 'years') => 0.00
+
+datediff(date.mdy(10,7,43), date.mdy(3,17,92), 'years') => -48.00
+
+datediff(date.mdy(3,17,92), date.mdy(2,25,96), 'years') => -3.00
+
+datediff(date.mdy(2,25,96), date.mdy(11,10,2038), 'years') => -42.00
+
+datediff(date.mdy(11,10,2038), date.mdy(7,18,2094), 'years') => -55.00
+
+datediff(date.mdy(2,29,1900), date.mdy(2,29,1904), 'years') => -3.00
+
+datediff(date.mdy(2,29,1904), date.mdy(2,29,1908), 'years') => -4.00
+
+datediff(date.mdy(2,29,1900), date.mdy(2,28,1903), 'years') => -2.00
+
+datediff(date.mdy(6,30,1680), date.mdy(6,10,1648), 'years') => 32.00
+
+datediff(date.mdy(7,24,1716), date.mdy(6,30,1680), 'years') => 36.00
+
+datediff(date.mdy(6,19,1768), date.mdy(7,24,1716), 'years') => 51.00
+
+datediff(date.mdy(8,2,1819), date.mdy(6,19,1768), 'years') => 51.00
+
+datediff(date.mdy(3,27,1839), date.mdy(8,2,1819), 'years') => 19.00
+
+datediff(date.mdy(4,19,1903), date.mdy(3,27,1839), 'years') => 64.00
+
+datediff(date.mdy(8,25,1929), date.mdy(4,19,1903), 'years') => 26.00
+
+datediff(date.mdy(9,29,1941), date.mdy(8,25,1929), 'years') => 12.00
+
+datediff(date.mdy(4,19,1943), date.mdy(9,29,1941), 'years') => 1.00
+
+datediff(date.mdy(10,7,1943), date.mdy(4,19,1943), 'years') => 0.00
+
+datediff(date.mdy(3,17,1992), date.mdy(10,7,1943), 'years') => 48.00
+
+datediff(date.mdy(2,25,1996), date.mdy(3,17,1992), 'years') => 3.00
+
+datediff(date.mdy(2,25,1996), date.mdy(9,29,1941), 'years') => 54.00
+
+datediff(date.mdy(4,19,43), date.mdy(9,29,1941), 'years') => 1.00
+
+datediff(date.mdy(10,7,43), date.mdy(4,19,43), 'years') => 0.00
+
+datediff(date.mdy(3,17,92), date.mdy(10,7,43), 'years') => 48.00
+
+datediff(date.mdy(2,25,96), date.mdy(3,17,92), 'years') => 3.00
+
+datediff(date.mdy(11,10,2038), date.mdy(2,25,96), 'years') => 42.00
+
+datediff(date.mdy(7,18,2094), date.mdy(11,10,2038), 'years') => 55.00
+
+datediff(date.mdy(2,29,1904), date.mdy(2,29,1900), 'years') => 3.00
+
+datediff(date.mdy(2,29,1908), date.mdy(2,29,1904), 'years') => 4.00
+
+datediff(date.mdy(2,28,1903), date.mdy(2,29,1900), 'years') => 2.00
+])
+done
+AT_CLEANUP
+
+AT_SETUP([expressions - DATEDIFF quarters])
+AT_KEYWORDS([expression expressions evaluate])
+AT_DATA([evaluate-base.sps], [dnl
+SET EPOCH 1940.
+DEBUG EVALUATE SET opt.
+DEBUG EVALUATE /datediff(date.mdy(6,10,1648), date.mdy(6,30,1680), 'quarters').
+DEBUG EVALUATE /datediff(date.mdy(6,30,1680), date.mdy(7,24,1716), 'quarters').
+DEBUG EVALUATE /datediff(date.mdy(7,24,1716), date.mdy(6,19,1768), 'quarters').
+DEBUG EVALUATE /datediff(date.mdy(6,19,1768), date.mdy(8,2,1819), 'quarters').
+DEBUG EVALUATE /datediff(date.mdy(8,2,1819), date.mdy(3,27,1839), 'quarters').
+DEBUG EVALUATE /datediff(date.mdy(3,27,1839), date.mdy(4,19,1903), 'quarters').
+DEBUG EVALUATE /datediff(date.mdy(4,19,1903), date.mdy(8,25,1929), 'quarters').
+DEBUG EVALUATE /datediff(date.mdy(8,25,1929), date.mdy(9,29,1941), 'quarters').
+DEBUG EVALUATE /datediff(date.mdy(9,29,1941), date.mdy(4,19,1943), 'quarters').
+DEBUG EVALUATE /datediff(date.mdy(4,19,1943), date.mdy(10,7,1943), 'quarters').
+DEBUG EVALUATE /datediff(date.mdy(10,7,1943), date.mdy(3,17,1992), 'quarters').
+DEBUG EVALUATE /datediff(date.mdy(3,17,1992), date.mdy(2,25,1996), 'quarters').
+DEBUG EVALUATE /datediff(date.mdy(9,29,1941), date.mdy(2,25,1996), 'quarters').
+DEBUG EVALUATE /datediff(date.mdy(9,29,1941), date.mdy(4,19,43), 'quarters').
+DEBUG EVALUATE /datediff(date.mdy(4,19,43), date.mdy(10,7,43), 'quarters').
+DEBUG EVALUATE /datediff(date.mdy(10,7,43), date.mdy(3,17,92), 'quarters').
+DEBUG EVALUATE /datediff(date.mdy(3,17,92), date.mdy(2,25,96), 'quarters').
+DEBUG EVALUATE /datediff(date.mdy(2,25,96), date.mdy(11,10,2038), 'quarters').
+DEBUG EVALUATE /datediff(date.mdy(11,10,2038), date.mdy(7,18,2094), 'quarters').
+DEBUG EVALUATE /datediff(date.mdy(2,29,1900), date.mdy(2,29,1904), 'quarters').
+DEBUG EVALUATE /datediff(date.mdy(2,29,1904), date.mdy(2,29,1908), 'quarters').
+DEBUG EVALUATE /datediff(date.mdy(2,29,1900), date.mdy(2,28,1903), 'quarters').
+
+DEBUG EVALUATE /datediff(date.mdy(6,30,1680), date.mdy(6,10,1648), 'quarters').
+DEBUG EVALUATE /datediff(date.mdy(7,24,1716), date.mdy(6,30,1680), 'quarters').
+DEBUG EVALUATE /datediff(date.mdy(6,19,1768), date.mdy(7,24,1716), 'quarters').
+DEBUG EVALUATE /datediff(date.mdy(8,2,1819), date.mdy(6,19,1768), 'quarters').
+DEBUG EVALUATE /datediff(date.mdy(3,27,1839), date.mdy(8,2,1819), 'quarters').
+DEBUG EVALUATE /datediff(date.mdy(4,19,1903), date.mdy(3,27,1839), 'quarters').
+DEBUG EVALUATE /datediff(date.mdy(8,25,1929), date.mdy(4,19,1903), 'quarters').
+DEBUG EVALUATE /datediff(date.mdy(9,29,1941), date.mdy(8,25,1929), 'quarters').
+DEBUG EVALUATE /datediff(date.mdy(4,19,1943), date.mdy(9,29,1941), 'quarters').
+DEBUG EVALUATE /datediff(date.mdy(10,7,1943), date.mdy(4,19,1943), 'quarters').
+DEBUG EVALUATE /datediff(date.mdy(3,17,1992), date.mdy(10,7,1943), 'quarters').
+DEBUG EVALUATE /datediff(date.mdy(2,25,1996), date.mdy(3,17,1992), 'quarters').
+DEBUG EVALUATE /datediff(date.mdy(2,25,1996), date.mdy(9,29,1941), 'quarters').
+DEBUG EVALUATE /datediff(date.mdy(4,19,43), date.mdy(9,29,1941), 'quarters').
+DEBUG EVALUATE /datediff(date.mdy(10,7,43), date.mdy(4,19,43), 'quarters').
+DEBUG EVALUATE /datediff(date.mdy(3,17,92), date.mdy(10,7,43), 'quarters').
+DEBUG EVALUATE /datediff(date.mdy(2,25,96), date.mdy(3,17,92), 'quarters').
+DEBUG EVALUATE /datediff(date.mdy(11,10,2038), date.mdy(2,25,96), 'quarters').
+DEBUG EVALUATE /datediff(date.mdy(7,18,2094), date.mdy(11,10,2038), 'quarters').
+DEBUG EVALUATE /datediff(date.mdy(2,29,1904), date.mdy(2,29,1900), 'quarters').
+DEBUG EVALUATE /datediff(date.mdy(2,29,1908), date.mdy(2,29,1904), 'quarters').
+DEBUG EVALUATE /datediff(date.mdy(2,28,1903), date.mdy(2,29,1900), 'quarters').
+])
+
+for opt in OPT NOOPT; do
+    AS_BOX([$opt])
+    sed "s/opt/$opt/" < evaluate-base.sps > evaluate.sps
+    AT_CHECK([pspp --testing-mode evaluate.sps], [0], [dnl
+datediff(date.mdy(6,10,1648), date.mdy(6,30,1680), 'quarters') => -128.00
+
+datediff(date.mdy(6,30,1680), date.mdy(7,24,1716), 'quarters') => -144.00
+
+datediff(date.mdy(7,24,1716), date.mdy(6,19,1768), 'quarters') => -207.00
+
+datediff(date.mdy(6,19,1768), date.mdy(8,2,1819), 'quarters') => -204.00
+
+datediff(date.mdy(8,2,1819), date.mdy(3,27,1839), 'quarters') => -78.00
+
+datediff(date.mdy(3,27,1839), date.mdy(4,19,1903), 'quarters') => -256.00
+
+datediff(date.mdy(4,19,1903), date.mdy(8,25,1929), 'quarters') => -105.00
+
+datediff(date.mdy(8,25,1929), date.mdy(9,29,1941), 'quarters') => -48.00
+
+datediff(date.mdy(9,29,1941), date.mdy(4,19,1943), 'quarters') => -6.00
+
+datediff(date.mdy(4,19,1943), date.mdy(10,7,1943), 'quarters') => -1.00
+
+datediff(date.mdy(10,7,1943), date.mdy(3,17,1992), 'quarters') => -193.00
+
+datediff(date.mdy(3,17,1992), date.mdy(2,25,1996), 'quarters') => -15.00
+
+datediff(date.mdy(9,29,1941), date.mdy(2,25,1996), 'quarters') => -217.00
+
+datediff(date.mdy(9,29,1941), date.mdy(4,19,43), 'quarters') => -6.00
+
+datediff(date.mdy(4,19,43), date.mdy(10,7,43), 'quarters') => -1.00
+
+datediff(date.mdy(10,7,43), date.mdy(3,17,92), 'quarters') => -193.00
+
+datediff(date.mdy(3,17,92), date.mdy(2,25,96), 'quarters') => -15.00
+
+datediff(date.mdy(2,25,96), date.mdy(11,10,2038), 'quarters') => -170.00
+
+datediff(date.mdy(11,10,2038), date.mdy(7,18,2094), 'quarters') => -222.00
+
+datediff(date.mdy(2,29,1900), date.mdy(2,29,1904), 'quarters') => -15.00
+
+datediff(date.mdy(2,29,1904), date.mdy(2,29,1908), 'quarters') => -16.00
+
+datediff(date.mdy(2,29,1900), date.mdy(2,28,1903), 'quarters') => -11.00
+
+datediff(date.mdy(6,30,1680), date.mdy(6,10,1648), 'quarters') => 128.00
+
+datediff(date.mdy(7,24,1716), date.mdy(6,30,1680), 'quarters') => 144.00
+
+datediff(date.mdy(6,19,1768), date.mdy(7,24,1716), 'quarters') => 207.00
+
+datediff(date.mdy(8,2,1819), date.mdy(6,19,1768), 'quarters') => 204.00
+
+datediff(date.mdy(3,27,1839), date.mdy(8,2,1819), 'quarters') => 78.00
+
+datediff(date.mdy(4,19,1903), date.mdy(3,27,1839), 'quarters') => 256.00
+
+datediff(date.mdy(8,25,1929), date.mdy(4,19,1903), 'quarters') => 105.00
+
+datediff(date.mdy(9,29,1941), date.mdy(8,25,1929), 'quarters') => 48.00
+
+datediff(date.mdy(4,19,1943), date.mdy(9,29,1941), 'quarters') => 6.00
+
+datediff(date.mdy(10,7,1943), date.mdy(4,19,1943), 'quarters') => 1.00
+
+datediff(date.mdy(3,17,1992), date.mdy(10,7,1943), 'quarters') => 193.00
+
+datediff(date.mdy(2,25,1996), date.mdy(3,17,1992), 'quarters') => 15.00
+
+datediff(date.mdy(2,25,1996), date.mdy(9,29,1941), 'quarters') => 217.00
+
+datediff(date.mdy(4,19,43), date.mdy(9,29,1941), 'quarters') => 6.00
+
+datediff(date.mdy(10,7,43), date.mdy(4,19,43), 'quarters') => 1.00
+
+datediff(date.mdy(3,17,92), date.mdy(10,7,43), 'quarters') => 193.00
+
+datediff(date.mdy(2,25,96), date.mdy(3,17,92), 'quarters') => 15.00
+
+datediff(date.mdy(11,10,2038), date.mdy(2,25,96), 'quarters') => 170.00
+
+datediff(date.mdy(7,18,2094), date.mdy(11,10,2038), 'quarters') => 222.00
+
+datediff(date.mdy(2,29,1904), date.mdy(2,29,1900), 'quarters') => 15.00
+
+datediff(date.mdy(2,29,1908), date.mdy(2,29,1904), 'quarters') => 16.00
+
+datediff(date.mdy(2,28,1903), date.mdy(2,29,1900), 'quarters') => 11.00
+])
+done
+AT_CLEANUP
+
+AT_SETUP([expressions - DATEDIFF months])
+AT_KEYWORDS([expression expressions evaluate])
+AT_DATA([evaluate-base.sps], [dnl
+SET EPOCH 1940.
+DEBUG EVALUATE SET opt.
+DEBUG EVALUATE /datediff(date.mdy(6,10,1648), date.mdy(6,30,1680), 'months').
+DEBUG EVALUATE /datediff(date.mdy(6,30,1680), date.mdy(7,24,1716), 'months').
+DEBUG EVALUATE /datediff(date.mdy(7,24,1716), date.mdy(6,19,1768), 'months').
+DEBUG EVALUATE /datediff(date.mdy(6,19,1768), date.mdy(8,2,1819), 'months').
+DEBUG EVALUATE /datediff(date.mdy(8,2,1819), date.mdy(3,27,1839), 'months').
+DEBUG EVALUATE /datediff(date.mdy(3,27,1839), date.mdy(4,19,1903), 'months').
+DEBUG EVALUATE /datediff(date.mdy(4,19,1903), date.mdy(8,25,1929), 'months').
+DEBUG EVALUATE /datediff(date.mdy(8,25,1929), date.mdy(9,29,1941), 'months').
+DEBUG EVALUATE /datediff(date.mdy(9,29,1941), date.mdy(4,19,1943), 'months').
+DEBUG EVALUATE /datediff(date.mdy(4,19,1943), date.mdy(10,7,1943), 'months').
+DEBUG EVALUATE /datediff(date.mdy(10,7,1943), date.mdy(3,17,1992), 'months').
+DEBUG EVALUATE /datediff(date.mdy(3,17,1992), date.mdy(2,25,1996), 'months').
+DEBUG EVALUATE /datediff(date.mdy(9,29,1941), date.mdy(2,25,1996), 'months').
+DEBUG EVALUATE /datediff(date.mdy(9,29,1941), date.mdy(4,19,43), 'months').
+DEBUG EVALUATE /datediff(date.mdy(4,19,43), date.mdy(10,7,43), 'months').
+DEBUG EVALUATE /datediff(date.mdy(10,7,43), date.mdy(3,17,92), 'months').
+DEBUG EVALUATE /datediff(date.mdy(3,17,92), date.mdy(2,25,96), 'months').
+DEBUG EVALUATE /datediff(date.mdy(2,25,96), date.mdy(11,10,2038), 'months').
+DEBUG EVALUATE /datediff(date.mdy(11,10,2038), date.mdy(7,18,2094), 'months').
+DEBUG EVALUATE /datediff(date.mdy(2,29,1900), date.mdy(2,29,1904), 'months').
+DEBUG EVALUATE /datediff(date.mdy(2,29,1904), date.mdy(2,29,1908), 'months').
+DEBUG EVALUATE /datediff(date.mdy(2,29,1900), date.mdy(2,28,1903), 'months').
+
+DEBUG EVALUATE /datediff(date.mdy(6,30,1680), date.mdy(6,10,1648), 'months').
+DEBUG EVALUATE /datediff(date.mdy(7,24,1716), date.mdy(6,30,1680), 'months').
+DEBUG EVALUATE /datediff(date.mdy(6,19,1768), date.mdy(7,24,1716), 'months').
+DEBUG EVALUATE /datediff(date.mdy(8,2,1819), date.mdy(6,19,1768), 'months').
+DEBUG EVALUATE /datediff(date.mdy(3,27,1839), date.mdy(8,2,1819), 'months').
+DEBUG EVALUATE /datediff(date.mdy(4,19,1903), date.mdy(3,27,1839), 'months').
+DEBUG EVALUATE /datediff(date.mdy(8,25,1929), date.mdy(4,19,1903), 'months').
+DEBUG EVALUATE /datediff(date.mdy(9,29,1941), date.mdy(8,25,1929), 'months').
+DEBUG EVALUATE /datediff(date.mdy(4,19,1943), date.mdy(9,29,1941), 'months').
+DEBUG EVALUATE /datediff(date.mdy(10,7,1943), date.mdy(4,19,1943), 'months').
+DEBUG EVALUATE /datediff(date.mdy(3,17,1992), date.mdy(10,7,1943), 'months').
+DEBUG EVALUATE /datediff(date.mdy(2,25,1996), date.mdy(3,17,1992), 'months').
+DEBUG EVALUATE /datediff(date.mdy(2,25,1996), date.mdy(9,29,1941), 'months').
+DEBUG EVALUATE /datediff(date.mdy(4,19,43), date.mdy(9,29,1941), 'months').
+DEBUG EVALUATE /datediff(date.mdy(10,7,43), date.mdy(4,19,43), 'months').
+DEBUG EVALUATE /datediff(date.mdy(3,17,92), date.mdy(10,7,43), 'months').
+DEBUG EVALUATE /datediff(date.mdy(2,25,96), date.mdy(3,17,92), 'months').
+DEBUG EVALUATE /datediff(date.mdy(11,10,2038), date.mdy(2,25,96), 'months').
+DEBUG EVALUATE /datediff(date.mdy(7,18,2094), date.mdy(11,10,2038), 'months').
+DEBUG EVALUATE /datediff(date.mdy(2,29,1904), date.mdy(2,29,1900), 'months').
+DEBUG EVALUATE /datediff(date.mdy(2,29,1908), date.mdy(2,29,1904), 'months').
+DEBUG EVALUATE /datediff(date.mdy(2,28,1903), date.mdy(2,29,1900), 'months').
+])
+
+for opt in OPT NOOPT; do
+    AS_BOX([$opt])
+    sed "s/opt/$opt/" < evaluate-base.sps > evaluate.sps
+    AT_CHECK([pspp --testing-mode evaluate.sps], [0], [dnl
+datediff(date.mdy(6,10,1648), date.mdy(6,30,1680), 'months') => -384.00
+
+datediff(date.mdy(6,30,1680), date.mdy(7,24,1716), 'months') => -432.00
+
+datediff(date.mdy(7,24,1716), date.mdy(6,19,1768), 'months') => -622.00
+
+datediff(date.mdy(6,19,1768), date.mdy(8,2,1819), 'months') => -613.00
+
+datediff(date.mdy(8,2,1819), date.mdy(3,27,1839), 'months') => -235.00
+
+datediff(date.mdy(3,27,1839), date.mdy(4,19,1903), 'months') => -768.00
+
+datediff(date.mdy(4,19,1903), date.mdy(8,25,1929), 'months') => -316.00
+
+datediff(date.mdy(8,25,1929), date.mdy(9,29,1941), 'months') => -145.00
+
+datediff(date.mdy(9,29,1941), date.mdy(4,19,1943), 'months') => -18.00
+
+datediff(date.mdy(4,19,1943), date.mdy(10,7,1943), 'months') => -5.00
+
+datediff(date.mdy(10,7,1943), date.mdy(3,17,1992), 'months') => -581.00
+
+datediff(date.mdy(3,17,1992), date.mdy(2,25,1996), 'months') => -47.00
+
+datediff(date.mdy(9,29,1941), date.mdy(2,25,1996), 'months') => -652.00
+
+datediff(date.mdy(9,29,1941), date.mdy(4,19,43), 'months') => -18.00
 
-AT_BANNER([expressions])
+datediff(date.mdy(4,19,43), date.mdy(10,7,43), 'months') => -5.00
 
-CHECK_EXPR_EVAL([numeric syntax],
-  [[1e2], [100.00]],
-  [[1e+2], [100.00]],
-  [[1e-2], [0.01]],
-  [[1e-99], [0.00]])
-
-CHECK_EXPR_EVAL([coercion to/from Boolean],
-  [[0 AND 1], [false]],
-  [[$true AND 1], [true]],
-  [[1 OR $false], [true]],
-  [[1 OR $sysmis], [true]],
-  [[2 OR $sysmis], [sysmis],
-   [error: DEBUG EVALUATE: An operand of the logical disjunction (`OR') operator was found to have a value other than 0 (false), 1 (true), or the system-missing value.  The result was forced to 0.]],
-  [[2 AND $sysmis], [false],
-   [error: DEBUG EVALUATE: An operand of the logical conjunction (`AND') operator was found to have a value other than 0 (false), 1 (true), or the system-missing value.  The result was forced to 0.]],
-  [['string' AND $sysmis], [error],
-   [error: DEBUG EVALUATE: Type mismatch while applying logical conjunction (`AND') operator: cannot convert string to boolean.]],
-  [[0 AND $sysmis], [false]],
-  [[(1>2) + 1], [1.00]],
-  [[$true + $false], [1.00]])
-
-CHECK_EXPR_EVAL([addition and subtraction],
-  [[1 + 2], [3.00]],
-  [[1 + $true], [2.00]],
-  [[$sysmis + 1], [sysmis]],
-  [[7676 + $sysmis], [sysmis]],
-  [[('foo') + 5], [error],
-   [error: DEBUG EVALUATE: Type mismatch while applying addition (`+') operator: cannot convert string to number.]],
-  dnl Arithmetic concatenation requires CONCAT:
-  [[('foo') + ('bar')], [error],
-   [error: DEBUG EVALUATE: Type mismatch while applying addition (`+') operator: cannot convert string to number.]],
-  dnl Lexical concatenation succeeds:
-  [['foo' + 'bar'], ["foobar"]],
-  [[1 +3 - 2 +4 -5], [1.00]],
-  [[1 - $true], [0.00]],
-  [[$true - 4/3], [-0.33]],
-  [['string' - 1e10], [error],
-   [error: DEBUG EVALUATE: Type mismatch while applying subtraction (`-') operator: cannot convert string to number.]],
-  [[9.5 - ''], [error],
-   [error: DEBUG EVALUATE: Type mismatch while applying subtraction (`-') operator: cannot convert string to number.]],
-  [[1 - 2], [-1.00]],
-  [[52 -23], [29.00]])
-
-CHECK_EXPR_EVAL([multiplication and division],
-  [[5 * 10], [50.00]],
-  [[10 * $true], [10.00]],
-  [[$true * 5], [5.00]],
-  [[1.5 * $true], [1.50]],
-  [[5 * $sysmis], [sysmis]],
-  [[$sysmis * 15], [sysmis]],
-  [[2 * 5 / 10], [1.00]],
-  [[1 / 2], [0.50]],
-  [[2 / 5], [0.40]],
-  [[12 / 3 / 2], [2.00]])
-
-CHECK_EXPR_EVAL([exponentiation],
-  [[2**8], [256.00]],
-  [[(2**3)**4], [4096.00]],
-  [[2**3**4], [4096.00],
-   [warning: DEBUG EVALUATE: The exponentiation operator (`**') is left-associative, even though right-associative semantics are more useful.  That is, `a**b**c' equals `(a**b)**c', not as `a**(b**c)'.  To disable this warning, insert parentheses.]])
-
-CHECK_EXPR_EVAL([unary minus],
-  [[2+-3], [-1.00]],
-  [[2*-3], [-6.00]],
-  [[-3**2], [-9.00]],
-  [[(-3)**2], [9.00]],
-  [[2**-1], [0.50]],
-  [[0**0], [sysmis]],
-  [[0**-1], [sysmis]],
-  [[(-3)**1.5], [sysmis]])
-
-CHECK_EXPR_EVAL([AND truth table],
-  [[$false AND $false], [false]],
-  [[$false AND $true], [false]],
-  [[$false AND $sysmis], [false]],
-  [[$true AND $false], [false]],
-  [[$true AND $true], [true]],
-  [[$true AND $sysmis], [sysmis]],
-  [[$sysmis AND $false], [false]],
-  [[$sysmis AND $true], [sysmis]],
-  [[$sysmis AND $sysmis], [sysmis]],
-  [[$false & $false], [false]],
-  [[$false & $true], [false]],
-  [[$false & $sysmis], [false]],
-  [[$true & $false], [false]],
-  [[$true & $true], [true]],
-  [[$true & $sysmis], [sysmis]],
-  [[$sysmis & $false], [false]],
-  [[$sysmis & $true], [sysmis]],
-  [[$sysmis & $sysmis], [sysmis]])
-
-CHECK_EXPR_EVAL([OR truth table],
-  [[$false OR $false], [false]],
-  [[$false OR $true], [true]],
-  [[$false OR $sysmis], [sysmis]],
-  [[$true OR $false], [true]],
-  [[$true OR $true], [true]],
-  [[$true OR $sysmis], [true]],
-  [[$sysmis OR $false], [sysmis]],
-  [[$sysmis OR $true], [true]],
-  [[$sysmis OR $sysmis], [sysmis]],
-  [[$false | $false], [false]],
-  [[$false | $true], [true]],
-  [[$false | $sysmis], [sysmis]],
-  [[$true | $false], [true]],
-  [[$true | $true], [true]],
-  [[$true | $sysmis], [true]],
-  [[$sysmis | $false], [sysmis]],
-  [[$sysmis | $true], [true]],
-  [[$sysmis | $sysmis], [sysmis]])
-
-CHECK_EXPR_EVAL([NOT truth table],
-  [[not $false], [true]],
-  [[not 0], [true]],
-  [[not 2.5], [true],
-   [error: DEBUG EVALUATE: An operand of the logical negation (`NOT') operator was found to have a value other than 0 (false), 1 (true), or the system-missing value.  The result was forced to 0.]],
-  [[not $true], [false]],
-  [[not 1], [false]],
-  [[not $sysmis], [sysmis]],
-  [[~ $false], [true]],
-  [[~ 0], [true]],
-  [[~ 2.5], [true],
-   [error: DEBUG EVALUATE: An operand of the logical negation (`NOT') operator was found to have a value other than 0 (false), 1 (true), or the system-missing value.  The result was forced to 0.]],
-  [[~ $true], [false]],
-  [[~ 1], [false]],
-  [[~ $sysmis], [sysmis]])
-
-CHECK_EXPR_EVAL([= <= <],
-  [[1 eq 1], [true]],
-  [[1 = 1], [true]],
-  [[1 eq 2], [false]],
-  [[2 = 3], [false]],
-  [[1 eq 'foobar'], [error],
-   [error: DEBUG EVALUATE: Type mismatch while applying numeric equality (`EQ') operator: cannot convert string to number.]],
-  [[5 eq 'foobar'], [error],
-   [error: DEBUG EVALUATE: Type mismatch while applying numeric equality (`EQ') operator: cannot convert string to number.]],
-  [['baz' = 10], [error],
-   [error: DEBUG EVALUATE: Type mismatch while applying string equality (`=') operator: cannot convert number to string.]],
-  [['quux' = 5.55], [error],
-   [error: DEBUG EVALUATE: Type mismatch while applying string equality (`=') operator: cannot convert number to string.]],
-  [['foobar' = 'foobar'], [true]],
-  [['quux' = 'bar'], [false]],
-  [['bar   ' = 'bar'], [true]],
-  [['asdf         ' = 'asdf  '], [true]],
-  [['asdfj   ' = 'asdf'], [false]],
-dnl Check precedence:
-  [[1 + 2 = 3], [true]],
-  [[1 >= 2 = 2 ge 3], [false],
-   [warning: DEBUG EVALUATE: Chaining relational operators (e.g. `a < b < c') will not produce the mathematically expected result.  Use the AND logical operator to fix the problem (e.g. `a < b AND b < c').  If chaining is really intended, parentheses will disable this warning (e.g. `(a < b) < c'.)]],
-dnl Mathematically true:
-  [[3 ne 2 ~= 1], [false],
-   [warning: DEBUG EVALUATE: Chaining relational operators (e.g. `a < b < c') will not produce the mathematically expected result.  Use the AND logical operator to fix the problem (e.g. `a < b AND b < c').  If chaining is really intended, parentheses will disable this warning (e.g. `(a < b) < c'.)]],
-  [[3 > 2 > 1], [false],
-   [warning: DEBUG EVALUATE: Chaining relational operators (e.g. `a < b < c') will not produce the mathematically expected result.  Use the AND logical operator to fix the problem (e.g. `a < b AND b < c').  If chaining is really intended, parentheses will disable this warning (e.g. `(a < b) < c'.)]],
-
-  [[1 <= 2], [true]],
-  [[2.5 <= 1.5], [false]],
-  [[1 le 2], [true]],
-  [[2 <= 2], [true]],
-  [[2 le 2], [true]],
-dnl Make sure <= token can't be split:
-  [[2 < = 2], [error],
-   [error: DEBUG EVALUATE: Syntax error at `='.]],
-  [[1 <= 'foobar'], [error],
-   [error: DEBUG EVALUATE: Type mismatch while applying numeric less-than-or-equal-to (`<=') operator: cannot convert string to number.]],
-  [[5 <= 'foobar'], [error],
-   [error: DEBUG EVALUATE: Type mismatch while applying numeric less-than-or-equal-to (`<=') operator: cannot convert string to number.]],
-  [['baz' <= 10], [error],
-   [error: DEBUG EVALUATE: Type mismatch while applying string less-than-or-equal-to (`<=') operator: cannot convert number to string.]],
-  [['quux' <= 5.55], [error],
-   [error: DEBUG EVALUATE: Type mismatch while applying string less-than-or-equal-to (`<=') operator: cannot convert number to string.]],
-  [['0123' <= '0123'], [true]],
-  [['0123' <= '0124'], [true]],
-  [['0124' le '0123'], [false]],
-  [['0123  ' <= '0123'], [true]],
-  [['0123' le '0123  '], [true]],
-
-  [[1 < 2], [true]],
-  [[2.5 < 1.5], [false]],
-  [[3.5 lt 4], [true]],
-  [[4 lt 3.5], [false]],
-  [[1 lt 'foobar'], [error],
-   [error: DEBUG EVALUATE: Type mismatch while applying numeric less than (`<') operator: cannot convert string to number.]],
-  [[5 lt 'foobar'], [error],
-   [error: DEBUG EVALUATE: Type mismatch while applying numeric less than (`<') operator: cannot convert string to number.]],
-  [['baz' < 10], [error],
-   [error: DEBUG EVALUATE: Type mismatch while applying string less than (`<') operator: cannot convert number to string.]],
-  [['quux' < 5.55], [error],
-   [error: DEBUG EVALUATE: Type mismatch while applying string less than (`<') operator: cannot convert number to string.]],
-  [['0123' lt '0123'], [false]],
-  [['0123' < '0124'], [true]],
-  [['0124' lt '0123'], [false]],
-  [['0123  ' < '0123'], [false]],
-  [['0123' lt '0123  '], [false]])
-
-CHECK_EXPR_EVAL([>= > <>],
-  [[1 >= 2], [false]],
-  [[2.5 >= 1.5], [true]],
-  [[1 ge 2], [false]],
-  [[2 >= 2], [true]],
-  [[2 ge 2], [true]],
-dnl Make sure >= token can't be split:
-  [[2 > = 2], [error],
-   [error: DEBUG EVALUATE: Syntax error at `='.]],
-  [[1 >= 'foobar'], [error],
-   [error: DEBUG EVALUATE: Type mismatch while applying numeric greater-than-or-equal-to (`>=') operator: cannot convert string to number.]],
-  [[5 ge 'foobar'], [error],
-   [error: DEBUG EVALUATE: Type mismatch while applying numeric greater-than-or-equal-to (`>=') operator: cannot convert string to number.]],
-  [['baz' ge 10], [error],
-   [error: DEBUG EVALUATE: Type mismatch while applying string greater-than-or-equal-to (`>=') operator: cannot convert number to string.]],
-  [['quux' >= 5.55], [error],
-   [error: DEBUG EVALUATE: Type mismatch while applying string greater-than-or-equal-to (`>=') operator: cannot convert number to string.]],
-  [['0123' ge '0123'], [true]],
-  [['0123' >= '0124'], [false]],
-  [['0124' >= '0123'], [true]],
-  [['0123  ' ge '0123'], [true]],
-  [['0123' >= '0123  '], [true]],
-
-  [[1 > 2], [false]],
-  [[2.5 > 1.5], [true]],
-  [[3.5 gt 4], [false]],
-  [[4 gt 3.5], [true]],
-  [[1 gt 'foobar'], [error],
-   [error: DEBUG EVALUATE: Type mismatch while applying numeric greater than (`>') operator: cannot convert string to number.]],
-  [[5 gt 'foobar'], [error],
-   [error: DEBUG EVALUATE: Type mismatch while applying numeric greater than (`>') operator: cannot convert string to number.]],
-  [['baz' > 10], [error],
-   [error: DEBUG EVALUATE: Type mismatch while applying string greater than (`>') operator: cannot convert number to string.]],
-  [['quux' > 5.55], [error],
-   [error: DEBUG EVALUATE: Type mismatch while applying string greater than (`>') operator: cannot convert number to string.]],
-  [['0123' gt '0123'], [false]],
-  [['0123' > '0124'], [false]],
-  [['0124' gt '0123'], [true]],
-  [['0123  ' > '0123'], [false]],
-  [['0123' gt '0123  '], [false]],
-
-  [[1 ne 1], [false]],
-  [[1 ~= 1], [false]],
-  [[1 <> 2], [true]],
-  [[2 ne 3], [true]],
-  [[1 ~= 'foobar'], [error],
-   [error: DEBUG EVALUATE: Type mismatch while applying numeric inequality (`<>') operator: cannot convert string to number.]],
-  [[5 <> 'foobar'], [error],
-   [error: DEBUG EVALUATE: Type mismatch while applying numeric inequality (`<>') operator: cannot convert string to number.]],
-  [['baz' ne 10], [error],
-   [error: DEBUG EVALUATE: Type mismatch while applying string inequality (`<>') operator: cannot convert number to string.]],
-  [['quux' ~= 5.55], [error],
-   [error: DEBUG EVALUATE: Type mismatch while applying string inequality (`<>') operator: cannot convert number to string.]],
-  [['foobar' <> 'foobar'], [false]],
-  [['quux' ne 'bar'], [true]],
-  [['bar   ' <> 'bar'], [false]],
-  [['asdf         ' ~= 'asdf  '], [false]],
-  [['asdfj   ' ne 'asdf'], [true]],
-dnl <> token can't be split:
-  [[1 < > 1], [error],
-   [error: DEBUG EVALUATE: Syntax error at `>'.]],
-dnl # ~= token can't be split:
-  [[1 ~ = 1], [error],
-   [error: DEBUG EVALUATE: Syntax error at `~': expecting end of command.]])
-
-CHECK_EXPR_EVAL([exp lg10 ln sqrt abs mod mod10 rnd trunc],
-  [[exp(10)], [22026.47]],
-  [[exp('x')], [error],
-   [error: DEBUG EVALUATE: Type mismatch invoking EXP(number) as exp(string).]],
-
-  [[lg10(500)], [2.70]],
-  [[lg10('x')], [error],
-   [error: DEBUG EVALUATE: Type mismatch invoking LG10(number) as lg10(string).]],
-
-  [[ln(10)], [2.30]],
-  [[ln('x')], [error],
-   [error: DEBUG EVALUATE: Type mismatch invoking LN(number) as ln(string).]],
-
-  [[sqrt(500)], [22.36]],
-  [[sqrt('x')], [error],
-   [error: DEBUG EVALUATE: Type mismatch invoking SQRT(number) as sqrt(string).]],
-
-  [[abs(-10.5)], [10.50]],
-  [[abs(-55.79)], [55.79]],
-  [[abs(22)], [22.00]],
-  [[abs(0)], [0.00]],
-
-  [[mod(55.5, 2)], [1.50]],
-  [[mod(-55.5, 2)], [-1.50]],
-  [[mod(55.5, -2)], [1.50]],
-  [[mod(-55.5, -2)], [-1.50]],
-  [[mod('a', 2)], [error],
-   [error: DEBUG EVALUATE: Type mismatch invoking MOD(number, number) as mod(string, number).]],
-  [[mod(2, 'a')], [error],
-   [error: DEBUG EVALUATE: Type mismatch invoking MOD(number, number) as mod(number, string).]],
-  [[mod('a', 'b')], [error],
-   [error: DEBUG EVALUATE: Type mismatch invoking MOD(number, number) as mod(string, string).]],
-
-  [[mod10(55.5)], [5.50]],
-  [[mod10(-55.5)], [-5.50]],
-  [[mod10('x')], [error],
-   [error: DEBUG EVALUATE: Type mismatch invoking MOD10(number) as mod10(string).]],
-
-  [[rnd(5.4)], [5.00]],
-  [[rnd(5.6)], [6.00]],
-  [[rnd(-5.4)], [-5.00]],
-  [[rnd(-5.6)], [-6.00]],
-  [[rnd(5.56, .1)], [5.60]],
-  [[rnd(-5.56, .1)], [-5.60]],
-  [[rnd(.5)], [1.00]],
-  [[rnd(.5 - 2**-53)], [1.00]],
-  [[rnd(.5 - 2**-52)], [1.00]],
-  [[rnd(.5 - 2**-51)], [1.00]],
-  [[rnd(.5 - 2**-45)], [0.00]],
-  [[rnd(.5 - 2**-45, 1, 10)], [1.00]],
-  [[rnd('x')], [error],
-   [error: DEBUG EVALUATE: Function invocation rnd(string) does not match any known function.  Candidates are:
-RND(number)
-RND(number, number)
-RND(number, number, number).]],
-
-  [[trunc(1.2)], [1.00]],
-  [[trunc(1.9)], [1.00]],
-  [[trunc(-1.2)], [-1.00]],
-  [[trunc(-1.9)], [-1.00]],
-  [[trunc(5.06, .1)], [5.00]],
-  [[trunc(-5.06, .1)], [-5.00]],
-  [[trunc(1)], [1.00]],
-  [[trunc(1 - 2**-53)], [1.00]],
-  [[trunc(1 - 2**-52)], [1.00]],
-  [[trunc(1 - 2**-51)], [1.00]],
-  [[trunc(1 - 2**-45)], [0.00]],
-  [[trunc(1 - 2**-45, 1, 10)], [1.00]],
-  [[trunc('x')], [error],
-   [error: DEBUG EVALUATE: Function invocation trunc(string) does not match any known function.  Candidates are:
-TRUNC(number)
-TRUNC(number, number)
-TRUNC(number, number, number).]])
-
-CHECK_EXPR_EVAL([acos arsin artan cos sin tan],
-  [[acos(.5) / 3.14159 * 180], [60.00]],
-  [[arcos(.75) / 3.14159 * 180], [41.41]],
-  [[arcos(-.5) / 3.14159 * 180], [120.00]],
-  [[acos(-.75) / 3.14159 * 180], [138.59]],
-  [[acos(-1) / 3.14159 * 180], [180.00]],
-  [[arcos(1) / 3.14159 * 180], [0.00]],
-  [[acos(-1.01)], [sysmis]],
-  [[arcos(1.01)], [sysmis]],
-  [[acos('x')], [error],
-   [error: DEBUG EVALUATE: Type mismatch invoking ACOS(number) as acos(string).]],
-
-  [[arsin(.5) / 3.14159 * 180], [30.00]],
-  [[asin(.25) / 3.14159 * 180], [14.48]],
-  [[arsin(-.5) / 3.14159 * 180], [-30.00]],
-  [[asin(-.25) / 3.14159 * 180], [-14.48]],
-  [[arsin(-1.01)], [sysmis]],
-  [[asin(1.01)], [sysmis]],
-  [[arsin('x')], [error],
-   [error: DEBUG EVALUATE: Type mismatch invoking ARSIN(number) as arsin(string).]],
-
-  [[artan(1) / 3.14159 * 180], [45.00]],
-  [[atan(10) / 3.14159 * 180], [84.29]],
-  [[artan(-1) / 3.14159 * 180], [-45.00]],
-  [[atan(-10) / 3.14159 * 180], [-84.29]],
-  [[artan('x')], [error],
-   [error: DEBUG EVALUATE: Type mismatch invoking ARTAN(number) as artan(string).]],
-
-  [[cos(60 / 180 * 3.14159)], [0.50]],
-  [[cos(45 / 180 * 3.14159)], [0.71]],
-  [[cos(30 / 180 * 3.14159)], [0.87]],
-  [[cos(15 / 180 * 3.14159)], [0.97]],
-  [[cos(-60 / 180 * 3.14159)], [0.50]],
-  [[cos(-45 / 180 * 3.14159)], [0.71]],
-  [[cos(-30 / 180 * 3.14159)], [0.87]],
-  [[cos(-15 / 180 * 3.14159)], [0.97]],
-  [[cos(123 / 180 * 3.14159)], [-0.54]],
-  [[cos(321 / 180 * 3.14159)], [0.78]],
-  [[cos('x')], [error],
-   [error: DEBUG EVALUATE: Type mismatch invoking COS(number) as cos(string).]],
-
-  [[sin(60 / 180 * 3.14159)], [0.87]],
-  [[sin(45 / 180 * 3.14159)], [0.71]],
-  [[sin(30 / 180 * 3.14159)], [0.50]],
-  [[sin(15 / 180 * 3.14159)], [0.26]],
-  [[sin(-60 / 180 * 3.14159)], [-0.87]],
-  [[sin(-45 / 180 * 3.14159)], [-0.71]],
-  [[sin(-30 / 180 * 3.14159)], [-0.50]],
-  [[sin(-15 / 180 * 3.14159)], [-0.26]],
-  [[sin(123 / 180 * 3.14159)], [0.84]],
-  [[sin(321 / 180 * 3.14159)], [-0.63]],
-  [[sin('x')], [error],
-   [error: DEBUG EVALUATE: Type mismatch invoking SIN(number) as sin(string).]],
-
-  [[tan(60 / 180 * 3.14159)], [1.73]],
-  [[tan(45 / 180 * 3.14159)], [1.00]],
-  [[tan(30 / 180 * 3.14159)], [0.58]],
-  [[tan(15 / 180 * 3.14159)], [0.27]],
-  [[tan(-60 / 180 * 3.14159)], [-1.73]],
-  [[tan(-45 / 180 * 3.14159)], [-1.00]],
-  [[tan(-30 / 180 * 3.14159)], [-0.58]],
-  [[tan(-15 / 180 * 3.14159)], [-0.27]],
-  [[tan(123 / 180 * 3.14159)], [-1.54]],
-  [[tan(321 / 180 * 3.14159)], [-0.81]],
-  [[tan('x')], [error],
-   [error: DEBUG EVALUATE: Type mismatch invoking TAN(number) as tan(string).]])
-# FIXME: a variable name as the argument to SYSMIS is a special case
-# that we don't yet test.  We also can't test VALUE this way.
-CHECK_EXPR_EVAL([missing nmiss nvalid sysmis any range max min],
-  [[missing(10)], [false]],
-  [[missing($sysmis)], [true]],
-  [[missing(asin(1.01))], [true]],
-  [[missing(asin(.5))], [false]],
-  [[missing('    ')], [error],
-   [error: DEBUG EVALUATE: Type mismatch invoking MISSING(number) as missing(string).]],
-
-  [[nmiss($sysmis)], [1.00]],
-  [[nmiss(0)], [0.00]],
-  [[nmiss($sysmis, $sysmis, $sysmis)], [3.00]],
-  [[nmiss(1, 2, 3, 4)], [0.00]],
-  [[nmiss(1, $sysmis, $sysmis, 2, 2, $sysmis, $sysmis, 3, 4)], [4.00]],
-
-  [[nvalid($sysmis)], [0.00]],
-  [[nvalid(0)], [1.00]],
-  [[nvalid($sysmis, $sysmis, $sysmis)], [0.00]],
-  [[nvalid(1, 2, 3, 4)], [4.00]],
-  [[nvalid(1, $sysmis, $sysmis, 2, 2, $sysmis, $sysmis, 3, 4)], [5.00]],
-
-  [[sysmis(10)], [false]],
-  [[sysmis($sysmis)], [true]],
-  [[sysmis(asin(1.01))], [true]],
-  [[sysmis(asin(.5))], [false]],
-  [[sysmis('    ')], [error],
-   [error: DEBUG EVALUATE: Function invocation sysmis(string) does not match any known function.  Candidates are:
-SYSMIS(num_variable)
-SYSMIS(number).]],
-
-  [[any($sysmis, 1, $sysmis, 3)], [sysmis]],
-  [[any(1, 1, 2, 3)], [true]],
-  [[any(1, $true, 2, 3)], [true]],
-  [[any(1, $false, 2, 3)], [false]],
-  [[any(2, 1, 2, 3)], [true]],
-  [[any(3, 1, 2, 3)], [true]],
-  [[any(5, 1, 2, 3)], [false]],
-  [[any(1, 1, 1, 1)], [true]],
-  [[any($sysmis, 1, 1, 1)], [sysmis]],
-  [[any(1, $sysmis, $sysmis, $sysmis)], [sysmis]],
-  [[any($sysmis, $sysmis, $sysmis, $sysmis)], [sysmis]],
-  [[any(1)], [error],
-   [error: DEBUG EVALUATE: Function invocation any(number) does not match any known function.  Candidates are:
-ANY(number, number[, number]...)
-ANY(string, string[, string]...).]],
-  [[any('1', 2, 3, 4)], [error],
-   [error: DEBUG EVALUATE: Function invocation any(string, number, number, number) does not match any known function.  Candidates are:
-ANY(number, number[, number]...)
-ANY(string, string[, string]...).]],
-  [[any(1, '2', 3, 4)], [error],
-   [error: DEBUG EVALUATE: Function invocation any(number, string, number, number) does not match any known function.  Candidates are:
-ANY(number, number[, number]...)
-ANY(string, string[, string]...).]],
-  [[any(1, 2, '3', 4)], [error],
-   [error: DEBUG EVALUATE: Function invocation any(number, number, string, number) does not match any known function.  Candidates are:
-ANY(number, number[, number]...)
-ANY(string, string[, string]...).]],
-  [[any(1, 2, 3, '4')], [error],
-   [error: DEBUG EVALUATE: Function invocation any(number, number, number, string) does not match any known function.  Candidates are:
-ANY(number, number[, number]...)
-ANY(string, string[, string]...).]],
-
-  [[any('', 'a', '', 'c')], [true]],
-  [[any('a', 'a', 'b', 'c')], [true]],
-  [[any('b', 'a', 'b', 'c')], [true]],
-  [[any('c', 'a', 'b', 'c')], [true]],
-  [[any('e', 'a', 'b', 'c')], [false]],
-  [[any('a', 'a', 'a', 'a')], [true]],
-  [[any('', 'a', 'a', 'a')], [false]],
-  [[any('a', '', '', '')], [false]],
-  [[any('a')], [error],
-   [error: DEBUG EVALUATE: Function invocation any(string) does not match any known function.  Candidates are:
-ANY(number, number[, number]...)
-ANY(string, string[, string]...).]],
-  [[any('a', 'a  ', 'b', 'c')], [true]],
-  [[any('b   ', 'a', 'b', 'c')], [true]],
-  [[any('c   ', 'a', 'b', 'c     ')], [true]],
-  [[any(a10, 'b', 'c', 'd')], [error],
-   [error: DEBUG EVALUATE: Function invocation any(format, string, string, string) does not match any known function.  Candidates are:
-ANY(number, number[, number]...)
-ANY(string, string[, string]...).]],
-  [[any('a', b, 'c', 'd')], [error],
-   [error: DEBUG EVALUATE: Unknown identifier b.]],
-  [[any('a', 'b', c, 'd')], [error],
-   [error: DEBUG EVALUATE: Unknown identifier c.]],
-  [[any('a', 'b', 'c', d)], [error],
-   [error: DEBUG EVALUATE: Unknown identifier d.]],
-
-  [[range(5, 1, 10)], [true]],
-  [[range(1, 1, 10)], [true]],
-  [[range(10, 1, 10)], [true]],
-  [[range(-1, 1, 10)], [false]],
-  [[range(12, 1, 10)], [false]],
-  [[range($sysmis, 1, 10)], [sysmis]],
-  [[range(5, 1, $sysmis)], [sysmis]],
-  [[range(5, $sysmis, 10)], [sysmis]],
-  [[range($sysmis, $sysmis, 10)], [sysmis ]],
-  [[range($sysmis, 1, $sysmis)], [sysmis]],
-  [[range($sysmis, $sysmis, $sysmis)], [sysmis]],
-  [[range(0, 1, 8, 10, 18)], [false]],
-  [[range(1, 1, 8, 10, 18)], [true]],
-  [[range(6, 1, 8, 10, 18)], [true]],
-  [[range(8, 1, 8, 10, 18)], [true]],
-  [[range(9, 1, 8, 10, 18)], [false]],
-  [[range(10, 1, 8, 10, 18)], [true]],
-  [[range(13, 1, 8, 10, 18)], [true]],
-  [[range(16, 1, 8, 10, 18)], [true]],
-  [[range(18, 1, 8, 10, 18)], [true]],
-  [[range(20, 1, 8, 10, 18)], [false]],
-  [[range(1)], [error],
-   [error: DEBUG EVALUATE: Function invocation range(number) does not match any known function.  Candidates are:
-RANGE(number, number, number[, number, number]...)
-RANGE(string, string, string[, string, string]...).]],
-  [[range(1, 2)], [error],
-   [error: DEBUG EVALUATE: RANGE(number, number, number[, number, number]...) must have an odd number of arguments.]],
-  [[range(1, 2, 3, 4)], [error],
-   [error: DEBUG EVALUATE: RANGE(number, number, number[, number, number]...) must have an odd number of arguments.]],
-  [[range(1, 2, 3, 4, 5, 6)], [error],
-   [error: DEBUG EVALUATE: RANGE(number, number, number[, number, number]...) must have an odd number of arguments.]],
-  [[range('1', 2, 3)], [error],
-   [error: DEBUG EVALUATE: Function invocation range(string, number, number) does not match any known function.  Candidates are:
-RANGE(number, number, number[, number, number]...)
-RANGE(string, string, string[, string, string]...).]],
-  [[range(1, '2', 3)], [error],
-   [error: DEBUG EVALUATE: Function invocation range(number, string, number) does not match any known function.  Candidates are:
-RANGE(number, number, number[, number, number]...)
-RANGE(string, string, string[, string, string]...).]],
-  [[range(1, 2, '3')], [error],
-   [error: DEBUG EVALUATE: Function invocation range(number, number, string) does not match any known function.  Candidates are:
-RANGE(number, number, number[, number, number]...)
-RANGE(string, string, string[, string, string]...).]],
-
-  [[range('123', '111', '888')], [true]],
-  [[range('111', '111', '888')], [true]],
-  [[range('888', '111', '888')], [true]],
-  [[range('110', '111', '888')], [false]],
-  [[range('889', '111', '888')], [false]],
-  [[range('000', '111', '888')], [false]],
-  [[range('999', '111', '888')], [false]],
-  [[range('123   ', '111', '888')], [true]],
-  [[range('123', '111   ', '888')], [true]],
-  [[range('123', '111', '888   ')], [true]],
-  [[range('123', '111    ', '888   ')], [true]],
-  [[range('00', '01', '08', '10', '18')], [false]],
-  [[range('01', '01', '08', '10', '18')], [true]],
-  [[range('06', '01', '08', '10', '18')], [true]],
-  [[range('08', '01', '08', '10', '18')], [true]],
-  [[range('09', '01', '08', '10', '18')], [false]],
-  [[range('10', '01', '08', '10', '18')], [true]],
-  [[range('15', '01', '08', '10', '18')], [true]],
-  [[range('18', '01', '08', '10', '18')], [true]],
-  [[range('19', '01', '08', '10', '18')], [false]],
-  [[range('1')], [error],
-   [error: DEBUG EVALUATE: Function invocation range(string) does not match any known function.  Candidates are:
-RANGE(number, number, number[, number, number]...)
-RANGE(string, string, string[, string, string]...).]],
-  [[range('1', '2')], [error],
-   [error: DEBUG EVALUATE: RANGE(string, string, string[, string, string]...) must have an odd number of arguments.]],
-  [[range('1', '2', '3', '4')], [error],
-   [error: DEBUG EVALUATE: RANGE(string, string, string[, string, string]...) must have an odd number of arguments.]],
-  [[range('1', '2', '3', '4', '5', '6')], [error],
-   [error: DEBUG EVALUATE: RANGE(string, string, string[, string, string]...) must have an odd number of arguments.]],
-  [[range(1, '2', '3')], [error],
-   [error: DEBUG EVALUATE: Function invocation range(number, string, string) does not match any known function.  Candidates are:
-RANGE(number, number, number[, number, number]...)
-RANGE(string, string, string[, string, string]...).]],
-  [[range('1', 2, '3')], [error],
-   [error: DEBUG EVALUATE: Function invocation range(string, number, string) does not match any known function.  Candidates are:
-RANGE(number, number, number[, number, number]...)
-RANGE(string, string, string[, string, string]...).]],
-  [[range('1', '2', 3)], [error],
-   [error: DEBUG EVALUATE: Function invocation range(string, string, number) does not match any known function.  Candidates are:
-RANGE(number, number, number[, number, number]...)
-RANGE(string, string, string[, string, string]...).]],
+datediff(date.mdy(10,7,43), date.mdy(3,17,92), 'months') => -581.00
 
-  [[max(1, 2, 3, 4, 5)], [5.00]],
-  [[max(1, $sysmis, 2, 3, $sysmis, 4, 5)], [5.00]],
-  [[max(1, 2)], [2.00]],
-  [[max()], [error],
-   [error: DEBUG EVALUATE: Function invocation max() does not match any known function.  Candidates are:
-MAX(number[, number]...)
-MAX(string[, string]...).]],
-  [[max(1)], [1.00]],
-  [[max(1, $sysmis)], [1.00]],
-  [[max(1, 2, 3, $sysmis)], [3.00]],
-  [[max.4(1, 2, 3, $sysmis)], [sysmis]],
-  [[max.4(1, 2, 3)], [error],
-   [error: DEBUG EVALUATE: For MAX(number[, number]...) with 3 arguments, at most 3 (not 4) may be required to be valid.]],
-
-  [[max("2", "3", "5", "1", "4")], ["5"]],
-  [[max("1", "2")], ["2"]],
-  [[max("1")], ["1"]],
-
-  [[min(1, 2, 3, 4, 5)], [1.00]],
-  [[min(1, $sysmis, 2, 3, $sysmis, 4, 5)], [1.00]],
-  [[min(1, 2)], [1.00]],
-  [[min()], [error],
-   [error: DEBUG EVALUATE: Function invocation min() does not match any known function.  Candidates are:
-MIN(number[, number]...)
-MIN(string[, string]...).]],
-  [[min(1)], [1.00]],
-  [[min(1, $sysmis)], [1.00]],
-  [[min(1, 2, 3, $sysmis)], [1.00]],
-  [[min.4(1, 2, 3, $sysmis)], [sysmis]],
-  [[min.4(1, 2, 3)], [error],
-   [error: DEBUG EVALUATE: For MIN(number[, number]...) with 3 arguments, at most 3 (not 4) may be required to be valid.]],
-
-  [[min("2", "3", "5", "1", "4")], ["1"]],
-  [[min("1", "2")], ["1"]],
-  [[min("1")], ["1"]])
-
-CHECK_EXPR_EVAL([cfvar mean median sd sum variance],
-  [[cfvar(1, 2, 3, 4, 5)], [0.53]],
-  [[cfvar(1, $sysmis, 2, 3, $sysmis, 4, 5)], [0.53]],
-  [[cfvar(1, 2)], [0.47]],
-  [[cfvar(1)], [error],
-   [error: DEBUG EVALUATE: Type mismatch invoking CFVAR(number, number[, number]...) as cfvar(number).]],
-  [[cfvar(1, $sysmis)], [sysmis]],
-  [[cfvar(1, 2, 3, $sysmis)], [0.50]],
-  [[cfvar.4(1, 2, 3, $sysmis)], [sysmis]],
-  [[cfvar.4(1, 2, 3)], [error],
-   [error: DEBUG EVALUATE: For CFVAR(number, number[, number]...) with 3 arguments, at most 3 (not 4) may be required to be valid.]],
-  [[cfvar('x')], [error],
-   [error: DEBUG EVALUATE: Type mismatch invoking CFVAR(number, number[, number]...) as cfvar(string).]],
-  [[cfvar('x', 1, 2, 3)], [error],
-   [error: DEBUG EVALUATE: Type mismatch invoking CFVAR(number, number[, number]...) as cfvar(string, number, number, number).]],
-
-  [[mean(1, 2, 3, 4, 5)], [3.00]],
-  [[mean(1, $sysmis, 2, 3, $sysmis, 4, 5)], [3.00]],
-  [[mean(1, 2)], [1.50]],
-  [[mean()], [error],
-   [error: DEBUG EVALUATE: Type mismatch invoking MEAN(number[, number]...) as mean().]],
-  [[mean(1)], [1.00]],
-  [[mean(1, $sysmis)], [1.00]],
-  [[mean(1, 2, 3, $sysmis)], [2.00]],
-  [[mean.4(1, 2, 3, $sysmis)], [sysmis]],
-  [[mean.4(1, 2, 3)], [error],
-   [error: DEBUG EVALUATE: For MEAN(number[, number]...) with 3 arguments, at most 3 (not 4) may be required to be valid.]],
-
-  [[median(1, 2, 3, 4, 5)], [3.00]],
-  [[median(2, 3, 4, 5, 1)], [3.00]],
-  [[median(2, 3, 4, 1, 5)], [3.00]],
-  [[median(2, 1, 4, 5, 3)], [3.00]],
-  [[median(1, 2, 3, 4)], [2.50]],
-  [[median(2, 3, 1, 4)], [2.50]],
-  [[median(2, 3, 4, 1)], [2.50]],
-  [[median(2, 1, 4, 3)], [2.50]],
-  [[median(1, $sysmis, 3, 4, 5)], [3.50]],
-  [[median(2, 3, 4, 5, $sysmis, 1)], [3.00]],
-  [[median($sysmis, $sysmis, $sysmis, 2, 3, 4, 1, 5)], [3.00]],
-  [[median(1, 2, 3)], [2.00]],
-  [[median(1)], [1.00]],
-  [[median(1, 2)], [1.50]],
-  [[median(1, 2, $sysmis)], [1.50]],
-  [[median(1, $sysmis, $sysmis)], [1.00]],
-  [[median($sysmis, $sysmis, $sysmis)], [sysmis]],
-  [[median.3(1, 2, $sysmis)], [sysmis]],
-  [[median.2(1, $sysmis)], [sysmis]],
-
-  [[sd(1, 2, 3, 4, 5)], [1.58]],
-  [[sd(1, $sysmis, 2, 3, $sysmis, 4, 5)], [1.58]],
-  [[sd(1, 2)], [0.71]],
-  [[sd(1)], [error],
-   [error: DEBUG EVALUATE: Type mismatch invoking SD(number, number[, number]...) as sd(number).]],
-  [[sd(1, $sysmis)], [sysmis]],
-  [[sd(1, 2, 3, $sysmis)], [1.00]],
-  [[sd.4(1, 2, 3, $sysmis)], [sysmis]],
-  [[sd.4(1, 2, 3)], [error],
-   [error: DEBUG EVALUATE: For SD(number, number[, number]...) with 3 arguments, at most 3 (not 4) may be required to be valid.]],
-  [[sd('x')], [error],
-   [error: DEBUG EVALUATE: Type mismatch invoking SD(number, number[, number]...) as sd(string).]],
-  [[sd('x', 1, 2, 3)], [error],
-   [error: DEBUG EVALUATE: Type mismatch invoking SD(number, number[, number]...) as sd(string, number, number, number).]],
-
-  [[sum(1, 2, 3, 4, 5)], [15.00]],
-  [[sum(1, $sysmis, 2, 3, $sysmis, 4, 5)], [15.00]],
-  [[sum(1, 2)], [3.00]],
-  [[sum()], [error],
-   [error: DEBUG EVALUATE: Type mismatch invoking SUM(number[, number]...) as sum().]],
-  [[sum(1)], [1.00]],
-  [[sum(1, $sysmis)], [1.00]],
-  [[sum(1, 2, 3, $sysmis)], [6.00]],
-  [[sum.4(1, 2, 3, $sysmis)], [sysmis]],
-  [[sum.4(1, 2, 3)], [error],
-   [error: DEBUG EVALUATE: For SUM(number[, number]...) with 3 arguments, at most 3 (not 4) may be required to be valid.]],
-
-  [[variance(1, 2, 3, 4, 5)], [2.50]],
-  [[variance(1, $sysmis, 2, 3, $sysmis, 4, 5)], [2.50]],
-  [[variance(1, 2)], [0.50]],
-  [[variance(1)], [error],
-   [error: DEBUG EVALUATE: Type mismatch invoking VARIANCE(number, number[, number]...) as variance(number).]],
-  [[variance(1, $sysmis)], [sysmis]],
-  [[variance(1, 2, 3, $sysmis)], [1.00]],
-  [[variance.4(1, 2, 3, $sysmis)], [sysmis]],
-  [[variance.4(1, 2, 3)], [error],
-   [error: DEBUG EVALUATE: For VARIANCE(number, number[, number]...) with 3 arguments, at most 3 (not 4) may be required to be valid.]],
-  [[variance('x')], [error],
-   [error: DEBUG EVALUATE: Type mismatch invoking VARIANCE(number, number[, number]...) as variance(string).]],
-  [[variance('x', 1, 2, 3)], [error],
-   [error: DEBUG EVALUATE: Type mismatch invoking VARIANCE(number, number[, number]...) as variance(string, number, number, number).]])
-
-CHECK_EXPR_EVAL([concat index rindex length lower],
-  [[concat('')], [""]],
-  [[concat('a', 'b')], ["ab"]],
-  [[concat('a', 'b', 'c', 'd', 'e', 'f', 'g', 'h')], ["abcdefgh"]],
-  [[concat('abcdefgh', 'ijklmnopq')], ["abcdefghijklmnopq"]],
-  [[concat('a', 1)], [error],
-   [error: DEBUG EVALUATE: Type mismatch invoking CONCAT(string[, string]...) as concat(string, number).]],
-  [[concat(1, 2)], [error],
-   [error: DEBUG EVALUATE: Type mismatch invoking CONCAT(string[, string]...) as concat(number, number).]],
-
-  [[index('abcbcde', 'bc')], [2.00]],
-  [[index('abcbcde', 'bcd')], [4.00]],
-  [[index('abcbcde', 'bcbc')], [2.00]],
-  [[index('abcdefgh', 'abc')], [1.00]],
-  [[index('abcdefgh', 'bcd')], [2.00]],
-  [[index('abcdefgh', 'cde')], [3.00]],
-  [[index('abcdefgh', 'def')], [4.00]],
-  [[index('abcdefgh', 'efg')], [5.00]],
-  [[index('abcdefgh', 'fgh')], [6.00]],
-  [[index('abcdefgh', 'fghi')], [0.00]],
-  [[index('abcdefgh', 'x')], [0.00]],
-  [[index('abcdefgh', 'abch')], [0.00]],
-  [[index('banana', 'na')], [3.00]],
-  [[index('banana', 'ana')], [2.00]],
-  [[index('', 'x')], [0.00]],
-  [[index('', '')], [sysmis]],
-  [[index('abcdefgh', '')], [sysmis]],
-  [[index('abcdefgh', 'alkjsfdjlskalkjfa')], [0.00]],
-
-  [[index('abcbcde', 'bc', 1)], [2.00]],
-  [[index('abcbcde', 'dc', 1)], [3.00]],
-  [[index('abcbcde', 'abc', 1)], [1.00]],
-  [[index('abcbcde', 'bc', 2)], [2.00]],
-  [[index('abcbcde', 'dc', 2)], [0.00]],
-  [[index('abcbcde', 'abc', 1)], [1.00]],
-  [[index('abcbcde', 'bccb', 2)], [2.00]],
-  [[index('abcbcde', 'bcbc', 2)], [2.00]],
-  [[index('abcbcde', 'bcbc', $sysmis)], [sysmis]],
-
-  [[rindex('abcbcde', 'bc')], [4.00]],
-  [[rindex('abcbcde', 'bcd')], [4.00]],
-  [[rindex('abcbcde', 'bcbc')], [2.00]],
-  [[rindex('abcdefgh', 'abc')], [1.00]],
-  [[rindex('abcdefgh', 'bcd')], [2.00]],
-  [[rindex('abcdefgh', 'cde')], [3.00]],
-  [[rindex('abcdefgh', 'def')], [4.00]],
-  [[rindex('abcdefgh', 'efg')], [5.00]],
-  [[rindex('abcdefgh', 'fgh')], [6.00]],
-  [[rindex('abcdefgh', 'fghi')], [0.00]],
-  [[rindex('abcdefgh', 'x')], [0.00]],
-  [[rindex('abcdefgh', 'abch')], [0.00]],
-  [[rindex('banana', 'na')], [5.00]],
-  [[rindex('banana', 'ana')], [4.00]],
-  [[rindex('', 'x')], [0.00]],
-  [[rindex('', '')], [sysmis]],
-  [[rindex('abcdefgh', '')], [sysmis]],
-  [[rindex('abcdefgh', 'alkjsfdjlskalkjfa')], [0.00]],
-
-  [[rindex('abcbcde', 'bc', 1)], [5.00]],
-  [[rindex('abcbcde', 'dc', 1)], [6.00]],
-  [[rindex('abcbcde', 'abc', 1)], [5.00]],
-  [[rindex('abcbcde', 'bc', 2)], [4.00]],
-  [[rindex('abcbcde', 'dc', 2)], [0.00]],
-  [[rindex('abcbcde', 'abc', 1)], [5.00]],
-  [[rindex('abcbcde', 'bccb', 2)], [4.00]],
-  [[rindex('abcbcde', 'bcbc', 2)], [4.00]],
-  [[rindex('abcbcde', 'bcbc', 0)], [sysmis]],
-  [[rindex('abcbcde', 'bcbc', $sysmis)], [sysmis]],
-  [[rindex('abcbcde', 'bcbcg', 2)], [sysmis]],
-  [[rindex('abcbcde', 'bcbcg', $sysmis)], [sysmis]],
-  [[rindex('abcbcde', 'bcbcg', 'x')], [error],
-   [error: DEBUG EVALUATE: Function invocation rindex(string, string, string) does not match any known function.  Candidates are:
-RINDEX(string, string)
-RINDEX(string, string, number).]],
-  [[rindex(1, 'bcdfkjl', 2)], [error],
-   [error: DEBUG EVALUATE: Function invocation rindex(number, string, number) does not match any known function.  Candidates are:
-RINDEX(string, string)
-RINDEX(string, string, number).]],
-  [[rindex('aksj', 2, 2)], [error],
-   [error: DEBUG EVALUATE: Function invocation rindex(string, number, number) does not match any known function.  Candidates are:
-RINDEX(string, string)
-RINDEX(string, string, number).]],
-  [[rindex(1, 2, 3)], [error],
-   [error: DEBUG EVALUATE: Function invocation rindex(number, number, number) does not match any known function.  Candidates are:
-RINDEX(string, string)
-RINDEX(string, string, number).]],
-  [[rindex(1, 2, '3')], [error],
-   [error: DEBUG EVALUATE: Function invocation rindex(number, number, string) does not match any known function.  Candidates are:
-RINDEX(string, string)
-RINDEX(string, string, number).]],
-
-  [[length('')], [0.00]],
-  [[length('a')], [1.00]],
-  [[length('xy')], [2.00]],
-  [[length('adsf    ')], [8.00]],
-  [[length('abcdefghijkl')], [12.00]],
-  [[length(0)], [error],
-   [error: DEBUG EVALUATE: Type mismatch invoking LENGTH(string) as length(number).]],
-  [[length($sysmis)], [error],
-   [error: DEBUG EVALUATE: Type mismatch invoking LENGTH(string) as length(number).]],
-
-  [[lower('ABCDEFGHIJKLMNOPQRSTUVWXYZ!@%&*089')], ["abcdefghijklmnopqrstuvwxyz!@%&*089"]],
-  [[lower('')], [""]],
-  [[lower(1)], [error],
-   [error: DEBUG EVALUATE: Type mismatch invoking LOWER(string) as lower(number).]])
-
-CHECK_EXPR_EVAL([replace],
-  [[replace('banana', 'an', 'AN')], ["bANANa"]],
-  [[replace('banana', 'an', 'a')], ["baaa"]],
-  [[replace('banana', 'an', '')], ["ba"]],
-  [[replace('banana', 'na', '')], ["ba"]],
-  [[replace('banana', 'ba', 'BA')], ["BAnana"]],
-  [[replace('banana', 'na', 'xyzzy')], ["baxyzzyxyzzy"]],
-  [[replace('banana', 'an', 'xyzzy', 1)], ["bxyzzyana"]],
-  [[replace('banana', 'an', 'xyzzy', 1.5)], ["bxyzzyana"]],
-  [[replace('banana', 'bananana', 'xyzzy')], ["banana"]],
-  [[replace('banana', '', 'xyzzy')], ["banana"]],
-  [[replace('banana', 'ba', '', 0)], ["banana"]],
-  [[replace('banana', 'ba', '', -1)], ["banana"]],
-  [[replace('banana', 'ba', '', $sysmis)], ["banana"]])
-
-CHECK_EXPR_EVAL([lpad number ltrim lpad rtrim rpad string strunc substr upcase],
-  [[lpad('abc', -1)], [""]],
-  [[lpad('abc', 0)], ["abc"]],
-  [[lpad('abc', 2)], ["abc"]],
-  [[lpad('abc', 3)], ["abc"]],
-  [[lpad('abc', 10)], ["       abc"]],
-  [[lpad('abc', 32768)], [""]],
-  [[lpad('abc', $sysmis)], [""]],
-  [[lpad('abc', -1, '*')], [""]],
-  [[lpad('abc', 0, '*')], ["abc"]],
-  [[lpad('abc', 2, '*')], ["abc"]],
-  [[lpad('abc', 3, '*')], ["abc"]],
-  [[lpad('abc', 10, '*')], ["*******abc"]],
-  [[lpad('abc', 32768, '*')], [""]],
-  [[lpad('abc', $sysmis, '*')], [""]],
-  [[lpad('abc', $sysmis, '')], [""]],
-  [[lpad('abc', $sysmis, 'xy')], [""]],
-  [[lpad(0, 10)], [error],
-   [error: DEBUG EVALUATE: Function invocation lpad(number, number) does not match any known function.  Candidates are:
-LPAD(string, number)
-LPAD(string, number, string).]],
-  [[lpad('abc', 'def')], [error],
-   [error: DEBUG EVALUATE: Function invocation lpad(string, string) does not match any known function.  Candidates are:
-LPAD(string, number)
-LPAD(string, number, string).]],
-  [[lpad(0, 10, ' ')], [error],
-   [error: DEBUG EVALUATE: Function invocation lpad(number, number, string) does not match any known function.  Candidates are:
-LPAD(string, number)
-LPAD(string, number, string).]],
-  [[lpad('abc', 'def', ' ')], [error],
-   [error: DEBUG EVALUATE: Function invocation lpad(string, string, string) does not match any known function.  Candidates are:
-LPAD(string, number)
-LPAD(string, number, string).]],
-  [[lpad('x', 5, 0)], [error],
-   [error: DEBUG EVALUATE: Function invocation lpad(string, number, number) does not match any known function.  Candidates are:
-LPAD(string, number)
-LPAD(string, number, string).]],
-  [[lpad('x', 5, 2)], [error],
-   [error: DEBUG EVALUATE: Function invocation lpad(string, number, number) does not match any known function.  Candidates are:
-LPAD(string, number)
-LPAD(string, number, string).]],
-
-  [[number("123", f3.0)], [123.00]],
-  [[number(" 123", f3.0)], [12.00]],
-  [[number("123", f3.1)], [12.30]],
-  [[number("   ", f3.1)], [sysmis]],
-  [[number("123", a8)], [error],
-   [error: DEBUG EVALUATE: Type mismatch invoking NUMBER(string, num_input_format) as number(string, format).]],
-dnl CCA is not an input format:
-  [[number("123", cca1.2)], [error],
-   [error: DEBUG EVALUATE: Type mismatch invoking NUMBER(string, num_input_format) as number(string, format).]],
-
-  [[ltrim('   abc')], ["abc"]],
-  [[rtrim('   abc   ')], ["   abc"]],
-  [[ltrim('abc')], ["abc"]],
-  [[ltrim('    abc')], ["      abc"]],
-  [[ltrim('    ')], [""]],
-  [[ltrim('')], [""]],
-  [[ltrim(8)], [error],
-   [error: DEBUG EVALUATE: Function invocation ltrim(number) does not match any known function.  Candidates are:
-LTRIM(string)
-LTRIM(string, string).]],
-  [[ltrim('***abc', '*')], ["abc"]],
-  [[ltrim('abc', '*')], ["abc"]],
-  [[ltrim('*abc', '*')], ["abc"]],
-  [[ltrim('', '*')], [""]],
-  [[ltrim(8, '*')], [error],
-   [error: DEBUG EVALUATE: Function invocation ltrim(number, string) does not match any known function.  Candidates are:
-LTRIM(string)
-LTRIM(string, string).]],
-  [[ltrim(' x', 8)], [error],
-   [error: DEBUG EVALUATE: Function invocation ltrim(string, number) does not match any known function.  Candidates are:
-LTRIM(string)
-LTRIM(string, string).]],
-  [[ltrim(8, 9)], [error],
-   [error: DEBUG EVALUATE: Function invocation ltrim(number, number) does not match any known function.  Candidates are:
-LTRIM(string)
-LTRIM(string, string).]],
-
-  [[rpad('abc', -1)], [""]],
-  [[rpad('abc', 0)], ["abc"]],
-  [[rpad('abc', 2)], ["abc"]],
-  [[rpad('abc', 3)], ["abc"]],
-  [[rpad('abc', 10)], ["abc       "]],
-  [[rpad('abc', 32768)], [""]],
-  [[rpad('abc', $sysmis)], [""]],
-  [[rpad('abc', -1, '*')], [""]],
-  [[rpad('abc', 0, '*')], ["abc"]],
-  [[rpad('abc', 2, '*')], ["abc"]],
-  [[rpad('abc', 3, '*')], ["abc"]],
-  [[rpad('abc', 10, '*')], ["abc*******"]],
-  [[rpad('abc', 32768, '*')], [""]],
-  [[rpad('abc', $sysmis, '*')], [""]],
-  [[rpad('abc', $sysmis, '')], [""]],
-  [[rpad('abc', $sysmis, 'xy')], [""]],
-  [[rpad(0, 10)], [error],
-   [error: DEBUG EVALUATE: Function invocation rpad(number, number) does not match any known function.  Candidates are:
-RPAD(string, number)
-RPAD(string, number, string).]],
-  [[rpad('abc', 'def')], [error],
-   [error: DEBUG EVALUATE: Function invocation rpad(string, string) does not match any known function.  Candidates are:
-RPAD(string, number)
-RPAD(string, number, string).]],
-  [[rpad(0, 10, ' ')], [error],
-   [error: DEBUG EVALUATE: Function invocation rpad(number, number, string) does not match any known function.  Candidates are:
-RPAD(string, number)
-RPAD(string, number, string).]],
-  [[rpad('abc', 'def', ' ')], [error],
-   [error: DEBUG EVALUATE: Function invocation rpad(string, string, string) does not match any known function.  Candidates are:
-RPAD(string, number)
-RPAD(string, number, string).]],
-  [[rpad('x', 5, 0)], [error],
-   [error: DEBUG EVALUATE: Function invocation rpad(string, number, number) does not match any known function.  Candidates are:
-RPAD(string, number)
-RPAD(string, number, string).]],
-  [[rpad('x', 5, 2)], [error],
-   [error: DEBUG EVALUATE: Function invocation rpad(string, number, number) does not match any known function.  Candidates are:
-RPAD(string, number)
-RPAD(string, number, string).]],
-
-  [[rtrim('abc   ')], ["abc"]],
-  [[rtrim('   abc   ')], ["   abc"]],
-  [[rtrim('abc')], ["abc"]],
-  [[rtrim('abc ')], ["abc      "]],
-  [[rtrim('    ')], [""]],
-  [[rtrim('')], [""]],
-  [[rtrim(8)], [error],
-   [error: DEBUG EVALUATE: Function invocation rtrim(number) does not match any known function.  Candidates are:
-RTRIM(string)
-RTRIM(string, string).]],
-  [[rtrim('abc***', '*')], ["abc"]],
-  [[rtrim('abc', '*')], ["abc"]],
-  [[rtrim('abc*', '*')], ["abc"]],
-  [[rtrim('', '*')], [""]],
-  [[rtrim(8, '*')], [error],
-   [error: DEBUG EVALUATE: Function invocation rtrim(number, string) does not match any known function.  Candidates are:
-RTRIM(string)
-RTRIM(string, string).]],
-  [[rtrim(' x', 8)], [error],
-   [error: DEBUG EVALUATE: Function invocation rtrim(string, number) does not match any known function.  Candidates are:
-RTRIM(string)
-RTRIM(string, string).]],
-  [[rtrim(8, 9)], [error],
-   [error: DEBUG EVALUATE: Function invocation rtrim(number, number) does not match any known function.  Candidates are:
-RTRIM(string)
-RTRIM(string, string).]],
+datediff(date.mdy(3,17,92), date.mdy(2,25,96), 'months') => -47.00
 
-  [[string(123.56, f5.1)], ["123.6"]],
-  [[string($sysmis, f5.1)], ["   . "]],
-  [[string("abc", A5)], [error],
-   [error: DEBUG EVALUATE: Type mismatch invoking STRING(number, num_output_format) as string(string, format).]],
-dnl E has a minimum width of 6 on output:
-  [[string(123, e1)], [error],
-   [error: DEBUG EVALUATE: Type mismatch invoking STRING(number, num_output_format) as string(number, format).]],
-  [[string(123, e6.0)], ["1E+002"]],
-
-  [[strunc('a c   ', 9)], ["a c"]],
-  [[strunc('a c   ', 7)], ["a c"]],
-  [[strunc('a c   ', 6)], ["a c"]],
-  [[strunc('a c   ', 5)], ["a c"]],
-  [[strunc('a c   ', 4)], ["a c"]],
-  [[strunc('a c   ', 3)], ["a c"]],
-  [[strunc('a c   ', 2)], ["a"]],
-  [[strunc('a c   ', 1)], ["a"]],
-  [[strunc('a c   ', 0)], [""]],
-  [[strunc('a c   ', -1)], [""]],
-  [[strunc('a c   ', $sysmis)], [""]],
-  [[strunc('  abc  ', 9)], ["  abc"]],
-  [[strunc('  abc  ', 8)], ["  abc"]],
-  [[strunc('  abc  ', 7)], ["  abc"]],
-  [[strunc('  abc  ', 6)], ["  abc"]],
-  [[strunc('  abc  ', 5)], ["  abc"]],
-  [[strunc('  abc  ', 4)], ["  ab"]],
-  [[strunc('  abc  ', 3)], ["  a"]],
-  [[strunc('  abc  ', 2)], [""]],
-  [[strunc('  abc  ', 1)], [""]],
-  [[strunc('  abc  ', -1)], [""]],
-  [[strunc('  abc  ', $sysmis)], [""]],
-
-  [[substr('abcdefgh', -5)], [""]],
-  [[substr('abcdefgh', 0)], [""]],
-  [[substr('abcdefgh', 1)], ["abcdefgh"]],
-  [[substr('abcdefgh', 3)], ["cdefgh"]],
-  [[substr('abcdefgh', 5)], ["efgh"]],
-  [[substr('abcdefgh', 6)], ["fgh"]],
-  [[substr('abcdefgh', 7)], ["gh"]],
-  [[substr('abcdefgh', 8)], ["h"]],
-  [[substr('abcdefgh', 9)], [""]],
-  [[substr('abcdefgh', 10)], [""]],
-  [[substr('abcdefgh', 20)], [""]],
-  [[substr('abcdefgh', $sysmis)], [""]],
-  [[substr(0, 10)], [error],
-   [error: DEBUG EVALUATE: Function invocation substr(number, number) does not match any known function.  Candidates are:
-SUBSTR(string, number)
-SUBSTR(string, number, number).]],
-  [[substr('abcd', 'abc')], [error],
-   [error: DEBUG EVALUATE: Function invocation substr(string, string) does not match any known function.  Candidates are:
-SUBSTR(string, number)
-SUBSTR(string, number, number).]],
-  [[substr(0, 'abc')], [error],
-   [error: DEBUG EVALUATE: Function invocation substr(number, string) does not match any known function.  Candidates are:
-SUBSTR(string, number)
-SUBSTR(string, number, number).]],
-
-  [[substr('abcdefgh', 0, 0)], [""]],
-  [[substr('abcdefgh', 3, 0)], [""]],
-  [[substr('abcdefgh', 5, 0)], [""]],
-  [[substr('abcdefgh', 9, 0)], [""]],
-  [[substr('abcdefgh', 0, 1)], [""]],
-  [[substr('abcdefgh', 0, 5)], [""]],
-  [[substr('abcdefgh', 1, 8)], ["abcdefgh"]],
-  [[substr('abcdefgh', 1, 10)], ["abcdefgh"]],
-  [[substr('abcdefgh', 1, 20)], ["abcdefgh"]],
-  [[substr('abcdefgh', 3, 4)], ["cdef"]],
-  [[substr('abcdefgh', 5, 2)], ["ef"]],
-  [[substr('abcdefgh', 6, 1)], ["f"]],
-  [[substr('abcdefgh', 7, 10)], ["gh"]],
-  [[substr('abcdefgh', 8, 1)], ["h"]],
-  [[substr('abcdefgh', 8, 2)], ["h"]],
-  [[substr('abcdefgh', 9, 11)], [""]],
-  [[substr('abcdefgh', 10, 52)], [""]],
-  [[substr('abcdefgh', 20, 1)], [""]],
-  [[substr('abcdefgh', $sysmis, 2)], [""]],
-  [[substr('abcdefgh', 9, $sysmis)], [""]],
-  [[substr('abcdefgh', $sysmis, $sysmis)], [""]],
-  [[substr('abc', 1, 'x')], [error],
-   [error: DEBUG EVALUATE: Function invocation substr(string, number, string) does not match any known function.  Candidates are:
-SUBSTR(string, number)
-SUBSTR(string, number, number).]],
-  [[substr(0, 10, 1)], [error],
-   [error: DEBUG EVALUATE: Function invocation substr(number, number, number) does not match any known function.  Candidates are:
-SUBSTR(string, number)
-SUBSTR(string, number, number).]],
-  [[substr(0, 10, 'x')], [error],
-   [error: DEBUG EVALUATE: Function invocation substr(number, number, string) does not match any known function.  Candidates are:
-SUBSTR(string, number)
-SUBSTR(string, number, number).]],
-  [[substr('abcd', 'abc', 0)], [error],
-   [error: DEBUG EVALUATE: Function invocation substr(string, string, number) does not match any known function.  Candidates are:
-SUBSTR(string, number)
-SUBSTR(string, number, number).]],
-  [[substr('abcd', 'abc', 'j')], [error],
-   [error: DEBUG EVALUATE: Function invocation substr(string, string, string) does not match any known function.  Candidates are:
-SUBSTR(string, number)
-SUBSTR(string, number, number).]],
-  [[substr(0, 'abc', 4)], [error],
-   [error: DEBUG EVALUATE: Function invocation substr(number, string, number) does not match any known function.  Candidates are:
-SUBSTR(string, number)
-SUBSTR(string, number, number).]],
-  [[substr(0, 'abc', 'k')], [error],
-   [error: DEBUG EVALUATE: Function invocation substr(number, string, string) does not match any known function.  Candidates are:
-SUBSTR(string, number)
-SUBSTR(string, number, number).]],
-
-  [[upcase('abcdefghijklmnopqrstuvwxyz!@%&*089')], ["ABCDEFGHIJKLMNOPQRSTUVWXYZ!@%&*089"]],
-  [[upcase('')], [""]],
-  [[upcase(1)], [error],
-   [error: DEBUG EVALUATE: Type mismatch invoking UPCASE(string) as upcase(number).]])
-
-CHECK_EXPR_EVAL([time ctime date yrmoda],
-  [[time.days(1)], [86400.00]],
-  [[time.days(-1)], [-86400.00]],
-  [[time.days(0.5)], [43200.00]],
-  [[time.days('x')], [error],
-   [error: DEBUG EVALUATE: Type mismatch invoking TIME.DAYS(number) as time.days(string).]],
-  [[time.days($sysmis)], [sysmis]],
-
-  [[time.hms(4,50,38)], [17438.00]],
-  [[time.hms(12,31,35)], [45095.00]],
-  [[time.hms(12,47,53)], [46073.00]],
-  [[time.hms(1,26,0)], [5160.00]],
-  [[time.hms(20,58,11)], [75491.00]],
-  [[time.hms(7,36,5)], [27365.00]],
-  [[time.hms(15,43,49)], [56629.00]],
-  [[time.hms(4,25,9)], [15909.00]],
-  [[time.hms(6,49,27)], [24567.00]],
-  [[time.hms(2,57,52)], [10672.00]],
-  [[time.hms(16,45,44)], [60344.00]],
-  [[time.hms(21,30,57)], [77457.00]],
-  [[time.hms(22,30,4)], [81004.00]],
-  [[time.hms(1,56,51)], [7011.00]],
-  [[time.hms(5, 6, 7)], [18367.00]],
-  [[time.hms(5, 6, 0)], [18360.00]],
-  [[time.hms(5, 0, 7)], [18007.00]],
-  [[time.hms(0, 6, 7)], [367.00]],
-  [[time.hms(-5, 6, -7)], [sysmis],
-   [warning: DEBUG EVALUATE: TIME.HMS cannot mix positive and negative arguments.]],
-  [[time.hms(-5, 5, -7)], [sysmis],
-   [warning: DEBUG EVALUATE: TIME.HMS cannot mix positive and negative arguments.]],
-  [[time.hms($sysmis, 6, 7)], [sysmis]],
-  [[time.hms(5, $sysmis, 7)], [sysmis]],
-  [[time.hms(5, $sysmis, 7)], [sysmis]],
-  [[time.hms($sysmis, $sysmis, 7)], [sysmis]],
-  [[time.hms(5, $sysmis, $sysmis)], [sysmis]],
-  [[time.hms($sysmis, $sysmis, 7)], [sysmis]],
-  [[time.hms($sysmis, $sysmis, $sysmis)], [sysmis]],
-
-  [[ctime.days(106272)], [1.23]],
-  [[ctime.hours(106272)], [29.52]],
-  [[ctime.minutes(106272)], [1771.20]],
-  [[ctime.seconds(106272)], [106272.00]],
-  [[ctime.days(-106272)], [-1.23]],
-  [[ctime.hours(-106272)], [-29.52]],
-  [[ctime.minutes(-106272)], [-1771.20]],
-  [[ctime.seconds(-106272)], [-106272.00]],
-  [[ctime.days($sysmis)], [sysmis]],
-  [[ctime.hours($sysmis)], [sysmis]],
-  [[ctime.minutes($sysmis)], [sysmis]],
-  [[ctime.seconds($sysmis)], [sysmis]],
-  [[ctime.days('a')], [error],
-   [error: DEBUG EVALUATE: Type mismatch invoking CTIME.DAYS(number) as ctime.days(string).]],
-  [[ctime.hours('b')], [error],
-   [error: DEBUG EVALUATE: Type mismatch invoking CTIME.HOURS(number) as ctime.hours(string).]],
-  [[ctime.minutes('c')], [error],
-   [error: DEBUG EVALUATE: Type mismatch invoking CTIME.MINUTES(number) as ctime.minutes(string).]],
-  [[ctime.seconds('d')], [error],
-   [error: DEBUG EVALUATE: Type mismatch invoking CTIME.SECONDS(number) as ctime.seconds(string).]],
-
-  [[ctime.days(date.dmy(15,10,1582))], [1.00]],
-  [[ctime.days(date.dmy(6,9,1719))], [50000.00]],
-  [[ctime.days(date.dmy(24,1,1583))], [102.00]],
-  [[ctime.days(date.dmy(14,12,1585))], [1157.00]],
-  [[ctime.days(date.dmy(26,11,1621))], [14288.00]],
-  [[ctime.days(date.dmy(25,12,1821))], [87365.00]],
-  [[ctime.days(date.dmy(3,12,1882))], [109623.00]],
-  [[ctime.days(date.dmy(6,4,2002))], [153211.00]],
-  [[ctime.days(date.dmy(19,12,1999))], [152372.00]],
-  [[ctime.days(date.dmy(1,10,1978))], [144623.00]],
-  [[ctime.days(date.dmy(0,10,1978))], [144622.00]],
-  [[ctime.days(date.dmy(32,10,1978))], [sysmis],
-   [error: DEBUG EVALUATE: Day 32 is not in acceptable range of 0 to 31.]],
-  [[ctime.days(date.dmy(31,0,1978))], [144349.00]],
-  [[ctime.days(date.dmy(31,13,1978))], [144745.00]],
-  [[ctime.days(date.dmy($sysmis,10,1978))], [sysmis]],
-  [[ctime.days(date.dmy(31,$sysmis,1978))], [sysmis]],
-  [[ctime.days(date.dmy(31,10,$sysmis))], [sysmis]],
-  [[ctime.days(date.dmy($sysmis,$sysmis,1978))], [sysmis]],
-  [[ctime.days(date.dmy(31,$sysmis,$sysmis))], [sysmis]],
-  [[ctime.days(date.dmy($sysmis,10,$sysmis))], [sysmis]],
-  [[ctime.days(date.dmy($sysmis,$sysmis,$sysmis))], [sysmis]],
-  [[date.dmy('a',1,2)], [error],
-   [error: DEBUG EVALUATE: Type mismatch invoking DATE.DMY(number, number, number) as date.dmy(string, number, number).]],
-  [[date.dmy(1,'a',2)], [error],
-   [error: DEBUG EVALUATE: Type mismatch invoking DATE.DMY(number, number, number) as date.dmy(number, string, number).]],
-  [[date.dmy(1,2,'a')], [error],
-   [error: DEBUG EVALUATE: Type mismatch invoking DATE.DMY(number, number, number) as date.dmy(number, number, string).]],
-dnl FIXME: check out-of-range and nearly out-of-range values
-dnl
-  [[yrmoda(1582,10,15)], [1.00]],
-  [[yrmoda(1719,9,6)], [50000.00]],
-  [[yrmoda(1583,1,24)], [102.00]],
-  [[yrmoda(1585,12,14)], [1157.00]],
-  [[yrmoda(1621,11,26)], [14288.00]],
-  [[yrmoda(1821,12,25)], [87365.00]],
-  [[yrmoda(1882,12,3)], [109623.00]],
-  [[yrmoda(2002,4,6)], [153211.00]],
-  [[yrmoda(1999,12,19)], [152372.00]],
-  [[yrmoda(1978,10,1)], [144623.00]],
-  [[yrmoda(1978,10,0)], [144622.00]],
-  [[yrmoda(1978,10,32)], [sysmis],
-   [error: DEBUG EVALUATE: Day 32 is not in acceptable range of 0 to 31.]],
-  [[yrmoda(1978,0,31)], [144349.00]],
-  [[yrmoda(1978,13,31)], [144745.00]],
-  [[yrmoda(1978,10,$sysmis)], [sysmis]],
-  [[yrmoda(1978,$sysmis,31)], [sysmis]],
-  [[yrmoda($sysmis,10,31)], [sysmis]],
-  [[yrmoda(1978,$sysmis,$sysmis)], [sysmis]],
-  [[yrmoda($sysmis,$sysmis,31)], [sysmis]],
-  [[yrmoda($sysmis,10,$sysmis)], [sysmis]],
-  [[yrmoda($sysmis,$sysmis,$sysmis)], [sysmis]],
-  [[yrmoda('a',1,2)], [error],
-   [error: DEBUG EVALUATE: Type mismatch invoking YRMODA(number, number, number) as yrmoda(string, number, number).]],
-  [[yrmoda(1,'a',2)], [error],
-   [error: DEBUG EVALUATE: Type mismatch invoking YRMODA(number, number, number) as yrmoda(number, string, number).]],
-  [[yrmoda(1,2,'a')], [error],
-   [error: DEBUG EVALUATE: Type mismatch invoking YRMODA(number, number, number) as yrmoda(number, number, string).]],
-dnl FIXME: check out-of-range and nearly out-of-range values
-dnl
-  [[ctime.days(date.mdy(6,10,1648)) + 577735], [601716.00]],
-  [[ctime.days(date.mdy(6,30,1680)) + 577735], [613424.00]],
-  [[ctime.days(date.mdy(7,24,1716)) + 577735], [626596.00]],
-  [[ctime.days(date.mdy(6,19,1768)) + 577735], [645554.00]],
-  [[ctime.days(date.mdy(8,2,1819)) + 577735], [664224.00]],
-  [[ctime.days(date.mdy(3,27,1839)) + 577735], [671401.00]],
-  [[ctime.days(date.mdy(4,19,1903)) + 577735], [694799.00]],
-  [[ctime.days(date.mdy(8,25,1929)) + 577735], [704424.00]],
-  [[ctime.days(date.mdy(9,29,1941)) + 577735], [708842.00]],
-  [[ctime.days(date.mdy(4,19,1943)) + 577735], [709409.00]],
-  [[ctime.days(date.mdy(10,7,1943)) + 577735], [709580.00]],
-  [[ctime.days(date.mdy(3,17,1992)) + 577735], [727274.00]],
-  [[ctime.days(date.mdy(2,25,1996)) + 577735], [728714.00]],
-  [[ctime.days(date.mdy(11,10,2038)) + 577735], [744313.00]],
-  [[ctime.days(date.mdy(7,18,2094)) + 577735], [764652.00]],
-dnl FIXME: check out-of-range and nearly out-of-range values
-dnl
-  [[ctime.days(date.mdy(10,15,1582))], [1.00]],
-  [[ctime.days(date.mdy(9,6,1719))], [50000.00]],
-  [[ctime.days(date.mdy(1,24,1583))], [102.00]],
-  [[ctime.days(date.mdy(12,14,1585))], [1157.00]],
-  [[ctime.days(date.mdy(11,26,1621))], [14288.00]],
-  [[ctime.days(date.mdy(12,25,1821))], [87365.00]],
-  [[ctime.days(date.mdy(12,3,1882))], [109623.00]],
-  [[ctime.days(date.mdy(4,6,2002))], [153211.00]],
-  [[ctime.days(date.mdy(12,19,1999))], [152372.00]],
-  [[ctime.days(date.mdy(10,1,1978))], [144623.00]],
-  [[ctime.days(date.mdy(10,0,1978))], [144622.00]],
-  [[ctime.days(date.mdy(10,32,1978))], [sysmis],
-   [error: DEBUG EVALUATE: Day 32 is not in acceptable range of 0 to 31.]],
-  [[ctime.days(date.mdy(0,31,1978))], [144349.00]],
-  [[ctime.days(date.mdy(13,31,1978))], [144745.00]],
-  [[ctime.days(date.mdy(10,$sysmis,1978))], [sysmis]],
-  [[ctime.days(date.mdy($sysmis,31,1978))], [sysmis]],
-  [[ctime.days(date.mdy(10,31,$sysmis))], [sysmis]],
-  [[ctime.days(date.mdy($sysmis,$sysmis,1978))], [sysmis]],
-  [[ctime.days(date.mdy($sysmis,31,$sysmis))], [sysmis]],
-  [[ctime.days(date.mdy(10,$sysmis,$sysmis))], [sysmis]],
-  [[ctime.days(date.mdy($sysmis,$sysmis,$sysmis))], [sysmis]],
-  [[date.mdy('a',1,2)], [error],
-   [error: DEBUG EVALUATE: Type mismatch invoking DATE.MDY(number, number, number) as date.mdy(string, number, number).]],
-  [[date.mdy(1,'a',2)], [error],
-   [error: DEBUG EVALUATE: Type mismatch invoking DATE.MDY(number, number, number) as date.mdy(number, string, number).]],
-  [[date.mdy(1,2,'a')], [error],
-   [error: DEBUG EVALUATE: Type mismatch invoking DATE.MDY(number, number, number) as date.mdy(number, number, string).]],
-  [[ctime.days(date.mdy(0,0,0))], [152353.00]],
-  [[ctime.days(date.mdy(0,0,999))], [sysmis],
-   [error: DEBUG EVALUATE: Date 0998-12-0 is before the earliest acceptable date of 1582-10-15.]],
-  [[date.mdy(1,1,1582)], [sysmis],
-   [error: DEBUG EVALUATE: Date 1582-1-1 is before the earliest acceptable date of 1582-10-15.]],
-  [[date.mdy(10,14,1582)], [sysmis],
-   [error: DEBUG EVALUATE: Date 1582-10-14 is before the earliest acceptable date of 1582-10-15.]],
-  [[date.mdy(10,15,1582)], [86400.00]],
-
-  [[ctime.days(date.moyr(1,2000))], [152385.00]],
-  [[ctime.days(date.moyr(2,2000))], [152416.00]],
-  [[ctime.days(date.moyr(3,2000))], [152445.00]],
-  [[ctime.days(date.moyr(4,2000))], [152476.00]],
-  [[ctime.days(date.moyr(5,2000))], [152506.00]],
-  [[ctime.days(date.moyr(13,2000))], [152751.00]],
-  [[ctime.days(date.moyr(14,2000))], [sysmis],
-   [error: DEBUG EVALUATE: Month 14 is not in acceptable range of 0 to 13.]],
-  [[ctime.days(date.moyr($sysmis,2000))], [sysmis]],
-  [[ctime.days(date.moyr(1,$sysmis))], [sysmis]],
-  [[ctime.days(date.moyr($sysmis,$sysmis))], [sysmis]],
-  [[date.moyr('a',2000)], [error],
-   [error: DEBUG EVALUATE: Type mismatch invoking DATE.MOYR(number, number) as date.moyr(string, number).]],
-  [[date.moyr(5,'a')], [error],
-   [error: DEBUG EVALUATE: Type mismatch invoking DATE.MOYR(number, number) as date.moyr(number, string).]],
-  [[date.moyr('a','b')], [error],
-   [error: DEBUG EVALUATE: Type mismatch invoking DATE.MOYR(number, number) as date.moyr(string, string).]],
-
-  [[ctime.days(date.qyr(1,2000))], [152385.00]],
-  [[ctime.days(date.qyr(2,2000))], [152476.00]],
-  [[ctime.days(date.qyr(5,2000))], [sysmis],
-   [warning: DEBUG EVALUATE: The first argument to DATE.QYR must be 1, 2, 3, or 4.]],
-  [[ctime.days(date.qyr(6,2000))], [sysmis],
-   [warning: DEBUG EVALUATE: The first argument to DATE.QYR must be 1, 2, 3, or 4.]],
-  [[ctime.days(date.qyr($sysmis,2000))], [sysmis]],
-  [[ctime.days(date.qyr(1,$sysmis))], [sysmis]],
-  [[ctime.days(date.qyr($sysmis,$sysmis))], [sysmis]],
-  [[date.qyr('a',2000)], [error],
-   [error: DEBUG EVALUATE: Type mismatch invoking DATE.QYR(number, number) as date.qyr(string, number).]],
-  [[date.qyr(5,'a')], [error],
-   [error: DEBUG EVALUATE: Type mismatch invoking DATE.QYR(number, number) as date.qyr(number, string).]],
-  [[date.qyr('a','b')], [error],
-   [error: DEBUG EVALUATE: Type mismatch invoking DATE.QYR(number, number) as date.qyr(string, string).]],
-
-  [[ctime.days(date.wkyr(1,2000))], [152385.00]],
-  [[ctime.days(date.wkyr(15,1999))], [152118.00]],
-  [[ctime.days(date.wkyr(36,1999))], [152265.00]],
-  [[ctime.days(date.wkyr(54,1999))], [sysmis],
-   [error: DEBUG EVALUATE: The week argument to DATE.WKYR is outside the acceptable range of 1 to 53.  The result will be system-missing.]],
-  [[ctime.days(date.wkyr($sysmis,1999))], [sysmis]],
-  [[ctime.days(date.wkyr(1,$sysmis))], [sysmis]],
-  [[ctime.days(date.wkyr($sysmis,$sysmis))], [sysmis]],
-  [[date.wkyr('a',1999)], [error],
-   [error: DEBUG EVALUATE: Type mismatch invoking DATE.WKYR(number, number) as date.wkyr(string, number).]],
-  [[date.wkyr(5,'a')], [error],
-   [error: DEBUG EVALUATE: Type mismatch invoking DATE.WKYR(number, number) as date.wkyr(number, string).]],
-  [[date.wkyr('a','b')], [error],
-   [error: DEBUG EVALUATE: Type mismatch invoking DATE.WKYR(number, number) as date.wkyr(string, string).]],
-
-  [[ctime.days(date.yrday(2000,1))], [152385.00]],
-  [[ctime.days(date.yrday(2000,100))], [152484.00]],
-  [[ctime.days(date.yrday(2000,253))], [152637.00]],
-  [[ctime.days(date.yrday(2000,500))], [sysmis],
-   [error: DEBUG EVALUATE: The day argument to DATE.YRDAY is outside the acceptable range of 1 to 366.  The result will be system-missing.]],
-  [[ctime.days(date.yrday(2000,-100))], [sysmis],
-   [error: DEBUG EVALUATE: The day argument to DATE.YRDAY is outside the acceptable range of 1 to 366.  The result will be system-missing.]],
-  [[ctime.days(date.yrday(1999,$sysmis))], [sysmis]],
-  [[ctime.days(date.yrday($sysmis,1))], [sysmis]],
-  [[ctime.days(date.yrday($sysmis,$sysmis))], [sysmis]],
-  [[date.yrday(1999,'a')], [error],
-   [error: DEBUG EVALUATE: Type mismatch invoking DATE.YRDAY(number, number) as date.yrday(number, string).]],
-  [[date.yrday('a',5)], [error],
-   [error: DEBUG EVALUATE: Type mismatch invoking DATE.YRDAY(number, number) as date.yrday(string, number).]],
-  [[date.yrday('a','b')], [error],
-   [error: DEBUG EVALUATE: Type mismatch invoking DATE.YRDAY(number, number) as date.yrday(string, string).]])
-
-CHECK_EXPR_EVAL([xdate],
-  [[xdate.date(date.mdy(6,10,1648) + time.hms(0,0,0)) / 86400], [23981.00]],
-  [[xdate.date(date.mdy(6,30,1680) + time.hms(4,50,38)) / 86400], [35689.00]],
-  [[xdate.date(date.mdy(7,24,1716) + time.hms(12,31,35)) / 86400], [48861.00]],
-  [[xdate.date(date.mdy(6,19,1768) + time.hms(12,47,53)) / 86400], [67819.00]],
-  [[xdate.date(date.mdy(8,2,1819) + time.hms(1,26,0)) / 86400], [86489.00]],
-  [[xdate.date(date.mdy(3,27,1839) + time.hms(20,58,11)) / 86400], [93666.00]],
-  [[xdate.date(date.mdy(4,19,1903) + time.hms(7,36,5)) / 86400], [117064.00]],
-  [[xdate.date(date.mdy(8,25,1929) + time.hms(15,43,49)) / 86400], [126689.00]],
-  [[xdate.date(date.mdy(9,29,1941) + time.hms(4,25,9)) / 86400], [131107.00]],
-  [[xdate.date(date.mdy(4,19,1943) + time.hms(6,49,27)) / 86400], [131674.00]],
-  [[xdate.date(date.mdy(10,7,1943) + time.hms(2,57,52)) / 86400], [131845.00]],
-  [[xdate.date(date.mdy(3,17,1992) + time.hms(16,45,44)) / 86400], [149539.00]],
-  [[xdate.date(date.mdy(2,25,1996) + time.hms(21,30,57)) / 86400], [150979.00]],
-  [[xdate.date(date.mdy(9,29,1941) + time.hms(4,25,9)) / 86400], [131107.00]],
-  [[xdate.date(date.mdy(4,19,43) + time.hms(6,49,27)) / 86400], [131674.00]],
-  [[xdate.date(date.mdy(10,7,43) + time.hms(2,57,52)) / 86400], [131845.00]],
-  [[xdate.date(date.mdy(3,17,92) + time.hms(16,45,44)) / 86400], [149539.00]],
-  [[xdate.date(date.mdy(2,25,96) + time.hms(21,30,57)) / 86400], [150979.00]],
-  [[xdate.date(date.mdy(11,10,2038) + time.hms(22,30,4)) / 86400], [166578.00]],
-  [[xdate.date(date.mdy(7,18,2094) + time.hms(1,56,51)) / 86400], [186917.00]],
-  [[xdate.date(123.4)], [0.00]],
-  [[xdate.date('')], [error],
-   [error: DEBUG EVALUATE: Type mismatch invoking XDATE.DATE(number) as xdate.date(string).]],
-
-  [[xdate.hour(date.mdy(6,10,1648) + time.hms(0,0,0))], [0.00]],
-  [[xdate.hour(date.mdy(6,30,1680) + time.hms(4,50,38))], [4.00]],
-  [[xdate.hour(date.mdy(7,24,1716) + time.hms(12,31,35))], [12.00]],
-  [[xdate.hour(date.mdy(6,19,1768) + time.hms(12,47,53))], [12.00]],
-  [[xdate.hour(date.mdy(8,2,1819) + time.hms(1,26,0))], [1.00]],
-  [[xdate.hour(date.mdy(3,27,1839) + time.hms(20,58,11))], [20.00]],
-  [[xdate.hour(date.mdy(4,19,1903) + time.hms(7,36,5))], [7.00]],
-  [[xdate.hour(date.mdy(8,25,1929) + time.hms(15,43,49))], [15.00]],
-  [[xdate.hour(date.mdy(9,29,1941) + time.hms(4,25,9))], [4.00]],
-  [[xdate.hour(date.mdy(4,19,1943) + time.hms(6,49,27))], [6.00]],
-  [[xdate.hour(date.mdy(10,7,1943) + time.hms(2,57,52))], [2.00]],
-  [[xdate.hour(date.mdy(3,17,1992) + time.hms(16,45,44))], [16.00]],
-  [[xdate.hour(date.mdy(2,25,1996) + time.hms(21,30,57))], [21.00]],
-  [[xdate.hour(date.mdy(9,29,1941) + time.hms(4,25,9))], [4.00]],
-  [[xdate.hour(date.mdy(4,19,43) + time.hms(6,49,27))], [6.00]],
-  [[xdate.hour(date.mdy(10,7,43) + time.hms(2,57,52))], [2.00]],
-  [[xdate.hour(date.mdy(3,17,92) + time.hms(16,45,44))], [16.00]],
-  [[xdate.hour(date.mdy(2,25,96) + time.hms(21,30,57))], [21.00]],
-  [[xdate.hour(date.mdy(11,10,2038) + time.hms(22,30,4))], [22.00]],
-  [[xdate.hour(date.mdy(7,18,2094) + time.hms(1,56,51))], [1.00]],
-  [[xdate.hour(-1)], [-1.00]],
-  [[xdate.hour(1)], [0.00]],
-  [[xdate.hour($sysmis)], [sysmis]],
-  [[xdate.hour('')], [error],
-   [error: DEBUG EVALUATE: Type mismatch invoking XDATE.HOUR(number) as xdate.hour(string).]],
-
-  [[xdate.jday(date.mdy(6,10,1648) + time.hms(0,0,0))], [162.00]],
-  [[xdate.jday(date.mdy(6,30,1680) + time.hms(4,50,38))], [182.00]],
-  [[xdate.jday(date.mdy(7,24,1716) + time.hms(12,31,35))], [206.00]],
-  [[xdate.jday(date.mdy(6,19,1768) + time.hms(12,47,53))], [171.00]],
-  [[xdate.jday(date.mdy(8,2,1819) + time.hms(1,26,0))], [214.00]],
-  [[xdate.jday(date.mdy(3,27,1839) + time.hms(20,58,11))], [86.00]],
-  [[xdate.jday(date.mdy(4,19,1903) + time.hms(7,36,5))], [109.00]],
-  [[xdate.jday(date.mdy(8,25,1929) + time.hms(15,43,49))], [237.00]],
-  [[xdate.jday(date.mdy(9,29,1941) + time.hms(4,25,9))], [272.00]],
-  [[xdate.jday(date.mdy(4,19,1943) + time.hms(6,49,27))], [109.00]],
-  [[xdate.jday(date.mdy(10,7,1943) + time.hms(2,57,52))], [280.00]],
-  [[xdate.jday(date.mdy(3,17,1992) + time.hms(16,45,44))], [77.00]],
-  [[xdate.jday(date.mdy(2,25,1996) + time.hms(21,30,57))], [56.00]],
-  [[xdate.jday(date.mdy(9,29,1941) + time.hms(4,25,9))], [272.00]],
-  [[xdate.jday(date.mdy(4,19,43) + time.hms(6,49,27))], [109.00]],
-  [[xdate.jday(date.mdy(10,7,43) + time.hms(2,57,52))], [280.00]],
-  [[xdate.jday(date.mdy(3,17,92) + time.hms(16,45,44))], [77.00]],
-  [[xdate.jday(date.mdy(2,25,96) + time.hms(21,30,57))], [56.00]],
-  [[xdate.jday(date.mdy(11,10,2038) + time.hms(22,30,4))], [314.00]],
-  [[xdate.jday(date.mdy(7,18,2094) + time.hms(1,56,51))], [199.00]],
-  [[xdate.jday(0)], [sysmis]],
-  [[xdate.jday(1)], [sysmis]],
-  [[xdate.jday(86400)], [288.00]],
-
-  [[xdate.mday(date.mdy(6,10,1648) + time.hms(0,0,0))], [10.00]],
-  [[xdate.mday(date.mdy(6,30,1680) + time.hms(4,50,38))], [30.00]],
-  [[xdate.mday(date.mdy(7,24,1716) + time.hms(12,31,35))], [24.00]],
-  [[xdate.mday(date.mdy(6,19,1768) + time.hms(12,47,53))], [19.00]],
-  [[xdate.mday(date.mdy(8,2,1819) + time.hms(1,26,0))], [2.00]],
-  [[xdate.mday(date.mdy(3,27,1839) + time.hms(20,58,11))], [27.00]],
-  [[xdate.mday(date.mdy(4,19,1903) + time.hms(7,36,5))], [19.00]],
-  [[xdate.mday(date.mdy(8,25,1929) + time.hms(15,43,49))], [25.00]],
-  [[xdate.mday(date.mdy(9,29,1941) + time.hms(4,25,9))], [29.00]],
-  [[xdate.mday(date.mdy(4,19,1943) + time.hms(6,49,27))], [19.00]],
-  [[xdate.mday(date.mdy(10,7,1943) + time.hms(2,57,52))], [7.00]],
-  [[xdate.mday(date.mdy(3,17,1992) + time.hms(16,45,44))], [17.00]],
-  [[xdate.mday(date.mdy(2,25,1996) + time.hms(21,30,57))], [25.00]],
-  [[xdate.mday(date.mdy(9,29,1941) + time.hms(4,25,9))], [29.00]],
-  [[xdate.mday(date.mdy(4,19,43) + time.hms(6,49,27))], [19.00]],
-  [[xdate.mday(date.mdy(10,7,43) + time.hms(2,57,52))], [7.00]],
-  [[xdate.mday(date.mdy(3,17,92) + time.hms(16,45,44))], [17.00]],
-  [[xdate.mday(date.mdy(2,25,96) + time.hms(21,30,57))], [25.00]],
-  [[xdate.mday(date.mdy(11,10,2038) + time.hms(22,30,4))], [10.00]],
-  [[xdate.mday(date.mdy(7,18,2094) + time.hms(1,56,51))], [18.00]],
-
-  [[xdate.minute(date.mdy(6,10,1648) + time.hms(0,0,0))], [0.00]],
-  [[xdate.minute(date.mdy(6,30,1680) + time.hms(4,50,38))], [50.00]],
-  [[xdate.minute(date.mdy(7,24,1716) + time.hms(12,31,35))], [31.00]],
-  [[xdate.minute(date.mdy(6,19,1768) + time.hms(12,47,53))], [47.00]],
-  [[xdate.minute(date.mdy(8,2,1819) + time.hms(1,26,0))], [26.00]],
-  [[xdate.minute(date.mdy(3,27,1839) + time.hms(20,58,11))], [58.00]],
-  [[xdate.minute(date.mdy(4,19,1903) + time.hms(7,36,5))], [36.00]],
-  [[xdate.minute(date.mdy(8,25,1929) + time.hms(15,43,49))], [43.00]],
-  [[xdate.minute(date.mdy(9,29,1941) + time.hms(4,25,9))], [25.00]],
-  [[xdate.minute(date.mdy(4,19,1943) + time.hms(6,49,27))], [49.00]],
-  [[xdate.minute(date.mdy(10,7,1943) + time.hms(2,57,52))], [57.00]],
-  [[xdate.minute(date.mdy(3,17,1992) + time.hms(16,45,44))], [45.00]],
-  [[xdate.minute(date.mdy(2,25,1996) + time.hms(21,30,57))], [30.00]],
-  [[xdate.minute(date.mdy(9,29,1941) + time.hms(4,25,9))], [25.00]],
-  [[xdate.minute(date.mdy(4,19,43) + time.hms(6,49,27))], [49.00]],
-  [[xdate.minute(date.mdy(10,7,43) + time.hms(2,57,52))], [57.00]],
-  [[xdate.minute(date.mdy(3,17,92) + time.hms(16,45,44))], [45.00]],
-  [[xdate.minute(date.mdy(2,25,96) + time.hms(21,30,57))], [30.00]],
-  [[xdate.minute(date.mdy(11,10,2038) + time.hms(22,30,4))], [30.00]],
-  [[xdate.minute(date.mdy(7,18,2094) + time.hms(1,56,51))], [56.00]],
-
-  [[xdate.month(date.mdy(6,10,1648) + time.hms(0,0,0))], [6.00]],
-  [[xdate.month(date.mdy(6,30,1680) + time.hms(4,50,38))], [6.00]],
-  [[xdate.month(date.mdy(7,24,1716) + time.hms(12,31,35))], [7.00]],
-  [[xdate.month(date.mdy(6,19,1768) + time.hms(12,47,53))], [6.00]],
-  [[xdate.month(date.mdy(8,2,1819) + time.hms(1,26,0))], [8.00]],
-  [[xdate.month(date.mdy(3,27,1839) + time.hms(20,58,11))], [3.00]],
-  [[xdate.month(date.mdy(4,19,1903) + time.hms(7,36,5))], [4.00]],
-  [[xdate.month(date.mdy(8,25,1929) + time.hms(15,43,49))], [8.00]],
-  [[xdate.month(date.mdy(9,29,1941) + time.hms(4,25,9))], [9.00]],
-  [[xdate.month(date.mdy(4,19,1943) + time.hms(6,49,27))], [4.00]],
-  [[xdate.month(date.mdy(10,7,1943) + time.hms(2,57,52))], [10.00]],
-  [[xdate.month(date.mdy(3,17,1992) + time.hms(16,45,44))], [3.00]],
-  [[xdate.month(date.mdy(2,25,1996) + time.hms(21,30,57))], [2.00]],
-  [[xdate.month(date.mdy(9,29,1941) + time.hms(4,25,9))], [9.00]],
-  [[xdate.month(date.mdy(4,19,43) + time.hms(6,49,27))], [4.00]],
-  [[xdate.month(date.mdy(10,7,43) + time.hms(2,57,52))], [10.00]],
-  [[xdate.month(date.mdy(3,17,92) + time.hms(16,45,44))], [3.00]],
-  [[xdate.month(date.mdy(2,25,96) + time.hms(21,30,57))], [2.00]],
-  [[xdate.month(date.mdy(11,10,2038) + time.hms(22,30,4))], [11.00]],
-  [[xdate.month(date.mdy(7,18,2094) + time.hms(1,56,51))], [7.00]],
-
-  [[xdate.quarter(date.mdy(6,10,1648) + time.hms(0,0,0))], [2.00]],
-  [[xdate.quarter(date.mdy(6,30,1680) + time.hms(4,50,38))], [2.00]],
-  [[xdate.quarter(date.mdy(7,24,1716) + time.hms(12,31,35))], [3.00]],
-  [[xdate.quarter(date.mdy(6,19,1768) + time.hms(12,47,53))], [2.00]],
-  [[xdate.quarter(date.mdy(8,2,1819) + time.hms(1,26,0))], [3.00]],
-  [[xdate.quarter(date.mdy(3,27,1839) + time.hms(20,58,11))], [1.00]],
-  [[xdate.quarter(date.mdy(4,19,1903) + time.hms(7,36,5))], [2.00]],
-  [[xdate.quarter(date.mdy(8,25,1929) + time.hms(15,43,49))], [3.00]],
-  [[xdate.quarter(date.mdy(9,29,1941) + time.hms(4,25,9))], [3.00]],
-  [[xdate.quarter(date.mdy(4,19,1943) + time.hms(6,49,27))], [2.00]],
-  [[xdate.quarter(date.mdy(10,7,1943) + time.hms(2,57,52))], [4.00]],
-  [[xdate.quarter(date.mdy(3,17,1992) + time.hms(16,45,44))], [1.00]],
-  [[xdate.quarter(date.mdy(2,25,1996) + time.hms(21,30,57))], [1.00]],
-  [[xdate.quarter(date.mdy(9,29,1941) + time.hms(4,25,9))], [3.00]],
-  [[xdate.quarter(date.mdy(4,19,43) + time.hms(6,49,27))], [2.00]],
-  [[xdate.quarter(date.mdy(10,7,43) + time.hms(2,57,52))], [4.00]],
-  [[xdate.quarter(date.mdy(3,17,92) + time.hms(16,45,44))], [1.00]],
-  [[xdate.quarter(date.mdy(2,25,96) + time.hms(21,30,57))], [1.00]],
-  [[xdate.quarter(date.mdy(11,10,2038) + time.hms(22,30,4))], [4.00]],
-  [[xdate.quarter(date.mdy(7,18,2094) + time.hms(1,56,51))], [3.00]],
-
-  [[xdate.second(date.mdy(6,10,1648) + time.hms(0,0,0))], [0.00]],
-  [[xdate.second(date.mdy(6,30,1680) + time.hms(4,50,38))], [38.00]],
-  [[xdate.second(date.mdy(7,24,1716) + time.hms(12,31,35))], [35.00]],
-  [[xdate.second(date.mdy(6,19,1768) + time.hms(12,47,53))], [53.00]],
-  [[xdate.second(date.mdy(8,2,1819) + time.hms(1,26,0))], [0.00]],
-  [[xdate.second(date.mdy(3,27,1839) + time.hms(20,58,11))], [11.00]],
-  [[xdate.second(date.mdy(4,19,1903) + time.hms(7,36,5))], [5.00]],
-  [[xdate.second(date.mdy(8,25,1929) + time.hms(15,43,49))], [49.00]],
-  [[xdate.second(date.mdy(9,29,1941) + time.hms(4,25,9))], [9.00]],
-  [[xdate.second(date.mdy(4,19,1943) + time.hms(6,49,27))], [27.00]],
-  [[xdate.second(date.mdy(10,7,1943) + time.hms(2,57,52))], [52.00]],
-  [[xdate.second(date.mdy(3,17,1992) + time.hms(16,45,44))], [44.00]],
-  [[xdate.second(date.mdy(2,25,1996) + time.hms(21,30,57))], [57.00]],
-  [[xdate.second(date.mdy(9,29,1941) + time.hms(4,25,9))], [9.00]],
-  [[xdate.second(date.mdy(4,19,43) + time.hms(6,49,27))], [27.00]],
-  [[xdate.second(date.mdy(10,7,43) + time.hms(2,57,52))], [52.00]],
-  [[xdate.second(date.mdy(3,17,92) + time.hms(16,45,44))], [44.00]],
-  [[xdate.second(date.mdy(2,25,96) + time.hms(21,30,57))], [57.00]],
-  [[xdate.second(date.mdy(11,10,2038) + time.hms(22,30,4))], [4.00]],
-  [[xdate.second(date.mdy(7,18,2094) + time.hms(1,56,51))], [51.00]],
-
-  [[xdate.tday(date.mdy(6,10,1648) + time.hms(0,0,0))], [23981.00]],
-  [[xdate.tday(date.mdy(6,30,1680) + time.hms(4,50,38))], [35689.00]],
-  [[xdate.tday(date.mdy(7,24,1716) + time.hms(12,31,35))], [48861.00]],
-  [[xdate.tday(date.mdy(6,19,1768) + time.hms(12,47,53))], [67819.00]],
-  [[xdate.tday(date.mdy(8,2,1819) + time.hms(1,26,0))], [86489.00]],
-  [[xdate.tday(date.mdy(3,27,1839) + time.hms(20,58,11))], [93666.00]],
-  [[xdate.tday(date.mdy(4,19,1903) + time.hms(7,36,5))], [117064.00]],
-  [[xdate.tday(date.mdy(8,25,1929) + time.hms(15,43,49))], [126689.00]],
-  [[xdate.tday(date.mdy(9,29,1941) + time.hms(4,25,9))], [131107.00]],
-  [[xdate.tday(date.mdy(4,19,1943) + time.hms(6,49,27))], [131674.00]],
-  [[xdate.tday(date.mdy(10,7,1943) + time.hms(2,57,52))], [131845.00]],
-  [[xdate.tday(date.mdy(3,17,1992) + time.hms(16,45,44))], [149539.00]],
-  [[xdate.tday(date.mdy(2,25,1996) + time.hms(21,30,57))], [150979.00]],
-  [[xdate.tday(date.mdy(9,29,1941) + time.hms(4,25,9))], [131107.00]],
-  [[xdate.tday(date.mdy(4,19,43) + time.hms(6,49,27))], [131674.00]],
-  [[xdate.tday(date.mdy(10,7,43) + time.hms(2,57,52))], [131845.00]],
-  [[xdate.tday(date.mdy(3,17,92) + time.hms(16,45,44))], [149539.00]],
-  [[xdate.tday(date.mdy(2,25,96) + time.hms(21,30,57))], [150979.00]],
-  [[xdate.tday(date.mdy(11,10,2038) + time.hms(22,30,4))], [166578.00]],
-  [[xdate.tday(date.mdy(7,18,2094) + time.hms(1,56,51))], [186917.00]],
-
-  [[xdate.time(date.mdy(6,10,1648) + time.hms(0,0,0))], [0.00]],
-  [[xdate.time(date.mdy(6,30,1680) + time.hms(4,50,38))], [17438.00]],
-  [[xdate.time(date.mdy(7,24,1716) + time.hms(12,31,35))], [45095.00]],
-  [[xdate.time(date.mdy(6,19,1768) + time.hms(12,47,53))], [46073.00]],
-  [[xdate.time(date.mdy(8,2,1819) + time.hms(1,26,0))], [5160.00]],
-  [[xdate.time(date.mdy(3,27,1839) + time.hms(20,58,11))], [75491.00]],
-  [[xdate.time(date.mdy(4,19,1903) + time.hms(7,36,5))], [27365.00]],
-  [[xdate.time(date.mdy(8,25,1929) + time.hms(15,43,49))], [56629.00]],
-  [[xdate.time(date.mdy(9,29,1941) + time.hms(4,25,9))], [15909.00]],
-  [[xdate.time(date.mdy(4,19,1943) + time.hms(6,49,27))], [24567.00]],
-  [[xdate.time(date.mdy(10,7,1943) + time.hms(2,57,52))], [10672.00]],
-  [[xdate.time(date.mdy(3,17,1992) + time.hms(16,45,44))], [60344.00]],
-  [[xdate.time(date.mdy(2,25,1996) + time.hms(21,30,57))], [77457.00]],
-  [[xdate.time(date.mdy(9,29,1941) + time.hms(4,25,9))], [15909.00]],
-  [[xdate.time(date.mdy(4,19,43) + time.hms(6,49,27))], [24567.00]],
-  [[xdate.time(date.mdy(10,7,43) + time.hms(2,57,52))], [10672.00]],
-  [[xdate.time(date.mdy(3,17,92) + time.hms(16,45,44))], [60344.00]],
-  [[xdate.time(date.mdy(2,25,96) + time.hms(21,30,57))], [77457.00]],
-  [[xdate.time(date.mdy(11,10,2038) + time.hms(22,30,4))], [81004.00]],
-  [[xdate.time(date.mdy(7,18,2094) + time.hms(1,56,51))], [7011.00]],
-
-  [[xdate.week(date.mdy(6,10,1648) + time.hms(0,0,0))], [24.00]],
-  [[xdate.week(date.mdy(6,30,1680) + time.hms(4,50,38))], [26.00]],
-  [[xdate.week(date.mdy(7,24,1716) + time.hms(12,31,35))], [30.00]],
-  [[xdate.week(date.mdy(6,19,1768) + time.hms(12,47,53))], [25.00]],
-  [[xdate.week(date.mdy(8,2,1819) + time.hms(1,26,0))], [31.00]],
-  [[xdate.week(date.mdy(3,27,1839) + time.hms(20,58,11))], [13.00]],
-  [[xdate.week(date.mdy(4,19,1903) + time.hms(7,36,5))], [16.00]],
-  [[xdate.week(date.mdy(8,25,1929) + time.hms(15,43,49))], [34.00]],
-  [[xdate.week(date.mdy(9,29,1941) + time.hms(4,25,9))], [39.00]],
-  [[xdate.week(date.mdy(4,19,1943) + time.hms(6,49,27))], [16.00]],
-  [[xdate.week(date.mdy(10,7,1943) + time.hms(2,57,52))], [40.00]],
-  [[xdate.week(date.mdy(3,17,1992) + time.hms(16,45,44))], [11.00]],
-  [[xdate.week(date.mdy(2,25,1996) + time.hms(21,30,57))], [8.00]],
-  [[xdate.week(date.mdy(9,29,1941) + time.hms(4,25,9))], [39.00]],
-  [[xdate.week(date.mdy(4,19,43) + time.hms(6,49,27))], [16.00]],
-  [[xdate.week(date.mdy(10,7,43) + time.hms(2,57,52))], [40.00]],
-  [[xdate.week(date.mdy(3,17,92) + time.hms(16,45,44))], [11.00]],
-  [[xdate.week(date.mdy(2,25,96) + time.hms(21,30,57))], [8.00]],
-  [[xdate.week(date.mdy(11,10,2038) + time.hms(22,30,4))], [45.00]],
-  [[xdate.week(date.mdy(7,18,2094) + time.hms(1,56,51))], [29.00]],
-
-  [[xdate.wkday(date.mdy(6,10,1648))], [4.00]],
-  [[xdate.wkday(date.mdy(6,30,1680))], [1.00]],
-  [[xdate.wkday(date.mdy(7,24,1716))], [6.00]],
-  [[xdate.wkday(date.mdy(6,19,1768))], [1.00]],
-  [[xdate.wkday(date.mdy(8,2,1819))], [2.00]],
-  [[xdate.wkday(date.mdy(3,27,1839))], [4.00]],
-  [[xdate.wkday(date.mdy(4,19,1903))], [1.00]],
-  [[xdate.wkday(date.mdy(8,25,1929))], [1.00]],
-  [[xdate.wkday(date.mdy(9,29,1941))], [2.00]],
-  [[xdate.wkday(date.mdy(4,19,1943))], [2.00]],
-  [[xdate.wkday(date.mdy(10,7,1943))], [5.00]],
-  [[xdate.wkday(date.mdy(3,17,1992))], [3.00]],
-  [[xdate.wkday(date.mdy(2,25,1996))], [1.00]],
-  [[xdate.wkday(date.mdy(9,29,1941))], [2.00]],
-  [[xdate.wkday(date.mdy(4,19,43))], [2.00]],
-  [[xdate.wkday(date.mdy(10,7,43))], [5.00]],
-  [[xdate.wkday(date.mdy(3,17,92))], [3.00]],
-  [[xdate.wkday(date.mdy(2,25,96))], [1.00]],
-  [[xdate.wkday(date.mdy(11,10,2038))], [4.00]],
-  [[xdate.wkday(date.mdy(7,18,2094))], [1.00]],
-
-  [[xdate.year(date.mdy(6,10,1648) + time.hms(0,0,0))], [1648.00]],
-  [[xdate.year(date.mdy(6,30,1680) + time.hms(4,50,38))], [1680.00]],
-  [[xdate.year(date.mdy(7,24,1716) + time.hms(12,31,35))], [1716.00]],
-  [[xdate.year(date.mdy(6,19,1768) + time.hms(12,47,53))], [1768.00]],
-  [[xdate.year(date.mdy(8,2,1819) + time.hms(1,26,0))], [1819.00]],
-  [[xdate.year(date.mdy(3,27,1839) + time.hms(20,58,11))], [1839.00]],
-  [[xdate.year(date.mdy(4,19,1903) + time.hms(7,36,5))], [1903.00]],
-  [[xdate.year(date.mdy(8,25,1929) + time.hms(15,43,49))], [1929.00]],
-  [[xdate.year(date.mdy(9,29,1941) + time.hms(4,25,9))], [1941.00]],
-  [[xdate.year(date.mdy(4,19,1943) + time.hms(6,49,27))], [1943.00]],
-  [[xdate.year(date.mdy(10,7,1943) + time.hms(2,57,52))], [1943.00]],
-  [[xdate.year(date.mdy(3,17,1992) + time.hms(16,45,44))], [1992.00]],
-  [[xdate.year(date.mdy(2,25,1996) + time.hms(21,30,57))], [1996.00]],
-  [[xdate.year(date.mdy(9,29,1941) + time.hms(4,25,9))], [1941.00]],
-  [[xdate.year(date.mdy(4,19,43) + time.hms(6,49,27))], [1943.00]],
-  [[xdate.year(date.mdy(10,7,43) + time.hms(2,57,52))], [1943.00]],
-  [[xdate.year(date.mdy(3,17,92) + time.hms(16,45,44))], [1992.00]],
-  [[xdate.year(date.mdy(2,25,96) + time.hms(21,30,57))], [1996.00]],
-  [[xdate.year(date.mdy(11,10,2038) + time.hms(22,30,4))], [2038.00]],
-  [[xdate.year(date.mdy(7,18,2094) + time.hms(1,56,51))], [2094.00]])
-
-CHECK_EXPR_EVAL([datediff],
-  [[datediff(date.mdy(6,10,1648), date.mdy(6,30,1680), 'years')], [-32.00]],
-  [[datediff(date.mdy(6,30,1680), date.mdy(7,24,1716), 'years')], [-36.00]],
-  [[datediff(date.mdy(7,24,1716), date.mdy(6,19,1768), 'years')], [-51.00]],
-  [[datediff(date.mdy(6,19,1768), date.mdy(8,2,1819), 'years')], [-51.00]],
-  [[datediff(date.mdy(8,2,1819), date.mdy(3,27,1839), 'years')], [-19.00]],
-  [[datediff(date.mdy(3,27,1839), date.mdy(4,19,1903), 'years')], [-64.00]],
-  [[datediff(date.mdy(4,19,1903), date.mdy(8,25,1929), 'years')], [-26.00]],
-  [[datediff(date.mdy(8,25,1929), date.mdy(9,29,1941), 'years')], [-12.00]],
-  [[datediff(date.mdy(9,29,1941), date.mdy(4,19,1943), 'years')], [-1.00]],
-  [[datediff(date.mdy(4,19,1943), date.mdy(10,7,1943), 'years')], [0.00]],
-  [[datediff(date.mdy(10,7,1943), date.mdy(3,17,1992), 'years')], [-48.00]],
-  [[datediff(date.mdy(3,17,1992), date.mdy(2,25,1996), 'years')], [-3.00]],
-  [[datediff(date.mdy(9,29,1941), date.mdy(2,25,1996), 'years')], [-54.00]],
-  [[datediff(date.mdy(9,29,1941), date.mdy(4,19,43), 'years')], [-1.00]],
-  [[datediff(date.mdy(4,19,43), date.mdy(10,7,43), 'years')], [0.00]],
-  [[datediff(date.mdy(10,7,43), date.mdy(3,17,92), 'years')], [-48.00]],
-  [[datediff(date.mdy(3,17,92), date.mdy(2,25,96), 'years')], [-3.00]],
-  [[datediff(date.mdy(2,25,96), date.mdy(11,10,2038), 'years')], [-42.00]],
-  [[datediff(date.mdy(11,10,2038), date.mdy(7,18,2094), 'years')], [-55.00]],
-  [[datediff(date.mdy(2,29,1900), date.mdy(2,29,1904), 'years')], [-3.00]],
-  [[datediff(date.mdy(2,29,1904), date.mdy(2,29,1908), 'years')], [-4.00]],
-  [[datediff(date.mdy(2,29,1900), date.mdy(2,28,1903), 'years')], [-2.00]],
-
-  [[datediff(date.mdy(6,10,1648), date.mdy(6,30,1680), 'quarters')], [-128.00]],
-  [[datediff(date.mdy(6,30,1680), date.mdy(7,24,1716), 'quarters')], [-144.00]],
-  [[datediff(date.mdy(7,24,1716), date.mdy(6,19,1768), 'quarters')], [-207.00]],
-  [[datediff(date.mdy(6,19,1768), date.mdy(8,2,1819), 'quarters')], [-204.00]],
-  [[datediff(date.mdy(8,2,1819), date.mdy(3,27,1839), 'quarters')], [-78.00]],
-  [[datediff(date.mdy(3,27,1839), date.mdy(4,19,1903), 'quarters')], [-256.00]],
-  [[datediff(date.mdy(4,19,1903), date.mdy(8,25,1929), 'quarters')], [-105.00]],
-  [[datediff(date.mdy(8,25,1929), date.mdy(9,29,1941), 'quarters')], [-48.00]],
-  [[datediff(date.mdy(9,29,1941), date.mdy(4,19,1943), 'quarters')], [-6.00]],
-  [[datediff(date.mdy(4,19,1943), date.mdy(10,7,1943), 'quarters')], [-1.00]],
-  [[datediff(date.mdy(10,7,1943), date.mdy(3,17,1992), 'quarters')], [-193.00]],
-  [[datediff(date.mdy(3,17,1992), date.mdy(2,25,1996), 'quarters')], [-15.00]],
-  [[datediff(date.mdy(9,29,1941), date.mdy(2,25,1996), 'quarters')], [-217.00]],
-  [[datediff(date.mdy(9,29,1941), date.mdy(4,19,43), 'quarters')], [-6.00]],
-  [[datediff(date.mdy(4,19,43), date.mdy(10,7,43), 'quarters')], [-1.00]],
-  [[datediff(date.mdy(10,7,43), date.mdy(3,17,92), 'quarters')], [-193.00]],
-  [[datediff(date.mdy(3,17,92), date.mdy(2,25,96), 'quarters')], [-15.00]],
-  [[datediff(date.mdy(2,25,96), date.mdy(11,10,2038), 'quarters')], [-170.00]],
-  [[datediff(date.mdy(11,10,2038), date.mdy(7,18,2094), 'quarters')], [-222.00]],
-  [[datediff(date.mdy(2,29,1900), date.mdy(2,29,1904), 'quarters')], [-15.00]],
-  [[datediff(date.mdy(2,29,1904), date.mdy(2,29,1908), 'quarters')], [-16.00]],
-  [[datediff(date.mdy(2,29,1900), date.mdy(2,28,1903), 'quarters')], [-11.00]],
-
-  [[datediff(date.mdy(6,10,1648), date.mdy(6,30,1680), 'months')], [-384.00]],
-  [[datediff(date.mdy(6,30,1680), date.mdy(7,24,1716), 'months')], [-432.00]],
-  [[datediff(date.mdy(7,24,1716), date.mdy(6,19,1768), 'months')], [-622.00]],
-  [[datediff(date.mdy(6,19,1768), date.mdy(8,2,1819), 'months')], [-613.00]],
-  [[datediff(date.mdy(8,2,1819), date.mdy(3,27,1839), 'months')], [-235.00]],
-  [[datediff(date.mdy(3,27,1839), date.mdy(4,19,1903), 'months')], [-768.00]],
-  [[datediff(date.mdy(4,19,1903), date.mdy(8,25,1929), 'months')], [-316.00]],
-  [[datediff(date.mdy(8,25,1929), date.mdy(9,29,1941), 'months')], [-145.00]],
-  [[datediff(date.mdy(9,29,1941), date.mdy(4,19,1943), 'months')], [-18.00]],
-  [[datediff(date.mdy(4,19,1943), date.mdy(10,7,1943), 'months')], [-5.00]],
-  [[datediff(date.mdy(10,7,1943), date.mdy(3,17,1992), 'months')], [-581.00]],
-  [[datediff(date.mdy(3,17,1992), date.mdy(2,25,1996), 'months')], [-47.00]],
-  [[datediff(date.mdy(9,29,1941), date.mdy(2,25,1996), 'months')], [-652.00]],
-  [[datediff(date.mdy(9,29,1941), date.mdy(4,19,43), 'months')], [-18.00]],
-  [[datediff(date.mdy(4,19,43), date.mdy(10,7,43), 'months')], [-5.00]],
-  [[datediff(date.mdy(10,7,43), date.mdy(3,17,92), 'months')], [-581.00]],
-  [[datediff(date.mdy(3,17,92), date.mdy(2,25,96), 'months')], [-47.00]],
-  [[datediff(date.mdy(2,25,96), date.mdy(11,10,2038), 'months')], [-512.00]],
-  [[datediff(date.mdy(11,10,2038), date.mdy(7,18,2094), 'months')], [-668.00]],
-  [[datediff(date.mdy(2,29,1900), date.mdy(2,29,1904), 'months')], [-47.00]],
-  [[datediff(date.mdy(2,29,1904), date.mdy(2,29,1908), 'months')], [-48.00]],
-  [[datediff(date.mdy(2,29,1900), date.mdy(2,28,1903), 'months')], [-35.00]],
-
-  [[datediff(date.mdy(6,10,1648), date.mdy(6,30,1680), 'weeks')], [-1672.00]],
-  [[datediff(date.mdy(6,30,1680), date.mdy(7,24,1716), 'weeks')], [-1881.00]],
-  [[datediff(date.mdy(7,24,1716), date.mdy(6,19,1768), 'weeks')], [-2708.00]],
-  [[datediff(date.mdy(6,19,1768), date.mdy(8,2,1819), 'weeks')], [-2667.00]],
-  [[datediff(date.mdy(8,2,1819), date.mdy(3,27,1839), 'weeks')], [-1025.00]],
-  [[datediff(date.mdy(3,27,1839), date.mdy(4,19,1903), 'weeks')], [-3342.00]],
-  [[datediff(date.mdy(4,19,1903), date.mdy(8,25,1929), 'weeks')], [-1375.00]],
-  [[datediff(date.mdy(8,25,1929), date.mdy(9,29,1941), 'weeks')], [-631.00]],
-  [[datediff(date.mdy(9,29,1941), date.mdy(4,19,1943), 'weeks')], [-81.00]],
-  [[datediff(date.mdy(4,19,1943), date.mdy(10,7,1943), 'weeks')], [-24.00]],
-  [[datediff(date.mdy(10,7,1943), date.mdy(3,17,1992), 'weeks')], [-2527.00]],
-  [[datediff(date.mdy(3,17,1992), date.mdy(2,25,1996), 'weeks')], [-205.00]],
-  [[datediff(date.mdy(9,29,1941), date.mdy(2,25,1996), 'weeks')], [-2838.00]],
-  [[datediff(date.mdy(9,29,1941), date.mdy(4,19,43), 'weeks')], [-81.00]],
-  [[datediff(date.mdy(4,19,43), date.mdy(10,7,43), 'weeks')], [-24.00]],
-  [[datediff(date.mdy(10,7,43), date.mdy(3,17,92), 'weeks')], [-2527.00]],
-  [[datediff(date.mdy(3,17,92), date.mdy(2,25,96), 'weeks')], [-205.00]],
-  [[datediff(date.mdy(2,25,96), date.mdy(11,10,2038), 'weeks')], [-2228.00]],
-  [[datediff(date.mdy(11,10,2038), date.mdy(7,18,2094), 'weeks')], [-2905.00]],
-  [[datediff(date.mdy(2,29,1900), date.mdy(2,29,1904), 'weeks')], [-208.00]],
-  [[datediff(date.mdy(2,29,1904), date.mdy(2,29,1908), 'weeks')], [-208.00]],
-  [[datediff(date.mdy(2,29,1900), date.mdy(2,28,1903), 'weeks')], [-156.00]],
-
-  [[datediff(date.mdy(6,10,1648), date.mdy(6,30,1680), 'days')], [-11708.00]],
-  [[datediff(date.mdy(6,30,1680), date.mdy(7,24,1716), 'days')], [-13172.00]],
-  [[datediff(date.mdy(7,24,1716), date.mdy(6,19,1768), 'days')], [-18958.00]],
-  [[datediff(date.mdy(6,19,1768), date.mdy(8,2,1819), 'days')], [-18670.00]],
-  [[datediff(date.mdy(8,2,1819), date.mdy(3,27,1839), 'days')], [-7177.00]],
-  [[datediff(date.mdy(3,27,1839), date.mdy(4,19,1903), 'days')], [-23398.00]],
-  [[datediff(date.mdy(4,19,1903), date.mdy(8,25,1929), 'days')], [-9625.00]],
-  [[datediff(date.mdy(8,25,1929), date.mdy(9,29,1941), 'days')], [-4418.00]],
-  [[datediff(date.mdy(9,29,1941), date.mdy(4,19,1943), 'days')], [-567.00]],
-  [[datediff(date.mdy(4,19,1943), date.mdy(10,7,1943), 'days')], [-171.00]],
-  [[datediff(date.mdy(10,7,1943), date.mdy(3,17,1992), 'days')], [-17694.00]],
-  [[datediff(date.mdy(3,17,1992), date.mdy(2,25,1996), 'days')], [-1440.00]],
-  [[datediff(date.mdy(9,29,1941), date.mdy(2,25,1996), 'days')], [-19872.00]],
-  [[datediff(date.mdy(9,29,1941), date.mdy(4,19,43), 'days')], [-567.00]],
-  [[datediff(date.mdy(4,19,43), date.mdy(10,7,43), 'days')], [-171.00]],
-  [[datediff(date.mdy(10,7,43), date.mdy(3,17,92), 'days')], [-17694.00]],
-  [[datediff(date.mdy(3,17,92), date.mdy(2,25,96), 'days')], [-1440.00]],
-  [[datediff(date.mdy(2,25,96), date.mdy(11,10,2038), 'days')], [-15599.00]],
-  [[datediff(date.mdy(11,10,2038), date.mdy(7,18,2094), 'days')], [-20339.00]],
-  [[datediff(date.mdy(2,29,1900), date.mdy(2,29,1904), 'days')], [-1460.00]],
-  [[datediff(date.mdy(2,29,1904), date.mdy(2,29,1908), 'days')], [-1461.00]],
-  [[datediff(date.mdy(2,29,1900), date.mdy(2,28,1903), 'days')], [-1094.00]],
-
-  [[datediff(date.mdy(6,30,1680), date.mdy(6,10,1648), 'years')], [32.00]],
-  [[datediff(date.mdy(7,24,1716), date.mdy(6,30,1680), 'years')], [36.00]],
-  [[datediff(date.mdy(6,19,1768), date.mdy(7,24,1716), 'years')], [51.00]],
-  [[datediff(date.mdy(8,2,1819), date.mdy(6,19,1768), 'years')], [51.00]],
-  [[datediff(date.mdy(3,27,1839), date.mdy(8,2,1819), 'years')], [19.00]],
-  [[datediff(date.mdy(4,19,1903), date.mdy(3,27,1839), 'years')], [64.00]],
-  [[datediff(date.mdy(8,25,1929), date.mdy(4,19,1903), 'years')], [26.00]],
-  [[datediff(date.mdy(9,29,1941), date.mdy(8,25,1929), 'years')], [12.00]],
-  [[datediff(date.mdy(4,19,1943), date.mdy(9,29,1941), 'years')], [1.00]],
-  [[datediff(date.mdy(10,7,1943), date.mdy(4,19,1943), 'years')], [0.00]],
-  [[datediff(date.mdy(3,17,1992), date.mdy(10,7,1943), 'years')], [48.00]],
-  [[datediff(date.mdy(2,25,1996), date.mdy(3,17,1992), 'years')], [3.00]],
-  [[datediff(date.mdy(2,25,1996), date.mdy(9,29,1941), 'years')], [54.00]],
-  [[datediff(date.mdy(4,19,43), date.mdy(9,29,1941), 'years')], [1.00]],
-  [[datediff(date.mdy(10,7,43), date.mdy(4,19,43), 'years')], [0.00]],
-  [[datediff(date.mdy(3,17,92), date.mdy(10,7,43), 'years')], [48.00]],
-  [[datediff(date.mdy(2,25,96), date.mdy(3,17,92), 'years')], [3.00]],
-  [[datediff(date.mdy(11,10,2038), date.mdy(2,25,96), 'years')], [42.00]],
-  [[datediff(date.mdy(7,18,2094), date.mdy(11,10,2038), 'years')], [55.00]],
-  [[datediff(date.mdy(2,29,1904), date.mdy(2,29,1900), 'years')], [3.00]],
-  [[datediff(date.mdy(2,29,1908), date.mdy(2,29,1904), 'years')], [4.00]],
-  [[datediff(date.mdy(2,28,1903), date.mdy(2,29,1900), 'years')], [2.00]],
-
-  [[datediff(date.mdy(6,30,1680), date.mdy(6,10,1648), 'months')], [384.00]],
-  [[datediff(date.mdy(7,24,1716), date.mdy(6,30,1680), 'months')], [432.00]],
-  [[datediff(date.mdy(6,19,1768), date.mdy(7,24,1716), 'months')], [622.00]],
-  [[datediff(date.mdy(8,2,1819), date.mdy(6,19,1768), 'months')], [613.00]],
-  [[datediff(date.mdy(3,27,1839), date.mdy(8,2,1819), 'months')], [235.00]],
-  [[datediff(date.mdy(4,19,1903), date.mdy(3,27,1839), 'months')], [768.00]],
-  [[datediff(date.mdy(8,25,1929), date.mdy(4,19,1903), 'months')], [316.00]],
-  [[datediff(date.mdy(9,29,1941), date.mdy(8,25,1929), 'months')], [145.00]],
-  [[datediff(date.mdy(4,19,1943), date.mdy(9,29,1941), 'months')], [18.00]],
-  [[datediff(date.mdy(10,7,1943), date.mdy(4,19,1943), 'months')], [5.00]],
-  [[datediff(date.mdy(3,17,1992), date.mdy(10,7,1943), 'months')], [581.00]],
-  [[datediff(date.mdy(2,25,1996), date.mdy(3,17,1992), 'months')], [47.00]],
-  [[datediff(date.mdy(2,25,1996), date.mdy(9,29,1941), 'months')], [652.00]],
-  [[datediff(date.mdy(4,19,43), date.mdy(9,29,1941), 'months')], [18.00]],
-  [[datediff(date.mdy(10,7,43), date.mdy(4,19,43), 'months')], [5.00]],
-  [[datediff(date.mdy(3,17,92), date.mdy(10,7,43), 'months')], [581.00]],
-  [[datediff(date.mdy(2,25,96), date.mdy(3,17,92), 'months')], [47.00]],
-  [[datediff(date.mdy(11,10,2038), date.mdy(2,25,96), 'months')], [512.00]],
-  [[datediff(date.mdy(7,18,2094), date.mdy(11,10,2038), 'months')], [668.00]],
-  [[datediff(date.mdy(2,29,1904), date.mdy(2,29,1900), 'months')], [47.00]],
-  [[datediff(date.mdy(2,29,1908), date.mdy(2,29,1904), 'months')], [48.00]],
-  [[datediff(date.mdy(2,28,1903), date.mdy(2,29,1900), 'months')], [35.00]],
-
-  [[datediff(date.mdy(6,30,1680), date.mdy(6,10,1648), 'quarters')], [128.00]],
-  [[datediff(date.mdy(7,24,1716), date.mdy(6,30,1680), 'quarters')], [144.00]],
-  [[datediff(date.mdy(6,19,1768), date.mdy(7,24,1716), 'quarters')], [207.00]],
-  [[datediff(date.mdy(8,2,1819), date.mdy(6,19,1768), 'quarters')], [204.00]],
-  [[datediff(date.mdy(3,27,1839), date.mdy(8,2,1819), 'quarters')], [78.00]],
-  [[datediff(date.mdy(4,19,1903), date.mdy(3,27,1839), 'quarters')], [256.00]],
-  [[datediff(date.mdy(8,25,1929), date.mdy(4,19,1903), 'quarters')], [105.00]],
-  [[datediff(date.mdy(9,29,1941), date.mdy(8,25,1929), 'quarters')], [48.00]],
-  [[datediff(date.mdy(4,19,1943), date.mdy(9,29,1941), 'quarters')], [6.00]],
-  [[datediff(date.mdy(10,7,1943), date.mdy(4,19,1943), 'quarters')], [1.00]],
-  [[datediff(date.mdy(3,17,1992), date.mdy(10,7,1943), 'quarters')], [193.00]],
-  [[datediff(date.mdy(2,25,1996), date.mdy(3,17,1992), 'quarters')], [15.00]],
-  [[datediff(date.mdy(2,25,1996), date.mdy(9,29,1941), 'quarters')], [217.00]],
-  [[datediff(date.mdy(4,19,43), date.mdy(9,29,1941), 'quarters')], [6.00]],
-  [[datediff(date.mdy(10,7,43), date.mdy(4,19,43), 'quarters')], [1.00]],
-  [[datediff(date.mdy(3,17,92), date.mdy(10,7,43), 'quarters')], [193.00]],
-  [[datediff(date.mdy(2,25,96), date.mdy(3,17,92), 'quarters')], [15.00]],
-  [[datediff(date.mdy(11,10,2038), date.mdy(2,25,96), 'quarters')], [170.00]],
-  [[datediff(date.mdy(7,18,2094), date.mdy(11,10,2038), 'quarters')], [222.00]],
-  [[datediff(date.mdy(2,29,1904), date.mdy(2,29,1900), 'quarters')], [15.00]],
-  [[datediff(date.mdy(2,29,1908), date.mdy(2,29,1904), 'quarters')], [16.00]],
-  [[datediff(date.mdy(2,28,1903), date.mdy(2,29,1900), 'quarters')], [11.00]],
+datediff(date.mdy(2,25,96), date.mdy(11,10,2038), 'months') => -512.00
+
+datediff(date.mdy(11,10,2038), date.mdy(7,18,2094), 'months') => -668.00
+
+datediff(date.mdy(2,29,1900), date.mdy(2,29,1904), 'months') => -47.00
+
+datediff(date.mdy(2,29,1904), date.mdy(2,29,1908), 'months') => -48.00
+
+datediff(date.mdy(2,29,1900), date.mdy(2,28,1903), 'months') => -35.00
+
+datediff(date.mdy(6,30,1680), date.mdy(6,10,1648), 'months') => 384.00
+
+datediff(date.mdy(7,24,1716), date.mdy(6,30,1680), 'months') => 432.00
+
+datediff(date.mdy(6,19,1768), date.mdy(7,24,1716), 'months') => 622.00
+
+datediff(date.mdy(8,2,1819), date.mdy(6,19,1768), 'months') => 613.00
+
+datediff(date.mdy(3,27,1839), date.mdy(8,2,1819), 'months') => 235.00
+
+datediff(date.mdy(4,19,1903), date.mdy(3,27,1839), 'months') => 768.00
+
+datediff(date.mdy(8,25,1929), date.mdy(4,19,1903), 'months') => 316.00
+
+datediff(date.mdy(9,29,1941), date.mdy(8,25,1929), 'months') => 145.00
+
+datediff(date.mdy(4,19,1943), date.mdy(9,29,1941), 'months') => 18.00
+
+datediff(date.mdy(10,7,1943), date.mdy(4,19,1943), 'months') => 5.00
+
+datediff(date.mdy(3,17,1992), date.mdy(10,7,1943), 'months') => 581.00
+
+datediff(date.mdy(2,25,1996), date.mdy(3,17,1992), 'months') => 47.00
+
+datediff(date.mdy(2,25,1996), date.mdy(9,29,1941), 'months') => 652.00
+
+datediff(date.mdy(4,19,43), date.mdy(9,29,1941), 'months') => 18.00
+
+datediff(date.mdy(10,7,43), date.mdy(4,19,43), 'months') => 5.00
+
+datediff(date.mdy(3,17,92), date.mdy(10,7,43), 'months') => 581.00
+
+datediff(date.mdy(2,25,96), date.mdy(3,17,92), 'months') => 47.00
+
+datediff(date.mdy(11,10,2038), date.mdy(2,25,96), 'months') => 512.00
+
+datediff(date.mdy(7,18,2094), date.mdy(11,10,2038), 'months') => 668.00
+
+datediff(date.mdy(2,29,1904), date.mdy(2,29,1900), 'months') => 47.00
+
+datediff(date.mdy(2,29,1908), date.mdy(2,29,1904), 'months') => 48.00
+
+datediff(date.mdy(2,28,1903), date.mdy(2,29,1900), 'months') => 35.00
+])
+done
+AT_CLEANUP
+
+AT_SETUP([expressions - DATEDIFF weeks])
+AT_KEYWORDS([expression expressions evaluate])
+AT_DATA([evaluate-base.sps], [dnl
+SET EPOCH 1940.
+DEBUG EVALUATE SET opt.
+DEBUG EVALUATE /datediff(date.mdy(6,10,1648), date.mdy(6,30,1680), 'weeks').
+DEBUG EVALUATE /datediff(date.mdy(6,30,1680), date.mdy(7,24,1716), 'weeks').
+DEBUG EVALUATE /datediff(date.mdy(7,24,1716), date.mdy(6,19,1768), 'weeks').
+DEBUG EVALUATE /datediff(date.mdy(6,19,1768), date.mdy(8,2,1819), 'weeks').
+DEBUG EVALUATE /datediff(date.mdy(8,2,1819), date.mdy(3,27,1839), 'weeks').
+DEBUG EVALUATE /datediff(date.mdy(3,27,1839), date.mdy(4,19,1903), 'weeks').
+DEBUG EVALUATE /datediff(date.mdy(4,19,1903), date.mdy(8,25,1929), 'weeks').
+DEBUG EVALUATE /datediff(date.mdy(8,25,1929), date.mdy(9,29,1941), 'weeks').
+DEBUG EVALUATE /datediff(date.mdy(9,29,1941), date.mdy(4,19,1943), 'weeks').
+DEBUG EVALUATE /datediff(date.mdy(4,19,1943), date.mdy(10,7,1943), 'weeks').
+DEBUG EVALUATE /datediff(date.mdy(10,7,1943), date.mdy(3,17,1992), 'weeks').
+DEBUG EVALUATE /datediff(date.mdy(3,17,1992), date.mdy(2,25,1996), 'weeks').
+DEBUG EVALUATE /datediff(date.mdy(9,29,1941), date.mdy(2,25,1996), 'weeks').
+DEBUG EVALUATE /datediff(date.mdy(9,29,1941), date.mdy(4,19,43), 'weeks').
+DEBUG EVALUATE /datediff(date.mdy(4,19,43), date.mdy(10,7,43), 'weeks').
+DEBUG EVALUATE /datediff(date.mdy(10,7,43), date.mdy(3,17,92), 'weeks').
+DEBUG EVALUATE /datediff(date.mdy(3,17,92), date.mdy(2,25,96), 'weeks').
+DEBUG EVALUATE /datediff(date.mdy(2,25,96), date.mdy(11,10,2038), 'weeks').
+DEBUG EVALUATE /datediff(date.mdy(11,10,2038), date.mdy(7,18,2094), 'weeks').
+DEBUG EVALUATE /datediff(date.mdy(2,29,1900), date.mdy(2,29,1904), 'weeks').
+DEBUG EVALUATE /datediff(date.mdy(2,29,1904), date.mdy(2,29,1908), 'weeks').
+DEBUG EVALUATE /datediff(date.mdy(2,29,1900), date.mdy(2,28,1903), 'weeks').
+])
+
+for opt in OPT NOOPT; do
+    AS_BOX([$opt])
+    sed "s/opt/$opt/" < evaluate-base.sps > evaluate.sps
+    AT_CHECK([pspp --testing-mode evaluate.sps], [0], [dnl
+datediff(date.mdy(6,10,1648), date.mdy(6,30,1680), 'weeks') => -1672.00
+
+datediff(date.mdy(6,30,1680), date.mdy(7,24,1716), 'weeks') => -1881.00
+
+datediff(date.mdy(7,24,1716), date.mdy(6,19,1768), 'weeks') => -2708.00
+
+datediff(date.mdy(6,19,1768), date.mdy(8,2,1819), 'weeks') => -2667.00
+
+datediff(date.mdy(8,2,1819), date.mdy(3,27,1839), 'weeks') => -1025.00
+
+datediff(date.mdy(3,27,1839), date.mdy(4,19,1903), 'weeks') => -3342.00
+
+datediff(date.mdy(4,19,1903), date.mdy(8,25,1929), 'weeks') => -1375.00
+
+datediff(date.mdy(8,25,1929), date.mdy(9,29,1941), 'weeks') => -631.00
+
+datediff(date.mdy(9,29,1941), date.mdy(4,19,1943), 'weeks') => -81.00
+
+datediff(date.mdy(4,19,1943), date.mdy(10,7,1943), 'weeks') => -24.00
+
+datediff(date.mdy(10,7,1943), date.mdy(3,17,1992), 'weeks') => -2527.00
+
+datediff(date.mdy(3,17,1992), date.mdy(2,25,1996), 'weeks') => -205.00
+
+datediff(date.mdy(9,29,1941), date.mdy(2,25,1996), 'weeks') => -2838.00
+
+datediff(date.mdy(9,29,1941), date.mdy(4,19,43), 'weeks') => -81.00
+
+datediff(date.mdy(4,19,43), date.mdy(10,7,43), 'weeks') => -24.00
+
+datediff(date.mdy(10,7,43), date.mdy(3,17,92), 'weeks') => -2527.00
+
+datediff(date.mdy(3,17,92), date.mdy(2,25,96), 'weeks') => -205.00
+
+datediff(date.mdy(2,25,96), date.mdy(11,10,2038), 'weeks') => -2228.00
+
+datediff(date.mdy(11,10,2038), date.mdy(7,18,2094), 'weeks') => -2905.00
+
+datediff(date.mdy(2,29,1900), date.mdy(2,29,1904), 'weeks') => -208.00
+
+datediff(date.mdy(2,29,1904), date.mdy(2,29,1908), 'weeks') => -208.00
+
+datediff(date.mdy(2,29,1900), date.mdy(2,28,1903), 'weeks') => -156.00
+])
+done
+AT_CLEANUP
+
+AT_SETUP([expressions - DATEDIFF days])
+AT_KEYWORDS([expression expressions evaluate])
+AT_DATA([evaluate-base.sps], [dnl
+SET EPOCH 1940.
+DEBUG EVALUATE SET opt.
+DEBUG EVALUATE /datediff(date.mdy(6,10,1648), date.mdy(6,30,1680), 'days').
+DEBUG EVALUATE /datediff(date.mdy(6,30,1680), date.mdy(7,24,1716), 'days').
+DEBUG EVALUATE /datediff(date.mdy(7,24,1716), date.mdy(6,19,1768), 'days').
+DEBUG EVALUATE /datediff(date.mdy(6,19,1768), date.mdy(8,2,1819), 'days').
+DEBUG EVALUATE /datediff(date.mdy(8,2,1819), date.mdy(3,27,1839), 'days').
+DEBUG EVALUATE /datediff(date.mdy(3,27,1839), date.mdy(4,19,1903), 'days').
+DEBUG EVALUATE /datediff(date.mdy(4,19,1903), date.mdy(8,25,1929), 'days').
+DEBUG EVALUATE /datediff(date.mdy(8,25,1929), date.mdy(9,29,1941), 'days').
+DEBUG EVALUATE /datediff(date.mdy(9,29,1941), date.mdy(4,19,1943), 'days').
+DEBUG EVALUATE /datediff(date.mdy(4,19,1943), date.mdy(10,7,1943), 'days').
+DEBUG EVALUATE /datediff(date.mdy(10,7,1943), date.mdy(3,17,1992), 'days').
+DEBUG EVALUATE /datediff(date.mdy(3,17,1992), date.mdy(2,25,1996), 'days').
+DEBUG EVALUATE /datediff(date.mdy(9,29,1941), date.mdy(2,25,1996), 'days').
+DEBUG EVALUATE /datediff(date.mdy(9,29,1941), date.mdy(4,19,43), 'days').
+DEBUG EVALUATE /datediff(date.mdy(4,19,43), date.mdy(10,7,43), 'days').
+DEBUG EVALUATE /datediff(date.mdy(10,7,43), date.mdy(3,17,92), 'days').
+DEBUG EVALUATE /datediff(date.mdy(3,17,92), date.mdy(2,25,96), 'days').
+DEBUG EVALUATE /datediff(date.mdy(2,25,96), date.mdy(11,10,2038), 'days').
+DEBUG EVALUATE /datediff(date.mdy(11,10,2038), date.mdy(7,18,2094), 'days').
+DEBUG EVALUATE /datediff(date.mdy(2,29,1900), date.mdy(2,29,1904), 'days').
+DEBUG EVALUATE /datediff(date.mdy(2,29,1904), date.mdy(2,29,1908), 'days').
+DEBUG EVALUATE /datediff(date.mdy(2,29,1900), date.mdy(2,28,1903), 'days').
+])
+
+for opt in OPT NOOPT; do
+    AS_BOX([$opt])
+    sed "s/opt/$opt/" < evaluate-base.sps > evaluate.sps
+    AT_CHECK([pspp --testing-mode evaluate.sps], [0], [dnl
+datediff(date.mdy(6,10,1648), date.mdy(6,30,1680), 'days') => -11708.00
+
+datediff(date.mdy(6,30,1680), date.mdy(7,24,1716), 'days') => -13172.00
+
+datediff(date.mdy(7,24,1716), date.mdy(6,19,1768), 'days') => -18958.00
+
+datediff(date.mdy(6,19,1768), date.mdy(8,2,1819), 'days') => -18670.00
+
+datediff(date.mdy(8,2,1819), date.mdy(3,27,1839), 'days') => -7177.00
+
+datediff(date.mdy(3,27,1839), date.mdy(4,19,1903), 'days') => -23398.00
+
+datediff(date.mdy(4,19,1903), date.mdy(8,25,1929), 'days') => -9625.00
+
+datediff(date.mdy(8,25,1929), date.mdy(9,29,1941), 'days') => -4418.00
+
+datediff(date.mdy(9,29,1941), date.mdy(4,19,1943), 'days') => -567.00
+
+datediff(date.mdy(4,19,1943), date.mdy(10,7,1943), 'days') => -171.00
+
+datediff(date.mdy(10,7,1943), date.mdy(3,17,1992), 'days') => -17694.00
+
+datediff(date.mdy(3,17,1992), date.mdy(2,25,1996), 'days') => -1440.00
+
+datediff(date.mdy(9,29,1941), date.mdy(2,25,1996), 'days') => -19872.00
+
+datediff(date.mdy(9,29,1941), date.mdy(4,19,43), 'days') => -567.00
+
+datediff(date.mdy(4,19,43), date.mdy(10,7,43), 'days') => -171.00
+
+datediff(date.mdy(10,7,43), date.mdy(3,17,92), 'days') => -17694.00
+
+datediff(date.mdy(3,17,92), date.mdy(2,25,96), 'days') => -1440.00
+
+datediff(date.mdy(2,25,96), date.mdy(11,10,2038), 'days') => -15599.00
+
+datediff(date.mdy(11,10,2038), date.mdy(7,18,2094), 'days') => -20339.00
+
+datediff(date.mdy(2,29,1900), date.mdy(2,29,1904), 'days') => -1460.00
+
+datediff(date.mdy(2,29,1904), date.mdy(2,29,1908), 'days') => -1461.00
+
+datediff(date.mdy(2,29,1900), date.mdy(2,28,1903), 'days') => -1094.00
+])
+done
+AT_CLEANUP
 
 dnl time of day is significant for DATEDIFF
-  [[datediff(date.mdy(10,15,1910) + 234, date.mdy(10,10,1910) + 123, 'days')],
-    [5.00]],
-  [[datediff(date.mdy(10,15,1910) + 123, date.mdy(10,10,1910) + 234, 'days')],
-    [4.00]],
-  [[datediff(date.mdy(10,24,1910) + 234, date.mdy(10,10,1910) + 123, 'weeks')],
-    [2.00]],
-  [[datediff(date.mdy(10,24,1910) + 123, date.mdy(10,10,1910) + 234, 'weeks')],
-    [1.00]],
-  [[datediff(date.mdy(10,10,1910) + 234, date.mdy(5,10,1910) + 123, 'months')],
-    [5.00]],
-  [[datediff(date.mdy(10,10,1910) + 123, date.mdy(5,10,1910) + 234, 'months')],
-    [4.00]],
-  [[datediff(date.mdy(5,10,1919) + 234, date.mdy(5,10,1910) + 123, 'years')],
-    [9.00]],
-  [[datediff(date.mdy(5,10,1919) + 123, date.mdy(5,10,1910) + 234, 'years')],
-    [8.00]],
-
-  [[datediff(date.mdy(10,10,1910) + 123, date.mdy(10,15,1910) + 234, 'days')],
-    [-5.00]],
-  [[datediff(date.mdy(10,10,1910) + 234, date.mdy(10,15,1910) + 123, 'days')],
-    [-4.00]],
-  [[datediff(date.mdy(10,10,1910) + 123, date.mdy(10,24,1910) + 234, 'weeks')],
-    [-2.00]],
-  [[datediff(date.mdy(10,10,1910) + 234, date.mdy(10,24,1910) + 123, 'weeks')],
-    [-1.00]],
-  [[datediff(date.mdy(5,10,1910) + 123, date.mdy(10,10,1910) + 234, 'months')],
-    [-5.00]],
-  [[datediff(date.mdy(5,10,1910) + 234, date.mdy(10,10,1910) + 123, 'months')],
-    [-4.00]],
-  [[datediff(date.mdy(5,10,1910) + 123, date.mdy(5,10,1919) + 234, 'years')],
-    [-9.00]],
-  [[datediff(date.mdy(5,10,1910) + 234, date.mdy(5,10,1919) + 123, 'years')],
-    [-8.00]])
-
-CHECK_EXPR_EVAL([datesum],
-dnl DATESUM with non-leap year
-  [[ctime.days(datesum(date.mdy(1,31,1900), 1, 'months') - date.mdy(1,1,1900))], [58.00]],
-  [[ctime.days(datesum(date.mdy(1,31,1900), 2, 'months') - date.mdy(1,1,1900))], [89.00]],
-  [[ctime.days(datesum(date.mdy(1,31,1900), 3, 'months') - date.mdy(1,1,1900))], [119.00]],
-  [[ctime.days(datesum(date.mdy(1,31,1900), 4, 'months') - date.mdy(1,1,1900))], [150.00]],
-  [[ctime.days(datesum(date.mdy(1,31,1900), 5.4, 'months') - date.mdy(1,1,1900))], [180.00]],
-  [[ctime.days(datesum(date.mdy(1,31,1900), 6, 'months') - date.mdy(1,1,1900))], [211.00]],
-  [[ctime.days(datesum(date.mdy(1,31,1900), 7, 'months') - date.mdy(1,1,1900))], [242.00]],
-  [[ctime.days(datesum(date.mdy(1,31,1900), 8, 'months') - date.mdy(1,1,1900))], [272.00]],
-  [[ctime.days(datesum(date.mdy(1,31,1900), 9, 'months') - date.mdy(1,1,1900))], [303.00]],
-  [[ctime.days(datesum(date.mdy(1,31,1900), 10, 'months') - date.mdy(1,1,1900))], [333.00]],
-  [[ctime.days(datesum(date.mdy(1,31,1900), 11, 'months') - date.mdy(1,1,1900))], [364.00]],
-  [[ctime.days(datesum(date.mdy(1,31,1900), 12, 'months') - date.mdy(1,1,1900))], [395.00]],
-  [[ctime.days(datesum(date.mdy(1,31,1900), 13.9, 'months') - date.mdy(1,1,1900))], [423.00]],
-  [[ctime.days(datesum(date.mdy(1,31,1900), 1, 'months', 'rollover') - date.mdy(1,1,1900))], [61.00]],
-  [[ctime.days(datesum(date.mdy(1,31,1900), 2, 'months', 'rollover') - date.mdy(1,1,1900))], [89.00]],
-  [[ctime.days(datesum(date.mdy(1,31,1900), 3.2, 'months', 'rollover') - date.mdy(1,1,1900))], [120.00]],
-  [[ctime.days(datesum(date.mdy(1,31,1900), 4, 'months', 'rollover') - date.mdy(1,1,1900))], [150.00]],
-  [[ctime.days(datesum(date.mdy(1,31,1900), 5, 'months', 'rollover') - date.mdy(1,1,1900))], [181.00]],
-  [[ctime.days(datesum(date.mdy(1,31,1900), 6, 'months', 'rollover') - date.mdy(1,1,1900))], [211.00]],
-  [[ctime.days(datesum(date.mdy(1,31,1900), 7, 'months', 'rollover') - date.mdy(1,1,1900))], [242.00]],
-  [[ctime.days(datesum(date.mdy(1,31,1900), 8, 'months', 'rollover') - date.mdy(1,1,1900))], [273.00]],
-  [[ctime.days(datesum(date.mdy(1,31,1900), 9, 'months', 'rollover') - date.mdy(1,1,1900))], [303.00]],
-  [[ctime.days(datesum(date.mdy(1,31,1900), 10, 'months', 'rollover') - date.mdy(1,1,1900))], [334.00]],
-  [[ctime.days(datesum(date.mdy(1,31,1900), 11, 'months', 'rollover') - date.mdy(1,1,1900))], [364.00]],
-  [[ctime.days(datesum(date.mdy(1,31,1900), 12, 'months', 'rollover') - date.mdy(1,1,1900))], [395.00]],
-  [[ctime.days(datesum(date.mdy(1,31,1900), 13, 'months', 'rollover') - date.mdy(1,1,1900))], [426.00]],
-
-dnl DATESUM with leap year
-  [[ctime.days(datesum(date.mdy(1,31,1904), 1, 'months') - date.mdy(1,1,1904))], [59.00]],
-  [[ctime.days(datesum(date.mdy(1,31,1904), 2.5, 'months') - date.mdy(1,1,1904))], [90.00]],
-  [[ctime.days(datesum(date.mdy(1,31,1904), 3, 'months') - date.mdy(1,1,1904))], [120.00]],
-  [[ctime.days(datesum(date.mdy(1,31,1904), 4.9, 'months') - date.mdy(1,1,1904))], [151.00]],
-  [[ctime.days(datesum(date.mdy(1,31,1904), 5.1, 'months') - date.mdy(1,1,1904))], [181.00]],
-  [[ctime.days(datesum(date.mdy(1,31,1904), 6, 'months') - date.mdy(1,1,1904))], [212.00]],
-  [[ctime.days(datesum(date.mdy(1,31,1904), 7, 'months') - date.mdy(1,1,1904))], [243.00]],
-  [[ctime.days(datesum(date.mdy(1,31,1904), 8, 'months') - date.mdy(1,1,1904))], [273.00]],
-  [[ctime.days(datesum(date.mdy(1,31,1904), 9, 'months') - date.mdy(1,1,1904))], [304.00]],
-  [[ctime.days(datesum(date.mdy(1,31,1904), 10, 'months') - date.mdy(1,1,1904))], [334.00]],
-  [[ctime.days(datesum(date.mdy(1,31,1904), 11, 'months') - date.mdy(1,1,1904))], [365.00]],
-  [[ctime.days(datesum(date.mdy(1,31,1904), 12, 'months') - date.mdy(1,1,1904))], [396.00]],
-  [[ctime.days(datesum(date.mdy(1,31,1904), 13, 'months') - date.mdy(1,1,1904))], [424.00]],
-  [[ctime.days(datesum(date.mdy(1,31,1904), 1, 'months', 'rollover') - date.mdy(1,1,1904))], [61.00]],
-  [[ctime.days(datesum(date.mdy(1,31,1904), 2, 'months', 'rollover') - date.mdy(1,1,1904))], [90.00]],
-  [[ctime.days(datesum(date.mdy(1,31,1904), 3, 'months', 'rollover') - date.mdy(1,1,1904))], [121.00]],
-  [[ctime.days(datesum(date.mdy(1,31,1904), 4, 'months', 'rollover') - date.mdy(1,1,1904))], [151.00]],
-  [[ctime.days(datesum(date.mdy(1,31,1904), 5, 'months', 'rollover') - date.mdy(1,1,1904))], [182.00]],
-  [[ctime.days(datesum(date.mdy(1,31,1904), 6, 'months', 'rollover') - date.mdy(1,1,1904))], [212.00]],
-  [[ctime.days(datesum(date.mdy(1,31,1904), 7, 'months', 'rollover') - date.mdy(1,1,1904))], [243.00]],
-  [[ctime.days(datesum(date.mdy(1,31,1904), 8, 'months', 'rollover') - date.mdy(1,1,1904))], [274.00]],
-  [[ctime.days(datesum(date.mdy(1,31,1904), 9, 'months', 'rollover') - date.mdy(1,1,1904))], [304.00]],
-  [[ctime.days(datesum(date.mdy(1,31,1904), 10, 'months', 'rollover') - date.mdy(1,1,1904))], [335.00]],
-  [[ctime.days(datesum(date.mdy(1,31,1904), 11, 'months', 'rollover') - date.mdy(1,1,1904))], [365.00]],
-  [[ctime.days(datesum(date.mdy(1,31,1904), 12, 'months', 'rollover') - date.mdy(1,1,1904))], [396.00]],
-  [[ctime.days(datesum(date.mdy(1,31,1904), 13, 'months', 'rollover') - date.mdy(1,1,1904))], [427.00]],
-
-  [[ctime.days(datesum(date.mdy(6,10,1648), 1, 'weeks') - date.mdy(6,10,1648))], [7.00]],
-  [[ctime.days(datesum(date.mdy(6,30,1680), 2.5, 'weeks') - date.mdy(6,30,1680))], [17.50]],
-  [[ctime.days(datesum(date.mdy(7,24,1716), -3, 'weeks') - date.mdy(7,24,1716))], [-21.00]],
-  [[ctime.days(datesum(date.mdy(6,19,1768), 4, 'weeks') - date.mdy(6,19,1768))], [28.00]],
-  [[ctime.days(datesum(date.mdy(8,2,1819), 5, 'weeks') - date.mdy(8,2,1819))], [35.00]],
-
-  [[ctime.days(datesum(date.mdy(6,10,1648), 1, 'days') - date.mdy(6,10,1648))], [1.00]],
-  [[ctime.days(datesum(date.mdy(6,30,1680), 2.5, 'days') - date.mdy(6,30,1680))], [2.50]],
-  [[ctime.days(datesum(date.mdy(7,24,1716), -3, 'days') - date.mdy(7,24,1716))], [-3.00]],
-  [[ctime.days(datesum(date.mdy(6,19,1768), 4, 'days') - date.mdy(6,19,1768))], [4.00]],
-  [[ctime.days(datesum(date.mdy(8,2,1819), 5, 'days') - date.mdy(8,2,1819))], [5.00]],
-  [[ctime.days(datesum(date.mdy(6,10,1648), 1, 'hours') - date.mdy(6,10,1648))], [0.04]],
-  [[ctime.days(datesum(date.mdy(6,30,1680), 2.5, 'hours') - date.mdy(6,30,1680))], [0.10]],
-  [[ctime.days(datesum(date.mdy(6,19,1768), -4, 'hours') - date.mdy(6,19,1768))], [-0.17]],
-  [[ctime.days(datesum(date.mdy(8,2,1819), 5, 'hours') - date.mdy(8,2,1819))], [0.21]],
-
-dnl DATESUM preserves time-of-day for units of days and longer.
-  [[ctime.days(datesum(date.mdy(8,2,1819) + time.hms(1,2,3), 5, 'days') - (date.mdy(8,2,1819) + time.hms(1,2,3)))], [5.00]],
-  [[ctime.days(datesum(date.mdy(8,2,1819) + time.hms(1,2,3), 5, 'weeks') - (date.mdy(8,2,1819) + time.hms(1,2,3)))], [35.00]],
-  [[ctime.days(datesum(date.mdy(8,2,1819) + time.hms(1,2,3), 5, 'months') - (date.mdy(8,2,1819) + time.hms(1,2,3)))], [153.00]],
-  [[ctime.days(datesum(date.mdy(8,2,1819) + time.hms(1,2,3), 5, 'years') - (date.mdy(8,2,1819) + time.hms(1,2,3)))], [1827.00]])
-
-CHECK_EXPR_EVAL([miscellaneous],
+AT_SETUP([expressions - DATEDIFF time of day])
+AT_KEYWORDS([expression expressions evaluate])
+AT_DATA([evaluate-base.sps], [dnl
+SET EPOCH 1940.
+DEBUG EVALUATE SET opt.
+DEBUG EVALUATE /datediff(date.mdy(10,15,1910) + 234, date.mdy(10,10,1910) + 123, 'days').
+DEBUG EVALUATE /datediff(date.mdy(10,15,1910) + 123, date.mdy(10,10,1910) + 234, 'days').
+DEBUG EVALUATE /datediff(date.mdy(10,24,1910) + 234, date.mdy(10,10,1910) + 123, 'weeks').
+DEBUG EVALUATE /datediff(date.mdy(10,24,1910) + 123, date.mdy(10,10,1910) + 234, 'weeks').
+DEBUG EVALUATE /datediff(date.mdy(10,10,1910) + 234, date.mdy(5,10,1910) + 123, 'months').
+DEBUG EVALUATE /datediff(date.mdy(10,10,1910) + 123, date.mdy(5,10,1910) + 234, 'months').
+DEBUG EVALUATE /datediff(date.mdy(5,10,1919) + 234, date.mdy(5,10,1910) + 123, 'years').
+DEBUG EVALUATE /datediff(date.mdy(5,10,1919) + 123, date.mdy(5,10,1910) + 234, 'years').
+
+DEBUG EVALUATE /datediff(date.mdy(10,10,1910) + 123, date.mdy(10,15,1910) + 234, 'days').
+DEBUG EVALUATE /datediff(date.mdy(10,10,1910) + 234, date.mdy(10,15,1910) + 123, 'days').
+DEBUG EVALUATE /datediff(date.mdy(10,10,1910) + 123, date.mdy(10,24,1910) + 234, 'weeks').
+DEBUG EVALUATE /datediff(date.mdy(10,10,1910) + 234, date.mdy(10,24,1910) + 123, 'weeks').
+DEBUG EVALUATE /datediff(date.mdy(5,10,1910) + 123, date.mdy(10,10,1910) + 234, 'months').
+DEBUG EVALUATE /datediff(date.mdy(5,10,1910) + 234, date.mdy(10,10,1910) + 123, 'months').
+DEBUG EVALUATE /datediff(date.mdy(5,10,1910) + 123, date.mdy(5,10,1919) + 234, 'years').
+DEBUG EVALUATE /datediff(date.mdy(5,10,1910) + 234, date.mdy(5,10,1919) + 123, 'years').
+])
+
+for opt in OPT NOOPT; do
+    AS_BOX([$opt])
+    sed "s/opt/$opt/" < evaluate-base.sps > evaluate.sps
+    AT_CHECK([pspp --testing-mode evaluate.sps], [0], [dnl
+datediff(date.mdy(10,15,1910) + 234, date.mdy(10,10,1910) + 123, 'days') =>
+5.00
+
+datediff(date.mdy(10,15,1910) + 123, date.mdy(10,10,1910) + 234, 'days') =>
+4.00
+
+datediff(date.mdy(10,24,1910) + 234, date.mdy(10,10,1910) + 123, 'weeks') =>
+2.00
+
+datediff(date.mdy(10,24,1910) + 123, date.mdy(10,10,1910) + 234, 'weeks') =>
+1.00
+
+datediff(date.mdy(10,10,1910) + 234, date.mdy(5,10,1910) + 123, 'months') =>
+5.00
+
+datediff(date.mdy(10,10,1910) + 123, date.mdy(5,10,1910) + 234, 'months') =>
+4.00
+
+datediff(date.mdy(5,10,1919) + 234, date.mdy(5,10,1910) + 123, 'years') => 9.00
+
+datediff(date.mdy(5,10,1919) + 123, date.mdy(5,10,1910) + 234, 'years') => 8.00
+
+datediff(date.mdy(10,10,1910) + 123, date.mdy(10,15,1910) + 234, 'days') =>
+-5.00
+
+datediff(date.mdy(10,10,1910) + 234, date.mdy(10,15,1910) + 123, 'days') =>
+-4.00
+
+datediff(date.mdy(10,10,1910) + 123, date.mdy(10,24,1910) + 234, 'weeks') =>
+-2.00
+
+datediff(date.mdy(10,10,1910) + 234, date.mdy(10,24,1910) + 123, 'weeks') =>
+-1.00
+
+datediff(date.mdy(5,10,1910) + 123, date.mdy(10,10,1910) + 234, 'months') =>
+-5.00
+
+datediff(date.mdy(5,10,1910) + 234, date.mdy(10,10,1910) + 123, 'months') =>
+-4.00
+
+datediff(date.mdy(5,10,1910) + 123, date.mdy(5,10,1919) + 234, 'years') =>
+-9.00
+
+datediff(date.mdy(5,10,1910) + 234, date.mdy(5,10,1919) + 123, 'years') =>
+-8.00
+])
+done
+AT_CLEANUP
+
+AT_SETUP([expressions - DATESUM with non-leap year])
+AT_KEYWORDS([expression expressions evaluate])
+AT_DATA([evaluate-base.sps], [dnl
+SET EPOCH 1940.
+DEBUG EVALUATE SET opt.
+DEBUG EVALUATE /ctime.days(datesum(date.mdy(1,31,1900), 1, 'months') - date.mdy(1,1,1900)).
+DEBUG EVALUATE /ctime.days(datesum(date.mdy(1,31,1900), 2, 'months') - date.mdy(1,1,1900)).
+DEBUG EVALUATE /ctime.days(datesum(date.mdy(1,31,1900), 3, 'months') - date.mdy(1,1,1900)).
+DEBUG EVALUATE /ctime.days(datesum(date.mdy(1,31,1900), 4, 'months') - date.mdy(1,1,1900)).
+DEBUG EVALUATE /ctime.days(datesum(date.mdy(1,31,1900), 5.4, 'months') - date.mdy(1,1,1900)).
+DEBUG EVALUATE /ctime.days(datesum(date.mdy(1,31,1900), 6, 'months') - date.mdy(1,1,1900)).
+DEBUG EVALUATE /ctime.days(datesum(date.mdy(1,31,1900), 7, 'months') - date.mdy(1,1,1900)).
+DEBUG EVALUATE /ctime.days(datesum(date.mdy(1,31,1900), 8, 'months') - date.mdy(1,1,1900)).
+DEBUG EVALUATE /ctime.days(datesum(date.mdy(1,31,1900), 9, 'months') - date.mdy(1,1,1900)).
+DEBUG EVALUATE /ctime.days(datesum(date.mdy(1,31,1900), 10, 'months') - date.mdy(1,1,1900)).
+DEBUG EVALUATE /ctime.days(datesum(date.mdy(1,31,1900), 11, 'months') - date.mdy(1,1,1900)).
+DEBUG EVALUATE /ctime.days(datesum(date.mdy(1,31,1900), 12, 'months') - date.mdy(1,1,1900)).
+DEBUG EVALUATE /ctime.days(datesum(date.mdy(1,31,1900), 13.9, 'months') - date.mdy(1,1,1900)).
+DEBUG EVALUATE /ctime.days(datesum(date.mdy(1,31,1900), 1, 'months', 'rollover') - date.mdy(1,1,1900)).
+DEBUG EVALUATE /ctime.days(datesum(date.mdy(1,31,1900), 2, 'months', 'rollover') - date.mdy(1,1,1900)).
+DEBUG EVALUATE /ctime.days(datesum(date.mdy(1,31,1900), 3.2, 'months', 'rollover') - date.mdy(1,1,1900)).
+DEBUG EVALUATE /ctime.days(datesum(date.mdy(1,31,1900), 4, 'months', 'rollover') - date.mdy(1,1,1900)).
+DEBUG EVALUATE /ctime.days(datesum(date.mdy(1,31,1900), 5, 'months', 'rollover') - date.mdy(1,1,1900)).
+DEBUG EVALUATE /ctime.days(datesum(date.mdy(1,31,1900), 6, 'months', 'rollover') - date.mdy(1,1,1900)).
+DEBUG EVALUATE /ctime.days(datesum(date.mdy(1,31,1900), 7, 'months', 'rollover') - date.mdy(1,1,1900)).
+DEBUG EVALUATE /ctime.days(datesum(date.mdy(1,31,1900), 8, 'months', 'rollover') - date.mdy(1,1,1900)).
+DEBUG EVALUATE /ctime.days(datesum(date.mdy(1,31,1900), 9, 'months', 'rollover') - date.mdy(1,1,1900)).
+DEBUG EVALUATE /ctime.days(datesum(date.mdy(1,31,1900), 10, 'months', 'rollover') - date.mdy(1,1,1900)).
+DEBUG EVALUATE /ctime.days(datesum(date.mdy(1,31,1900), 11, 'months', 'rollover') - date.mdy(1,1,1900)).
+DEBUG EVALUATE /ctime.days(datesum(date.mdy(1,31,1900), 12, 'months', 'rollover') - date.mdy(1,1,1900)).
+DEBUG EVALUATE /ctime.days(datesum(date.mdy(1,31,1900), 13, 'months', 'rollover') - date.mdy(1,1,1900)).
+])
+
+for opt in OPT NOOPT; do
+    AS_BOX([$opt])
+    sed "s/opt/$opt/" < evaluate-base.sps > evaluate.sps
+    AT_CHECK([pspp --testing-mode evaluate.sps], [0], [dnl
+ctime.days(datesum(date.mdy(1,31,1900), 1, 'months') - date.mdy(1,1,1900)) =>
+58.00
+
+ctime.days(datesum(date.mdy(1,31,1900), 2, 'months') - date.mdy(1,1,1900)) =>
+89.00
+
+ctime.days(datesum(date.mdy(1,31,1900), 3, 'months') - date.mdy(1,1,1900)) =>
+119.00
+
+ctime.days(datesum(date.mdy(1,31,1900), 4, 'months') - date.mdy(1,1,1900)) =>
+150.00
+
+ctime.days(datesum(date.mdy(1,31,1900), 5.4, 'months') - date.mdy(1,1,1900)) =>
+180.00
+
+ctime.days(datesum(date.mdy(1,31,1900), 6, 'months') - date.mdy(1,1,1900)) =>
+211.00
+
+ctime.days(datesum(date.mdy(1,31,1900), 7, 'months') - date.mdy(1,1,1900)) =>
+242.00
+
+ctime.days(datesum(date.mdy(1,31,1900), 8, 'months') - date.mdy(1,1,1900)) =>
+272.00
+
+ctime.days(datesum(date.mdy(1,31,1900), 9, 'months') - date.mdy(1,1,1900)) =>
+303.00
+
+ctime.days(datesum(date.mdy(1,31,1900), 10, 'months') - date.mdy(1,1,1900)) =>
+333.00
+
+ctime.days(datesum(date.mdy(1,31,1900), 11, 'months') - date.mdy(1,1,1900)) =>
+364.00
+
+ctime.days(datesum(date.mdy(1,31,1900), 12, 'months') - date.mdy(1,1,1900)) =>
+395.00
+
+ctime.days(datesum(date.mdy(1,31,1900), 13.9, 'months') - date.mdy(1,1,1900))
+=> 423.00
+
+ctime.days(datesum(date.mdy(1,31,1900), 1, 'months', 'rollover') - date.
+mdy(1,1,1900)) => 61.00
+
+ctime.days(datesum(date.mdy(1,31,1900), 2, 'months', 'rollover') - date.
+mdy(1,1,1900)) => 89.00
+
+ctime.days(datesum(date.mdy(1,31,1900), 3.2, 'months', 'rollover') - date.
+mdy(1,1,1900)) => 120.00
+
+ctime.days(datesum(date.mdy(1,31,1900), 4, 'months', 'rollover') - date.
+mdy(1,1,1900)) => 150.00
+
+ctime.days(datesum(date.mdy(1,31,1900), 5, 'months', 'rollover') - date.
+mdy(1,1,1900)) => 181.00
+
+ctime.days(datesum(date.mdy(1,31,1900), 6, 'months', 'rollover') - date.
+mdy(1,1,1900)) => 211.00
+
+ctime.days(datesum(date.mdy(1,31,1900), 7, 'months', 'rollover') - date.
+mdy(1,1,1900)) => 242.00
+
+ctime.days(datesum(date.mdy(1,31,1900), 8, 'months', 'rollover') - date.
+mdy(1,1,1900)) => 273.00
+
+ctime.days(datesum(date.mdy(1,31,1900), 9, 'months', 'rollover') - date.
+mdy(1,1,1900)) => 303.00
+
+ctime.days(datesum(date.mdy(1,31,1900), 10, 'months', 'rollover') - date.
+mdy(1,1,1900)) => 334.00
+
+ctime.days(datesum(date.mdy(1,31,1900), 11, 'months', 'rollover') - date.
+mdy(1,1,1900)) => 364.00
+
+ctime.days(datesum(date.mdy(1,31,1900), 12, 'months', 'rollover') - date.
+mdy(1,1,1900)) => 395.00
+
+ctime.days(datesum(date.mdy(1,31,1900), 13, 'months', 'rollover') - date.
+mdy(1,1,1900)) => 426.00
+])
+done
+AT_CLEANUP
+
+AT_SETUP([expressions - DATESUM with leap year])
+AT_KEYWORDS([expression expressions evaluate])
+AT_DATA([evaluate-base.sps], [dnl
+SET EPOCH 1940.
+DEBUG EVALUATE SET opt.
+DEBUG EVALUATE /ctime.days(datesum(date.mdy(1,31,1904), 1, 'months') - date.mdy(1,1,1904)).
+DEBUG EVALUATE /ctime.days(datesum(date.mdy(1,31,1904), 2.5, 'months') - date.mdy(1,1,1904)).
+DEBUG EVALUATE /ctime.days(datesum(date.mdy(1,31,1904), 3, 'months') - date.mdy(1,1,1904)).
+DEBUG EVALUATE /ctime.days(datesum(date.mdy(1,31,1904), 4.9, 'months') - date.mdy(1,1,1904)).
+DEBUG EVALUATE /ctime.days(datesum(date.mdy(1,31,1904), 5.1, 'months') - date.mdy(1,1,1904)).
+DEBUG EVALUATE /ctime.days(datesum(date.mdy(1,31,1904), 6, 'months') - date.mdy(1,1,1904)).
+DEBUG EVALUATE /ctime.days(datesum(date.mdy(1,31,1904), 7, 'months') - date.mdy(1,1,1904)).
+DEBUG EVALUATE /ctime.days(datesum(date.mdy(1,31,1904), 8, 'months') - date.mdy(1,1,1904)).
+DEBUG EVALUATE /ctime.days(datesum(date.mdy(1,31,1904), 9, 'months') - date.mdy(1,1,1904)).
+DEBUG EVALUATE /ctime.days(datesum(date.mdy(1,31,1904), 10, 'months') - date.mdy(1,1,1904)).
+DEBUG EVALUATE /ctime.days(datesum(date.mdy(1,31,1904), 11, 'months') - date.mdy(1,1,1904)).
+DEBUG EVALUATE /ctime.days(datesum(date.mdy(1,31,1904), 12, 'months') - date.mdy(1,1,1904)).
+DEBUG EVALUATE /ctime.days(datesum(date.mdy(1,31,1904), 13, 'months') - date.mdy(1,1,1904)).
+DEBUG EVALUATE /ctime.days(datesum(date.mdy(1,31,1904), 1, 'months', 'rollover') - date.mdy(1,1,1904)).
+DEBUG EVALUATE /ctime.days(datesum(date.mdy(1,31,1904), 2, 'months', 'rollover') - date.mdy(1,1,1904)).
+DEBUG EVALUATE /ctime.days(datesum(date.mdy(1,31,1904), 3, 'months', 'rollover') - date.mdy(1,1,1904)).
+DEBUG EVALUATE /ctime.days(datesum(date.mdy(1,31,1904), 4, 'months', 'rollover') - date.mdy(1,1,1904)).
+DEBUG EVALUATE /ctime.days(datesum(date.mdy(1,31,1904), 5, 'months', 'rollover') - date.mdy(1,1,1904)).
+DEBUG EVALUATE /ctime.days(datesum(date.mdy(1,31,1904), 6, 'months', 'rollover') - date.mdy(1,1,1904)).
+DEBUG EVALUATE /ctime.days(datesum(date.mdy(1,31,1904), 7, 'months', 'rollover') - date.mdy(1,1,1904)).
+DEBUG EVALUATE /ctime.days(datesum(date.mdy(1,31,1904), 8, 'months', 'rollover') - date.mdy(1,1,1904)).
+DEBUG EVALUATE /ctime.days(datesum(date.mdy(1,31,1904), 9, 'months', 'rollover') - date.mdy(1,1,1904)).
+DEBUG EVALUATE /ctime.days(datesum(date.mdy(1,31,1904), 10, 'months', 'rollover') - date.mdy(1,1,1904)).
+DEBUG EVALUATE /ctime.days(datesum(date.mdy(1,31,1904), 11, 'months', 'rollover') - date.mdy(1,1,1904)).
+DEBUG EVALUATE /ctime.days(datesum(date.mdy(1,31,1904), 12, 'months', 'rollover') - date.mdy(1,1,1904)).
+DEBUG EVALUATE /ctime.days(datesum(date.mdy(1,31,1904), 13, 'months', 'rollover') - date.mdy(1,1,1904)).
+])
+
+for opt in OPT NOOPT; do
+    AS_BOX([$opt])
+    sed "s/opt/$opt/" < evaluate-base.sps > evaluate.sps
+    AT_CHECK([pspp --testing-mode evaluate.sps], [0], [dnl
+ctime.days(datesum(date.mdy(1,31,1904), 1, 'months') - date.mdy(1,1,1904)) =>
+59.00
+
+ctime.days(datesum(date.mdy(1,31,1904), 2.5, 'months') - date.mdy(1,1,1904)) =>
+90.00
+
+ctime.days(datesum(date.mdy(1,31,1904), 3, 'months') - date.mdy(1,1,1904)) =>
+120.00
+
+ctime.days(datesum(date.mdy(1,31,1904), 4.9, 'months') - date.mdy(1,1,1904)) =>
+151.00
+
+ctime.days(datesum(date.mdy(1,31,1904), 5.1, 'months') - date.mdy(1,1,1904)) =>
+181.00
+
+ctime.days(datesum(date.mdy(1,31,1904), 6, 'months') - date.mdy(1,1,1904)) =>
+212.00
+
+ctime.days(datesum(date.mdy(1,31,1904), 7, 'months') - date.mdy(1,1,1904)) =>
+243.00
+
+ctime.days(datesum(date.mdy(1,31,1904), 8, 'months') - date.mdy(1,1,1904)) =>
+273.00
+
+ctime.days(datesum(date.mdy(1,31,1904), 9, 'months') - date.mdy(1,1,1904)) =>
+304.00
+
+ctime.days(datesum(date.mdy(1,31,1904), 10, 'months') - date.mdy(1,1,1904)) =>
+334.00
+
+ctime.days(datesum(date.mdy(1,31,1904), 11, 'months') - date.mdy(1,1,1904)) =>
+365.00
+
+ctime.days(datesum(date.mdy(1,31,1904), 12, 'months') - date.mdy(1,1,1904)) =>
+396.00
+
+ctime.days(datesum(date.mdy(1,31,1904), 13, 'months') - date.mdy(1,1,1904)) =>
+424.00
+
+ctime.days(datesum(date.mdy(1,31,1904), 1, 'months', 'rollover') - date.
+mdy(1,1,1904)) => 61.00
+
+ctime.days(datesum(date.mdy(1,31,1904), 2, 'months', 'rollover') - date.
+mdy(1,1,1904)) => 90.00
+
+ctime.days(datesum(date.mdy(1,31,1904), 3, 'months', 'rollover') - date.
+mdy(1,1,1904)) => 121.00
+
+ctime.days(datesum(date.mdy(1,31,1904), 4, 'months', 'rollover') - date.
+mdy(1,1,1904)) => 151.00
+
+ctime.days(datesum(date.mdy(1,31,1904), 5, 'months', 'rollover') - date.
+mdy(1,1,1904)) => 182.00
+
+ctime.days(datesum(date.mdy(1,31,1904), 6, 'months', 'rollover') - date.
+mdy(1,1,1904)) => 212.00
+
+ctime.days(datesum(date.mdy(1,31,1904), 7, 'months', 'rollover') - date.
+mdy(1,1,1904)) => 243.00
+
+ctime.days(datesum(date.mdy(1,31,1904), 8, 'months', 'rollover') - date.
+mdy(1,1,1904)) => 274.00
+
+ctime.days(datesum(date.mdy(1,31,1904), 9, 'months', 'rollover') - date.
+mdy(1,1,1904)) => 304.00
+
+ctime.days(datesum(date.mdy(1,31,1904), 10, 'months', 'rollover') - date.
+mdy(1,1,1904)) => 335.00
+
+ctime.days(datesum(date.mdy(1,31,1904), 11, 'months', 'rollover') - date.
+mdy(1,1,1904)) => 365.00
+
+ctime.days(datesum(date.mdy(1,31,1904), 12, 'months', 'rollover') - date.
+mdy(1,1,1904)) => 396.00
+
+ctime.days(datesum(date.mdy(1,31,1904), 13, 'months', 'rollover') - date.
+mdy(1,1,1904)) => 427.00
+])
+done
+AT_CLEANUP
+
+AT_SETUP([expressions - DATESUM])
+AT_KEYWORDS([expression expressions evaluate])
+AT_DATA([evaluate-base.sps], [dnl
+SET EPOCH 1940.
+DEBUG EVALUATE SET opt.
+DEBUG EVALUATE /ctime.days(datesum(date.mdy(6,10,1648), 1, 'weeks') - date.mdy(6,10,1648)).
+DEBUG EVALUATE /ctime.days(datesum(date.mdy(6,30,1680), 2.5, 'weeks') - date.mdy(6,30,1680)).
+DEBUG EVALUATE /ctime.days(datesum(date.mdy(7,24,1716), -3, 'weeks') - date.mdy(7,24,1716)).
+DEBUG EVALUATE /ctime.days(datesum(date.mdy(6,19,1768), 4, 'weeks') - date.mdy(6,19,1768)).
+DEBUG EVALUATE /ctime.days(datesum(date.mdy(8,2,1819), 5, 'weeks') - date.mdy(8,2,1819)).
+
+DEBUG EVALUATE /ctime.days(datesum(date.mdy(6,10,1648), 1, 'days') - date.mdy(6,10,1648)).
+DEBUG EVALUATE /ctime.days(datesum(date.mdy(6,30,1680), 2.5, 'days') - date.mdy(6,30,1680)).
+DEBUG EVALUATE /ctime.days(datesum(date.mdy(7,24,1716), -3, 'days') - date.mdy(7,24,1716)).
+DEBUG EVALUATE /ctime.days(datesum(date.mdy(6,19,1768), 4, 'days') - date.mdy(6,19,1768)).
+DEBUG EVALUATE /ctime.days(datesum(date.mdy(8,2,1819), 5, 'days') - date.mdy(8,2,1819)).
+DEBUG EVALUATE /ctime.days(datesum(date.mdy(6,10,1648), 1, 'hours') - date.mdy(6,10,1648)).
+DEBUG EVALUATE /ctime.days(datesum(date.mdy(6,30,1680), 2.5, 'hours') - date.mdy(6,30,1680)).
+DEBUG EVALUATE /ctime.days(datesum(date.mdy(6,19,1768), -4, 'hours') - date.mdy(6,19,1768)).
+DEBUG EVALUATE /ctime.days(datesum(date.mdy(8,2,1819), 5, 'hours') - date.mdy(8,2,1819)).
+])
+
+for opt in OPT NOOPT; do
+    AS_BOX([$opt])
+    sed "s/opt/$opt/" < evaluate-base.sps > evaluate.sps
+    AT_CHECK([pspp --testing-mode evaluate.sps], [0], [dnl
+ctime.days(datesum(date.mdy(6,10,1648), 1, 'weeks') - date.mdy(6,10,1648)) =>
+7.00
+
+ctime.days(datesum(date.mdy(6,30,1680), 2.5, 'weeks') - date.mdy(6,30,1680)) =>
+17.50
+
+ctime.days(datesum(date.mdy(7,24,1716), -3, 'weeks') - date.mdy(7,24,1716)) =>
+-21.00
+
+ctime.days(datesum(date.mdy(6,19,1768), 4, 'weeks') - date.mdy(6,19,1768)) =>
+28.00
+
+ctime.days(datesum(date.mdy(8,2,1819), 5, 'weeks') - date.mdy(8,2,1819)) =>
+35.00
+
+ctime.days(datesum(date.mdy(6,10,1648), 1, 'days') - date.mdy(6,10,1648)) =>
+1.00
+
+ctime.days(datesum(date.mdy(6,30,1680), 2.5, 'days') - date.mdy(6,30,1680)) =>
+2.50
+
+ctime.days(datesum(date.mdy(7,24,1716), -3, 'days') - date.mdy(7,24,1716)) =>
+-3.00
+
+ctime.days(datesum(date.mdy(6,19,1768), 4, 'days') - date.mdy(6,19,1768)) =>
+4.00
+
+ctime.days(datesum(date.mdy(8,2,1819), 5, 'days') - date.mdy(8,2,1819)) => 5.00
+
+ctime.days(datesum(date.mdy(6,10,1648), 1, 'hours') - date.mdy(6,10,1648)) =>
+0.04
+
+ctime.days(datesum(date.mdy(6,30,1680), 2.5, 'hours') - date.mdy(6,30,1680)) =>
+0.10
+
+ctime.days(datesum(date.mdy(6,19,1768), -4, 'hours') - date.mdy(6,19,1768)) =>
+-0.17
+
+ctime.days(datesum(date.mdy(8,2,1819), 5, 'hours') - date.mdy(8,2,1819)) =>
+0.21
+])
+done
+AT_CLEANUP
+
+AT_SETUP([expressions - DATESUM preserves time of day for units of days and longer])
+AT_KEYWORDS([expression expressions evaluate])
+AT_DATA([evaluate-base.sps], [dnl
+SET EPOCH 1940.
+DEBUG EVALUATE SET opt.
+DEBUG EVALUATE /ctime.days(datesum(date.mdy(8,2,1819) + time.hms(1,2,3), 5, 'days') - (date.mdy(8,2,1819) + time.hms(1,2,3))).
+DEBUG EVALUATE /ctime.days(datesum(date.mdy(8,2,1819) + time.hms(1,2,3), 5, 'weeks') - (date.mdy(8,2,1819) + time.hms(1,2,3))).
+DEBUG EVALUATE /ctime.days(datesum(date.mdy(8,2,1819) + time.hms(1,2,3), 5, 'months') - (date.mdy(8,2,1819) + time.hms(1,2,3))).
+DEBUG EVALUATE /ctime.days(datesum(date.mdy(8,2,1819) + time.hms(1,2,3), 5, 'years') - (date.mdy(8,2,1819) + time.hms(1,2,3))).
+])
+
+for opt in OPT NOOPT; do
+    AS_BOX([$opt])
+    sed "s/opt/$opt/" < evaluate-base.sps > evaluate.sps
+    AT_CHECK([pspp --testing-mode evaluate.sps], [0], [dnl
+ctime.days(datesum(date.mdy(8,2,1819) + time.hms(1,2,3), 5, 'days') - (date.
+mdy(8,2,1819) + time.hms(1,2,3))) => 5.00
+
+ctime.days(datesum(date.mdy(8,2,1819) + time.hms(1,2,3), 5, 'weeks') - (date.
+mdy(8,2,1819) + time.hms(1,2,3))) => 35.00
+
+ctime.days(datesum(date.mdy(8,2,1819) + time.hms(1,2,3), 5, 'months') - (date.
+mdy(8,2,1819) + time.hms(1,2,3))) => 153.00
+
+ctime.days(datesum(date.mdy(8,2,1819) + time.hms(1,2,3), 5, 'years') - (date.
+mdy(8,2,1819) + time.hms(1,2,3))) => 1827.00
+])
+done
+AT_CLEANUP
+
 dnl These test values are from Applied Statistics, Algorithm AS 310.
-  [[1000 * ncdf.beta(.868,10,20,150)], [937.66]],
-  [[1000 * ncdf.beta(.9,10,10,120)], [730.68]],
-  [[1000 * ncdf.beta(.88,15,5,80)], [160.43]],
-  [[1000 * ncdf.beta(.85,20,10,110)], [186.75]],
-  [[1000 * ncdf.beta(.66,20,30,65)], [655.94]],
-  [[1000 * ncdf.beta(.72,20,50,130)], [979.69]],
-  [[1000 * ncdf.beta(.72,30,20,80)], [116.24]],
-  [[1000 * ncdf.beta(.8,30,40,130)], [993.04]],
+AT_SETUP([expressions - NCDF.BETA])
+AT_KEYWORDS([expression expressions evaluate])
+AT_DATA([evaluate-base.sps], [dnl
+SET EPOCH 1940.
+DEBUG EVALUATE SET opt.
+DEBUG EVALUATE /1000 * ncdf.beta(.868,10,20,150).
+DEBUG EVALUATE /1000 * ncdf.beta(.9,10,10,120).
+DEBUG EVALUATE /1000 * ncdf.beta(.88,15,5,80).
+DEBUG EVALUATE /1000 * ncdf.beta(.85,20,10,110).
+DEBUG EVALUATE /1000 * ncdf.beta(.66,20,30,65).
+DEBUG EVALUATE /1000 * ncdf.beta(.72,20,50,130).
+DEBUG EVALUATE /1000 * ncdf.beta(.72,30,20,80).
+DEBUG EVALUATE /1000 * ncdf.beta(.8,30,40,130).
+])
+
+for opt in OPT NOOPT; do
+    AS_BOX([$opt])
+    sed "s/opt/$opt/" < evaluate-base.sps > evaluate.sps
+    AT_CHECK([pspp --testing-mode evaluate.sps], [0], [dnl
+1000 * ncdf.beta(.868,10,20,150) => 937.66
+
+1000 * ncdf.beta(.9,10,10,120) => 730.68
+
+1000 * ncdf.beta(.88,15,5,80) => 160.43
+
+1000 * ncdf.beta(.85,20,10,110) => 186.75
+
+1000 * ncdf.beta(.66,20,30,65) => 655.94
+
+1000 * ncdf.beta(.72,20,50,130) => 979.69
+
+1000 * ncdf.beta(.72,30,20,80) => 116.24
+
+1000 * ncdf.beta(.8,30,40,130) => 993.04
+])
+done
+AT_CLEANUP
 
 dnl FIXME: LAG
 dnl
   [[X], [1.00], [], [(X = 1.00)]],
-  [[SYSMIS(1)], [false]],
-  [[SYSMIS($SYSMIS)], [true]],
-  [[SYSMIS(1 + $SYSMIS)], [true]],
 
 dnl FIXME: out-of-range and nearly out-of-range values on dates
 dnl
 dnl Tests correctness of generic optimizations in optimize_tree().
-  [[x + 0], [10.00], [], [(X = 10.00)]],
-  [[x - 0], [-3.00], [], [(X = -3.00)]],
-  [[0 + x], [5.00], [], [(X = 5.00)]],
-  [[x * 1], [10.00], [], [(X = 10.00)]],
-  [[1 * x], [-3.00], [], [(X = -3.00)]],
-  [[x / 1], [5.00], [], [(X = 5.00)]],
-  [[0 * x], [0.00], [], [(X = 10.00)]],
-  [[x * 0], [0.00], [], [(X = -3.00)]],
-  [[0 / x], [0.00], [], [(X = 5.00)]],
-  [[mod(0, x)], [0.00], [], [(X = 5.00)]],
-  [[x ** 1], [5.00], [], [(X = 5.00)]],
-  [[x ** 2], [25.00], [], [(X = 5.00)]])
-
-CHECK_EXPR_EVAL([negative checks],
-  [[$nonexistent], [error], [error: DEBUG EVALUATE: Unknown system variable $nonexistent.]],
-  [[RANGE(1, 2)], [error], [error: DEBUG EVALUATE: RANGE(number, number, number[, number, number]...) must have an odd number of arguments.]],
-  [[CONCAT.1('a', 'b')], [error], [error: DEBUG EVALUATE: CONCAT(string[, string]...) function cannot accept suffix .1 to specify the minimum number of valid arguments.]],
-  [[foobar(x)], [error], [error: DEBUG EVALUATE: No function or vector named foobar.]],
-  [[CONCAT.1('a' b)], [error], [error: DEBUG EVALUATE: Syntax error at `b': expecting `,' or `)'.]],
-  [[NCDF.CHISQ(1, 2, 3)], [error], [error: DEBUG EVALUATE: NCDF.CHISQ(number, number, number) is not available in this version of PSPP.]])
-
-AT_SETUP([LAG function])
+AT_SETUP([expressions - generic tree optimizations])
+AT_KEYWORDS([expression expressions evaluate])
+AT_DATA([evaluate-base.sps], [dnl
+SET EPOCH 1940.
+DEBUG EVALUATE SET opt.
+DEBUG EVALUATE (x=10) /x + 0.
+DEBUG EVALUATE (x=-3) /x - 0.
+DEBUG EVALUATE (x=5) /0 + x.
+DEBUG EVALUATE (x=10) /x * 1.
+DEBUG EVALUATE (x=-3) /1 * x.
+DEBUG EVALUATE (x=5) /x / 1.
+DEBUG EVALUATE (x=10) /0 * x.
+DEBUG EVALUATE (x=-3) /x * 0.
+DEBUG EVALUATE (x=5) /0 / x.
+DEBUG EVALUATE (x=5) /mod(0, x).
+DEBUG EVALUATE (x=5) /x ** 1.
+DEBUG EVALUATE (x=5) /x ** 2.
+])
+
+for opt in OPT NOOPT; do
+    AS_BOX([$opt])
+    sed "s/opt/$opt/" < evaluate-base.sps > evaluate.sps
+    AT_CHECK([pspp --testing-mode evaluate.sps], [0], [dnl
+x + 0 => 10.00
+
+x - 0 => -3.00
+
+0 + x => 5.00
+
+x * 1 => 10.00
+
+1 * x => -3.00
+
+x / 1 => 5.00
+
+0 * x => 0.00
+
+x * 0 => 0.00
+
+0 / x => 0.00
+
+mod(0, x) => 0.00
+
+x ** 1 => 5.00
+
+x ** 2 => 25.00
+])
+done
+AT_CLEANUP
+
+AT_SETUP([expressions - LAG])
 AT_DATA([lag.sps], [dnl
 data list /W 1.
 begin data.
@@ -2037,7 +7594,7 @@ W,X,Y,Z
 ])
 AT_CLEANUP
 
-AT_SETUP([LAG crash bug])
+AT_SETUP([expression - LAG crash])
 AT_DATA([lag.sps], [dnl
 DATA LIST LIST /x.
 BEGIN DATA
@@ -2062,7 +7619,7 @@ hello
 AT_CLEANUP
 
 dnl Tests for a bug which caused UNIFORM(x) to always return zero.
-AT_SETUP([UNIFORM function])
+AT_SETUP([expressions - UNIFORM])
 AT_DATA([uniform.sps], [dnl
 set seed=10.
 input program.
@@ -2104,7 +7661,7 @@ R1
 ])
 AT_CLEANUP
 
-AT_SETUP([VALUELABEL function])
+AT_SETUP([expressions - VALUELABEL])
 AT_DATA([valuelabel.sps], [dnl
 DATA LIST notable /n 1 s 2(a).
 VALUE LABELS /n 0 'Very dissatisfied'
@@ -2148,7 +7705,7 @@ n,s,nlabel,slabel
 ])
 AT_CLEANUP
 
-AT_SETUP([variables in expressions])
+AT_SETUP([expressions - variables])
 AT_DATA([variables.sps], [dnl
 DATA LIST NOTABLE/N1 TO N5 1-5.
 MISSING VALUES N1 TO N5 (3 THRU 5, 1).
@@ -2197,7 +7754,7 @@ N1,N2,N3,N4,N5,P1,P2,P3,P4,P5,MC,VC,S1,S2,S3,S4,S5,M1,M2,M3,M4,M5,V1,V2,V3,V4,V5
 ])
 AT_CLEANUP
 
-AT_SETUP([vectors in expressions])
+AT_SETUP([expressions - vectors])
 AT_DATA([vectors.sps], [dnl
 DATA LIST NOTABLE /N1 TO N5 1-5.
 MISSING VALUES N1 TO N5 (3 THRU 5, 1).
index 02b713f8b96aad762efa8cf8c5b1f60143639de1..bb3dc3db1383bee7dfb0bf6ec344e6d2e93ad496 100644 (file)
@@ -17,6 +17,7 @@ dnl
 AT_BANNER([expression parsing])
 
 AT_SETUP([parse expression with unknown variable crash])
+AT_KEYWORDS([expression expressions parse])
 AT_DATA([parse.sps], [dnl
 INPUT PROGRAM.
 LOOP c=1 to 10.
@@ -39,94 +40,114 @@ parse.sps:11: error: Stopping syntax file processing here to avoid a cascade of
 AT_CLEANUP
 
 AT_SETUP([parsing boolean expression with type mismatch])
+AT_KEYWORDS([expression expressions parse])
 AT_DATA([parse.sps], [dnl
 DATA LIST NOTABLE/x 1(A).
 IF 'foo'.
 ])
-AT_CHECK([pspp -O format=csv parse.sps], [1], [dnl
-"parse.sps:2: error: IF: Type mismatch: expression has string type, but a boolean value is required here."
+AT_CHECK([pspp parse.sps], [1], [dnl
+parse.sps:2.4-2.8: error: IF: Type mismatch: expression has string type, but a
+boolean value is required here.
+    2 | IF 'foo'.
+      |    ^~~~~
 ])
 AT_CLEANUP
 
 AT_SETUP([parsing numeric expression with type mismatch])
+AT_KEYWORDS([expression expressions parse])
 AT_DATA([parse.sps], [dnl
 DATA LIST NOTABLE/x 1.
 COMPUTE x='foo'.
 ])
-AT_CHECK([pspp -O format=csv parse.sps], [1], [dnl
-"parse.sps:2: error: COMPUTE: Type mismatch: expression has string type, but a numeric value is required here."
+AT_CHECK([pspp parse.sps], [1], [dnl
+parse.sps:2.11-2.15: error: COMPUTE: Type mismatch: expression has type
+'string', but a numeric value is required.
+    2 | COMPUTE x='foo'.
+      |           ^~~~~
 ])
 AT_CLEANUP
 
 AT_SETUP([parsing string expression with type mismatch])
-AT_KEYWORDS([expression negative])
+AT_KEYWORDS([expression expressions parse negative])
 AT_DATA([parse.sps], [dnl
 DATA LIST NOTABLE/x 1(A).
 COMPUTE x=1.
 ])
-AT_CHECK([pspp -O format=csv parse.sps], [1], [dnl
-"parse.sps:2: error: COMPUTE: Type mismatch: expression has number type, but a string value is required here."
+AT_CHECK([pspp parse.sps], [1], [dnl
+parse.sps:2.11: error: COMPUTE: Type mismatch: expression has type 'number',
+but a string value is required.
+    2 | COMPUTE x=1.
+      |           ^
 ])
 AT_CLEANUP
 
 AT_SETUP([assigning string expression to new variable])
-AT_KEYWORDS([expression negative])
+AT_KEYWORDS([expression expressions parse negative])
 AT_DATA([parse.sps], [dnl
 DATA LIST NOTABLE/x 1(A).
 COMPUTE y='a'.
 ])
-AT_CHECK([pspp -O format=csv parse.sps], [1], [dnl
-"parse.sps:2: error: COMPUTE: This command tries to create a new variable y by assigning a string value to it, but this is not supported.  Use the STRING command to create the new variable with the correct width before assigning to it, e.g. STRING y(A20)."
+AT_CHECK([pspp parse.sps], [1], [dnl
+parse.sps:2: error: COMPUTE: This command tries to create a new variable y by
+assigning a string value to it, but this is not supported.  Use the STRING
+command to create the new variable with the correct width before assigning to
+it, e.g. STRING y(A20).
 ])
 AT_CLEANUP
 
 AT_SETUP([parse expression with unknown system variable])
-AT_KEYWORDS([expression negative])
+AT_KEYWORDS([expression expressions parse negative])
 AT_DATA([parse.sps], [dnl
 DATA LIST NOTABLE/x 1.
 COMPUTE x=$nonexistent.
 ])
-AT_CHECK([pspp -O format=csv parse.sps], [1], [dnl
+AT_CHECK([pspp parse.sps], [1], [dnl
 parse.sps:2: error: COMPUTE: Unknown system variable $nonexistent.
 ])
 AT_CLEANUP
 
 AT_SETUP([parse expression with unknown identifier])
-AT_KEYWORDS([expression negative])
+AT_KEYWORDS([expression expressions parse negative])
 AT_DATA([parse.sps], [dnl
 DATA LIST NOTABLE/x 1.
 COMPUTE x=y.
 ])
-AT_CHECK([pspp -O format=csv parse.sps], [1], [dnl
+AT_CHECK([pspp parse.sps], [1], [dnl
 parse.sps:2: error: COMPUTE: Unknown identifier y.
 ])
 AT_CLEANUP
 
 AT_SETUP([parse expression with extension function in compatibility mode])
-AT_KEYWORDS([expression negative])
+AT_KEYWORDS([expression expressions parse negative])
 AT_DATA([parse.sps], [dnl
 DEBUG EVALUATE/ACOS(0)*0.
 ])
-AT_CHECK([pspp --testing-mode --syntax=compatible -O format=csv parse.sps], [0], [dnl
-parse.sps:1: warning: DEBUG EVALUATE: ACOS(number) is a PSPP extension.
+AT_CHECK([pspp --testing-mode --syntax=compatible parse.sps], [0], [dnl
+parse.sps:1.16-1.22: warning: DEBUG EVALUATE: ACOS(number) is a PSPP extension.
+    1 | DEBUG EVALUATE/ACOS(0)*0.
+      |                ^~~~~~~
 
-0.00
+ACOS(0)*0 => 0.00
 ])
 AT_CLEANUP
 
 AT_SETUP([LAG expression following TEMPORARY])
-AT_KEYWORDS([expression negative])
+AT_KEYWORDS([expression expressions parse negative])
 AT_DATA([parse.sps], [dnl
 DATA LIST NOTABLE/x 1.
 TEMPORARY
 COMPUTE y=LAG(x).
 ])
-AT_CHECK([pspp -O format=csv parse.sps], [1], [dnl
-parse.sps:3: error: COMPUTE: LAG(num_variable) may not appear after TEMPORARY.
+AT_CHECK([pspp parse.sps], [1], [dnl
+parse.sps:3.11-3.16: error: COMPUTE: LAG(num_variable) may not appear after
+TEMPORARY.
+    3 | COMPUTE y=LAG(x).
+      |           ^~~~~~
 ])
 AT_CLEANUP
 
 AT_SETUP([parse expression with invalid logical expression])
+AT_KEYWORDS([expression expressions parse negative])
 AT_DATA([parse.sps], [dnl
 INPUT PROGRAM.
 LOOP c=1 to 10.
@@ -138,7 +159,359 @@ END INPUT PROGRAM.
 
 SELECT IF 2.
 ])
-AT_CHECK([pspp -O format=csv parse.sps], [1], [dnl
-"parse.sps:9: error: SELECT IF: A logical expression was found to have a value other than 0 (false), 1 (true), or the system-missing value.  The result was forced to 0."
+AT_CHECK([pspp parse.sps], [1], [dnl
+parse.sps:9.11: error: SELECT IF: This expression, which must be 0 or 1,
+evaluated to 2.  It will be treated as 0.
+    9 | SELECT IF 2.
+      |           ^
+])
+AT_CLEANUP
+
+AT_SETUP([chaining operators that shouldn't be])
+AT_KEYWORDS([expression expressions parse negative])
+AT_DATA([parse.sps], [dnl
+INPUT PROGRAM.
+* These should provoke warnings.
+COMPUTE a = 1 < 2 < 3.
+COMPUTE b = 1 > 2 < 0.
+COMPUTE c = 2**3**4.
+
+* These should not provoke warnings.
+COMPUTE d = (1 < 2) < 3.
+COMPUTE e = (2**3)**4.
+END INPUT PROGRAM.
+])
+AT_CHECK([pspp parse.sps], [1], [dnl
+parse.sps:3.13-3.21: warning: COMPUTE: Chaining relational operators (e.g. `a <
+b < c') will not produce the mathematically expected result.  Use the AND
+logical operator to fix the problem (e.g. `a < b AND b < c').  To disable this
+warning, insert parentheses.
+    3 | COMPUTE a = 1 < 2 < 3.
+      |             ^~~~~~~~~
+
+parse.sps:4.13-4.21: warning: COMPUTE: Chaining relational operators (e.g. `a <
+b < c') will not produce the mathematically expected result.  Use the AND
+logical operator to fix the problem (e.g. `a < b AND b < c').  To disable this
+warning, insert parentheses.
+    4 | COMPUTE b = 1 > 2 < 0.
+      |             ^~~~~~~~~
+
+parse.sps:5.13-5.19: warning: COMPUTE: The exponentiation operator (`**') is
+left-associative: `a**b**c' equals `(a**b)**c', not `a**(b**c)'.  To disable
+this warning, insert parentheses.
+    5 | COMPUTE c = 2**3**4.
+      |             ^~~~~~~
+
+parse.sps:10: error: INPUT PROGRAM: Input program must contain DATA LIST or END
+FILE.
+])
+AT_CLEANUP
+
+AT_SETUP([binary operator type mismatch])
+AT_KEYWORDS([expression expressions parse negative])
+AT_DATA([parse.sps], [dnl
+DEBUG EVALUATE /1 + 'a'.
+DEBUG EVALUATE /'a' + 1.
+DEBUG EVALUATE /'a' + 'a'.
+DEBUG EVALUATE /'a' + ('a').
+
+DEBUG EVALUATE /1 < 'a'.
+DEBUG EVALUATE /'a' < 1.
+DEBUG EVALUATE /'a' < 'b' < 'c'.
 ])
+AT_CHECK([pspp --testing-mode parse.sps], [1], [dnl
+parse.sps:1.17-1.23: error: DEBUG EVALUATE: Both operands of + must be numeric.
+    1 | DEBUG EVALUATE /1 + 'a'.
+      |                 ^~~~~~~
+
+parse.sps:1.17: note: DEBUG EVALUATE: This operand has type 'number'.
+    1 | DEBUG EVALUATE /1 + 'a'.
+      |                 ^
+
+parse.sps:1.21-1.23: note: DEBUG EVALUATE: This operand has type 'string'.
+    1 | DEBUG EVALUATE /1 + 'a'.
+      |                     ^~~
+
+1 + 'a' => error
+
+parse.sps:2.17-2.23: error: DEBUG EVALUATE: Both operands of + must be numeric.
+    2 | DEBUG EVALUATE /'a' + 1.
+      |                 ^~~~~~~
+
+parse.sps:2.17-2.19: note: DEBUG EVALUATE: This operand has type 'string'.
+    2 | DEBUG EVALUATE /'a' + 1.
+      |                 ^~~
+
+parse.sps:2.23: note: DEBUG EVALUATE: This operand has type 'number'.
+    2 | DEBUG EVALUATE /'a' + 1.
+      |                       ^
+
+'a' + 1 => error
+
+'a' + 'a' => "aa"
+
+parse.sps:4.17-4.26: error: DEBUG EVALUATE: Both operands of + must be numeric.
+    4 | DEBUG EVALUATE /'a' + ('a').
+      |                 ^~~~~~~~~~
+
+parse.sps:4.17-4.19: note: DEBUG EVALUATE: This operand has type 'string'.
+    4 | DEBUG EVALUATE /'a' + ('a').
+      |                 ^~~
+
+parse.sps:4.24-4.26: note: DEBUG EVALUATE: This operand has type 'string'.
+    4 | DEBUG EVALUATE /'a' + ('a').
+      |                        ^~~
+
+'a' + ('a') => error
+
+parse.sps:6.17-6.23: error: DEBUG EVALUATE: Both operands of < must have the
+same type.
+    6 | DEBUG EVALUATE /1 < 'a'.
+      |                 ^~~~~~~
+
+parse.sps:6.17: note: DEBUG EVALUATE: This operand has type 'number'.
+    6 | DEBUG EVALUATE /1 < 'a'.
+      |                 ^
+
+parse.sps:6.21-6.23: note: DEBUG EVALUATE: This operand has type 'string'.
+    6 | DEBUG EVALUATE /1 < 'a'.
+      |                     ^~~
+
+1 < 'a' => error
+
+parse.sps:7.17-7.23: error: DEBUG EVALUATE: Both operands of < must have the
+same type.
+    7 | DEBUG EVALUATE /'a' < 1.
+      |                 ^~~~~~~
+
+parse.sps:7.17-7.19: note: DEBUG EVALUATE: This operand has type 'string'.
+    7 | DEBUG EVALUATE /'a' < 1.
+      |                 ^~~
+
+parse.sps:7.23: note: DEBUG EVALUATE: This operand has type 'number'.
+    7 | DEBUG EVALUATE /'a' < 1.
+      |                       ^
+
+'a' < 1 => error
+
+parse.sps:8.17-8.31: error: DEBUG EVALUATE: Both operands of < must have the
+same type.
+    8 | DEBUG EVALUATE /'a' < 'b' < 'c'.
+      |                 ^~~~~~~~~~~~~~~
+
+parse.sps:8.17-8.25: note: DEBUG EVALUATE: This operand has type 'number'.
+    8 | DEBUG EVALUATE /'a' < 'b' < 'c'.
+      |                 ^~~~~~~~~
+
+parse.sps:8.29-8.31: note: DEBUG EVALUATE: This operand has type 'string'.
+    8 | DEBUG EVALUATE /'a' < 'b' < 'c'.
+      |                             ^~~
+
+'a' < 'b' < 'c' => error
+])
+AT_CLEANUP
+
+AT_SETUP([unary operator type mismatch])
+AT_KEYWORDS([expression expressions parse negative])
+AT_DATA([parse.sps], [dnl
+DEBUG EVALUATE /-'a'.
+DEBUG EVALUATE /----'a'.
+DEBUG EVALUATE /NOT 'a'.
+DEBUG EVALUATE /NOT NOT NOT 'a'.
+DEBUG EVALUATE /NOT F5.2.
+])
+AT_CHECK([pspp --testing-mode parse.sps], [1], [dnl
+parse.sps:1.17-1.20: error: DEBUG EVALUATE: The unary - operator requires a
+numeric operand.
+    1 | DEBUG EVALUATE /-'a'.
+      |                 ^~~~
+
+parse.sps:1.18-1.20: note: DEBUG EVALUATE: The operand of - has type 'string'.
+    1 | DEBUG EVALUATE /-'a'.
+      |                  ^~~
+
+-'a' => error
+
+parse.sps:2.17-2.23: error: DEBUG EVALUATE: The unary - operator requires a
+numeric operand.
+    2 | DEBUG EVALUATE /----'a'.
+      |                 ^~~~~~~
+
+parse.sps:2.21-2.23: note: DEBUG EVALUATE: The operand of - has type 'string'.
+    2 | DEBUG EVALUATE /----'a'.
+      |                     ^~~
+
+----'a' => error
+
+parse.sps:3.17-3.23: error: DEBUG EVALUATE: The unary NOT operator requires a
+numeric operand.
+    3 | DEBUG EVALUATE /NOT 'a'.
+      |                 ^~~~~~~
+
+parse.sps:3.21-3.23: note: DEBUG EVALUATE: The operand of NOT has type
+'string'.
+    3 | DEBUG EVALUATE /NOT 'a'.
+      |                     ^~~
+
+NOT 'a' => error
+
+parse.sps:4.17-4.31: error: DEBUG EVALUATE: The unary NOT operator requires a
+numeric operand.
+    4 | DEBUG EVALUATE /NOT NOT NOT 'a'.
+      |                 ^~~~~~~~~~~~~~~
+
+parse.sps:4.29-4.31: note: DEBUG EVALUATE: The operand of NOT has type
+'string'.
+    4 | DEBUG EVALUATE /NOT NOT NOT 'a'.
+      |                             ^~~
+
+NOT NOT NOT 'a' => error
+
+parse.sps:5.17-5.24: error: DEBUG EVALUATE: The unary NOT operator requires a
+numeric operand.
+    5 | DEBUG EVALUATE /NOT F5.2.
+      |                 ^~~~~~~~
+
+parse.sps:5.21-5.24: note: DEBUG EVALUATE: The operand of NOT has type
+'format'.
+    5 | DEBUG EVALUATE /NOT F5.2.
+      |                     ^~~~
+
+NOT F5.2 => error
+])
+AT_CLEANUP
+
+AT_SETUP([parsing with negative numbers])
+AT_KEYWORDS([expression expressions parse])
+AT_DATA([parse.sps], [dnl
+DEBUG EVALUATE NOOPT POSTFIX /-2**3.
+DEBUG EVALUATE NOOPT POSTFIX /-2**-3**-4.
+DEBUG EVALUATE/1 - 2.
+])
+AT_CHECK([pspp --testing-mode parse.sps], [0], [dnl
+number: n<2> number: n<3> POW NEG return_number
+
+parse.sps:2.31-2.40: warning: DEBUG EVALUATE: The exponentiation operator
+(`**') is left-associative: `a**b**c' equals `(a**b)**c', not `a**(b**c)'.  To
+disable this warning, insert parentheses.
+    2 | DEBUG EVALUATE NOOPT POSTFIX /-2**-3**-4.
+      |                               ^~~~~~~~~~
+
+number: n<2> number: n<-3> POW number: n<-4> POW NEG return_number
+
+1 - 2 => -1.00
+])
+AT_CLEANUP
+
+AT_SETUP([system variables])
+AT_KEYWORDS([expression expressions parse])
+AT_DATA([parse.sps], [dnl
+DEBUG EVALUATE /$WIDTH.
+DEBUG EVALUATE /$LENGTH.
+DEBUG EVALUATE /$SYSMIS.
+])
+AT_CHECK([pspp --testing-mode parse.sps], [0], [dnl
+$WIDTH => 79.00
+
+$LENGTH => 24.00
+
+$SYSMIS => sysmis
+])
+AT_CLEANUP
+
+# This test will fail if the current date changes during the test.
+AT_SETUP([system variables - $DATE $DATE11])
+AT_KEYWORDS([expression expressions parse])
+# Get the date in the formats that $DATE and $DATE11 support.
+date=$(date +%d-%^b-%y)
+date11=$(date +%d-%^b-%Y)
+echo "date=$date"              # Should be date=DD-MMM-YY.
+echo "date11=$date11"  # Should be date11=DD-MMM-YYYY.
+
+# Maybe we don't have the 'date' program or it doesn't work as we
+# expect.  Check by trying to see if $date and $date11 are in the
+# expected format.  If not, skip the test.
+AS_CASE([$date],
+  [[[0-9][0-9]-[A-Z][A-Z][A-Z]-[0-9][0-9]]], [],
+  [AT_SKIP_IF([:])])
+AS_CASE([$date11],
+  [[[0-9][0-9]-[A-Z][A-Z][A-Z]-[0-9][0-9][0-9][0-9]]], [],
+  [AT_SKIP_IF([:])])
+
+AT_DATA([parse.sps], [dnl
+DEBUG EVALUATE /$DATE.
+DEBUG EVALUATE /$DATE11.
+])
+AT_CHECK_UNQUOTED([pspp --testing-mode parse.sps], [0], [dnl
+\$DATE => "$date"
+
+\$DATE11 => "$date11"
+])
+AT_CLEANUP
+
+AT_SETUP([expressions - negative checks])
+AT_KEYWORDS([expression expressions parse])
+AT_DATA([evaluate-base.sps], [dnl
+SET EPOCH 1940.
+DEBUG EVALUATE SET opt.
+DEBUG EVALUATE /$nonexistent.
+DEBUG EVALUATE /RANGE(1, 2).
+DEBUG EVALUATE /CONCAT.1('a', 'b').
+DEBUG EVALUATE /foobar(x).
+DEBUG EVALUATE /CONCAT.1('a' b).
+DEBUG EVALUATE /NCDF.CHISQ(1, 2, 3).
+DEBUG EVALUATE (a=1)(b=2) VECTOR/v('abc').
+])
+
+for opt in OPT NOOPT; do
+    AS_BOX([$opt])
+    sed "s/opt/$opt/" < evaluate-base.sps > evaluate.sps
+    AT_CHECK([pspp --testing-mode evaluate.sps], [1],
+[[evaluate.sps:3: error: DEBUG EVALUATE: Unknown system variable $nonexistent.
+
+$nonexistent => error
+
+evaluate.sps:4.17-4.27: error: DEBUG EVALUATE: RANGE(number, number, number[,
+number, number]...) must have an odd number of arguments.
+    4 | DEBUG EVALUATE /RANGE(1, 2).
+      |                 ^~~~~~~~~~~
+
+RANGE(1, 2) => error
+
+evaluate.sps:5.17-5.34: error: DEBUG EVALUATE: CONCAT(string[, string]...)
+function cannot accept suffix .1 to specify the minimum number of valid
+arguments.
+    5 | DEBUG EVALUATE /CONCAT.1('a', 'b').
+      |                 ^~~~~~~~~~~~~~~~~~
+
+CONCAT.1('a', 'b') => error
+
+evaluate.sps:6: error: DEBUG EVALUATE: No function or vector named foobar.
+
+foobar(x) => error
+
+evaluate.sps:7.30: error: DEBUG EVALUATE: Syntax error at `b': expecting `,' or
+`)'.
+
+CONCAT.1('a' b) => error
+
+evaluate.sps:8.17-8.35: error: DEBUG EVALUATE: NCDF.CHISQ(number, number,
+number) is not available in this version of PSPP.
+    8 | DEBUG EVALUATE /NCDF.CHISQ(1, 2, 3).
+      |                 ^~~~~~~~~~~~~~~~~~~
+
+NCDF.CHISQ(1, 2, 3) => error
+
+evaluate.sps:9.34-9.41: error: DEBUG EVALUATE: A vector index must be numeric.
+    9 | DEBUG EVALUATE (a=1)(b=2) VECTOR/v('abc').
+      |                                  ^~~~~~~~
+
+evaluate.sps:9.36-9.40: note: DEBUG EVALUATE: This vector index has type
+'string'.
+    9 | DEBUG EVALUATE (a=1)(b=2) VECTOR/v('abc').
+      |                                    ^~~~~
+
+v('abc') => error
+]])
+done
 AT_CLEANUP