#include "gettext.h"
#define _(msgid) gettext (msgid)
-static bool
-force_macro_id (struct lexer *lexer)
-{
- return lex_token (lexer) == T_MACRO_ID || lex_force_id (lexer);
-}
-
static bool
match_macro_id (struct lexer *lexer, const char *keyword)
{
int
cmd_define (struct lexer *lexer, struct dataset *ds UNUSED)
{
- if (!force_macro_id (lexer))
- return CMD_FAILURE;
+ /* Parse macro name.
+
+ The macro name is a T_STRING token, even though it's an identifier,
+ because that's the way that the segmenter prevents it from getting
+ macro-expanded. */
+ if (lex_token (lexer) != T_STRING)
+ {
+ lex_error (lexer, _("expecting identifier"));
+ return CMD_FAILURE;
+ }
+ const char *name = lex_tokcstr (lexer);
+ if (!id_is_plausible (name + (name[0] == '!'), false))
+ {
+ lex_error (lexer, _("expecting identifier"));
+ return CMD_FAILURE;
+ }
- /* Parse macro name. */
struct macro *m = xmalloc (sizeof *m);
*m = (struct macro) {
- .name = ss_xstrdup (lex_tokss (lexer)),
+ .name = xstrdup (name),
.location = xmalloc (sizeof *m->location),
};
*m->location = (struct msg_location) {
case SEG_INLINE_DATA:
case SEG_DOCUMENT:
case SEG_MACRO_BODY:
+ case SEG_MACRO_NAME:
*token = (struct token) { .type = T_STRING };
ss_alloc_substring (&token->string, s);
return TOKENIZE_TOKEN;
S_DEFINE_2,
S_DEFINE_3,
S_DEFINE_4,
+ S_DEFINE_5,
S_BEGIN_DATA_1,
S_BEGIN_DATA_2,
S_BEGIN_DATA_3,
case SEG_DO_REPEAT_COMMAND:
case SEG_INLINE_DATA:
case SEG_MACRO_ID:
+ case SEG_MACRO_NAME:
case SEG_MACRO_BODY:
case SEG_START_DOCUMENT:
case SEG_DOCUMENT:
- The DEFINE keyword.
+ - An identifier. We transform this into SEG_MACRO_NAME instead of
+ SEG_IDENTIFIER or SEG_MACRO_NAME because this identifier must never be
+ macro-expanded.
+
- Anything but "(".
- "(" followed by a sequence of tokens possibly including balanced parentheses
line, even.
*/
static int
-segmenter_parse_define_1__ (struct segmenter *s,
- const char *input, size_t n, bool eof,
- enum segment_type *type)
+segmenter_parse_define_1_2__ (struct segmenter *s,
+ const char *input, size_t n, bool eof,
+ enum segment_type *type)
{
int ofs = segmenter_subparse (s, input, n, eof, type);
if (ofs < 0)
return -1;
- if (*type == SEG_SEPARATE_COMMANDS
+ if (s->state == S_DEFINE_1
+ && (*type == SEG_IDENTIFIER || *type == SEG_MACRO_ID))
+ {
+ *type = SEG_MACRO_NAME;
+ s->state = S_DEFINE_2;
+ }
+ else if (*type == SEG_SEPARATE_COMMANDS
|| *type == SEG_END_COMMAND
|| *type == SEG_START_COMMAND)
{
}
else if (*type == SEG_PUNCT && input[0] == '(')
{
- s->state = S_DEFINE_2;
+ s->state = S_DEFINE_3;
s->nest = 1;
return ofs;
}
}
static int
-segmenter_parse_define_2__ (struct segmenter *s,
+segmenter_parse_define_3__ (struct segmenter *s,
const char *input, size_t n, bool eof,
enum segment_type *type)
{
s->nest--;
if (!s->nest)
{
- s->state = S_DEFINE_3;
+ s->state = S_DEFINE_4;
s->substate = 0;
}
return ofs;
/* We are in the body of a macro definition, looking for additional lines of
the body or !ENDDEFINE. */
static int
-segmenter_parse_define_3__ (struct segmenter *s,
+segmenter_parse_define_4__ (struct segmenter *s,
const char *input, size_t n, bool eof,
enum segment_type *type)
{
report it as spaces because it's not significant. */
*type = (s->substate == 0 && is_all_spaces (input, ofs)
? SEG_SPACES : SEG_MACRO_BODY);
- s->state = S_DEFINE_4;
+ s->state = S_DEFINE_5;
s->substate = 1;
return ofs;
}
}
static int
-segmenter_parse_define_4__ (struct segmenter *s,
+segmenter_parse_define_5__ (struct segmenter *s,
const char *input, size_t n, bool eof,
enum segment_type *type)
{
if (ofs < 0)
return -1;
- s->state = S_DEFINE_3;
+ s->state = S_DEFINE_4;
return ofs;
}
return segmenter_parse_do_repeat_3__ (s, input, n, eof, type);
case S_DEFINE_1:
- return segmenter_parse_define_1__ (s, input, n, eof, type);
case S_DEFINE_2:
- return segmenter_parse_define_2__ (s, input, n, eof, type);
+ return segmenter_parse_define_1_2__ (s, input, n, eof, type);
case S_DEFINE_3:
return segmenter_parse_define_3__ (s, input, n, eof, type);
case S_DEFINE_4:
return segmenter_parse_define_4__ (s, input, n, eof, type);
+ case S_DEFINE_5:
+ return segmenter_parse_define_5__ (s, input, n, eof, type);
case S_BEGIN_DATA_1:
return segmenter_parse_begin_data_1__ (s, input, n, eof, type);
case S_DEFINE_1:
case S_DEFINE_2:
- return s->substate & SS_START_OF_COMMAND ? PROMPT_FIRST : PROMPT_LATER;
case S_DEFINE_3:
+ return s->substate & SS_START_OF_COMMAND ? PROMPT_FIRST : PROMPT_LATER;
case S_DEFINE_4:
+ case S_DEFINE_5:
return PROMPT_DEFINE;
case S_BEGIN_DATA_1:
SEG_TYPE(INLINE_DATA) \
\
SEG_TYPE(MACRO_ID) \
+ SEG_TYPE(MACRO_NAME) \
SEG_TYPE(MACRO_BODY) \
\
SEG_TYPE(START_DOCUMENT) \
])
AT_CLEANUP
+AT_SETUP([redefining a macro])
+AT_DATA([define.sps], [dnl
+DEFINE !macro() 0 !ENDDEFINE.
+DEFINE !macro() 1 !ENDDEFINE.
+DEBUG EXPAND.
+!macro.
+])
+AT_CHECK([pspp --testing-mode define.sps], [0], [dnl
+1
+])
+AT_CLEANUP
+
AT_SETUP([macro expansion - one !TOKENS(1) positional argument])
AT_KEYWORDS([TOKENS])
AT_DATA([define.sps], [dnl
])
AT_DATA([expout-base], [dnl
ID "define"
-MACRO_ID "!macro1"
+STRING "!macro1"
LPAREN
RPAREN
STRING "var1 var2 var3"
])
AT_DATA([expout-base], [dnl
ID "define"
-MACRO_ID "!macro1"
+STRING "!macro1"
LPAREN
RPAREN
STRING " var1 var2 var3"
])
AT_DATA([expout-base], [dnl
ID "define"
-MACRO_ID "!macro1"
+STRING "!macro1"
LPAREN
RPAREN
STRING "var1 var2 var3"
])
AT_DATA([expout-base], [dnl
ID "define"
-MACRO_ID "!macro1"
+STRING "!macro1"
LPAREN
RPAREN
STRING "var1 var2 var3"
])
AT_DATA([expout-base], [dnl
ID "define"
-MACRO_ID "!macro1"
+STRING "!macro1"
LPAREN
RPAREN
MACRO_ID "!enddefine"
])
AT_DATA([expout-base], [dnl
ID "define"
-MACRO_ID "!macro1"
+STRING "!macro1"
LPAREN
RPAREN
STRING ""
])
AT_DATA([expout-base], [dnl
ID "define"
-MACRO_ID "!macro1"
+STRING "!macro1"
LPAREN
ID "a"
LPAREN
])
AT_DATA([expout-base], [dnl
ID "define"
-MACRO_ID "!macro1"
+STRING "!macro1"
LPAREN
ID "a"
LPAREN
])
AT_DATA([expout-base], [dnl
ID "define"
-MACRO_ID "!macro1"
+STRING "!macro1"
LPAREN
ID "x"
COMMA
])
AT_DATA([expout-base], [dnl
ID "define"
-MACRO_ID "!macro1"
+STRING "!macro1"
ENDCMD
ID "data"
ID "list"
])
AT_DATA([expout-base], [dnl
ID "define"
-MACRO_ID "!macro1"
+STRING "!macro1"
ID "x"
ENDCMD
ID "data"
])
AT_DATA([expout-base], [dnl
ID "define"
-MACRO_ID "!macro1"
+STRING "!macro1"
LPAREN
ENDCMD
ID "x"
])
AT_DATA([expout-base], [dnl
ID "define"
-MACRO_ID "!macro1"
+STRING "!macro1"
ENDCMD
ID "data"
ID "list"
])
AT_DATA([expout-base], [dnl
ID "define"
-MACRO_ID "!macro1"
+STRING "!macro1"
LPAREN
RPAREN
STRING "content line 1"
])
AT_DATA([expout-base], [dnl
identifier define space
-macro_id !macro1
+macro_name !macro1
punct (
punct )
spaces
])
AT_DATA([expout-base], [dnl
identifier define space
-macro_id !macro1
+macro_name !macro1
punct (
punct )
macro_body _var1_var2_var3_/*_!enddefine
])
AT_DATA([expout-base], [dnl
identifier define space
-macro_id !macro1
+macro_name !macro1
punct (
punct )
spaces
])
AT_DATA([expout-base], [dnl
identifier define space
-macro_id !macro1
+macro_name !macro1
punct (
punct )
macro_body var1_var2_var3
])
AT_DATA([expout-base], [dnl
identifier define space
-macro_id !macro1
+macro_name !macro1
punct (
punct )
spaces
])
AT_DATA([expout-base], [dnl
identifier define space
-macro_id !macro1
+macro_name !macro1
punct (
punct )
spaces
])
AT_DATA([expout-base], [dnl
identifier define space
-macro_id !macro1
+macro_name !macro1
punct (
identifier a
punct (
])
AT_DATA([expout-base], [dnl
identifier define space
-macro_id !macro1
+macro_name !macro1
punct (
newline \n (later)
])
AT_DATA([expout-base], [dnl
identifier define space
-macro_id !macro1
+macro_name !macro1
newline \n (later)
punct (
])
AT_DATA([expout-base], [dnl
identifier define space
-macro_id !macro1
+macro_name !macro1
end_command .
newline \n (first)
])
AT_DATA([expout-base], [dnl
identifier define space
-macro_id !macro1
+macro_name !macro1
newline \n (later)
identifier x
])
AT_DATA([expout-base], [dnl
identifier define space
-macro_id !macro1
+macro_name !macro1
punct (
end_command .
newline \n (first)
])
AT_DATA([expout-base], [dnl
identifier define space
-macro_id !macro1
+macro_name !macro1
end_command .
newline \n (first)
])
AT_DATA([expout-base], [dnl
identifier define space
-macro_id !macro1
+macro_name !macro1
punct (
punct )
spaces