Fix error reporting in configuration parsing
This commit is contained in:
parent
6bf57d3450
commit
9285974bff
77
common.c
77
common.c
|
@ -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;
|
||||||
|
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",
|
error_set (e, "near line %u, column %u: %s",
|
||||||
self->line + 1, self->column + 1, description);
|
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);
|
||||||
|
|
Loading…
Reference in New Issue