{
if (token->type != SCAN_SKIP)
{
+ printf ("error\n");
/* XXX report error */
}
}
expand_macro_function (struct parse_macro_function_ctx *ctx,
struct string *output, size_t *input_consumed);
+/* Returns true if the pair of tokens starting at offset OFS within MTS are !*,
+ false otherwise. */
+static bool
+is_bang_star (const struct macro_token *mts, size_t n, size_t ofs)
+{
+ return (ofs + 1 < n
+ && mts[ofs].token.type == T_MACRO_ID
+ && ss_equals (mts[ofs].token.string, ss_cstr ("!"))
+ && mts[ofs + 1].token.type == T_ASTERISK);
+}
+
static size_t
parse_function_arg (struct parse_macro_function_ctx *ctx,
size_t i, struct string *farg)
return 1;
}
+ if (is_bang_star (ctx->input, ctx->n_input, i))
+ {
+ for (size_t i = 0; i < ctx->me->macro->n_params; i++)
+ {
+ if (!ctx->me->macro->params[i].positional)
+ break;
+
+ const struct macro_tokens *marg = ctx->me->args[i];
+ for (size_t j = 0; j < marg->n; j++)
+ {
+ if (i || j)
+ ds_put_byte (farg, ' ');
+ ds_put_substring (farg, marg->mts[j].representation);
+ }
+ }
+ return 2;
+ }
+
struct parse_macro_function_ctx subctx = {
.input = &ctx->input[i],
.n_input = ctx->n_input - i,
}
static bool
-string_is_quoted_string (const char *s, struct string *content)
+unquote_string (const char *s, struct string *content)
{
struct string_lexer slex;
string_lexer_init (&slex, s, strlen (s), SEG_MODE_INTERACTIVE /* XXX */);
return true;
}
+static const char *
+unquote_string_in_place (const char *s, struct string *tmp)
+{
+ ds_init_empty (tmp);
+ return unquote_string (s, tmp) ? ds_cstr (tmp) : s;
+}
+
+static bool
+parse_integer (const char *s, int *np)
+{
+ errno = 0;
+
+ char *tail;
+ long int n = strtol (s, &tail, 10);
+ *np = n < INT_MIN ? INT_MIN : n > INT_MAX ? INT_MAX : n;
+ tail += strspn (tail, CC_SPACES);
+ return *tail == '\0' && errno != ERANGE && n == *np;
+}
+
static bool
expand_macro_function (struct parse_macro_function_ctx *ctx,
struct string *output,
else if (parse_macro_function (ctx, &args, ss_cstr ("!blanks"), 1, 1,
input_consumed))
{
- char *tail;
- errno = 0;
- int n = strtol (args.strings[0], &tail, 10);
- if (*tail != '\0' || n < 0 || errno == ERANGE)
+ int n;
+ if (!parse_integer (args.strings[0], &n))
{
printf ("argument to !BLANKS must be non-negative integer (not \"%s\")\n", args.strings[0]);
string_array_destroy (&args);
input_consumed))
{
for (size_t i = 0; i < args.n; i++)
- if (!string_is_quoted_string (args.strings[i], output))
+ if (!unquote_string (args.strings[i], output))
ds_put_cstr (output, args.strings[i]);
}
+ else if (parse_macro_function (ctx, &args, ss_cstr ("!head"), 1, 1,
+ input_consumed))
+ {
+ struct string tmp;
+ const char *s = unquote_string_in_place (args.strings[0], &tmp);
+
+ struct macro_tokens mts = { .n = 0 };
+ macro_tokens_from_string (&mts, ss_cstr (s), SEG_MODE_INTERACTIVE /* XXX */);
+ if (mts.n > 0)
+ ds_put_substring (output, mts.mts[0].representation);
+ macro_tokens_uninit (&mts);
+ ds_destroy (&tmp);
+ }
+ else if (parse_macro_function (ctx, &args, ss_cstr ("!index"), 2, 2,
+ input_consumed))
+ {
+ const char *haystack = args.strings[0];
+ const char *needle = strstr (haystack, args.strings[1]);
+ ds_put_format (output, "%zu", needle ? needle - haystack + 1 : 0);
+ }
else if (parse_macro_function (ctx, &args, ss_cstr ("!quote"), 1, 1,
input_consumed))
{
- if (string_is_quoted_string (args.strings[0], NULL))
+ if (unquote_string (args.strings[0], NULL))
ds_put_cstr (output, args.strings[0]);
else
{
ds_put_byte (output, '\'');
}
}
+ else if (parse_macro_function (ctx, &args, ss_cstr ("!substr"), 2, 3,
+ input_consumed))
+ {
+ int start;
+ if (!parse_integer (args.strings[1], &start) || start < 1)
+ {
+ printf ("second argument to !SUBSTR must be positive integer (not \"%s\")\n", args.strings[1]);
+ string_array_destroy (&args);
+ return false;
+ }
+
+ int count = INT_MAX;
+ if (args.n > 2 && (!parse_integer (args.strings[2], &count) || count < 0))
+ {
+ printf ("third argument to !SUBSTR must be non-negative integer (not \"%s\")\n", args.strings[1]);
+ string_array_destroy (&args);
+ return false;
+ }
+
+ struct substring s = ss_cstr (args.strings[0]);
+ ds_put_substring (output, ss_substr (s, start - 1, count));
+ }
+ else if (parse_macro_function (ctx, &args, ss_cstr ("!tail"), 1, 1,
+ input_consumed))
+ {
+ struct string tmp;
+ const char *s = unquote_string_in_place (args.strings[0], &tmp);
+
+ struct macro_tokens mts = { .n = 0 };
+ macro_tokens_from_string (&mts, ss_cstr (s), SEG_MODE_INTERACTIVE /* XXX */);
+ if (mts.n > 1)
+ {
+ struct macro_tokens tail = { .mts = mts.mts + 1, .n = mts.n - 1 };
+ macro_tokens_to_representation (&tail, output);
+ }
+ macro_tokens_uninit (&mts);
+ ds_destroy (&tmp);
+ }
else if (parse_macro_function (ctx, &args, ss_cstr ("!unquote"), 1, 1,
input_consumed))
{
- if (!string_is_quoted_string (args.strings[0], output))
+ if (!unquote_string (args.strings[0], output))
ds_put_cstr (output, args.strings[0]);
}
+ else if (parse_macro_function (ctx, &args, ss_cstr ("!upcase"), 1, 1,
+ input_consumed))
+ {
+ struct string tmp;
+ const char *s = unquote_string_in_place (args.strings[0], &tmp);
+ char *upper = utf8_to_upper (s);
+ ds_put_cstr (output, upper);
+ free (upper);
+ ds_destroy (&tmp);
+ }
+ else if (parse_macro_function (ctx, &args, ss_cstr ("!eval"), 1, 1,
+ input_consumed))
+ {
+ struct macro_tokens mts = { .n = 0 };
+ 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_tokens_to_representation (&exp, output);
+ macro_tokens_uninit (&exp);
+ macro_tokens_uninit (&mts);
+ }
else if (ctx->n_input > 0
&& ctx->input[0].token.type == T_MACRO_ID
&& ss_equals_case (ctx->input[0].token.string, ss_cstr ("!null")))
const struct macro_expander *me, bool *expand,
struct macro_tokens *exp)
{
- /* Macro expansion:
-
- - Macro names in macro bodies are not expanded by default. !EVAL()
- expands them.
-
- - Macro names in arguments to macro invocations (outside of macro bodies)
- are expanded by default, unless !NOEXPAND. */
if (nesting_countdown <= 0)
{
printf ("maximum nesting level exceeded\n");
macro_tokens_add (exp, &arg->mts[i]);
continue;
}
+
+ if (is_bang_star (mts->mts, mts->n, i))
+ {
+ for (size_t j = 0; j < me->macro->n_params; j++)
+ {
+ const struct macro_param *param = &me->macro->params[j];
+ if (!param->positional)
+ break;
+
+ const struct macro_tokens *arg = me->args[j];
+ if (*expand && param->expand_arg)
+ macro_expand (arg, nesting_countdown, macros, NULL, expand, exp);
+ else
+ for (size_t k = 0; k < arg->n; k++)
+ macro_tokens_add (exp, &arg->mts[k]);
+ }
+ i++;
+ continue;
+ }
}
if (*expand)