{
struct ll_list sources; /* Contains "struct lex_source"s. */
struct macro_set *macros;
+
+ /* Temporarily stores errors and warnings to be emitted by the lexer while
+ lexing is going on, to avoid reentrancy. */
+ struct msg **messages;
+ size_t n_messages, allocated_messages;
};
static struct lex_source *lex_source__ (const struct lexer *);
{
struct lex_source *source, *next;
+ assert (!lexer->messages);
+
ll_for_each_safe (source, next, struct lex_source, ll, &lexer->sources)
{
ll_remove (&source->ll);
.location = lex_token_location_rw (src, token, token),
.text = ss_xstrdup (token->token.string),
};
- msg_emit (m);
+
+ struct lexer *lexer = src->lexer;
+ if (lexer->n_messages >= lexer->allocated_messages)
+ lexer->messages = x2nrealloc (lexer->messages, &lexer->allocated_messages,
+ sizeof *lexer->messages);
+ lexer->messages[lexer->n_messages++] = m;
}
/* Attempts to append an additional token to 'pp' in SRC, reading more from the
return false;
}
-/* Attempts to obtain at least one new token into 'lookahead' in SRC.
-
- Returns true if successful, false on failure. In the latter case, SRC is
- exhausted and 'src->eof' is now true. */
static bool
-lex_source_get_parse (struct lex_source *src)
+lex_source_get_parse__ (struct lex_source *src)
{
struct merger m = MERGER_INIT;
struct token out;
}
}
}
+
+/* Attempts to obtain at least one new token into 'lookahead' in SRC.
+
+ Returns true if successful, false on failure. In the latter case, SRC is
+ exhausted and 'src->eof' is now true. */
+static bool
+lex_source_get_parse (struct lex_source *src)
+{
+ bool ok = lex_source_get_parse__ (src);
+ struct lexer *lexer = src->lexer;
+ if (lexer->n_messages)
+ {
+ struct msg **messages = lexer->messages;
+ size_t n = lexer->n_messages;
+
+ lexer->messages = NULL;
+ lexer->n_messages = lexer->allocated_messages = 0;
+
+ for (size_t i = 0; i < n; i++)
+ msg_emit (messages[i]);
+ free (messages);
+ }
+ return ok;
+}
\f
static void
lex_source_push_endcmd__ (struct lex_source *src)
])
AT_CLEANUP
+
+AT_SETUP([lexer crash due to reentrancy in error processing])
+dnl ^ is an invalid character in input that triggers an error message.
+dnl 100 of them, as shown below, exceeds the 100-error limit. The
+dnl minus sign causes the lexer to look ahead for a number, and then
+dnl the ^ encountered afterward causes an error too, and then the
+dnl message emission handler might reenter the lexer looking for a
+dnl location, which can then cause the lexer to try to get a token
+dnl again. It's a whole mess and the new way of avoiding reentrancy
+dnl by keeping a collection of messages to emit until we've almost
+dnl returned to the top level is much less prone to error.
+AT_DATA([lexer.sps], [dnl
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^-^
+])
+AT_CHECK([pspp lexer.sps], [1], [ignore])
+AT_CLEANUP
\ No newline at end of file