X-Git-Url: https://pintos-os.org/cgi-bin/gitweb.cgi?a=blobdiff_plain;ds=inline;f=src%2Flanguage%2Flexer%2Fmacro.c;h=415b84bade8c4477742ccd43859b25fe50fb5000;hb=a36fb40ac81f2ac6b8283764ce782ec65d18e15c;hp=b120b35d725e432fecbb96f5f503ee9031cc0b2f;hpb=52d7c80a1a54c4946c4962399667c40200236bc3;p=pspp diff --git a/src/language/lexer/macro.c b/src/language/lexer/macro.c index b120b35d72..415b84bade 100644 --- a/src/language/lexer/macro.c +++ b/src/language/lexer/macro.c @@ -26,10 +26,14 @@ #include "language/lexer/segment.h" #include "language/lexer/scan.h" #include "libpspp/assertion.h" +#include "libpspp/cast.h" #include "libpspp/i18n.h" #include "libpspp/message.h" #include "libpspp/str.h" #include "libpspp/string-array.h" +#include "libpspp/string-map.h" + +#include "gl/c-ctype.h" #include "gettext.h" #define _(msgid) gettext (msgid) @@ -704,18 +708,20 @@ macro_expander_add (struct macro_expander *me, const struct macro_token *mt) */ struct parse_macro_function_ctx { - struct macro_token *input; + const struct macro_token *input; size_t n_input; int nesting_countdown; const struct macro_set *macros; const struct macro_expander *me; + struct string_map *vars; bool *expand; }; static void macro_expand (const struct macro_tokens *, int nesting_countdown, const struct macro_set *, - const struct macro_expander *, bool *expand, struct macro_tokens *exp); + const struct macro_expander *, struct string_map *vars, + bool *expand, struct macro_tokens *exp); static bool expand_macro_function (struct parse_macro_function_ctx *ctx, @@ -736,7 +742,7 @@ static size_t parse_function_arg (struct parse_macro_function_ctx *ctx, size_t i, struct string *farg) { - struct macro_token *tokens = ctx->input; + const struct macro_token *tokens = ctx->input; const struct token *token = &tokens[i].token; if (token->type == T_MACRO_ID) { @@ -773,12 +779,25 @@ parse_function_arg (struct parse_macro_function_ctx *ctx, return 2; } + if (ctx->vars) + { + const char *value = string_map_find__ (ctx->vars, + token->string.string, + token->string.length); + if (value) + { + ds_put_cstr (farg, value); + return 1; + } + } + struct parse_macro_function_ctx subctx = { .input = &ctx->input[i], .n_input = ctx->n_input - i, .nesting_countdown = ctx->nesting_countdown, .macros = ctx->macros, .me = ctx->me, + .vars = ctx->vars, .expand = ctx->expand, }; size_t subinput_consumed; @@ -797,7 +816,7 @@ parse_macro_function (struct parse_macro_function_ctx *ctx, int min_args, int max_args, size_t *input_consumed) { - struct macro_token *tokens = ctx->input; + const struct macro_token *tokens = ctx->input; size_t n_tokens = ctx->n_input; if (!n_tokens @@ -1032,8 +1051,8 @@ expand_macro_function (struct parse_macro_function_ctx *ctx, macro_tokens_from_string (&mts, ss_cstr (args.strings[0]), SEG_MODE_INTERACTIVE /* XXX */); struct macro_tokens exp = { .n = 0 }; - macro_expand (&mts, ctx->nesting_countdown - 1, ctx->macros, - ctx->me, ctx->expand, &exp); + macro_expand (&mts, ctx->nesting_countdown - 1, ctx->macros, ctx->me, + ctx->vars, ctx->expand, &exp); macro_tokens_to_representation (&exp, output); macro_tokens_uninit (&exp); macro_tokens_uninit (&mts); @@ -1052,11 +1071,400 @@ expand_macro_function (struct parse_macro_function_ctx *ctx, return true; } +struct expr_context + { + int nesting_countdown; + const struct macro_set *macros; + const struct macro_expander *me; + struct string_map *vars; + bool *expand; + }; + +static char *macro_evaluate_or (const struct expr_context *ctx, + const struct macro_token **tokens, + const struct macro_token *end); + +static char * +macro_evaluate_literal (const struct expr_context *ctx, + const struct macro_token **tokens, + const struct macro_token *end) +{ + const struct macro_token *p = *tokens; + if (p >= end) + return NULL; + if (p->token.type == T_LPAREN) + { + p++; + char *value = macro_evaluate_or (ctx, &p, end); + if (!value) + return NULL; + if (p >= end || p->token.type != T_RPAREN) + { + free (value); + printf ("expecting ')' in macro expression\n"); + return NULL; + } + p++; + *tokens = p; + return value; + } + + struct parse_macro_function_ctx fctx = { + .input = p, + .n_input = end - p, + .nesting_countdown = ctx->nesting_countdown, + .macros = ctx->macros, + .me = ctx->me, + .vars = ctx->vars, + .expand = ctx->expand, + }; + struct string function_output = DS_EMPTY_INITIALIZER; + size_t function_consumed = parse_function_arg (&fctx, 0, &function_output); + *tokens = p + function_consumed; + return ds_steal_cstr (&function_output); +} + +/* Returns true if MT is valid as a macro operator. Only operators written as + symbols (e.g. <>) are usable in macro expressions, not operator written as + letters (e.g. EQ). */ +static bool +is_macro_operator (const struct macro_token *mt) +{ + return (mt->representation.length > 0 + && !c_isalpha (mt->representation.string[0])); +} + +static enum token_type +parse_relational_op (const struct macro_token *mt) +{ + switch (mt->token.type) + { + case T_EQUALS: + return T_EQ; + + case T_NE: + case T_LT: + case T_GT: + case T_LE: + case T_GE: + return is_macro_operator (mt) ? mt->token.type : T_STOP; + + case T_MACRO_ID: + return (ss_equals_case (mt->token.string, ss_cstr ("!EQ")) ? T_EQ + : ss_equals_case (mt->token.string, ss_cstr ("!NE")) ? T_NE + : ss_equals_case (mt->token.string, ss_cstr ("!LT")) ? T_LT + : ss_equals_case (mt->token.string, ss_cstr ("!GT")) ? T_GT + : ss_equals_case (mt->token.string, ss_cstr ("!LE")) ? T_LE + : ss_equals_case (mt->token.string, ss_cstr ("!GE")) ? T_GE + : T_STOP); + + default: + return T_STOP; + } +} + +static char * +macro_evaluate_relational (const struct expr_context *ctx, + const struct macro_token **tokens, + const struct macro_token *end) +{ + const struct macro_token *p = *tokens; + char *lhs = macro_evaluate_literal (ctx, &p, end); + if (!lhs) + return NULL; + + enum token_type op = p >= end ? T_STOP : parse_relational_op (p); + if (op == T_STOP) + { + *tokens = p; + return lhs; + } + p++; + + char *rhs = macro_evaluate_literal (ctx, &p, end); + if (!rhs) + { + free (lhs); + return NULL; + } + + struct string lhs_tmp, rhs_tmp; + int cmp = strcmp/*XXX*/ (unquote_string_in_place (lhs, &lhs_tmp), + unquote_string_in_place (rhs, &rhs_tmp)); + ds_destroy (&lhs_tmp); + ds_destroy (&rhs_tmp); + + free (lhs); + free (rhs); + + bool b = (op == T_EQUALS || op == T_EQ ? !cmp + : op == T_NE ? cmp + : op == T_LT ? cmp < 0 + : op == T_GT ? cmp > 0 + : op == T_LE ? cmp <= 0 + :/*op == T_GE*/cmp >= 0); + + *tokens = p; + return xstrdup (b ? "1" : "0"); +} + +static char * +macro_evaluate_not (const struct expr_context *ctx, + const struct macro_token **tokens, + const struct macro_token *end) +{ + const struct macro_token *p = *tokens; + + unsigned int negations = 0; + while (p < end + && (ss_equals_case (p->representation, ss_cstr ("!NOT")) + || ss_equals (p->representation, ss_cstr ("~")))) + { + p++; + negations++; + } + + char *operand = macro_evaluate_relational (ctx, &p, end); + if (!operand || !negations) + { + *tokens = p; + return operand; + } + + bool b = strcmp (operand, "0") ^ (negations & 1); + free (operand); + *tokens = p; + return xstrdup (b ? "1" : "0"); +} + +static char * +macro_evaluate_and (const struct expr_context *ctx, + const struct macro_token **tokens, + const struct macro_token *end) +{ + const struct macro_token *p = *tokens; + char *lhs = macro_evaluate_not (ctx, &p, end); + if (!lhs) + return NULL; + + while (p < end + && (ss_equals_case (p->representation, ss_cstr ("!AND")) + || ss_equals (p->representation, ss_cstr ("&")))) + { + p++; + char *rhs = macro_evaluate_not (ctx, &p, end); + if (!rhs) + { + free (lhs); + return NULL; + } + + bool b = strcmp (lhs, "0") && strcmp (rhs, "0"); + free (lhs); + free (rhs); + lhs = xstrdup (b ? "1" : "0"); + } + *tokens = p; + return lhs; +} + +static char * +macro_evaluate_or (const struct expr_context *ctx, + const struct macro_token **tokens, + const struct macro_token *end) +{ + const struct macro_token *p = *tokens; + char *lhs = macro_evaluate_and (ctx, &p, end); + if (!lhs) + return NULL; + + while (p < end + && (ss_equals_case (p->representation, ss_cstr ("!OR")) + || ss_equals (p->representation, ss_cstr ("|")))) + { + p++; + char *rhs = macro_evaluate_and (ctx, &p, end); + if (!rhs) + { + free (lhs); + return NULL; + } + + bool b = strcmp (lhs, "0") || strcmp (rhs, "0"); + free (lhs); + free (rhs); + lhs = xstrdup (b ? "1" : "0"); + } + *tokens = p; + return lhs; +} + +static char * +macro_evaluate_expression (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) +{ + const struct expr_context ctx = { + .nesting_countdown = nesting_countdown, + .macros = macros, + .me = me, + .vars = vars, + .expand = expand, + }; + return macro_evaluate_or (&ctx, tokens, *tokens + n_tokens); +} + +static const struct macro_token * +find_ifend_clause (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 ("!IF"))) + nesting++; + else if (ss_equals_case (p->token.string, ss_cstr ("!IFEND"))) + { + if (!nesting) + return p; + nesting--; + } + else if (ss_equals_case (p->token.string, ss_cstr ("!ELSE")) && !nesting) + return p; + } + return NULL; +} + +static size_t +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) +{ + 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 ("!IF"))) + return 0; + + p++; + char *result = macro_evaluate_expression (&p, end - p, + nesting_countdown, macros, me, vars, + expand); + if (!result) + return 0; + bool b = strcmp (result, "0"); + free (result); + + if (p >= end + || p->token.type != T_MACRO_ID + || !ss_equals_case (p->token.string, ss_cstr ("!THEN"))) + { + printf ("!THEN expected\n"); + return 0; + } + + const struct macro_token *start_then = p + 1; + const struct macro_token *end_then = find_ifend_clause (start_then, end); + if (!end_then) + { + printf ("!ELSE or !IFEND expected\n"); + return 0; + } + + const struct macro_token *start_else, *end_if; + if (ss_equals_case (end_then->token.string, ss_cstr ("!ELSE"))) + { + start_else = end_then + 1; + end_if = find_ifend_clause (start_else, end); + if (!end_if + || !ss_equals_case (end_if->token.string, ss_cstr ("!IFEND"))) + { + printf ("!IFEND expected\n"); + return 0; + } + } + else + { + start_else = NULL; + end_if = end_then; + } + + const struct macro_token *start; + size_t n; + if (b) + { + start = start_then; + n = end_then - start_then; + } + else if (start_else) + { + start = start_else; + n = end_if - start_else; + } + else + { + start = NULL; + n = 0; + } + + if (n) + { + struct macro_tokens mts = { + .mts = CONST_CAST (struct macro_token *, start), + .n = n, + }; + macro_expand (&mts, nesting_countdown, macros, me, vars, expand, exp); + } + return (end_if + 1) - tokens; +} + +static size_t +macro_parse_let (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) +{ + 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 ("!LET"))) + return 0; + p++; + + if (p >= end || p->token.type != T_MACRO_ID) + { + printf ("expected macro variable name following !LET\n"); + return 0; + } + const struct substring var_name = p->token.string; + p++; + + if (p >= end || p->token.type != T_EQUALS) + { + printf ("expected = following !LET\n"); + return 0; + } + p++; + + char *value = macro_evaluate_expression (&p, end - p, + nesting_countdown, macros, me, vars, + expand); + if (!value) + return 0; + + string_map_replace_nocopy (vars, ss_xstrdup (var_name), value); + return p - tokens; +} + static void macro_expand (const struct macro_tokens *mts, int nesting_countdown, const struct macro_set *macros, - const struct macro_expander *me, bool *expand, - struct macro_tokens *exp) + const struct macro_expander *me, struct string_map *vars, + bool *expand, struct macro_tokens *exp) { if (nesting_countdown <= 0) { @@ -1066,6 +1474,9 @@ macro_expand (const struct macro_tokens *mts, return; } + struct string_map own_vars = STRING_MAP_INITIALIZER (own_vars); + if (!vars) + vars = &own_vars; for (size_t i = 0; i < mts->n; i++) { const struct macro_token *mt = &mts->mts[i]; @@ -1079,7 +1490,8 @@ macro_expand (const struct macro_tokens *mts, const struct macro_tokens *arg = me->args[param - me->macro->params]; //macro_tokens_print (arg, stdout); if (*expand && param->expand_arg) - macro_expand (arg, nesting_countdown, macros, NULL, expand, exp); + macro_expand (arg, nesting_countdown, macros, NULL, NULL, + expand, exp); else for (size_t i = 0; i < arg->n; i++) macro_tokens_add (exp, &arg->mts[i]); @@ -1096,7 +1508,8 @@ macro_expand (const struct macro_tokens *mts, const struct macro_tokens *arg = me->args[j]; if (*expand && param->expand_arg) - macro_expand (arg, nesting_countdown, macros, NULL, expand, exp); + macro_expand (arg, nesting_countdown, macros, NULL, NULL, + expand, exp); else for (size_t k = 0; k < arg->n; k++) macro_tokens_add (exp, &arg->mts[k]); @@ -1104,6 +1517,27 @@ macro_expand (const struct macro_tokens *mts, i++; continue; } + + size_t n = macro_expand_if (&mts->mts[i], mts->n - i, + nesting_countdown, macros, me, vars, + expand, exp); + if (n > 0) + { + i += n - 1; + continue; + } + } + + if (token->type == T_MACRO_ID && vars) + { + const char *value = string_map_find__ (vars, token->string.string, + token->string.length); + if (value) + { + macro_tokens_from_string (exp, ss_cstr (value), + SEG_MODE_INTERACTIVE /* XXX */); + continue; + } } if (*expand) @@ -1120,7 +1554,7 @@ macro_expand (const struct macro_tokens *mts, { i += retval - 1; macro_expand (&subme->macro->body, nesting_countdown - 1, macros, - subme, expand, exp); + subme, NULL, expand, exp); macro_expander_destroy (subme); continue; } @@ -1134,14 +1568,13 @@ macro_expand (const struct macro_tokens *mts, continue; } - /* Maybe each arg should just be a string, either a quoted string or a - non-quoted string containing tokens. */ struct parse_macro_function_ctx ctx = { .input = &mts->mts[i], .n_input = mts->n - i, .nesting_countdown = nesting_countdown, .macros = macros, .me = me, + .vars = vars, .expand = expand, }; struct string function_output = DS_EMPTY_INITIALIZER; @@ -1157,6 +1590,15 @@ macro_expand (const struct macro_tokens *mts, continue; } + size_t n = macro_parse_let (&mts->mts[i], mts->n - i, + nesting_countdown, macros, me, vars, + expand); + 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"))) @@ -1164,6 +1606,8 @@ macro_expand (const struct macro_tokens *mts, else macro_tokens_add (exp, mt); } + if (vars == &own_vars) + string_map_destroy (&own_vars); } void @@ -1179,7 +1623,7 @@ macro_expander_get_expansion (struct macro_expander *me, struct macro_tokens *ex bool expand = true; macro_expand (&me->macro->body, settings_get_mnest (), - me->macros, me, &expand, exp); + me->macros, me, NULL, &expand, exp); #if 0 printf ("expansion:\n");