#include "libpspp/string-map.h"
#include "gl/c-ctype.h"
+#include "gl/ftoastr.h"
#include "gettext.h"
#define _(msgid) gettext (msgid)
};
struct state state = {
- .segmenter = SEGMENTER_INIT (mode),
+ .segmenter = segmenter_init (mode, true),
.body = src,
};
struct state saved = state;
if (!n_tokens
|| tokens[0].token.type != T_MACRO_ID
- || !ss_equals_case (tokens[0].token.string, function))
+ || !ss_equals_case (tokens[0].token.string, function)) /* XXX abbrevs allowed */
return false;
if (n_tokens < 2 || tokens[1].token.type != T_LPAREN)
};
struct string function_output = DS_EMPTY_INITIALIZER;
size_t function_consumed = parse_function_arg (&fctx, 0, &function_output);
+ struct string unquoted = DS_EMPTY_INITIALIZER;
+ if (unquote_string (ds_cstr (&function_output), &unquoted))
+ {
+ ds_swap (&function_output, &unquoted);
+ ds_destroy (&unquoted);
+ }
*tokens = p + function_consumed;
return ds_steal_cstr (&function_output);
}
return macro_evaluate_or (&ctx, tokens, *tokens + n_tokens);
}
+static bool
+macro_evaluate_number (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, double *number)
+{
+ char *s = macro_evaluate_expression (tokens, n_tokens, nesting_countdown,
+ macros, me, vars, expand);
+ if (!s)
+ return false;
+
+ struct macro_tokens mts = { .n = 0 };
+ macro_tokens_from_string (&mts, ss_cstr (s), SEG_MODE_INTERACTIVE /* XXX */);
+ if (mts.n != 1 || !token_is_number (&mts.mts[0].token))
+ {
+ macro_tokens_print (&mts, stdout);
+ printf ("expression must evaluate to a number (not %s)\n", s);
+ free (s);
+ macro_tokens_uninit (&mts);
+ return false;
+ }
+
+ *number = token_number (&mts.mts[0].token);
+ free (s);
+ macro_tokens_uninit (&mts);
+ return true;
+}
+
static const struct macro_token *
find_ifend_clause (const struct macro_token *p, const struct macro_token *end)
{
return p - tokens;
}
+static const struct macro_token *
+find_doend (const struct macro_token *p, const struct macro_token *end)
+{
+ size_t nesting = 0;
+ for (; p < end; p++)
+ {
+ if (p->token.type != T_MACRO_ID)
+ continue;
+
+ if (ss_equals_case (p->token.string, ss_cstr ("!DO")))
+ nesting++;
+ else if (ss_equals_case (p->token.string, ss_cstr ("!DOEND")))
+ {
+ if (!nesting)
+ return p;
+ nesting--;
+ }
+ }
+ printf ("missing !DOEND\n");
+ return NULL;
+}
+
+static size_t
+macro_expand_do (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)
+{
+ const struct macro_token *p = tokens;
+ const struct macro_token *end = tokens + n_tokens;
+
+ if (p >= end || !ss_equals_case (p->token.string, ss_cstr ("!DO")))
+ return 0;
+ p++;
+
+ if (p >= end || p->token.type != T_MACRO_ID)
+ {
+ printf ("expected macro variable name following !DO\n");
+ return 0;
+ }
+ const struct substring var_name = p->token.string;
+ p++;
+
+ if (p < end && p->token.type == T_MACRO_ID
+ && ss_equals_case (p->token.string, ss_cstr ("!IN")))
+ {
+ p++;
+ char *list = macro_evaluate_expression (&p, end - p,
+ nesting_countdown, macros, me, vars,
+ expand);
+ if (!list)
+ return 0;
+
+ struct macro_tokens items = { .n = 0 };
+ macro_tokens_from_string (&items, ss_cstr (list),
+ SEG_MODE_INTERACTIVE /* XXX */);
+ free (list);
+
+ const struct macro_token *do_end = find_doend (p, end);
+ if (!do_end)
+ {
+ macro_tokens_uninit (&items);
+ return 0;
+ }
+
+ const struct macro_tokens inner = {
+ .mts = CONST_CAST (struct macro_token *, p),
+ .n = do_end - p
+ };
+ for (size_t i = 0; i < items.n; i++)
+ {
+ string_map_replace_nocopy (vars, ss_xstrdup (var_name),
+ ss_xstrdup (items.mts[i].representation));
+ macro_expand (&inner, nesting_countdown, macros,
+ me, vars, expand, exp);
+ }
+ return do_end - tokens + 1;
+ }
+ else if (p < end && p->token.type == T_EQUALS)
+ {
+ p++;
+ double first;
+ if (!macro_evaluate_number (&p, end - p, nesting_countdown, macros, me,
+ vars, expand, &first))
+ return 0;
+
+ if (p >= end || p->token.type != T_MACRO_ID
+ || !ss_equals_case (p->token.string, ss_cstr ("!TO")))
+ {
+ printf ("expecting !TO\n");
+ return 0;
+ }
+ p++;
+
+ double last;
+ if (!macro_evaluate_number (&p, end - p, nesting_countdown, macros, me,
+ vars, expand, &last))
+ return 0;
+
+ double by = 1.0;
+ if (p < end && p->token.type == T_MACRO_ID
+ && ss_equals_case (p->token.string, ss_cstr ("!BY")))
+ {
+ p++;
+ if (!macro_evaluate_number (&p, end - p, nesting_countdown, macros, me,
+ vars, expand, &by))
+ return 0;
+
+ if (by == 0.0)
+ {
+ printf ("!BY value cannot be zero\n");
+ return 0;
+ }
+ }
+
+ const struct macro_token *do_end = find_doend (p, end);
+ if (!do_end)
+ return 0;
+ const struct macro_tokens inner = {
+ .mts = CONST_CAST (struct macro_token *, p),
+ .n = do_end - p
+ };
+
+ 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);
+ }
+
+ return do_end - tokens + 1;
+ }
+ else
+ {
+ printf ("expecting = or !IN in !DO loop\n");
+ return 0;
+ }
+}
+
static void
macro_expand (const struct macro_tokens *mts,
int nesting_countdown, const struct macro_set *macros,
continue;
}
+ n = macro_expand_do (&mts->mts[i], mts->n - i,
+ nesting_countdown, macros, me, vars,
+ expand, exp);
+ if (n > 0)
+ {
+ i += n - 1;
+ continue;
+ }
+
if (ss_equals_case (token->string, ss_cstr ("!onexpand")))
*expand = true;
else if (ss_equals_case (token->string, ss_cstr ("!offexpand")))