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
1 changed files with 64 additions and 15 deletions

View File

@ -673,16 +673,42 @@ enum config_token
CONFIG_T_STRING ///< CONFIG_ITEM_STRING{,_LIST} 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 struct config_tokenizer
{ {
const char *p; const char *p; ///< Current position in input
size_t len; size_t len; ///< How many bytes of input are left
unsigned line; bool report_line; ///< Whether to count lines at all
unsigned column; unsigned line; ///< Current line
unsigned column; ///< Current column
int64_t integer; int64_t integer; ///< Parsed boolean or integer value
struct str string; struct str string; ///< Parsed string value
}; };
/// Input has to be null-terminated anyway /// 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); memset (self, 0, sizeof *self);
self->p = p; self->p = p;
self->len = len; self->len = len;
self->report_line = true;
str_init (&self->string); str_init (&self->string);
} }
@ -711,7 +738,7 @@ static int
config_tokenizer_advance (struct config_tokenizer *self) config_tokenizer_advance (struct config_tokenizer *self)
{ {
int c = *self->p++; int c = *self->p++;
if (c == '\n') if (c == '\n' && self->report_line)
{ {
self->column = 0; self->column = 0;
self->line++; self->line++;
@ -723,13 +750,29 @@ config_tokenizer_advance (struct config_tokenizer *self)
return c; return c;
} }
static void config_tokenizer_error (struct config_tokenizer *self,
struct error **e, const char *format, ...) ATTRIBUTE_PRINTF (3, 4);
static void static void
config_tokenizer_error (struct config_tokenizer *self, 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 struct str description;
error_set (e, "near line %u, column %u: %s", str_init (&description);
self->line + 1, self->column + 1, 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 static enum config_token
@ -856,9 +899,10 @@ config_parser_expect
if (config_parser_accept (self, token, out)) if (config_parser_accept (self, token, out))
return; return;
// TODO: fill in "X" and "Y"
config_tokenizer_error (&self->tokenizer, &self->error, 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); longjmp (out, 1);
} }
@ -903,9 +947,9 @@ config_parser_parse_value (struct config_parser *self, jmp_buf out)
if (ACCEPT (CONFIG_T_STRING)) if (ACCEPT (CONFIG_T_STRING))
return config_item_string (&self->tokenizer.string); return config_item_string (&self->tokenizer.string);
// TODO: fill in "X" as the token name
config_tokenizer_error (&self->tokenizer, &self->error, 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); longjmp (out, 1);
} }
@ -1003,7 +1047,12 @@ config_item_parse (const char *script, size_t len,
} }
if (single_value_only) 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); object = config_parser_parse_value (&parser, err);
}
else else
object = config_parser_parse_object (&parser, err); object = config_parser_parse_object (&parser, err);
config_parser_expect (&parser, CONFIG_T_ABORT, err); config_parser_expect (&parser, CONFIG_T_ABORT, err);