macro's name. Use the @code{!POSITIONAL} keyword to declare a
positional argument.
-When a macro is called, every positional argument must be given a
-value in the same order as the defintion.
+When a macro is called, the positional argument values appear in the
+same order as their definitions, before any keyword argument values.
References to a positional argument in a macro body are numbered:
@code{!1} is the first positional argument, @code{!2} the second, and
@item
A @dfn{keyword} argument has a name. In the macro call, its value is
specified with the syntax @code{@i{name}=@i{value}}. The names allow
-keyword argument values to take any order in the call, and even to be
-omitted. When one is omitted, a default value is used: either the
-value specified in @code{!DEFAULT(@i{value})}, or an empty value
-otherwise.
+keyword argument values to take any order in the call.
In declaration and calls, a keyword argument's name may not begin with
@samp{!}, but references to it in the macro body do start with a
If a macro has both positional and keyword arguments, then the
positional arguments must come first in the DEFINE command, and their
-values also come first in macro calls.
+values also come first in macro calls. A keyword argument may be
+omitted by leaving its keyword out of the call, and a positional
+argument may be omitted by putting a command terminator where it would
+appear. (The latter case also omits any following positional
+arguments and all keyword arguments, if there are any.) When an
+argument is omitted, a default value is used: either the value
+specified in @code{!DEFAULT(@i{value})}, or an empty value otherwise.
Each argument declaration specifies the form of its value:
const struct msg_location *loc)
{
const struct macro_param *p = mc->param;
+ struct macro_tokens **argp = &mc->args[p - mc->macro->params];
const struct token *token = &mt->token;
- if ((token->type == T_ENDCMD || token->type == T_STOP)
- && p->arg_type != ARG_CMDEND)
+ if (token->type == T_ENDCMD || token->type == T_STOP)
{
- mc_error (mc, loc,
- _("Unexpected end of command reading argument %s "
- "to macro %s."), mc->param->name, mc->macro->name);
+ if (*argp)
+ {
+ switch (p->arg_type)
+ {
+ case ARG_CMDEND:
+ /* This is OK, it's the expected way to end the argument. */
+ break;
- mc->state = MC_ERROR;
- return -1;
+ case ARG_N_TOKENS:
+ mc_error (mc, loc,
+ ngettext (_("Reached end of command expecting %zu "
+ "more token in argument %s to macro %s."),
+ _("Reached end of command expecting %zu "
+ "more tokens in argument %s to macro %s."),
+ p->n_tokens - (*argp)->n),
+ p->n_tokens - (*argp)->n, p->name, mc->macro->name);
+ break;
+
+ case ARG_CHAREND:
+ case ARG_ENCLOSE:
+ {
+ char *end = token_to_string (&p->end);
+ mc_error (mc, loc, _("Reached end of command expecting \"%s\" "
+ "in argument %s to macro %s."),
+ end, p->name, mc->macro->name);
+ free (end);
+ }
+ break;
+ }
+ }
+
+ /* The end of a command ends the current argument, precludes any further
+ arguments, and is not itself part of the argument. */
+ return mc_finished (mc);
}
mc->n_tokens++;
- struct macro_tokens **argp = &mc->args[p - mc->macro->params];
if (!*argp)
*argp = xzalloc (sizeof **argp);
bool add_token; /* Should we add 'mt' to the current arg? */
bool next_arg; /* Should we advance to the next arg? */
- if (p->arg_type == ARG_N_TOKENS)
+ switch (p->arg_type)
{
+ case ARG_N_TOKENS:
next_arg = (*argp)->n + 1 >= p->n_tokens;
add_token = true;
- }
- else
- {
- next_arg = (p->arg_type == ARG_CMDEND
- ? token->type == T_ENDCMD || token->type == T_STOP
- : token_equal (token, &p->end));
+ break;
+
+ case ARG_CHAREND:
+ case ARG_ENCLOSE:
+ next_arg = token_equal (token, &p->end);
add_token = !next_arg;
+ break;
+
+ case ARG_CMDEND:
+ next_arg = false;
+ add_token = true;
+ break;
+
+ default:
+ NOT_REACHED ();
}
if (add_token)
mc_enclose (struct macro_call *mc, const struct macro_token *mt,
const struct msg_location *loc)
{
- const struct token *token = &mt->token;
mc->n_tokens++;
- if (token_equal (&mc->param->start, token))
+ const struct token *token = &mt->token;
+ const struct macro_param *p = mc->param;
+ if (token_equal (&p->start, token))
{
+ struct macro_tokens **argp = &mc->args[p - mc->macro->params];
+ *argp = xzalloc (sizeof **argp);
mc->state = MC_ARG;
return 0;
}
-
- return mc_expected (mc, mt, loc, &mc->param->start);
+ else if (p->positional && (token->type == T_ENDCMD || token->type == T_STOP))
+ return mc_finished (mc);
+ else
+ return mc_expected (mc, mt, loc, &p->start);
}
static const struct macro_param *
token->string);
if (p)
{
- size_t arg_index = p - mc->macro->params;
- mc->param = p;
- if (mc->args[arg_index])
+ struct macro_tokens **argp = &mc->args[p - mc->macro->params];
+ if (*argp)
{
mc_error (mc, loc,
_("Argument %s multiply specified in call to macro %s."),
return -1;
}
+ *argp = xzalloc (sizeof **argp);
+ mc->param = p;
mc->n_tokens++;
mc->state = MC_EQUALS;
return 0;
cmd(1 2 3 4)
-cmd2(5 6, 7)
+cmd2(5 6, )
+
+note: unexpanded token "7"
"Three !TOKENS(1) arguments."
])
AT_CLEANUP
-AT_SETUP([macro expansion with positional arguments - negative])
+AT_SETUP([macro call missing positional !TOKENS arguments])
+AT_KEYWORDS([TOKENS])
AT_DATA([define.sps], [dnl
-DEFINE !title(!positional !tokens(1)) !1 !ENDDEFINE.
-DEFINE !p(!positional !tokens(1)
- /!positional !tokens(1)
- /!positional !tokens(1))
+DEFINE !p(!positional !tokens(1) !default(x)
+ /!positional !tokens(1) !default(y)
+ /!positional !tokens(1) !default(z))
(!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 c.
!p a b.
!p a.
!p.
+])
+AT_CHECK([pspp --testing-mode define.sps], [0], [dnl
+(a, b, c)
-!title "Missing charend delimiter."
-!ce a b c.
+(a, b, z)
-!title "Missing start delimiter."
-!enc1 a b c.
+(a, y, z)
-!title "Missing end delimiter."
-!enc1{a b c.
+(x, y, z)
+])
+AT_CLEANUP
+
+AT_SETUP([macro call incomplete positional !TOKENS arguments])
+AT_KEYWORDS([TOKENS])
+AT_DATA([define.sps], [dnl
+DEFINE !p(!positional !tokens(2) !default(x)
+ /!positional !tokens(2) !default(y)
+ /!positional !tokens(2) !default(z))
+(!1, !2, !3)
+!ENDDEFINE.
+DEBUG EXPAND.
+!p a1 a2 b1 b2 c1 c2.
+!p a1 a2 b1 b2 c1.
+!p a1 a2 b1 b2.
+!p a1 a2 b1.
+!p a1 a2.
+!p a1.
+!p.
])
AT_CHECK([pspp --testing-mode define.sps], [1], [dnl
-"Too few tokens for !TOKENS."
+(a1 a2, b1 b2, c1 c2)
-define.sps:13.7: error: DEBUG EXPAND: Unexpected end of command reading
-argument !3 to macro !p.
+define.sps:8.18: error: DEBUG EXPAND: Reached end of command expecting 1 more
+token in argument !3 to macro !p.
-note: unexpanded token "!p"
+(a1 a2, b1 b2, c1)
-note: unexpanded token "a"
+(a1 a2, b1 b2, z)
-note: unexpanded token "b"
+define.sps:10.12: error: DEBUG EXPAND: Reached end of command expecting 1 more
+token in argument !2 to macro !p.
+
+(a1 a2, b1, z)
+
+(a1 a2, y, z)
+
+define.sps:12.6: error: DEBUG EXPAND: Reached end of command expecting 1 more
+token in argument !1 to macro !p.
+
+(a1, y, z)
+
+(x, y, z)
+])
+AT_CLEANUP
+
+AT_SETUP([macro call empty positional !CHAREND arguments])
+AT_KEYWORDS([CHAREND])
+AT_DATA([define.sps], [dnl
+DEFINE !p(!positional !charend(',') !default(x)
+ /!positional !charend(';') !default(y)
+ /!positional !charend(':') !default(z))
+(!1, !2, !3)
+!ENDDEFINE.
+DEBUG EXPAND.
+!p a,b;c:.
+!p a,b;:.
+!p a,;c:.
+!p a,;:.
+!p,b;c:.
+!p,b;:.
+!p,;c:.
+!p,;:.
+])
+AT_CHECK([pspp --testing-mode define.sps], [0], [dnl
+(a, b, c)
+
+(a, b, )
+
+(a, , c)
+
+(a, , )
+
+(, b, c)
+
+(, b, )
+
+(, , c)
+
+(, , )
+])
+AT_CLEANUP
+
+AT_SETUP([macro call missing positional !CHAREND arguments])
+AT_DATA([define.sps], [dnl
+DEFINE !p(!positional !charend(',') !default(x)
+ /!positional !charend(';') !default(y)
+ /!positional !charend(':') !default(z))
+(!1, !2, !3)
+!ENDDEFINE.
+DEBUG EXPAND.
+!p a,b;c:.
+!p a,b;.
+!p a,;.
+!p ,b;.
+!p ,;.
+
+!p a,.
+!p ,.
+
+!p.
+])
+AT_CHECK([pspp --testing-mode define.sps], [0], [dnl
+(a, b, c)
+
+(a, b, z)
+
+(a, , z)
+
+(, b, z)
+
+(, , z)
+
+(a, y, z)
+
+(, y, z)
+
+(x, y, z)
+])
+AT_CLEANUP
+
+AT_SETUP([macro call incomplete positional !CHAREND arguments])
+AT_KEYWORDS([CHAREND])
+AT_DATA([define.sps], [dnl
+DEFINE !p(!positional !charend(',') !default(x)
+ /!positional !charend(';') !default(y)
+ /!positional !charend(':') !default(z))
+(!1, !2, !3)
+!ENDDEFINE.
+DEBUG EXPAND.
+!p a,b;c:.
+!p a,b;c.
+!p a,b;.
+!p a,b.
+!p a,.
+!p a.
+!p.
+])
+AT_CHECK([pspp --testing-mode define.sps], [1], [dnl
+(a, b, c)
+
+define.sps:8.9: error: DEBUG EXPAND: Reached end of command expecting ":" in
+argument !3 to macro !p.
+
+(a, b, c)
-define.sps:14.5: error: DEBUG EXPAND: Unexpected end of command reading
+(a, b, z)
+
+define.sps:10.7: error: DEBUG EXPAND: Reached end of command expecting ";" in
argument !2 to macro !p.
-note: unexpanded token "!p"
+(a, b, z)
-note: unexpanded token "a"
+(a, y, z)
-define.sps:15.3: error: DEBUG EXPAND: Unexpected end of command reading
+define.sps:12.5: error: DEBUG EXPAND: Reached end of command expecting "," in
argument !1 to macro !p.
-note: unexpanded token "!p"
+(a, y, z)
-"Missing charend delimiter."
+(x, y, z)
+])
+AT_CLEANUP
-define.sps:18.10: error: DEBUG EXPAND: Unexpected end of command reading
-argument !1 to macro !ce.
+AT_SETUP([macro call missing positional !ENCLOSE arguments])
+AT_KEYWORDS([ENCLOSE])
+AT_DATA([define.sps], [dnl
+DEFINE !p(!positional !enclose('(',')') !default(x)
+ /!positional !enclose('<','>') !default(y)
+ /!positional !enclose('{','}') !default(z))
+(!1, !2, !3)
+!ENDDEFINE.
+DEBUG EXPAND.
+!p (a)<b>{c}.
+!p (a)<b>.
+!p (a).
+!p.
+])
+AT_CHECK([pspp --testing-mode define.sps], [0], [dnl
+(a, b, c)
-note: unexpanded token "!ce"
+(a, b, z)
-note: unexpanded token "a"
+(a, y, z)
-note: unexpanded token "b"
+(x, y, z)
+])
+AT_CLEANUP
-note: unexpanded token "c"
+AT_SETUP([macro call incomplete positional !ENCLOSE arguments])
+AT_KEYWORDS([ENCLOSE])
+AT_DATA([define.sps], [dnl
+DEFINE !p(!positional !enclose('(',')') !default(x)
+ /!positional !enclose('<','>') !default(y)
+ /!positional !enclose('{','}') !default(z))
+(!1, !2, !3)
+!ENDDEFINE.
+DEBUG EXPAND.
+!p (a)<b>{c}.
+!p (a)<b>{c.
+!p (a)<b>{.
+!p (a)<b>.
+!p (a)<b.
+!p (a)<.
+!p (a).
+!p (a.
+!p (.
+!p.
+])
+AT_CHECK([pspp --testing-mode define.sps], [1], [dnl
+(a, b, c)
-"Missing start delimiter."
+define.sps:8.12: error: DEBUG EXPAND: Reached end of command expecting "}" in
+argument !3 to macro !p.
-define.sps:21.7: error: DEBUG EXPAND: Found `a' while expecting `{' reading
-argument !1 to macro !enc1.
+(a, b, c)
-note: unexpanded token "!enc1"
+define.sps:9.11: error: DEBUG EXPAND: Reached end of command expecting "}" in
+argument !3 to macro !p.
-note: unexpanded token "a"
+(a, b, )
-note: unexpanded token "b"
+(a, b, z)
-note: unexpanded token "c"
+define.sps:11.9: error: DEBUG EXPAND: Reached end of command expecting ">" in
+argument !2 to macro !p.
-"Missing end delimiter."
+(a, b, z)
-define.sps:24.12: error: DEBUG EXPAND: Unexpected end of command reading
-argument !1 to macro !enc1.
+define.sps:12.8: error: DEBUG EXPAND: Reached end of command expecting ">" in
+argument !2 to macro !p.
-note: unexpanded token "!enc1"
+(a, , z)
-note: unexpanded token "{"
+(a, y, z)
-note: unexpanded token "a"
+define.sps:14.6: error: DEBUG EXPAND: Reached end of command expecting ")" in
+argument !1 to macro !p.
-note: unexpanded token "b"
+(a, y, z)
-note: unexpanded token "c"
+define.sps:15.5: error: DEBUG EXPAND: Reached end of command expecting ")" in
+argument !1 to macro !p.
+
+(, y, z)
+
+(x, y, z)
])
AT_CLEANUP
note: unexpanded token "arg1"
-define.sps:4.9: error: DEBUG EXPAND: Unexpected end of command reading argument
-!arg1 to macro !k.
+define.sps:4.9: error: DEBUG EXPAND: Reached end of command expecting 1 more
+token in argument !arg1 to macro !k.
-note: unexpanded token "!k"
-
-note: unexpanded token "arg1"
-
-note: unexpanded token "="
+k( )
])
AT_CLEANUP
note: unexpanded token "arg1"
-define.sps:7.9: 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.10: error: DEBUG EXPAND: Unexpected end of command reading
+define.sps:7.9: error: DEBUG EXPAND: Reached end of command expecting "/" in
argument !arg1 to macro !k.
-note: unexpanded token "!k"
-
-note: unexpanded token "arg1"
+k(, )
-note: unexpanded token "="
+define.sps:8.10: error: DEBUG EXPAND: Reached end of command expecting "/" in
+argument !arg1 to macro !k.
-note: unexpanded token "x"
+k(x, )
-define.sps:9.18: error: DEBUG EXPAND: Unexpected end of command reading
+define.sps:9.18: error: DEBUG EXPAND: Reached end of command expecting "/" in
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"
+k(x, y)
])
AT_CLEANUP
note: unexpanded token "x"
-define.sps:9.11: error: DEBUG EXPAND: Unexpected end of command reading
+define.sps:9.11: error: DEBUG EXPAND: Reached end of command expecting "@:}@" in
argument !arg1 to macro !k.
-note: unexpanded token "!k"
-
-note: unexpanded token "arg1"
-
-note: unexpanded token "="
-
-note: unexpanded token "@{:@"
-
-note: unexpanded token "x"
+k(x, )
define.sps:10.17: error: DEBUG EXPAND: Found `.' while expecting `=' reading
argument !arg2 to macro !k.
AT_CHECK([pspp --testing-mode define.sps], [1], [dnl
In the expansion of `!DO',
define.sps:1-3: inside the expansion of `!for',
-define.sps:7.1-7.11: error: DEBUG EXPAND: !DO loop over list exceeded maximum
+define.sps:7.1-7.10: error: DEBUG EXPAND: !DO loop over list exceeded maximum
number of iterations 2. (Use SET MITERATE to change the limit.)
( (a) (b) ).
In the expansion of `!DO',
define.sps:1-3: inside the expansion of `!for',
-define.sps:8.1-8.24: error: DEBUG EXPAND: !DO loop over list exceeded maximum
+define.sps:8.1-8.23: error: DEBUG EXPAND: !DO loop over list exceeded maximum
number of iterations 2. (Use SET MITERATE to change the limit.)
( (foo) (bar) ).