/* An entry in the stack of macros and macro directives being expanded. The
stack is maintained as a linked list. Entries are not dynamically allocated
- but on the program stack. */
+ but on the program stack.
+
+ The outermost entry, where 'next' is NULL, represents the source location of
+ the call to the macro. */
struct macro_expansion_stack
{
- /* Points to an outer stack entry, or NULL if this is the outermost. */
- const struct macro_expansion_stack *next;
-
- /* A macro name or !IF, !DO, etc. */
- const char *name;
-
- /* Location of the macro definition, if available. */
- const char *file_name;
- int first_line;
- int last_line;
+ const struct macro_expansion_stack *next; /* Next outer stack entry. */
+ const char *name; /* A macro name or !IF, !DO, etc. */
+ const struct msg_location *location; /* Source location if available. */
};
/* Reports an error during macro expansion. STACK is the stack for reporting
the location of the error, MT is the optional token at which the error was
detected, and FORMAT along with the varargs is the message to report. */
-static void PRINTF_FORMAT (3, 4)
-macro_error (const struct macro_expansion_stack *stack,
- const struct macro_token *mt,
- const char *format, ...)
+static void PRINTF_FORMAT (3, 0)
+macro_error_valist (const struct macro_expansion_stack *stack,
+ const struct macro_token *mt, const char *format,
+ va_list args)
{
struct msg_stack **ms = NULL;
size_t allocated_ms = 0;
size_t n_ms = 0;
- for (const struct macro_expansion_stack *p = stack; p; p = p->next)
+ const struct macro_expansion_stack *p;
+ for (p = stack; p && p->next; p = p->next)
{
if (n_ms >= allocated_ms)
ms = x2nrealloc (ms, &allocated_ms, sizeof *ms);
ms[n_ms] = xmalloc (sizeof *ms[n_ms]);
*ms[n_ms] = (struct msg_stack) {
- .location = {
- .file_name = xstrdup_if_nonnull (p->file_name),
- .first_line = p->first_line,
- .last_line = p->last_line,
- },
+ .location = msg_location_dup (p->location),
.description = description,
};
n_ms++;
}
- va_list args;
- va_start (args, format);
- char *s = xvasprintf (format, args);
- va_end (args);
-
struct msg *m = xmalloc (sizeof *m);
*m = (struct msg) {
.category = MSG_C_SYNTAX,
.severity = MSG_S_ERROR,
.stack = ms,
.n_stack = n_ms,
- .text = s,
+ .location = msg_location_dup (p ? p->location : NULL),
+ .text = xvasprintf (format, args),
};
msg_emit (m);
}
+/* Reports an error during macro expansion. STACK is the stack for reporting
+ the location of the error, MT is the optional token at which the error was
+ detected, and FORMAT along with the varargs is the message to report. */
+static void PRINTF_FORMAT (3, 4)
+macro_error (const struct macro_expansion_stack *stack,
+ const struct macro_token *mt, const char *format, ...)
+{
+ va_list args;
+ va_start (args, format);
+ macro_error_valist (stack, mt, format, args);
+ va_end (args);
+}
+
void
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 state
- {
- struct segmenter segmenter;
- struct substring body;
- };
-
- struct state state = {
- .segmenter = segmenter_init (mode, true),
- .body = src,
- };
- struct state saved = state;
+ struct segmenter segmenter = segmenter_init (mode, true);
+ struct substring body = src;
- while (state.body.length > 0)
+ while (body.length > 0)
{
+ enum segment_type type;
+ int seg_len = segmenter_push (&segmenter, body.string,
+ body.length, true, &type);
+ assert (seg_len >= 0);
+
struct macro_token mt = {
.token = { .type = T_STOP },
- .syntax = { .string = state.body.string },
+ .syntax = ss_head (body, seg_len),
};
- struct token *token = &mt.token;
-
- struct scanner scanner;
- scanner_init (&scanner, token);
+ enum tokenize_result result
+ = token_from_segment (type, mt.syntax, &mt.token);
+ ss_advance (&body, seg_len);
- for (;;)
+ switch (result)
{
- enum segment_type type;
- int seg_len = segmenter_push (&state.segmenter, state.body.string,
- state.body.length, true, &type);
- assert (seg_len >= 0);
-
- struct substring segment = ss_head (state.body, seg_len);
- ss_advance (&state.body, seg_len);
-
- enum scan_result result = scanner_push (&scanner, type, segment, token);
- if (result == SCAN_SAVE)
- saved = state;
- else if (result == SCAN_BACK)
- {
- state = saved;
- break;
- }
- else if (result == SCAN_DONE)
- break;
- }
+ case TOKENIZE_EMPTY:
+ break;
- /* We have a token in 'token'. */
- mt.syntax.length = state.body.string - mt.syntax.string;
- if (is_scan_type (token->type))
- {
- if (token->type != SCAN_SKIP)
- {
- char *s = scan_token_to_error (token);
- if (stack)
- {
- mt.token.type = T_STRING;
- macro_error (stack, &mt, "%s", s);
- }
- else
- msg (SE, "%s", s);
- free (s);
- }
+ case TOKENIZE_TOKEN:
+ macro_tokens_add (mts, &mt);
+ break;
+
+ case TOKENIZE_ERROR:
+ macro_error (stack, &mt, "%s", mt.token.string.string);
+ break;
}
- else
- macro_tokens_add (mts, &mt);
- token_uninit (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);
+ token_uninit (&mt.token);
+ }
}
void
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;
}
return;
free (m->name);
- free (m->file_name);
+ msg_location_destroy (m->location);
for (size_t i = 0; i < m->n_params; i++)
{
struct macro_param *p = &m->params[i];
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);
hmap_insert (&set->macros, &m->hmap_node, hash_macro_name (m->name));
}
\f
-/* Macro call parsing.. */
+/* Macro call parsing. */
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_set *macros;
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
}
}
-static int
-mc_error (struct macro_call *mc)
+static void PRINTF_FORMAT (3, 4)
+mc_error (const struct macro_call *mc, const struct msg_location *loc,
+ const char *format, ...)
{
- mc->state = MC_ERROR;
- return -1;
+ va_list args;
+ va_start (args, format);
+ if (!mc->stack)
+ {
+ const struct macro_expansion_stack stack = { .location = loc };
+ macro_error_valist (&stack, NULL, format, args);
+ }
+ else
+ macro_error_valist (mc->stack, NULL, format, args);
+ va_end (args);
}
static int
-mc_add_arg (struct macro_call *mc, const struct macro_token *mt)
+mc_add_arg (struct macro_call *mc, const struct macro_token *mt,
+ 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)
{
- msg (SE, _("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;
- return mc_error (mc);
+ 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_expected (struct macro_call *mc, const struct macro_token *actual,
- const struct token *expected)
+ const struct msg_location *loc, const struct token *expected)
{
const struct substring actual_s = (actual->syntax.length ? actual->syntax
: ss_cstr (_("<end of input>")));
char *expected_s = token_to_string (expected);
- msg (SE, _("Found `%.*s' while expecting `%s' reading argument %s "
- "to macro %s."),
- (int) actual_s.length, actual_s.string, expected_s,
- mc->param->name, mc->macro->name);
+ mc_error (mc, loc,
+ _("Found `%.*s' while expecting `%s' reading argument %s "
+ "to macro %s."),
+ (int) actual_s.length, actual_s.string, expected_s,
+ mc->param->name, mc->macro->name);
free (expected_s);
- return mc_error (mc);
+ return mc_finished (mc);
}
static int
-mc_enclose (struct macro_call *mc, const struct macro_token *mt)
+mc_enclose (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_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, &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 *
}
static int
-mc_keyword (struct macro_call *mc, const struct macro_token *mt)
+mc_keyword (struct macro_call *mc, const struct macro_token *mt,
+ const struct msg_location *loc)
{
const struct token *token = &mt->token;
if (token->type != T_ID)
token->string);
if (p)
{
- size_t arg_index = p - mc->macro->params;
- mc->param = p;
- if (mc->args[arg_index])
- {
- msg (SE,
- _("Argument %s multiply specified in call to macro %s."),
- p->name, mc->macro->name);
- return mc_error (mc);
- }
+ 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;
}
static int
-mc_equals (struct macro_call *mc, const struct macro_token *mt)
+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->state = MC_ARG;
+ mc->n_tokens++;
+ mc->state = mc->param->arg_type == ARG_ENCLOSE ? MC_ENCLOSE : MC_ARG;
return 0;
}
- return mc_expected (mc, mt, &(struct token) { .type = T_EQUALS });
+ return mc_expected (mc, mt, loc, &(struct token) { .type = T_EQUALS });
}
-/* 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().
-
- If TOKEN is not the first token of a macro call, returns -1 and sets *MCP to
- NULL. */
-int
-macro_call_create (const struct macro_set *macros,
- const struct token *token,
- struct macro_call **mcp)
+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)
{
const struct macro *macro = (token->type == T_ID || token->type == T_MACRO_ID
? macro_set_find (macros, token->string.string)
: MC_ARG),
.args = macro->n_params ? xcalloc (macro->n_params, sizeof *mc->args) : NULL,
.param = macro->params,
+ .stack = stack,
+ .me = me,
};
*mcp = mc;
return mc->state == MC_FINISHED ? 1 : 0;
}
+/* 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_expand().
+
+ If TOKEN is not the first token of a macro call, returns -1 and sets *MCP to
+ NULL. */
+int
+macro_call_create (const struct macro_set *macros,
+ const struct token *token,
+ struct macro_call **mcp)
+{
+ return macro_call_create__ (macros, NULL, NULL, token, mcp);
+}
+
void
macro_call_destroy (struct macro_call *mc)
{
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)
+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);
+ return mc_add_arg (mc, mt, loc);
case MC_ENCLOSE:
- return mc_enclose (mc, mt);
+ return mc_enclose (mc, mt, loc);
case MC_KEYWORD:
- return mc_keyword (mc, mt);
+ return mc_keyword (mc, mt, loc);
case MC_EQUALS:
- return mc_equals (mc, mt);
+ return mc_equals (mc, mt, loc);
default:
NOT_REACHED ();
int nesting_countdown; /* Remaining nesting levels. */
const struct macro_expansion_stack *stack; /* Stack for error reporting. */
bool *expand; /* May macro calls be expanded? */
- struct stringi_map *vars; /* Variables from !DO and !LET. */
+ struct stringi_map *vars; /* Variables from !do and !let. */
/* Only nonnull if inside a !DO loop. */
bool *break_; /* Set to true to break out of loop. */
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; )
{
string_lexer_init (&slex, s, strlen (s), segmenter_mode, true);
struct token token1;
- if (!string_lexer_next (&slex, &token1))
- return false;
-
- if (token1.type != T_STRING)
+ if (string_lexer_next (&slex, &token1) != SLR_TOKEN
+ || token1.type != T_STRING)
{
token_uninit (&token1);
return false;
}
struct token token2;
- if (string_lexer_next (&slex, &token2))
+ if (string_lexer_next (&slex, &token2) != SLR_END)
{
token_uninit (&token1);
token_uninit (&token2);
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, 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
= { .token = { .type = T_ENDCMD } };
- n_call = macro_call_add (submc, j < n ? &mts[j] : &endcmd);
+ n_call = macro_call_add (submc, j < n ? &mts[j] : &endcmd, NULL);
}
if (n_call > 0)
{
struct stringi_map vars = STRINGI_MAP_INITIALIZER (vars);
struct macro_expansion_stack stack = {
.name = submc->macro->name,
- .file_name = submc->macro->file_name,
- .first_line = submc->macro->first_line,
- .last_line = submc->macro->last_line,
+ .location = submc->macro->location,
.next = me->stack,
};
struct macro_expander subme = {
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;
void
macro_call_expand (struct macro_call *mc, enum segmenter_mode segmenter_mode,
+ const struct msg_location *call_loc,
struct macro_tokens *exp)
{
assert (mc->state == MC_FINISHED);
bool expand = true;
struct stringi_map vars = STRINGI_MAP_INITIALIZER (vars);
- struct macro_expansion_stack stack = {
+ struct macro_expansion_stack stack0 = {
+ .location = call_loc,
+ };
+ struct macro_expansion_stack stack1 = {
+ .next = &stack0,
.name = mc->macro->name,
- .file_name = mc->macro->file_name,
- .first_line = mc->macro->first_line,
- .last_line = mc->macro->last_line,
+ .location = mc->macro->location,
};
struct macro_expander me = {
.macros = mc->macros,
.break_ = NULL,
.vars = &vars,
.nesting_countdown = settings_get_mnest (),
- .stack = &stack,
+ .stack = &stack1,
};
const struct macro_tokens *body = &mc->macro->body;