#include "libpspp/str.h"
#include "libpspp/string-array.h"
#include "libpspp/string-map.h"
+#include "libpspp/stringi-set.h"
#include "gl/c-ctype.h"
#include "gl/ftoastr.h"
ds_put_substring (s, mt->representation);
}
+bool
+is_macro_keyword (struct substring s)
+{
+ static struct stringi_set keywords = STRINGI_SET_INITIALIZER (keywords);
+ if (stringi_set_is_empty (&keywords))
+ {
+ static const char *kws[] = {
+ "BREAK",
+ "CHAREND",
+ "CMDEND",
+ "DEFAULT",
+ "DO",
+ "DOEND",
+ "ELSE",
+ "ENCLOSE",
+ "ENDDEFINE",
+ "IF",
+ "IFEND",
+ "IN",
+ "LET",
+ "NOEXPAND",
+ "OFFEXPAND",
+ "ONEXPAND",
+ "POSITIONAL",
+ "THEN",
+ "TOKENS",
+ };
+ for (size_t i = 0; i < sizeof kws / sizeof *kws; i++)
+ stringi_set_insert (&keywords, kws[i]);
+ }
+
+ ss_ltrim (&s, ss_cstr ("!"));
+ return stringi_set_contains_len (&keywords, s.string, s.length);
+}
+
void
macro_tokens_copy (struct macro_tokens *dst, const struct macro_tokens *src)
{
}
void
-macro_tokens_to_representation (struct macro_tokens *mts, struct string *s)
+macro_tokens_to_representation (struct macro_tokens *mts, struct string *s,
+ size_t *ofs, size_t *len)
{
+ assert ((ofs != NULL) == (len != NULL));
+
if (!mts->n)
return;
- macro_token_to_representation (&mts->mts[0], s);
- for (size_t i = 1; i < mts->n; i++)
+ for (size_t i = 0; i < mts->n; i++)
{
- enum token_type prev = mts->mts[i - 1].token.type;
- enum token_type next = mts->mts[i].token.type;
-
- if (prev == T_ENDCMD)
- ds_put_byte (s, '\n');
- else
+ if (i > 0)
{
- enum token_class pc = classify_token (prev);
- enum token_class nc = classify_token (next);
- if (needs_space (pc, nc))
- ds_put_byte (s, ' ');
+ enum token_type prev = mts->mts[i - 1].token.type;
+ enum token_type next = mts->mts[i].token.type;
+
+ if (prev == T_ENDCMD)
+ ds_put_byte (s, '\n');
+ else
+ {
+ enum token_class pc = classify_token (prev);
+ enum token_class nc = classify_token (next);
+ if (needs_space (pc, nc))
+ ds_put_byte (s, ' ');
+ }
}
+ if (ofs)
+ ofs[i] = s->ss.length;
macro_token_to_representation (&mts->mts[i], s);
+ if (len)
+ len[i] = s->ss.length - ofs[i];
}
}
static const struct macro_param *
macro_find_parameter_by_name (const struct macro *m, struct substring name)
{
- if (ss_first (name) == '!')
- ss_advance (&name, 1);
+ ss_ltrim (&name, ss_cstr ("!"));
for (size_t i = 0; i < m->n_params; i++)
{
macro_expand (const struct macro_tokens *,
int nesting_countdown, const struct macro_set *,
const struct macro_expander *, struct string_map *vars,
- bool *expand, struct macro_tokens *exp);
+ bool *expand, bool *break_, struct macro_tokens *exp);
static bool
expand_macro_function (struct parse_macro_function_ctx *ctx,
unquote_string (const char *s, struct string *content)
{
struct string_lexer slex;
- string_lexer_init (&slex, s, strlen (s), SEG_MODE_INTERACTIVE /* XXX */);
+ string_lexer_init (&slex, s, strlen (s), SEG_MODE_INTERACTIVE /* XXX */,
+ true);
struct token token1;
if (!string_lexer_next (&slex, &token1))
if (mts.n > 1)
{
struct macro_tokens tail = { .mts = mts.mts + 1, .n = mts.n - 1 };
- macro_tokens_to_representation (&tail, output);
+ macro_tokens_to_representation (&tail, output, NULL, NULL);
}
macro_tokens_uninit (&mts);
ds_destroy (&tmp);
SEG_MODE_INTERACTIVE /* XXX */);
struct macro_tokens exp = { .n = 0 };
macro_expand (&mts, ctx->nesting_countdown - 1, ctx->macros, ctx->me,
- ctx->vars, ctx->expand, &exp);
- macro_tokens_to_representation (&exp, output);
+ ctx->vars, ctx->expand, NULL, &exp);
+ macro_tokens_to_representation (&exp, output, NULL, NULL);
macro_tokens_uninit (&exp);
macro_tokens_uninit (&mts);
}
macro_expand_if (const struct macro_token *tokens, size_t n_tokens,
int nesting_countdown, const struct macro_set *macros,
const struct macro_expander *me, struct string_map *vars,
- bool *expand, struct macro_tokens *exp)
+ bool *expand, bool *break_, struct macro_tokens *exp)
{
const struct macro_token *p = tokens;
const struct macro_token *end = tokens + n_tokens;
.mts = CONST_CAST (struct macro_token *, start),
.n = n,
};
- macro_expand (&mts, nesting_countdown, macros, me, vars, expand, exp);
+ macro_expand (&mts, nesting_countdown, macros, me, vars, expand,
+ break_, exp);
}
return (end_if + 1) - tokens;
}
return 0;
}
const struct substring var_name = p->token.string;
+ if (is_macro_keyword (var_name)
+ || macro_find_parameter_by_name (me->macro, var_name))
+ {
+ printf ("cannot use argument name or macro keyword as !LET variable\n");
+ return 0;
+ }
p++;
if (p >= end || p->token.type != T_EQUALS)
return 0;
}
const struct substring var_name = p->token.string;
+ if (is_macro_keyword (var_name)
+ || macro_find_parameter_by_name (me->macro, var_name))
+ {
+ printf ("cannot use argument name or macro keyword as !DO variable\n");
+ return 0;
+ }
p++;
+ int miterate = settings_get_miterate ();
if (p < end && p->token.type == T_MACRO_ID
&& ss_equals_case (p->token.string, ss_cstr ("!IN")))
{
};
for (size_t i = 0; i < items.n; i++)
{
+ if (i >= miterate)
+ {
+ printf ("exceeded maximum number of iterations %d\n", miterate);
+ break;
+ }
string_map_replace_nocopy (vars, ss_xstrdup (var_name),
ss_xstrdup (items.mts[i].representation));
+
+ bool break_ = false;
macro_expand (&inner, nesting_countdown, macros,
- me, vars, expand, exp);
+ me, vars, expand, &break_, exp);
+ if (break_)
+ break;
}
return do_end - tokens + 1;
}
};
if ((by > 0 && first <= last) || (by < 0 && first >= last))
- for (double index = first;
- by > 0 ? (index <= last) : (index >= last);
- index += by)
- {
- char index_s[DBL_BUFSIZE_BOUND];
- c_dtoastr (index_s, sizeof index_s, 0, 0, index);
- string_map_replace_nocopy (vars, ss_xstrdup (var_name),
- xstrdup (index_s));
- macro_expand (&inner, nesting_countdown, macros,
- me, vars, expand, exp);
- }
+ {
+ int i = 0;
+ for (double index = first;
+ by > 0 ? (index <= last) : (index >= last);
+ index += by)
+ {
+ if (i++ > miterate)
+ {
+ printf ("exceeded maximum number of iterations %d\n",
+ miterate);
+ break;
+ }
+
+ char index_s[DBL_BUFSIZE_BOUND];
+ c_dtoastr (index_s, sizeof index_s, 0, 0, index);
+ string_map_replace_nocopy (vars, ss_xstrdup (var_name),
+ xstrdup (index_s));
+
+ bool break_ = false;
+ macro_expand (&inner, nesting_countdown, macros,
+ me, vars, expand, &break_, exp);
+ if (break_)
+ break;
+ }
+ }
return do_end - tokens + 1;
}
macro_expand (const struct macro_tokens *mts,
int nesting_countdown, const struct macro_set *macros,
const struct macro_expander *me, struct string_map *vars,
- bool *expand, struct macro_tokens *exp)
+ bool *expand, bool *break_, struct macro_tokens *exp)
{
if (nesting_countdown <= 0)
{
struct string_map own_vars = STRING_MAP_INITIALIZER (own_vars);
if (!vars)
vars = &own_vars;
- for (size_t i = 0; i < mts->n; i++)
+
+ for (size_t i = 0; i < mts->n && (!break_ || !*break_); i++)
{
const struct macro_token *mt = &mts->mts[i];
const struct token *token = &mt->token;
//macro_tokens_print (arg, stdout);
if (*expand && param->expand_arg)
macro_expand (arg, nesting_countdown, macros, NULL, NULL,
- expand, exp);
+ expand, break_, exp);
else
for (size_t i = 0; i < arg->n; i++)
macro_tokens_add (exp, &arg->mts[i]);
const struct macro_tokens *arg = me->args[j];
if (*expand && param->expand_arg)
macro_expand (arg, nesting_countdown, macros, NULL, NULL,
- expand, exp);
+ expand, break_, exp);
else
for (size_t k = 0; k < arg->n; k++)
macro_tokens_add (exp, &arg->mts[k]);
size_t n = macro_expand_if (&mts->mts[i], mts->n - i,
nesting_countdown, macros, me, vars,
- expand, exp);
+ expand, break_, exp);
if (n > 0)
{
i += n - 1;
{
i += retval - 1;
macro_expand (&subme->macro->body, nesting_countdown - 1, macros,
- subme, NULL, expand, exp);
+ subme, NULL, expand, break_, exp);
macro_expander_destroy (subme);
continue;
}
continue;
}
+ if (ss_equals_case (token->string, ss_cstr ("!break")))
+ {
+ if (!break_)
+ printf ("!BREAK outside !DO\n");
+ else
+ {
+ *break_ = true;
+ break;
+ }
+ }
+
struct parse_macro_function_ctx ctx = {
.input = &mts->mts[i],
.n_input = mts->n - i,
bool expand = true;
macro_expand (&me->macro->body, settings_get_mnest (),
- me->macros, me, NULL, &expand, exp);
+ me->macros, me, NULL, &expand, NULL, exp);
#if 0
printf ("expansion:\n");