macro_token_copy (struct macro_token *dst, const struct macro_token *src)
{
token_copy (&dst->token, &src->token);
- ss_alloc_substring (&dst->syntax, src->syntax);
+ dst->syntax = ss_clone (src->syntax);
}
void
macro_token_copy (macro_tokens_add_uninit (mts), mt);
}
-/* Tokenizes SRC according to MODE and appends the tokens to MTS. Uses STACK,
- if nonull, for error reporting. */
+/* Tokenizes SRC according to MODE and appends the tokens to MTS, using STACK
+ for error reporting. */
static void
-macro_tokens_from_string__ (struct macro_tokens *mts, const struct substring src,
- enum segmenter_mode mode,
- const struct macro_expansion_stack *stack)
+macro_tokens_from_string (struct macro_tokens *mts, const struct substring src,
+ enum segmenter_mode mode,
+ const struct macro_expansion_stack *stack)
{
struct segmenter segmenter = segmenter_init (mode, true);
struct substring body = src;
while (body.length > 0)
{
- struct macro_token mt = {
- .token = { .type = T_STOP },
- .syntax = { .string = body.string },
- };
- struct token *token = &mt.token;
-
enum segment_type type;
int seg_len = segmenter_push (&segmenter, body.string,
body.length, true, &type);
assert (seg_len >= 0);
- struct substring segment = ss_head (body, seg_len);
- enum tokenize_result result = token_from_segment (type, segment, token);
+ struct macro_token mt = {
+ .token = { .type = T_STOP },
+ .syntax = ss_head (body, seg_len),
+ };
+ enum tokenize_result result
+ = token_from_segment (type, mt.syntax, &mt.token);
ss_advance (&body, seg_len);
switch (result)
break;
case TOKENIZE_TOKEN:
- mt.syntax.length = body.string - mt.syntax.string;
macro_tokens_add (mts, &mt);
break;
case TOKENIZE_ERROR:
- mt.syntax.length = body.string - mt.syntax.string;
- macro_error (stack, &mt, "%s", token->string.string);
+ macro_error (stack, &mt, "%s", mt.token.string.string);
break;
}
- token_uninit (token);
+ token_uninit (&mt.token);
}
}
-/* Tokenizes SRC according to MODE and appends the tokens to MTS. */
-void
-macro_tokens_from_string (struct macro_tokens *mts, const struct substring src,
- enum segmenter_mode mode)
-{
- macro_tokens_from_string__ (mts, src, mode, NULL);
-}
-
void
macro_tokens_print (const struct macro_tokens *mts, FILE *stream)
{
case T_RPAREN:
case T_LBRACK:
case T_RBRACK:
+ case T_LCURLY:
+ case T_RCURLY:
return TC_PUNCT;
case T_PLUS:
case T_ASTERISK:
case T_SLASH:
case T_EQUALS:
+ case T_COLON:
case T_AND:
case T_OR:
case T_NOT:
return TC_BINOP;
case T_COMMA:
+ case T_SEMICOLON:
return TC_COMMA;
}
free (p->name);
macro_tokens_uninit (&p->def);
-
- switch (p->arg_type)
- {
- case ARG_N_TOKENS:
- break;
-
- case ARG_CHAREND:
- token_uninit (&p->charend);
- break;
-
- case ARG_ENCLOSE:
- token_uninit (&p->enclose[0]);
- token_uninit (&p->enclose[1]);
- break;
-
- case ARG_CMDEND:
- break;
- }
+ token_uninit (&p->start);
+ token_uninit (&p->end);
}
free (m->params);
macro_tokens_uninit (&m->body);
enum mc_state
{
- /* Error state. */
- MC_ERROR,
-
/* Accumulating tokens in mc->params toward the end of any type of
argument. */
MC_ARG,
const struct macro *macro;
struct macro_tokens **args;
const struct macro_expansion_stack *stack;
+ const struct macro_expander *me;
enum mc_state state;
size_t n_tokens;
const struct macro_param *param; /* Parameter currently being parsed. */
};
+static bool macro_expand_arg (const struct token *,
+ const struct macro_expander *,
+ struct macro_tokens *exp);
+
/* Completes macro expansion by initializing arguments that weren't supplied to
their defaults. */
static int
const struct msg_location *loc)
{
const struct macro_param *p = mc->param;
+ struct macro_tokens **argp = &mc->args[p - mc->macro->params];
const struct token *token = &mt->token;
- if ((token->type == T_ENDCMD || token->type == T_STOP)
- && p->arg_type != ARG_CMDEND)
+ if (token->type == T_ENDCMD || token->type == T_STOP)
{
- mc_error (mc, loc,
- _("Unexpected end of command reading argument %s "
- "to macro %s."), mc->param->name, mc->macro->name);
+ if (*argp)
+ {
+ switch (p->arg_type)
+ {
+ case ARG_CMDEND:
+ /* This is OK, it's the expected way to end the argument. */
+ break;
- mc->state = MC_ERROR;
- return -1;
+ case ARG_N_TOKENS:
+ mc_error (mc, loc,
+ ngettext (_("Reached end of command expecting %zu "
+ "more token in argument %s to macro %s."),
+ _("Reached end of command expecting %zu "
+ "more tokens in argument %s to macro %s."),
+ p->n_tokens - (*argp)->n),
+ p->n_tokens - (*argp)->n, p->name, mc->macro->name);
+ break;
+
+ case ARG_CHAREND:
+ case ARG_ENCLOSE:
+ {
+ char *end = token_to_string (&p->end);
+ mc_error (mc, loc, _("Reached end of command expecting \"%s\" "
+ "in argument %s to macro %s."),
+ end, p->name, mc->macro->name);
+ free (end);
+ }
+ break;
+ }
+ }
+
+ /* The end of a command ends the current argument, precludes any further
+ arguments, and is not itself part of the argument. */
+ return mc_finished (mc);
}
mc->n_tokens++;
- struct macro_tokens **argp = &mc->args[p - mc->macro->params];
if (!*argp)
*argp = xzalloc (sizeof **argp);
- struct macro_tokens *arg = *argp;
- if (p->arg_type == ARG_N_TOKENS)
- {
- macro_tokens_add (arg, mt);
- if (arg->n >= p->n_tokens)
- return mc_next_arg (mc);
- return 0;
- }
- else if (p->arg_type == ARG_CMDEND)
+
+ bool add_token; /* Should we add 'mt' to the current arg? */
+ bool next_arg; /* Should we advance to the next arg? */
+ switch (p->arg_type)
{
- if (token->type == T_ENDCMD || token->type == T_STOP)
- return mc_next_arg (mc);
- macro_tokens_add (arg, mt);
- return 0;
+ case ARG_N_TOKENS:
+ next_arg = (*argp)->n + 1 >= p->n_tokens;
+ add_token = true;
+ break;
+
+ case ARG_CHAREND:
+ case ARG_ENCLOSE:
+ next_arg = token_equal (token, &p->end);
+ add_token = !next_arg;
+ break;
+
+ case ARG_CMDEND:
+ next_arg = false;
+ add_token = true;
+ break;
+
+ default:
+ NOT_REACHED ();
}
- else
+
+ if (add_token)
{
- const struct token *end
- = p->arg_type == ARG_CHAREND ? &p->charend : &p->enclose[1];
- if (token_equal (token, end))
- return mc_next_arg (mc);
- macro_tokens_add (arg, mt);
- return 0;
+ if (!macro_expand_arg (&mt->token, mc->me, *argp))
+ macro_tokens_add (*argp, mt);
}
+ return next_arg ? mc_next_arg (mc) : 0;
}
static int
mc->param->name, mc->macro->name);
free (expected_s);
- mc->state = MC_ERROR;
- return -1;
+ return mc_finished (mc);
}
static int
const struct msg_location *loc)
{
const struct token *token = &mt->token;
- mc->n_tokens++;
-
- if (token_equal (&mc->param->enclose[0], token))
+ const struct macro_param *p = mc->param;
+ if (token_equal (&p->start, token))
{
+ mc->n_tokens++;
+
+ struct macro_tokens **argp = &mc->args[p - mc->macro->params];
+ if (!*argp)
+ *argp = xzalloc (sizeof **argp);
mc->state = MC_ARG;
return 0;
}
-
- return mc_expected (mc, mt, loc, &mc->param->enclose[0]);
+ else if (p->positional && (token->type == T_ENDCMD || token->type == T_STOP))
+ return mc_finished (mc);
+ else
+ return mc_expected (mc, mt, loc, &p->start);
}
static const struct macro_param *
token->string);
if (p)
{
- size_t arg_index = p - mc->macro->params;
- mc->param = p;
- if (mc->args[arg_index])
- {
- mc_error (mc, loc,
- _("Argument %s multiply specified in call to macro %s."),
- p->name, mc->macro->name);
- mc->state = MC_ERROR;
- return -1;
- }
+ struct macro_tokens **argp = &mc->args[p - mc->macro->params];
+ if (*argp)
+ mc_error (mc, loc,
+ _("Argument %s multiply specified in call to macro %s."),
+ p->name, mc->macro->name);
+ *argp = xzalloc (sizeof **argp);
+ mc->param = p;
mc->n_tokens++;
mc->state = MC_EQUALS;
return 0;
mc_equals (struct macro_call *mc, const struct macro_token *mt,
const struct msg_location *loc)
{
- const struct token *token = &mt->token;
- mc->n_tokens++;
-
- if (token->type == T_EQUALS)
+ if (mt->token.type == T_EQUALS)
{
+ mc->n_tokens++;
mc->state = mc->param->arg_type == ARG_ENCLOSE ? MC_ENCLOSE : MC_ARG;
return 0;
}
static int
macro_call_create__ (const struct macro_set *macros,
const struct macro_expansion_stack *stack,
+ const struct macro_expander *me,
const struct token *token,
struct macro_call **mcp)
{
.args = macro->n_params ? xcalloc (macro->n_params, sizeof *mc->args) : NULL,
.param = macro->params,
.stack = stack,
+ .me = me,
};
*mcp = mc;
/* If TOKEN is the first token of a call to a macro in MACROS, create a new
macro expander, initializes *MCP to it. Returns 0 if more tokens are needed
and should be added via macro_call_add() or 1 if the caller should next call
- macro_call_get_expansion().
+ macro_call_expand().
If TOKEN is not the first token of a macro call, returns -1 and sets *MCP to
NULL. */
const struct token *token,
struct macro_call **mcp)
{
- return macro_call_create__ (macros, NULL, token, mcp);
+ return macro_call_create__ (macros, NULL, NULL, token, mcp);
}
void
Returns a positive number to indicate that the returned number of tokens
invoke a macro. The number returned might be less than the number of tokens
added because it can take a few tokens of lookahead to determine whether the
- macro invocation is finished. The caller should call
- macro_call_get_expansion() to obtain the expansion. */
+ macro invocation is finished. The caller should call macro_call_expand() to
+ obtain the expansion. */
int
macro_call_add (struct macro_call *mc, const struct macro_token *mt,
const struct msg_location *loc)
{
switch (mc->state)
{
- case MC_ERROR:
- return -1;
-
case MC_ARG:
return mc_add_arg (mc, mt, loc);
const struct macro_token *input, size_t n_input,
struct string *output);
-/* Returns true if the N tokens within MTS start with !*, false otherwise. */
-static bool
-is_bang_star (const struct macro_token *mts, size_t n)
-{
- return (n > 1
- && mts[0].token.type == T_MACRO_ID
- && ss_equals (mts[0].token.string, ss_cstr ("!"))
- && mts[1].token.type == T_ASTERISK);
-}
-
/* Parses one function argument from the N_INPUT tokens in INPUT
Each argument to a macro function is one of:
return 1;
}
- if (is_bang_star (input, n_input))
+ if (ss_equals (token->string, ss_cstr ("!*")))
{
for (size_t i = 0; i < me->macro->n_params; i++)
{
ds_put_byte (farg, ' ');
macro_tokens_to_syntax (me->args[i], farg, NULL, NULL);
}
- return 2;
+ return 1;
}
const char *var = stringi_map_find__ (me->vars,
const char *function,
struct string_array *args)
{
- if (n < 2 || mts[1].token.type != T_LPAREN)
- {
- macro_error (me->stack, n > 1 ? &mts[1] : NULL,
- _("`(' expected following %s."), function);
- return 0;
- }
+ assert (n >= 2 && mts[1].token.type == T_LPAREN);
for (size_t i = 2; i < n; )
{
return false;
}
- ds_put_substring (content, token1.string);
+ if (content)
+ ds_put_substring (content, token1.string);
token_uninit (&token1);
return true;
}
MF_HEAD,
MF_INDEX,
MF_LENGTH,
- MF_NULL,
MF_QUOTE,
MF_SUBSTR,
MF_TAIL,
[MF_HEAD] = { "!HEAD", 1, 1 },
[MF_INDEX] = { "!INDEX", 2, 2 },
[MF_LENGTH] = { "!LENGTH", 1, 1 },
- [MF_NULL] = { "!NULL", 0, 0 },
[MF_QUOTE] = { "!QUOTE", 1, 1 },
[MF_SUBSTR] = { "!SUBSTR", 2, 3 },
[MF_TAIL] = { "!TAIL", 1, 1 },
[MF_UPCASE] = { "!UPCASE", 1, 1 },
};
- /* Is this a macro function? */
+ if (lex_id_match_n (ss_cstr ("!NULL"), input[0].token.string, 4))
+ return 1;
+
+ if (n_input < 2 || input[1].token.type != T_LPAREN)
+ {
+ /* Only consider macro functions when the name is followed by '('. */
+ return 0;
+ }
+
+ /* Is this a macro function name? */
const struct macro_function *mf;
for (mf = mfs; ; mf++)
{
}
enum macro_function_id id = mf - mfs;
- if (id == MF_NULL)
- return 1;
struct string_array args = STRING_ARRAY_INITIALIZER;
size_t n_consumed = parse_function_args (me, input, n_input, mf->name, &args);
if (!n_consumed)
- return 0;
+ {
+ string_array_destroy (&args);
+ return 0;
+ }
if (args.n < mf->min_args || args.n > mf->max_args)
{
mf->name);
else
NOT_REACHED ();
+ string_array_destroy (&args);
return 0;
}
me->segmenter_mode, &tmp);
struct macro_tokens mts = { .n = 0 };
- macro_tokens_from_string__ (&mts, ss_cstr (s), me->segmenter_mode,
- me->stack);
+ macro_tokens_from_string (&mts, ss_cstr (s), me->segmenter_mode,
+ me->stack);
if (mts.n > 0)
ds_put_substring (output, mts.mts[0].syntax);
macro_tokens_uninit (&mts);
me->segmenter_mode, &tmp);
struct macro_tokens mts = { .n = 0 };
- macro_tokens_from_string__ (&mts, ss_cstr (s), me->segmenter_mode,
- me->stack);
+ macro_tokens_from_string (&mts, ss_cstr (s), me->segmenter_mode,
+ me->stack);
if (mts.n > 1)
{
struct macro_tokens tail = { .mts = mts.mts + 1, .n = mts.n - 1 };
case MF_EVAL:
{
struct macro_tokens mts = { .n = 0 };
- macro_tokens_from_string__ (&mts, ss_cstr (args.strings[0]),
- me->segmenter_mode, me->stack);
+ macro_tokens_from_string (&mts, ss_cstr (args.strings[0]),
+ me->segmenter_mode, me->stack);
struct macro_tokens exp = { .n = 0 };
struct macro_expansion_stack stack = {
.name = "!EVAL",
return false;
struct macro_tokens mts = { .n = 0 };
- macro_tokens_from_string__ (&mts, ss_cstr (s), me->segmenter_mode, me->stack);
+ macro_tokens_from_string (&mts, ss_cstr (s), me->segmenter_mode, me->stack);
if (mts.n != 1 || !token_is_number (&mts.mts[0].token))
{
macro_error (me->stack, mts.n > 0 ? &mts.mts[0] : NULL,
return 0;
struct macro_tokens items = { .n = 0 };
- macro_tokens_from_string__ (&items, ss_cstr (list), me->segmenter_mode,
- me->stack);
+ macro_tokens_from_string (&items, ss_cstr (list), me->segmenter_mode,
+ me->stack);
free (list);
const struct macro_token *do_end = find_doend (subme.stack, p, end);
macro_expand (p, do_end - p, &subme, exp);
}
+ macro_tokens_uninit (&items);
return do_end - tokens + 1;
}
else if (p < end && p->token.type == T_EQUALS)
}
static void
-macro_expand_arg (const struct macro_expander *me, size_t idx,
+macro_expand_arg__ (const struct macro_expander *me, size_t idx,
struct macro_tokens *exp)
{
const struct macro_param *param = &me->macro->params[idx];
macro_tokens_add (exp, &arg->mts[i]);
}
+static bool
+macro_expand_arg (const struct token *token, const struct macro_expander *me,
+ struct macro_tokens *exp)
+{
+ if (!me || token->type != T_MACRO_ID)
+ return false;
+
+ /* Macro arguments. */
+ if (me->macro)
+ {
+ const struct macro_param *param = macro_find_parameter_by_name (
+ me->macro, token->string);
+ if (param)
+ {
+ macro_expand_arg__ (me, param - me->macro->params, exp);
+ return true;
+ }
+ else if (ss_equals (token->string, ss_cstr ("!*")))
+ {
+ for (size_t j = 0; j < me->macro->n_params; j++)
+ macro_expand_arg__ (me, j, exp);
+ return true;
+ }
+ }
+
+ /* Variables set by !DO or !LET. */
+ const char *var = stringi_map_find__ (me->vars, token->string.string,
+ token->string.length);
+ if (var)
+ {
+ macro_tokens_from_string (exp, ss_cstr (var),
+ me->segmenter_mode, me->stack);
+ return true;
+ }
+
+ return false;
+}
+
static size_t
macro_expand__ (const struct macro_token *mts, size_t n,
const struct macro_expander *me,
if (*me->expand)
{
struct macro_call *submc;
- int n_call = macro_call_create__ (me->macros, me->stack, token, &submc);
+ int n_call = macro_call_create__ (me->macros, me->stack, me,
+ token, &submc);
for (size_t j = 1; !n_call; j++)
{
const struct macro_token endcmd
return 1;
}
- /* Parameters. */
- if (me->macro)
- {
- const struct macro_param *param = macro_find_parameter_by_name (
- me->macro, token->string);
- if (param)
- {
- macro_expand_arg (me, param - me->macro->params, exp);
- return 1;
- }
- else if (is_bang_star (mts, n))
- {
- for (size_t j = 0; j < me->macro->n_params; j++)
- macro_expand_arg (me, j, exp);
- return 2;
- }
- }
-
- /* Variables set by !DO or !LET. */
- const char *var = stringi_map_find__ (me->vars, token->string.string,
- token->string.length);
- if (var)
- {
- macro_tokens_from_string__ (exp, ss_cstr (var),
- me->segmenter_mode, me->stack);
- return 1;
- }
+ /* Parameters and macro variables. */
+ if (macro_expand_arg (token, me, exp))
+ return 1;
/* Macro functions. */
struct string function_output = DS_EMPTY_INITIALIZER;
size_t n_function = expand_macro_function (me, mts, n, &function_output);
if (n_function)
{
- macro_tokens_from_string__ (exp, function_output.ss,
- me->segmenter_mode, me->stack);
+ macro_tokens_from_string (exp, function_output.ss,
+ me->segmenter_mode, me->stack);
ds_destroy (&function_output);
return n_function;