diff --git a/common.c b/common.c index 5f86396..4b7e6bf 100644 --- a/common.c +++ b/common.c @@ -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);