X-Git-Url: https://pintos-os.org/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=src%2Flanguage%2Flexer%2Fmacro.c;fp=src%2Flanguage%2Flexer%2Fmacro.c;h=ca32800f30a9dc5129d8b6f38f52a597da4398e5;hb=86a735868a2c4cabf1dfc3971ba6d5d96e51daf0;hp=415b84bade8c4477742ccd43859b25fe50fb5000;hpb=aaa693a2c7f2ab9e11504b50184018d188efdc0b;p=pspp diff --git a/src/language/lexer/macro.c b/src/language/lexer/macro.c index 415b84bade..ca32800f30 100644 --- a/src/language/lexer/macro.c +++ b/src/language/lexer/macro.c @@ -34,6 +34,7 @@ #include "libpspp/string-map.h" #include "gl/c-ctype.h" +#include "gl/ftoastr.h" #include "gettext.h" #define _(msgid) gettext (msgid) @@ -103,7 +104,7 @@ macro_tokens_from_string (struct macro_tokens *mts, const struct substring src, }; struct state state = { - .segmenter = SEGMENTER_INIT (mode), + .segmenter = segmenter_init (mode, true), .body = src, }; struct state saved = state; @@ -821,7 +822,7 @@ parse_macro_function (struct parse_macro_function_ctx *ctx, 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) @@ -1120,6 +1121,12 @@ macro_evaluate_literal (const struct expr_context *ctx, }; 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); } @@ -1315,6 +1322,34 @@ macro_evaluate_expression (const struct macro_token **tokens, size_t n_tokens, 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) { @@ -1460,6 +1495,151 @@ macro_parse_let (const struct macro_token *tokens, size_t n_tokens, 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, @@ -1599,6 +1779,15 @@ macro_expand (const struct macro_tokens *mts, 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")))