1 /* PSPP - a program for statistical analysis.
2 Copyright (C) 2021 Free Software Foundation, Inc.
4 This program is free software: you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation, either version 3 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program. If not, see <http://www.gnu.org/licenses/>. */
21 #include "language/command.h"
22 #include "language/lexer/lexer.h"
23 #include "language/lexer/macro.h"
24 #include "language/lexer/scan.h"
25 #include "language/lexer/token.h"
26 #include "libpspp/message.h"
28 #include "gl/xalloc.h"
31 #define _(msgid) gettext (msgid)
34 force_macro_id (struct lexer
*lexer
)
36 return lex_token (lexer
) == T_MACRO_ID
|| lex_force_id (lexer
);
40 match_macro_id (struct lexer
*lexer
, const char *keyword
)
42 if (keyword
[0] != '!')
43 return lex_match_id (lexer
, keyword
);
44 else if (lex_token (lexer
) == T_MACRO_ID
45 && lex_id_match_n (ss_cstr (keyword
), lex_tokss (lexer
), 4))
54 /* Obtains a quoted string from LEXER and then tokenizes the quoted string's
55 content to obtain a single TOKEN. Returns true if successful, false
56 otherwise. The caller takes ownership of TOKEN on success, otherwise TOKEN
59 parse_quoted_token (struct lexer
*lexer
, struct token
*token
)
61 if (!lex_force_string (lexer
))
64 struct substring s
= lex_tokss (lexer
);
65 struct string_lexer slex
;
66 string_lexer_init (&slex
, s
.string
, s
.length
, SEG_MODE_INTERACTIVE
, true);
67 struct token another_token
= { .type
= T_STOP
};
68 if (string_lexer_next (&slex
, token
) != SLR_TOKEN
69 || string_lexer_next (&slex
, &another_token
) != SLR_END
)
72 token_uninit (&another_token
);
73 lex_error (lexer
, _("String must contain exactly one token."));
81 cmd_define (struct lexer
*lexer
, struct dataset
*ds UNUSED
)
83 if (!force_macro_id (lexer
))
86 /* Parse macro name. */
87 struct macro
*m
= xmalloc (sizeof *m
);
89 .name
= ss_xstrdup (lex_tokss (lexer
)),
90 .location
= xmalloc (sizeof *m
->location
),
92 *m
->location
= (struct msg_location
) {
93 .file_name
= xstrdup_if_nonnull (lex_get_file_name (lexer
)),
94 .first_line
= lex_get_first_line_number (lexer
, 0),
98 if (!lex_force_match (lexer
, T_LPAREN
))
101 size_t allocated_params
= 0;
102 while (!lex_match (lexer
, T_RPAREN
))
104 if (m
->n_params
>= allocated_params
)
105 m
->params
= x2nrealloc (m
->params
, &allocated_params
,
108 size_t param_index
= m
->n_params
++;
109 struct macro_param
*p
= &m
->params
[param_index
];
110 *p
= (struct macro_param
) { .expand_arg
= true };
112 /* Parse parameter name. */
113 if (match_macro_id (lexer
, "!POSITIONAL"))
115 if (param_index
> 0 && !m
->params
[param_index
- 1].positional
)
117 lex_error (lexer
, _("Positional parameters must precede "
118 "keyword parameters."));
122 p
->positional
= true;
123 p
->name
= xasprintf ("!%zu", param_index
+ 1);
127 if (lex_token (lexer
) == T_MACRO_ID
)
129 lex_error (lexer
, _("Keyword macro parameter must be named in "
130 "definition without \"!\" prefix."));
133 if (!lex_force_id (lexer
))
136 if (is_macro_keyword (lex_tokss (lexer
)))
138 lex_error (lexer
, _("Cannot use macro keyword \"%s\" "
139 "as an argument name."),
140 lex_tokcstr (lexer
));
144 p
->positional
= false;
145 p
->name
= xasprintf ("!%s", lex_tokcstr (lexer
));
148 if (!lex_force_match (lexer
, T_EQUALS
))
152 /* Parse default value. */
153 if (match_macro_id (lexer
, "!DEFAULT"))
155 if (!lex_force_match (lexer
, T_LPAREN
))
158 /* XXX Should this handle balanced inner parentheses? */
159 while (!lex_match (lexer
, T_RPAREN
))
161 if (lex_token (lexer
) == T_ENDCMD
)
163 lex_error_expecting (lexer
, ")");
166 char *syntax
= lex_next_representation (lexer
, 0, 0);
167 const struct macro_token mt
= {
168 .token
= *lex_next (lexer
, 0),
169 .syntax
= ss_cstr (syntax
),
171 macro_tokens_add (&p
->def
, &mt
);
178 if (match_macro_id (lexer
, "!NOEXPAND"))
179 p
->expand_arg
= false;
181 if (match_macro_id (lexer
, "!TOKENS"))
183 if (!lex_force_match (lexer
, T_LPAREN
)
184 || !lex_force_int_range (lexer
, "!TOKENS", 1, INT_MAX
))
186 p
->arg_type
= ARG_N_TOKENS
;
187 p
->n_tokens
= lex_integer (lexer
);
189 if (!lex_force_match (lexer
, T_RPAREN
))
192 else if (match_macro_id (lexer
, "!CHAREND"))
194 p
->arg_type
= ARG_CHAREND
;
195 p
->charend
= (struct token
) { .type
= T_STOP
};
197 if (!lex_force_match (lexer
, T_LPAREN
)
198 || !parse_quoted_token (lexer
, &p
->charend
)
199 || !lex_force_match (lexer
, T_RPAREN
))
202 else if (match_macro_id (lexer
, "!ENCLOSE"))
204 p
->arg_type
= ARG_ENCLOSE
;
205 p
->enclose
[0] = p
->enclose
[1] = (struct token
) { .type
= T_STOP
};
207 if (!lex_force_match (lexer
, T_LPAREN
)
208 || !parse_quoted_token (lexer
, &p
->enclose
[0])
209 || !lex_force_match (lexer
, T_COMMA
)
210 || !parse_quoted_token (lexer
, &p
->enclose
[1])
211 || !lex_force_match (lexer
, T_RPAREN
))
214 else if (match_macro_id (lexer
, "!CMDEND"))
215 p
->arg_type
= ARG_CMDEND
;
218 lex_error_expecting (lexer
, "!TOKENS", "!CHAREND",
219 "!ENCLOSE", "!CMDEND");
223 if (lex_token (lexer
) != T_RPAREN
&& !lex_force_match (lexer
, T_SLASH
))
227 struct string body
= DS_EMPTY_INITIALIZER
;
228 while (!match_macro_id (lexer
, "!ENDDEFINE"))
230 if (lex_token (lexer
) != T_STRING
)
232 lex_error (lexer
, _("Expecting macro body or !ENDDEFINE"));
237 ds_put_substring (&body
, lex_tokss (lexer
));
238 ds_put_byte (&body
, '\n');
241 m
->location
->last_line
= lex_get_last_line_number (lexer
, 0);
243 macro_tokens_from_string (&m
->body
, body
.ss
, lex_get_syntax_mode (lexer
));
246 lex_define_macro (lexer
, m
);
256 cmd_debug_expand (struct lexer
*lexer
, struct dataset
*ds UNUSED
)
258 settings_set_mprint (true);
260 while (lex_token (lexer
) != T_STOP
)
262 if (!lex_next_is_from_macro (lexer
, 0) && lex_token (lexer
) != T_ENDCMD
)
264 char *rep
= lex_next_representation (lexer
, 0, 0);
265 msg (MN
, "unexpanded token \"%s\"", rep
);