expressions: Fix built sources.
[pspp] / tests / language / expressions / evaluate.at
index d16bac5df7a6f2c0ae07cebabb5435d73ad2b718..cf0fdd0783442f0c082fc0450a68ba6a26861602 100644 (file)
@@ -1,17 +1,35 @@
+dnl PSPP - a program for statistical analysis.
+dnl Copyright (C) 2017 Free Software Foundation, Inc.
+dnl
+dnl This program is free software: you can redistribute it and/or modify
+dnl it under the terms of the GNU General Public License as published by
+dnl the Free Software Foundation, either version 3 of the License, or
+dnl (at your option) any later version.
+dnl
+dnl This program is distributed in the hope that it will be useful,
+dnl but WITHOUT ANY WARRANTY; without even the implied warranty of
+dnl MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+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], [2])
-   AT_CHECK([pspp --testing-mode --error-file=- --no-output 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])],
-     [m4_foreach([check], [m4_shift($@)],
+     [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
@@ -20,390 +38,1565 @@ 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])
 
-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 in expression 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 in expression 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 in expression at `GT'.]],
-dnl # ~= token can't be split:
-  [[1 ~ = 1], [error],
-   [error: DEBUG EVALUATE: Syntax error expecting end of command at `NOT'.]])
-
-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('x')], [error],
-   [error: DEBUG EVALUATE: Type mismatch invoking RND(number) as rnd(string).]],
-
-  [[trunc(1.2)], [1.00]],
-  [[trunc(1.9)], [1.00]],
-  [[trunc(-1.2)], [-1.00]],
-  [[trunc(-1.9)], [-1.00]],
-  [[trunc('x')], [error],
-   [error: DEBUG EVALUATE: Type mismatch invoking TRUNC(number) as trunc(string).]])
-
-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).]])
+AT_SETUP([expressions - numeric syntax])
+AT_KEYWORDS([expression expressions evaluate])
+AT_DATA([evaluate-base.sps], [
+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], [
+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], [
+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], [
+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], [
+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], [
+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], [
+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], [
+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], [
+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], [
+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], [
+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], [
+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').
+      |                 ^~~~~~~~
+
+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').
+      |                 ^~~~~~~~~
+
+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).
+      |                 ^~~~~~~~~~~
+
+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').
+      |                 ^~~~~~~~~~~
+
+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').
+      |                 ^~~~~~~~~~~~~
+
+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], [
+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.
+      |                 ^~~~~~~~~
+
+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.
+      |                 ^~~~~~~~~
+
+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.
+      |                 ^~~~~~~~~
+
+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').
+      |                 ^~~~~~~~
+
+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').
+      |                 ^~~~~~~~
+
+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').
+      |                 ^~~~~~~~
+
+TAN('x') => error
+])
+done
+AT_CLEANUP
+
+AT_SETUP([expressions - MISSING NMISS NVALID SYSMIS ANY RANGE MAX MIN])
+AT_KEYWORDS([expression expressions evaluate])
+AT_DATA([evaluate-base.sps], [
+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=SYSMIS)/x.
+
+DEBUG EVALUATE (x=5) VECTOR/v(1).
+DEBUG EVALUATE (x=5 MISSING) VECTOR/v(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).
+])
+
+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
+])
+done
+AT_CLEANUP
+
 # 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],
@@ -482,7 +1675,7 @@ ANY(string, string[, string]...).]],
   [[any('a', 'a  ', 'b', 'c')], [true]],
   [[any('b   ', 'a', 'b', 'c')], [true]],
   [[any('c   ', 'a', 'b', 'c     ')], [true]],
-  [[any(a, 'b', 'c', 'd')], [error],
+  [[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]...).]],
@@ -519,11 +1712,11 @@ ANY(string, string[, string]...).]],
 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 even number of arguments in list.]],
+   [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 even number of arguments in list.]],
+   [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 even number of arguments in list.]],
+   [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]...)
@@ -562,11 +1755,11 @@ RANGE(string, string, string[, string, string]...).]],
 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 even number of arguments in list.]],
+   [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 even number of arguments in list.]],
+   [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 even number of arguments in list.]],
+   [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]...)
@@ -592,7 +1785,7 @@ MAX(string[, string]...).]],
   [[max(1, 2, 3, $sysmis)], [3.00]],
   [[max.4(1, 2, 3, $sysmis)], [sysmis]],
   [[max.4(1, 2, 3)], [error],
-   [error: DEBUG EVALUATE: With MAX(number[, number]...), using minimum valid argument count of 4 does not make sense when passing only 3 arguments in list.]],
+   [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"]],
@@ -610,13 +1803,13 @@ MIN(string[, string]...).]],
   [[min(1, 2, 3, $sysmis)], [1.00]],
   [[min.4(1, 2, 3, $sysmis)], [sysmis]],
   [[min.4(1, 2, 3)], [error],
-   [error: DEBUG EVALUATE: With MIN(number[, number]...), using minimum valid argument count of 4 does not make sense when passing only 3 arguments in list.]],
+   [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 sd sum variance],
+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]],
@@ -626,7 +1819,7 @@ CHECK_EXPR_EVAL([cfvar mean sd sum variance],
   [[cfvar(1, 2, 3, $sysmis)], [0.50]],
   [[cfvar.4(1, 2, 3, $sysmis)], [sysmis]],
   [[cfvar.4(1, 2, 3)], [error],
-   [error: DEBUG EVALUATE: With CFVAR(number, number[, number]...), using minimum valid argument count of 4 does not make sense when passing only 3 arguments in list.]],
+   [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],
@@ -642,8 +1835,27 @@ CHECK_EXPR_EVAL([cfvar mean sd sum variance],
   [[mean(1, 2, 3, $sysmis)], [2.00]],
   [[mean.4(1, 2, 3, $sysmis)], [sysmis]],
   [[mean.4(1, 2, 3)], [error],
-   [error: DEBUG EVALUATE: With MEAN(number[, number]...), using minimum valid argument count of 4 does not make sense when passing only 3 arguments in list.]],
-
+   [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]],
@@ -654,7 +1866,7 @@ CHECK_EXPR_EVAL([cfvar mean sd sum variance],
   [[sd(1, 2, 3, $sysmis)], [1.00]],
   [[sd.4(1, 2, 3, $sysmis)], [sysmis]],
   [[sd.4(1, 2, 3)], [error],
-   [error: DEBUG EVALUATE: With SD(number, number[, number]...), using minimum valid argument count of 4 does not make sense when passing only 3 arguments in list.]],
+   [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],
@@ -670,7 +1882,7 @@ CHECK_EXPR_EVAL([cfvar mean sd sum variance],
   [[sum(1, 2, 3, $sysmis)], [6.00]],
   [[sum.4(1, 2, 3, $sysmis)], [sysmis]],
   [[sum.4(1, 2, 3)], [error],
-   [error: DEBUG EVALUATE: With SUM(number[, number]...), using minimum valid argument count of 4 does not make sense when passing only 3 arguments in list.]],
+   [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]],
@@ -681,7 +1893,7 @@ CHECK_EXPR_EVAL([cfvar mean sd sum variance],
   [[variance(1, 2, 3, $sysmis)], [1.00]],
   [[variance.4(1, 2, 3, $sysmis)], [sysmis]],
   [[variance.4(1, 2, 3)], [error],
-   [error: DEBUG EVALUATE: With VARIANCE(number, number[, number]...), using minimum valid argument count of 4 does not make sense when passing only 3 arguments in list.]],
+   [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],
@@ -753,6 +1965,7 @@ CHECK_EXPR_EVAL([concat index rindex length lower],
   [[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]],
@@ -792,7 +2005,22 @@ RINDEX(string, string, number).]],
   [[lower(1)], [error],
    [error: DEBUG EVALUATE: Type mismatch invoking LOWER(string) as lower(number).]])
 
-CHECK_EXPR_EVAL([lpad number ltrim lpad rtrim rpad string substr upcase],
+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"]],
@@ -948,6 +2176,29 @@ dnl E has a minimum width of 6 on output:
    [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"]],
@@ -1281,7 +2532,7 @@ CHECK_EXPR_EVAL([xdate],
   [[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,41) + time.hms(4,25,9)) / 86400], [131107.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]],
@@ -1305,7 +2556,7 @@ CHECK_EXPR_EVAL([xdate],
   [[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,41) + time.hms(4,25,9))], [4.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]],
@@ -1331,7 +2582,7 @@ CHECK_EXPR_EVAL([xdate],
   [[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,41) + time.hms(4,25,9))], [272.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]],
@@ -1355,7 +2606,7 @@ CHECK_EXPR_EVAL([xdate],
   [[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,41) + time.hms(4,25,9))], [29.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]],
@@ -1376,7 +2627,7 @@ CHECK_EXPR_EVAL([xdate],
   [[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,41) + time.hms(4,25,9))], [25.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]],
@@ -1397,7 +2648,7 @@ CHECK_EXPR_EVAL([xdate],
   [[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,41) + time.hms(4,25,9))], [9.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]],
@@ -1418,7 +2669,7 @@ CHECK_EXPR_EVAL([xdate],
   [[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,41) + time.hms(4,25,9))], [3.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]],
@@ -1439,7 +2690,7 @@ CHECK_EXPR_EVAL([xdate],
   [[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,41) + time.hms(4,25,9))], [9.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]],
@@ -1460,7 +2711,7 @@ CHECK_EXPR_EVAL([xdate],
   [[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,41) + time.hms(4,25,9))], [131107.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]],
@@ -1481,7 +2732,7 @@ CHECK_EXPR_EVAL([xdate],
   [[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,41) + time.hms(4,25,9))], [15909.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]],
@@ -1502,7 +2753,7 @@ CHECK_EXPR_EVAL([xdate],
   [[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,41) + time.hms(4,25,9))], [39.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]],
@@ -1523,7 +2774,7 @@ CHECK_EXPR_EVAL([xdate],
   [[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,41))], [2.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]],
@@ -1544,7 +2795,7 @@ CHECK_EXPR_EVAL([xdate],
   [[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,41) + time.hms(4,25,9))], [1941.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]],
@@ -1565,8 +2816,8 @@ CHECK_EXPR_EVAL([datediff],
   [[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,41), date.mdy(2,25,1996), 'years')], [-54.00]],
-  [[datediff(date.mdy(9,29,41), date.mdy(4,19,43), 'years')], [-1.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]],
@@ -1588,8 +2839,8 @@ CHECK_EXPR_EVAL([datediff],
   [[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,41), date.mdy(2,25,1996), 'quarters')], [-217.00]],
-  [[datediff(date.mdy(9,29,41), date.mdy(4,19,43), 'quarters')], [-6.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]],
@@ -1611,8 +2862,8 @@ CHECK_EXPR_EVAL([datediff],
   [[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,41), date.mdy(2,25,1996), 'months')], [-652.00]],
-  [[datediff(date.mdy(9,29,41), date.mdy(4,19,43), 'months')], [-18.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]],
@@ -1634,8 +2885,8 @@ CHECK_EXPR_EVAL([datediff],
   [[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,41), date.mdy(2,25,1996), 'weeks')], [-2838.00]],
-  [[datediff(date.mdy(9,29,41), date.mdy(4,19,43), 'weeks')], [-81.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]],
@@ -1657,8 +2908,8 @@ CHECK_EXPR_EVAL([datediff],
   [[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,41), date.mdy(2,25,1996), 'days')], [-19872.00]],
-  [[datediff(date.mdy(9,29,41), date.mdy(4,19,43), 'days')], [-567.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]],
@@ -1680,8 +2931,8 @@ CHECK_EXPR_EVAL([datediff],
   [[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,41), 'years')], [54.00]],
-  [[datediff(date.mdy(4,19,43), date.mdy(9,29,41), 'years')], [1.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]],
@@ -1703,8 +2954,8 @@ CHECK_EXPR_EVAL([datediff],
   [[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,41), 'months')], [652.00]],
-  [[datediff(date.mdy(4,19,43), date.mdy(9,29,41), 'months')], [18.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]],
@@ -1726,8 +2977,8 @@ CHECK_EXPR_EVAL([datediff],
   [[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,41), 'quarters')], [217.00]],
-  [[datediff(date.mdy(4,19,43), date.mdy(9,29,41), 'quarters')], [6.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]],
@@ -1735,7 +2986,42 @@ CHECK_EXPR_EVAL([datediff],
   [[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,28,1903), date.mdy(2,29,1900), 'quarters')], [11.00]],
+
+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
@@ -1808,7 +3094,13 @@ dnl DATESUM with leap year
   [[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]])
+  [[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],
 dnl These test values are from Applied Statistics, Algorithm AS 310.
@@ -1843,3 +3135,231 @@ dnl Tests correctness of generic optimizations in optimize_tree().
   [[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_DATA([lag.sps], [dnl
+data list /W 1.
+begin data.
+1
+2
+3
+4
+5
+end data.
+
+compute X=lag(w,1).
+compute Y=lag(x).
+compute Z=lag(w,2).
+list.
+])
+AT_CHECK([pspp -o pspp.csv lag.sps])
+AT_CHECK([cat pspp.csv], [0], [dnl
+Table: Reading 1 record from INLINE.
+Variable,Record,Columns,Format
+W,1,1-1,F1.0
+
+Table: Data List
+W,X,Y,Z
+1,.  ,.  ,.  @&t@
+2,1.00,.  ,.  @&t@
+3,2.00,1.00,1.00
+4,3.00,2.00,2.00
+5,4.00,3.00,3.00
+])
+AT_CLEANUP
+
+AT_SETUP([LAG crash bug])
+AT_DATA([lag.sps], [dnl
+DATA LIST LIST /x.
+BEGIN DATA
+1
+2
+END DATA.
+
+DO IF (x <> LAG(x) ).
+       ECHO 'hello'.
+END IF.
+
+EXECUTE.
+])
+AT_CHECK([pspp -o pspp.csv lag.sps])
+AT_CHECK([cat pspp.csv], [0], [dnl
+Table: Reading free-form data from INLINE.
+Variable,Format
+x,F8.0
+
+hello
+])
+AT_CLEANUP
+
+dnl Tests for a bug which caused UNIFORM(x) to always return zero.
+AT_SETUP([UNIFORM function])
+AT_DATA([uniform.sps], [dnl
+set seed=10.
+input program.
++ loop #i = 1 to 20.
++    do repeat response=R1.
++       compute response = uniform(10).
++    end repeat.
++    end case.
++ end loop.
++ end file.
+end input program.
+
+list.
+])
+AT_CHECK([pspp -o pspp.csv uniform.sps])
+AT_CHECK([cat pspp.csv], [0], [dnl
+Table: Data List
+R1
+7.71
+2.99
+.21
+4.95
+6.34
+4.43
+7.49
+8.32
+4.99
+5.83
+2.25
+.25
+1.98
+7.09
+7.61
+2.66
+1.69
+2.64
+.88
+1.50
+])
+AT_CLEANUP
+
+AT_SETUP([VALUELABEL function])
+AT_DATA([valuelabel.sps], [dnl
+DATA LIST notable /n 1 s 2(a).
+VALUE LABELS /n 0 'Very dissatisfied'
+                1 'Dissatisfied'
+               1.5 'Slightly Peeved'
+                2 'Neutral'
+                3 'Satisfied'
+                4 'Very satisfied'.
+VALUE LABELS /s 'a' 'Wouldn''t buy again'
+                'b' 'Unhappy'
+                'c' 'Bored'
+                'd' 'Satiated'
+                'e' 'Elated'.
+STRING nlabel slabel(a10).
+COMPUTE nlabel = VALUELABEL(n).
+COMPUTE slabel = VALUELABEL(s).
+LIST.
+BEGIN DATA.
+
+0a
+1b
+2c
+3d
+4e
+5f
+6g
+END DATA.
+])
+AT_CHECK([pspp -o pspp.csv valuelabel.sps])
+AT_CHECK([cat pspp.csv], [0], [dnl
+Table: Data List
+n,s,nlabel,slabel
+.,,,
+0,a,Very dissa,Wouldn't b
+1,b,Dissatisfi,Unhappy
+2,c,Neutral,Bored
+3,d,Satisfied,Satiated
+4,e,Very satis,Elated
+5,f,,
+6,g,,
+])
+AT_CLEANUP
+
+AT_SETUP([variables in expressions])
+AT_DATA([variables.sps], [dnl
+DATA LIST NOTABLE/N1 TO N5 1-5.
+MISSING VALUES N1 TO N5 (3 THRU 5, 1).
+BEGIN DATA.
+12345
+6789
+END DATA.
+
+COMPUTE P1=N1.
+COMPUTE P2=N2.
+COMPUTE P3=N3.
+COMPUTE P4=N4.
+COMPUTE P5=N5.
+
+COMPUTE MC=NMISS(N1 TO N5).
+COMPUTE VC=NVALID(N1 TO N5).
+
+COMPUTE S1=SYSMIS(N1).
+COMPUTE S2=SYSMIS(N2).
+COMPUTE S3=SYSMIS(N3).
+COMPUTE S4=SYSMIS(N4).
+COMPUTE S5=SYSMIS(N5).
+
+COMPUTE M1=MISSING(N1).
+COMPUTE M2=MISSING(N2).
+COMPUTE M3=MISSING(N3).
+COMPUTE M4=MISSING(N4).
+COMPUTE M5=MISSING(N5).
+
+COMPUTE V1=VALUE(N1).
+COMPUTE V2=VALUE(N2).
+COMPUTE V3=VALUE(N3).
+COMPUTE V4=VALUE(N4).
+COMPUTE V5=VALUE(N5).
+
+FORMATS ALL (F1).
+
+LIST.
+])
+AT_CHECK([pspp -o pspp.csv variables.sps])
+AT_CHECK([cat pspp.csv], [0], [dnl
+Table: Data List
+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
+1,2,3,4,5,.,2,.,.,.,4,1,0,0,0,0,0,1,0,1,1,1,1,2,3,4,5
+6,7,8,9,.,6,7,8,9,.,1,4,0,0,0,0,1,0,0,0,0,1,6,7,8,9,.
+])
+AT_CLEANUP
+
+AT_SETUP([vectors in expressions])
+AT_DATA([vectors.sps], [dnl
+DATA LIST NOTABLE /N1 TO N5 1-5.
+MISSING VALUES N1 TO N5 (3 THRU 5, 1).
+BEGIN DATA.
+12345
+6789
+END DATA.
+
+VECTOR N=N1 TO N5.
+VECTOR X(5).
+LOOP I=1 TO 5.
+COMPUTE X(I)=N(I) + 1.
+END LOOP.
+
+FORMATS ALL (F2).
+
+LIST.
+])
+AT_CHECK([pspp -o pspp.csv vectors.sps])
+AT_CHECK([cat pspp.csv], [0], [dnl
+Table: Data List
+N1,N2,N3,N4,N5,X1,X2,X3,X4,X5,I
+1,2,3,4,5,.,3,.,.,.,5
+6,7,8,9,.,7,8,9,10,.,5
+])
+AT_CLEANUP