Fix error reporting in configuration parsing

This commit is contained in:
Přemysl Eric Janouch 2015-05-01 18:39:38 +02:00
parent 6bf57d3450
commit 9285974bff

View File

@ -673,16 +673,42 @@ enum config_token
CONFIG_T_STRING ///< CONFIG_ITEM_STRING{,_LIST}
};
static const char *
config_token_name (enum config_token token)
{
switch (token)
{
case CONFIG_T_ABORT: return "end of input";
case CONFIG_T_WORD: return "word";
case CONFIG_T_EQUALS: return "equal sign";
case CONFIG_T_LBRACE: return "left brace";
case CONFIG_T_RBRACE: return "right brace";
case CONFIG_T_NEWLINE: return "newline";
case CONFIG_T_NULL: return "null value";
case CONFIG_T_BOOLEAN: return "boolean";
case CONFIG_T_INTEGER: return "integer";
case CONFIG_T_STRING: return "string";
default:
hard_assert (!"invalid token value");
}
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
struct config_tokenizer
{
const char *p;
size_t len;
const char *p; ///< Current position in input
size_t len; ///< How many bytes of input are left
unsigned line;
unsigned column;
bool report_line; ///< Whether to count lines at all
unsigned line; ///< Current line
unsigned column; ///< Current column
int64_t integer;
struct str string;
int64_t integer; ///< Parsed boolean or integer value
struct str string; ///< Parsed string value
};
/// Input has to be null-terminated anyway
@ -692,6 +718,7 @@ config_tokenizer_init (struct config_tokenizer *self, const char *p, size_t len)
memset (self, 0, sizeof *self);
self->p = p;
self->len = len;
self->report_line = true;
str_init (&self->string);
}
@ -711,7 +738,7 @@ static int
config_tokenizer_advance (struct config_tokenizer *self)
{
int c = *self->p++;
if (c == '\n')
if (c == '\n' && self->report_line)
{
self->column = 0;
self->line++;
@ -723,13 +750,29 @@ config_tokenizer_advance (struct config_tokenizer *self)
return c;
}
static void config_tokenizer_error (struct config_tokenizer *self,
struct error **e, const char *format, ...) ATTRIBUTE_PRINTF (3, 4);
static void
config_tokenizer_error (struct config_tokenizer *self,
struct error **e, const char *description)
struct error **e, const char *format, ...)
{
// FIXME: we don't always want to specify the line
error_set (e, "near line %u, column %u: %s",
self->line + 1, self->column + 1, description);
struct str description;
str_init (&description);
va_list ap;
va_start (ap, format);
str_append_vprintf (&description, format, ap);
va_end (ap);
if (self->report_line)
error_set (e, "near line %u, column %u: %s",
self->line + 1, self->column + 1, description.str);
else
error_set (e, "near character %u: %s",
self->column + 1, description.str);
str_free (&description);
}
static enum config_token
@ -856,9 +899,10 @@ config_parser_expect
if (config_parser_accept (self, token, out))
return;
// TODO: fill in "X" and "Y"
config_tokenizer_error (&self->tokenizer, &self->error,
"unexpected X, expected Y");
"unexpected `%s', expected `%s'",
config_token_name (self->token),
config_token_name (token));
longjmp (out, 1);
}
@ -903,9 +947,9 @@ config_parser_parse_value (struct config_parser *self, jmp_buf out)
if (ACCEPT (CONFIG_T_STRING))
return config_item_string (&self->tokenizer.string);
// TODO: fill in "X" as the token name
config_tokenizer_error (&self->tokenizer, &self->error,
"unexpected X, expected a value");
"unexpected `%s', expected a value",
config_token_name (self->token));
longjmp (out, 1);
}
@ -1003,7 +1047,12 @@ config_item_parse (const char *script, size_t len,
}
if (single_value_only)
{
// This is really only intended for in-program configuration
// and telling the line number would look awkward
parser.tokenizer.report_line = false;
object = config_parser_parse_value (&parser, err);
}
else
object = config_parser_parse_object (&parser, err);
config_parser_expect (&parser, CONFIG_T_ABORT, err);