int last_line;
};
+static void PRINTF_FORMAT (2, 3)
+macro_error (const struct macro_expansion_stack *stack,
+ const char *format, ...)
+{
+ 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)
+ {
+ 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,
+ },
+ .description = (
+ p == stack
+ /* TRANSLATORS: These strings are used for explaining the context of
+ an error. The "While expanding" message appears first, followed
+ by zero or more of the "inside expansion" messages, like this:
+
+ foo.sps:12: While expanding 'innermost',
+ foo.sps:23: inside expansion of 'next_inner',
+ foo.sps:34: inside expansion of 'next_inner2',
+ foo.sps:45: inside expansion of 'outermost',
+ foo.sps:76: This is the actual error message. */
+ ? xasprintf (_("While expanding \"%s\","), p->name)
+ : xasprintf (_("inside expansion of \"%s\","), p->name))
+ };
+ n_ms++;
+ }
+
+ va_list args;
+ va_start (args, format);
+
+ struct msg *m = xmalloc (sizeof *m);
+ *m = (struct msg) {
+ .category = MSG_C_SYNTAX,
+ .severity = MSG_S_ERROR,
+ .stack = ms,
+ .n_stack = n_ms,
+ .text = xvasprintf (format, args),
+ };
+ msg_emit (m);
+
+ va_end (args);
+}
enum me_state
{
if (n_tokens < 2 || tokens[1].token.type != T_LPAREN)
{
- printf ("`(' expected following %s'\n", function.string);
+ macro_error (ctx->stack, _("`(' expected following %s"), function.string);
return false;
}
*input_consumed = i + 1;
if (args->n < min_args || args->n > max_args)
{
- printf ("Wrong number of arguments to %s.\n", function.string);
+ macro_error (ctx->stack,
+ _("Wrong number of arguments to macro function %s."),
+ function.string);
goto error;
}
return true;
i++;
else if (tokens[i].token.type != T_RPAREN)
{
- printf ("Expecting `,' or `)' in %s invocation.", function.string);
+ macro_error (ctx->stack,
+ _("`,' or `)' expected in call to macro function %s."),
+ function.string);
goto error;
}
}
unexpected_end:
- printf ("Missing closing parenthesis in arguments to %s.\n",
- function.string);
+ macro_error (ctx->stack, _("Missing `)' in call to macro function %s."),
+ function.string);
/* Fall through. */
error:
string_array_destroy (args);
int n;
if (!parse_integer (args.strings[0], &n))
{
- printf ("argument to !BLANKS must be non-negative integer (not \"%s\")\n", args.strings[0]);
+ macro_error (ctx->stack,
+ _("Argument to !BLANKS must be non-negative integer "
+ "(not \"%s\")"), args.strings[0]);
string_array_destroy (&args);
return false;
}
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]);
+ macro_error (ctx->stack, _("Second argument of !SUBSTR must be "
+ "positive integer (not \"%s\")"),
+ 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]);
+ macro_error (ctx->stack, _("Third argument of !SUBSTR must be "
+ "non-negative integer (not \"%s\")"),
+ args.strings[1]);
string_array_destroy (&args);
return false;
}
if (p >= end || p->token.type != T_RPAREN)
{
free (value);
- printf ("expecting ')' in macro expression\n");
+ macro_error (ctx->stack, _("Expecting ')' in macro expression."));
return NULL;
}
p++;
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);
+ macro_error (stack, _("Macro expression must evaluate to "
+ "a number (not \"%s\")"), s);
free (s);
macro_tokens_uninit (&mts);
return false;
|| p->token.type != T_MACRO_ID
|| !ss_equals_case (p->token.string, ss_cstr ("!THEN")))
{
- printf ("!THEN expected\n");
+ macro_error (stack, _("!THEN expected in macro !IF construct."));
return 0;
}
const struct macro_token *end_then = find_ifend_clause (start_then, end);
if (!end_then)
{
- printf ("!ELSE or !IFEND expected\n");
+ macro_error (stack,
+ _("!ELSE or !IFEND expected in macro !IF construct."));
return 0;
}
if (!end_if
|| !ss_equals_case (end_if->token.string, ss_cstr ("!IFEND")))
{
- printf ("!IFEND expected\n");
+ macro_error (stack, _("!IFEND expected in macro !IF construct."));
return 0;
}
}
return (end_if + 1) - tokens;
}
-static void PRINTF_FORMAT (2, 3)
-macro_error (const struct macro_expansion_stack *stack,
- const char *format, ...)
-{
- va_list args;
- va_start (args, format);
- char *s = xvasprintf (format, args);
- va_end (args);
-
- /* foo.sps:12: While expanding 'innermost',
- foo.sps:23: inside expansion of 'next_inner',
- foo.sps:34: inside expansion of 'next_inner2',
- foo.sps:45: inside expansion of 'outermost':
- error. */
- struct string header = DS_EMPTY_INITIALIZER;
- if (stack->file_name || stack->first_line)
- {
- if (stack->file_name)
- ds_put_format (&header, "%s:", stack->file_name);
- if (stack->first_line)
- {
- if (stack->last_line > stack->first_line)
- ds_put_format (&header, "%d-%d:",
- stack->first_line, stack->last_line);
- else
- ds_put_format (&header, "%d", stack->first_line);
- }
- ds_put_byte (&header, ' ');
- }
- ds_put_format (&header, "While expanding \"%s\"\n", stack->name);
- while ((stack = stack->next) != NULL)
- {
- if (stack->file_name || stack->first_line)
- {
- if (stack->file_name)
- ds_put_format (&header, "%s:", stack->file_name);
- if (stack->first_line)
- {
- if (stack->last_line > stack->first_line)
- ds_put_format (&header, "%d-%d:",
- stack->first_line, stack->last_line);
- else
- ds_put_format (&header, "%d", stack->first_line);
- }
- ds_put_byte (&header, ' ');
- }
- ds_put_format (&header, ", inside expansion of \"%s\"\n", stack->name);
- }
- msg (SE, "%s: %s", ds_cstr (&header), s);
-
- ds_destroy (&header);
- free (s);
-}
-
static size_t
macro_parse_let (const struct macro_token *tokens, size_t n_tokens,
int nesting_countdown, const struct macro_set *macros,
if (p >= end || p->token.type != T_MACRO_ID)
{
- macro_error (stack, "expected macro variable name following !LET");
+ macro_error (stack, _("Expected macro variable name following !LET."));
return 0;
}
const struct substring var_name = p->token.string;
if (is_macro_keyword (var_name)
|| macro_find_parameter_by_name (me->macro, var_name))
{
- printf ("cannot use argument name or macro keyword %.*s as !LET variable\n", (int) var_name.length, var_name.string);
+ macro_error (stack, _("Cannot use argument name or macro keyword "
+ "\"%.*s\" as !LET variable"),
+ (int) var_name.length, var_name.string);
return 0;
}
p++;
if (p >= end || p->token.type != T_EQUALS)
{
- printf ("expected = following !LET\n");
+ macro_error (stack, _("Expected `=' following !LET"));
return 0;
}
p++;
}
static const struct macro_token *
-find_doend (const struct macro_token *p, const struct macro_token *end)
+find_doend (const struct macro_expansion_stack *stack,
+ const struct macro_token *p, const struct macro_token *end)
{
size_t nesting = 0;
for (; p < end; p++)
nesting--;
}
}
- printf ("missing !DOEND\n");
+ macro_error (stack, _("Missing !DOEND."));
return NULL;
}
if (p >= end || p->token.type != T_MACRO_ID)
{
- printf ("expected macro variable name following !DO\n");
+ macro_error (stack, _("Expected macro variable name following !DO"));
return 0;
}
const struct substring var_name = p->token.string;
if (is_macro_keyword (var_name)
|| macro_find_parameter_by_name (me->macro, var_name))
{
- printf ("cannot use argument name or macro keyword %.*s as !DO variable\n", (int) var_name.length, var_name.string);
+ macro_error (stack, _("Cannot use argument name or macro "
+ "keyword \"%.*s\" as !DO variable"),
+ (int) var_name.length, var_name.string);
return 0;
}
p++;
SEG_MODE_INTERACTIVE /* XXX */);
free (list);
- const struct macro_token *do_end = find_doend (p, end);
+ const struct macro_token *do_end = find_doend (stack, p, end);
if (!do_end)
{
macro_tokens_uninit (&items);
{
if (i >= miterate)
{
- printf ("exceeded maximum number of iterations %d\n", miterate);
+ macro_error (stack, _("!DO loop over list exceeded "
+ "maximum number of iterations %d. "
+ "(Use SET MITERATE to change the limit.)"),
+ miterate);
break;
}
string_map_replace_nocopy (vars, ss_xstrdup (var_name),
if (p >= end || p->token.type != T_MACRO_ID
|| !ss_equals_case (p->token.string, ss_cstr ("!TO")))
{
- printf ("expecting !TO\n");
+ macro_error (stack, _("Expected !TO in numerical !DO loop"));
return 0;
}
p++;
if (by == 0.0)
{
- printf ("!BY value cannot be zero\n");
+ macro_error (stack, _("!BY value cannot be zero."));
return 0;
}
}
- const struct macro_token *do_end = find_doend (p, end);
+ const struct macro_token *do_end = find_doend (stack, p, end);
if (!do_end)
return 0;
const struct macro_tokens inner = {
{
if (i++ > miterate)
{
- printf ("exceeded maximum number of iterations %d\n",
- miterate);
+ macro_error (stack,
+ _("Numerical !DO loop exceeded "
+ "maximum number of iterations %d. "
+ "(Use SET MITERATE to change the limit.)"),
+ miterate);
break;
}
}
else
{
- printf ("expecting = or !IN in !DO loop\n");
+ macro_error (stack, _("Expected `=' or !IN in !DO loop."));
return 0;
}
}
{
if (nesting_countdown <= 0)
{
- printf ("maximum nesting level exceeded\n");
+ macro_error (stack, _("Maximum nesting level %d exceeded."),
+ settings_get_mnest ());
for (size_t i = 0; i < mts->n; i++)
macro_tokens_add (exp, &mts->mts[i]);
return;
if (param)
{
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, NULL,
&(struct macro_expansion_stack) {
if (ss_equals_case (token->string, ss_cstr ("!break")))
{
if (!break_)
- printf ("!BREAK outside !DO\n");
+ macro_error (stack, _("!BREAK outside !DO."));
else
{
*break_ = true;