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 nGeneral 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 .
dnl
AT_BANNER([DEFINE])
m4_define([PSPP_CHECK_MACRO_EXPANSION],
[AT_SETUP([macro expansion - $1])
AT_KEYWORDS([m4_bpatsubst([$1], [!], [])])
AT_DATA([define.sps], [$2
DEBUG EXPAND.
$3
])
AT_CAPTURE_FILE([define.sps])
AT_DATA([expout], [$4
])
AT_CHECK([pspp --testing-mode define.sps | sed '/^$/d'], [$6], [expout])
AT_CLEANUP])
AT_SETUP([simple macro expansion])
AT_DATA([define.sps], [dnl
DEFINE !macro()
a b c d
e f g h.
i j k l
1,2,3,4.
5+6+7.
m(n,o).
"a" "b" "c" 'a' 'b' 'c'.
"x "" y".
!ENDDEFINE.
DEBUG EXPAND.
!macro
])
AT_CHECK([pspp --testing-mode define.sps], [0], [dnl
a b c d e f g h.
i j k l 1, 2, 3, 4.
5 + 6 + 7.
m(n, o).
"a" "b" "c" 'a' 'b' 'c'.
"x "" y".
])
AT_CLEANUP
PSPP_CHECK_MACRO_EXPANSION([one !TOKENS(1) positional argument],
[DEFINE !t1(!positional !tokens(1)) t1 (!1) !ENDDEFINE.],
[!t1 a.
!t1 b.
!t1 a b.],
[t1(a)
t1(b)
t1(a)
note: unexpanded token "b"])
AT_SETUP([macro expansion with positional arguments])
AT_DATA([define.sps], [dnl
DEFINE !title(!positional !tokens(1)) !1 !ENDDEFINE.
DEFINE !t1(!positional !tokens(1)) t1 (!1) !ENDDEFINE.
DEFINE !t2(!positional !tokens(2)) t2 (!1) !ENDDEFINE.
DEFINE !ce(!positional !charend('/')) ce (!1) !ENDDEFINE.
DEFINE !ce2(!positional !charend('(')
/!positional !charend(')'))
ce2 (!1, !2)
!ENDDEFINE.
DEFINE !e(!positional !enclose('{','}')) e (!1) !ENDDEFINE.
DEFINE !cmd(!positional !cmdend) cmd(!1) !ENDDEFINE.
DEFINE !cmd2(!positional !cmdend
/!positional !tokens(1))
cmd2(!1, !2)
!ENDDEFINE.
DEFINE !p(!positional !tokens(1)
/!positional !tokens(1)
/!positional !tokens(1))
p(!1, !2, !3)(!*)
!ENDDEFINE.
DEBUG EXPAND.
!title "!TOKENS(1) argument."
!t1 a.
!t1 b.
!t1 a b.
!title "!TOKENS(2) argument."
!t2 a b.
!t2 b c d.
!title "!CHAREND argument."
!ce/.
!ce x/.
!ce x y/.
!ce x y z/.
!title "Two !CHAREND arguments."
!ce2 x(y).
!ce2 1 2 3 4().
!title "!ENCLOSE argument."
!e {}.
!e {a}.
!e {a b}.
!title "!CMDEND argument."
!cmd 1 2 3 4.
!cmd2 5 6.
7.
!title "Three !TOKENS(1) arguments."
!p a b c.
!p 1 -2 -3.
])
AT_CHECK([pspp --testing-mode define.sps], [0], [dnl
"!TOKENS(1) argument."
t1(a)
t1(b)
t1(a)
note: unexpanded token "b"
"!TOKENS(2) argument."
t2(a b)
t2(b c)
note: unexpanded token "d"
"!CHAREND argument."
ce( )
ce(x)
ce(x y)
ce(x y z)
"Two !CHAREND arguments."
ce2(x, y)
ce2(1 2 3 4, )
"!ENCLOSE argument."
e( )
e(a)
e(a b)
"!CMDEND argument."
cmd(1 2 3 4)
cmd2(5 6, 7)
"Three !TOKENS(1) arguments."
p(a, b, c) (a b c)
p(1, -2, -3) (1 -2 -3)
])
AT_CLEANUP
AT_SETUP([macro expansion with positional arguments - negative])
AT_DATA([define.sps], [dnl
DEFINE !title(!positional !tokens(1)) !1 !ENDDEFINE.
DEFINE !p(!positional !tokens(1)
/!positional !tokens(1)
/!positional !tokens(1))
(!1, !2, !3)
!ENDDEFINE.
DEFINE !ce(!positional !charend('/')) ce(!1) !ENDDEFINE.
DEFINE !enc1(!positional !enclose('{', '}')) enc1(!1) !ENDDEFINE.
DEBUG EXPAND.
!title "Too few tokens for !TOKENS."
!p a b.
!p a.
!p.
!title "Missing charend delimiter."
!ce a b c.
!title "Missing start delimiter."
!enc1 a b c.
!title "Missing end delimiter."
!enc1{a b c.
])
AT_CHECK([pspp --testing-mode define.sps], [1], [dnl
"Too few tokens for !TOKENS."
define.sps:13: error: DEBUG EXPAND: Unexpected end of command reading
argument !3 to macro !p.
note: unexpanded token "!p"
note: unexpanded token "a"
note: unexpanded token "b"
define.sps:14: error: DEBUG EXPAND: Unexpected end of command reading
argument !2 to macro !p.
note: unexpanded token "!p"
note: unexpanded token "a"
define.sps:15: error: DEBUG EXPAND: Unexpected end of command reading
argument !1 to macro !p.
note: unexpanded token "!p"
"Missing charend delimiter."
define.sps:18: error: DEBUG EXPAND: Unexpected end of command reading
argument !1 to macro !ce.
note: unexpanded token "!ce"
note: unexpanded token "a"
note: unexpanded token "b"
note: unexpanded token "c"
"Missing start delimiter."
define.sps:21: error: DEBUG EXPAND: Found `a' while expecting `{' reading
argument !1 to macro !enc1.
note: unexpanded token "!enc1"
note: unexpanded token "a"
note: unexpanded token "b"
note: unexpanded token "c"
"Missing end delimiter."
define.sps:24: error: DEBUG EXPAND: Unexpected end of command reading
argument !1 to macro !enc1.
note: unexpanded token "!enc1"
note: unexpanded token "{"
note: unexpanded token "a"
note: unexpanded token "b"
note: unexpanded token "c"
])
AT_CLEANUP
AT_SETUP([keyword macro argument name with ! prefix])
AT_DATA([define.sps], [dnl
DEFINE !macro(!x=!TOKENS(1).
])
AT_CHECK([pspp -O format=csv define.sps], [1], [dnl
"define.sps:1.15-1.16: error: DEFINE: Syntax error at `!x': Keyword macro parameter must be named in definition without ""!"" prefix."
])
AT_CLEANUP
AT_SETUP([reserved macro keyword argument name])
AT_DATA([define.sps], [dnl
DEFINE !macro(if=!TOKENS(1).
])
AT_CHECK([pspp -O format=csv define.sps], [1], [dnl
"define.sps:1.15-1.16: error: DEFINE: Syntax error at `if': Cannot use macro keyword ""if"" as an argument name."
])
AT_CLEANUP
PSPP_CHECK_MACRO_EXPANSION([one !TOKENS(1) keyword argument],
[DEFINE !k(arg1 = !TOKENS(1)) k(!arg1) !ENDDEFINE.],
[!k arg1=x.
!k arg1=x y.
!k.],
[k(x)
k(x)
note: unexpanded token "y"
k( )])
PSPP_CHECK_MACRO_EXPANSION([one !TOKENS(1) keyword argument - negative],
[DEFINE !k(arg1 = !TOKENS(1)) k(!arg1) !ENDDEFINE.],
[!k arg1.
!k arg1=.], [dnl
define.sps:3: error: DEBUG EXPAND: Found `.' while expecting `=' reading
argument !arg1 to macro !k.
note: unexpanded token "!k"
note: unexpanded token "arg1"
define.sps:4: error: DEBUG EXPAND: Unexpected end of command reading argument !
arg1 to macro !k.
note: unexpanded token "!k"
note: unexpanded token "arg1"
note: unexpanded token "="], [1])
PSPP_CHECK_MACRO_EXPANSION([!CHAREND('/') keyword arguments], [dnl
DEFINE !k(arg1 = !CHAREND('/')
/arg2 = !CHAREND('/'))
k(!arg1, !arg2)
!ENDDEFINE.],
[!k arg1=x/ arg2=y/.
!k arg1=x/.
!k arg2=y/.
!k.],
[k(x, y)
k(x, )
k(, y)
k(, )])
PSPP_CHECK_MACRO_EXPANSION([!CHAREND('/') keyword arguments - negative], [dnl
DEFINE !k(arg1 = !CHAREND('/')
/arg2 = !CHAREND('/'))
k(!arg1, !arg2)
!ENDDEFINE.],
[!k arg1.
!k arg1=.
!k arg1=x.
!k arg1=x/ arg2=y.],
[define.sps:6: error: DEBUG EXPAND: Found `.' while expecting `=' reading
argument !arg1 to macro !k.
note: unexpanded token "!k"
note: unexpanded token "arg1"
define.sps:7: error: DEBUG EXPAND: Unexpected end of command reading argument !
arg1 to macro !k.
note: unexpanded token "!k"
note: unexpanded token "arg1"
note: unexpanded token "="
define.sps:8: error: DEBUG EXPAND: Unexpected end of command reading argument !
arg1 to macro !k.
note: unexpanded token "!k"
note: unexpanded token "arg1"
note: unexpanded token "="
note: unexpanded token "x"
define.sps:9: error: DEBUG EXPAND: Unexpected end of command reading argument !
arg2 to macro !k.
note: unexpanded token "!k"
note: unexpanded token "arg1"
note: unexpanded token "="
note: unexpanded token "x"
note: unexpanded token "/"
note: unexpanded token "arg2"
note: unexpanded token "="
note: unexpanded token "y"])
PSPP_CHECK_MACRO_EXPANSION([default keyword arguments],
[DEFINE !k(arg1 = !DEFAULT(a b c) !CMDEND) k(!arg1) !ENDDEFINE],
[!k arg1=x.
!k],
[k(x)
k(a b c)])
dnl Keep this test in sync with the examples for !BLANKS in the manual.
PSPP_CHECK_MACRO_EXPANSION([!BLANKS],
[DEFINE !b()
!BLANKS(0).
!QUOTE(!BLANKS(0)).
!BLANKS(1).
!QUOTE(!BLANKS(1)).
!BLANKS(2).
!QUOTE(!BLANKS(2)).
!BLANKS(5).
!QUOTE(!BLANKS(5)).
!ENDDEFINE],
[!b.],
[.
''.
.
' '.
.
' '.
.
' '.])
dnl Keep this test in sync with the examples for !CONCAT in the manual.
PSPP_CHECK_MACRO_EXPANSION([!CONCAT],
[DEFINE !c()
!CONCAT(x, y).
!CONCAT('x', 'y').
!CONCAT(12, 34).
!CONCAT(!NULL, 123).
!CONCAT(x, 0).
!CONCAT(x, 0, y).
!CONCAT(0, x).
!CONCAT(0, x, y).
!ENDDEFINE],
[!c.],
[xy.
xy.
1234.
123.
x0.
x0y.
0 x.
0 xy.])
dnl Keep this test in sync with the examples for !EVAL in the manual.
PSPP_CHECK_MACRO_EXPANSION([!EVAL],
[DEFINE !vars() a b c !ENDDEFINE.
DEFINE !e()
!vars.
!QUOTE(!vars).
!EVAL(!vars).
!QUOTE(!EVAL(!vars)).
!ENDDEFINE
DEFINE !e2(!positional !enclose('(',')'))
!1.
!QUOTE(!1).
!EVAL(!1).
!QUOTE(!EVAL(!1)).
!ENDDEFINE],
[!e.
!e2(!vars)],
[a b c.
'!vars'.
a b c.
'a b c'.
a b c.
'!vars'.
a b c.
'a b c'.])
dnl Keep this test in sync with the examples for !HEAD in the manual.
PSPP_CHECK_MACRO_EXPANSION([!HEAD],
[DEFINE !h()
!HEAD('a b c').
!HEAD('a').
!HEAD(!NULL).
!HEAD('').
!ENDDEFINE],
[!h.],
[a.
a.
.
.])
dnl Keep this test in sync with the examples for !TAIL in the manual.
PSPP_CHECK_MACRO_EXPANSION([!TAIL],
[DEFINE !t()
!TAIL('a b c').
!TAIL('a').
!TAIL(!NULL).
!TAIL('').
!ENDDEFINE],
[!t.],
[b c.
.
.
.])
dnl Keep this test in sync with the examples for !INDEX in the manual.
PSPP_CHECK_MACRO_EXPANSION([!INDEX],
[DEFINE !i()
!INDEX(banana, an).
!INDEX(banana, nan).
!INDEX(banana, apple).
!INDEX("banana", nan).
!INDEX("banana", "nan").
!INDEX(!UNQUOTE("banana"), !UNQUOTE("nan")).
!ENDDEFINE],
[!i.],
[2.
3.
0.
4.
0.
3.])
dnl Keep this test in sync with the examples for !LENGTH in the manual.
PSPP_CHECK_MACRO_EXPANSION([!LENGTH],
[DEFINE !l()
!LENGTH(123).
!LENGTH(123.00).
!LENGTH( 123 ).
!LENGTH("123").
!LENGTH(xyzzy).
!LENGTH("xyzzy").
!LENGTH("xy""zzy").
!LENGTH(!UNQUOTE("xyzzy")).
!LENGTH(!UNQUOTE("xy""zzy")).
!LENGTH(!NULL).
!ENDDEFINE.
DEFINE !la(!positional !enclose('(',')'))
!LENGTH(!1).
!ENDDEFINE.],
[!l.
!la(a b c).
!la().],
[3.
6.
3.
5.
5.
7.
9.
5.
6.
0.
5.
0.])
dnl Keep this test in sync with the examples for !SUBSTR in the manual.
PSPP_CHECK_MACRO_EXPANSION([!SUBSTR],
[DEFINE !s()
!SUBSTR(banana, 3).
!SUBSTR(banana, 3, 3).
!SUBSTR("banana", 1, 3).
!SUBSTR(!UNQUOTE("banana"), 3).
!SUBSTR("banana", 3, 3).
!SUBSTR(banana, 3, 0).
!SUBSTR(banana, 3, 10).
!SUBSTR(banana, 10, 3).
!ENDDEFINE.],
[!s.],
[define.sps:1-10: At `"ba' in the expansion of `!s',dnl "
define.sps:12: error: DEBUG EXPAND: Unterminated string constant.
nana.
nan.
.
nana.
ana.
.
nana.
.])
dnl Keep this test in sync with the examples for !UPCASE in the manual.
PSPP_CHECK_MACRO_EXPANSION([!UPCASE],
[DEFINE !u()
!UPCASE(freckle).
!UPCASE('freckle').
!UPCASE('a b c').
!UPCASE('A B C').
!ENDDEFINE.],
[!u.],
[FRECKLE.
FRECKLE.
A B C.
A B C.])
dnl !* is implemented separately inside and outside function arguments
dnl so this test makes sure to include both.
PSPP_CHECK_MACRO_EXPANSION([!*], [dnl
DEFINE !m(!POSITIONAL !TOKENS(1)
/!POSITIONAL !TOKENS(1))
!*/
!LENGTH(!*)/
!SUBSTR(!*, 3)/
!QUOTE(!*).
!ENDDEFINE.],
[!m 123 b
!m 2 3
!m '' 'b'.
], [123 b / 5 / 3 b / '123 b'.
2 3 / 3 / 3 / '2 3'.
'' 'b' / 6 / 'b' / ''''' ''b'''.])
AT_SETUP([macro maximum nesting level (MNEST)])
AT_KEYWORDS([MNEST])
AT_DATA([define.sps], [dnl
DEFINE !macro()
!macro
!ENDDEFINE.
!macro.
])
AT_CHECK([pspp -O format=csv define.sps], [1], [dnl
"define.sps:1-3: In the expansion of `!macro',
define.sps:1-3: inside the expansion of `!macro',
define.sps:1-3: inside the expansion of `!macro',
define.sps:1-3: inside the expansion of `!macro',
define.sps:1-3: inside the expansion of `!macro',
define.sps:1-3: inside the expansion of `!macro',
define.sps:1-3: inside the expansion of `!macro',
define.sps:1-3: inside the expansion of `!macro',
define.sps:1-3: inside the expansion of `!macro',
define.sps:1-3: inside the expansion of `!macro',
define.sps:1-3: inside the expansion of `!macro',
define.sps:1-3: inside the expansion of `!macro',
define.sps:1-3: inside the expansion of `!macro',
define.sps:1-3: inside the expansion of `!macro',
define.sps:1-3: inside the expansion of `!macro',
define.sps:1-3: inside the expansion of `!macro',
define.sps:1-3: inside the expansion of `!macro',
define.sps:1-3: inside the expansion of `!macro',
define.sps:1-3: inside the expansion of `!macro',
define.sps:1-3: inside the expansion of `!macro',
define.sps:1-3: inside the expansion of `!macro',
define.sps:1-3: inside the expansion of `!macro',
define.sps:1-3: inside the expansion of `!macro',
define.sps:1-3: inside the expansion of `!macro',
define.sps:1-3: inside the expansion of `!macro',
define.sps:1-3: inside the expansion of `!macro',
define.sps:1-3: inside the expansion of `!macro',
define.sps:1-3: inside the expansion of `!macro',
define.sps:1-3: inside the expansion of `!macro',
define.sps:1-3: inside the expansion of `!macro',
define.sps:1-3: inside the expansion of `!macro',
define.sps:1-3: inside the expansion of `!macro',
define.sps:1-3: inside the expansion of `!macro',
define.sps:1-3: inside the expansion of `!macro',
define.sps:1-3: inside the expansion of `!macro',
define.sps:1-3: inside the expansion of `!macro',
define.sps:1-3: inside the expansion of `!macro',
define.sps:1-3: inside the expansion of `!macro',
define.sps:1-3: inside the expansion of `!macro',
define.sps:1-3: inside the expansion of `!macro',
define.sps:1-3: inside the expansion of `!macro',
define.sps:1-3: inside the expansion of `!macro',
define.sps:1-3: inside the expansion of `!macro',
define.sps:1-3: inside the expansion of `!macro',
define.sps:1-3: inside the expansion of `!macro',
define.sps:1-3: inside the expansion of `!macro',
define.sps:1-3: inside the expansion of `!macro',
define.sps:1-3: inside the expansion of `!macro',
define.sps:1-3: inside the expansion of `!macro',
define.sps:1-3: inside the expansion of `!macro',
define.sps:1-3: inside the expansion of `!macro',
define.sps:4: error: DEFINE: Maximum nesting level 50 exceeded. (Use SET MNEST to change the limit.)"
define.sps:4.1-4.6: error: Syntax error at `!macro' (in expansion of `!macro'): expecting command name.
])
AT_CLEANUP
AT_SETUP([macro !IF condition])
AT_KEYWORDS([if])
for operators in \
'!eq !ne !lt !gt !le !ge' \
' = <> < > <= >='
do
set $operators
AS_BOX([$operators])
cat > define.sps < < > <= >='
do
set $operators
AS_BOX([$operators])
cat > define.sps < define.sps <title.sps <expout <