/* Lexical analysis. */
enum json_lex_state lex_state;
struct ds buffer; /* Buffer for accumulating token text. */
+ int line_number;
+ int column_number;
+ int byte_number;
/* Parsing. */
enum json_parse_state parse_state;
return json;
}
+struct json *
+json_array_create_1(struct json *elem0)
+{
+ struct json **elems = xmalloc(sizeof *elems);
+ elems[0] = elem0;
+ return json_array_create(elems, 1);
+}
+
struct json *
json_array_create_2(struct json *elem0, struct json *elem1)
{
static bool
json_equal_object(const struct shash *a, const struct shash *b)
{
- struct shash_node *node;
+ struct shash_node *a_node;
if (shash_count(a) != shash_count(b)) {
return false;
}
- SHASH_FOR_EACH (node, a) {
- if (!shash_find(b, node->name)) {
+ SHASH_FOR_EACH (a_node, a) {
+ struct shash_node *b_node = shash_find(b, a_node->name);
+ if (!b_node || !json_equal(a_node->data, b_node->data)) {
return false;
}
}
{
const char *cp = ds_cstr(&p->buffer);
unsigned long long int significand = 0;
+ struct json_token token;
int sig_digits = 0;
bool imprecise = false;
bool negative = false;
&& significand <= (negative
? (unsigned long long int) LLONG_MAX + 1
: LLONG_MAX)) {
- struct json_token token;
token.type = T_INTEGER;
token.u.integer = negative ? -significand : significand;
json_parser_input(p, &token);
}
}
- if (pow10 + sig_digits <= DBL_MAX_10_EXP) {
- struct json_token token;
- token.type = T_REAL;
- token.u.real = significand * pow(10.0, pow10);
- if (token.u.real <= DBL_MAX) {
- if (negative && token.u.real) {
- token.u.real = -token.u.real;
- }
- json_parser_input(p, &token);
- return;
- }
+ token.type = T_REAL;
+ if (!str_to_double(ds_cstr(&p->buffer), &token.u.real)) {
+ json_error(p, "number outside valid range");
+ return;
+ }
+ /* Suppress negative zero. */
+ if (token.u.real == 0) {
+ token.u.real = 0;
}
- json_error(p, "number outside valid range");
+ json_parser_input(p, &token);
}
static bool
}
static bool
-json_lex_input(struct json_parser *p, int c)
+json_lex_input(struct json_parser *p, unsigned char c)
{
struct json_token token;
+ p->byte_number++;
+ if (c == '\n') {
+ p->column_number = 0;
+ p->line_number++;
+ } else {
+ p->column_number++;
+ }
+
switch (p->lex_state) {
case JSON_LEX_START:
switch (c) {
struct json *
json_from_file(const char *file_name)
{
- struct json_parser *p;
struct json *json;
FILE *stream;
- /* Open file. */
stream = fopen(file_name, "r");
if (!stream) {
return json_string_create_nocopy(
xasprintf("error opening \"%s\": %s", file_name, strerror(errno)));
}
+ json = json_from_stream(stream);
+ fclose(stream);
+
+ return json;
+}
+
+/* Parses the contents of 'stream' as a JSON object or array, and returns a
+ * newly allocated 'struct json'. The caller must free the returned structure
+ * with json_destroy() when it is no longer needed.
+ *
+ * The file must be encoded in UTF-8.
+ *
+ * See json_from_string() for return value semantics.
+ */
+struct json *
+json_from_stream(FILE *stream)
+{
+ struct json_parser *p;
+ struct json *json;
- /* Read and parse file. */
p = json_parser_create(JSPF_TRAILER);
for (;;) {
char buffer[BUFSIZ];
}
json = json_parser_finish(p);
- /* Close file and check for I/O errors. */
if (ferror(stream)) {
json_destroy(json);
json = json_string_create_nocopy(
- xasprintf("error reading \"%s\": %s", file_name, strerror(errno)));
+ xasprintf("error reading JSON stream: %s", strerror(errno)));
}
- fclose(stream);
return json;
}
json_error(struct json_parser *p, const char *format, ...)
{
if (!p->error) {
+ struct ds msg;
va_list args;
+ ds_init(&msg);
+ ds_put_format(&msg, "line %d, column %d, byte %d: ",
+ p->line_number, p->column_number, p->byte_number);
va_start(args, format);
- p->error = xvasprintf(format, args);
+ ds_put_format_valist(&msg, format, args);
va_end(args);
+ p->error = ds_steal_cstr(&msg);
+
p->done = true;
}
}