summaryrefslogtreecommitdiff
path: root/src/libjson/neoas-json-lexer.h
diff options
context:
space:
mode:
Diffstat (limited to 'src/libjson/neoas-json-lexer.h')
-rw-r--r--src/libjson/neoas-json-lexer.h1036
1 files changed, 1036 insertions, 0 deletions
diff --git a/src/libjson/neoas-json-lexer.h b/src/libjson/neoas-json-lexer.h
new file mode 100644
index 0000000..d57d3c8
--- /dev/null
+++ b/src/libjson/neoas-json-lexer.h
@@ -0,0 +1,1036 @@
+#ifndef NEOAS_JSON_LEXER_H_INCLUDED
+# define NEOAS_JSON_LEXER_H_INCLUDED
+
+# include <stdbool.h>
+# include <stddef.h>
+# include <stdint.h>
+# include <sys/types.h>
+# include <math.h>
+
+struct memory_allocator;
+
+struct json_lexer;
+
+static inline int json_lexer_init (const struct memory_allocator *allocator,
+ struct json_lexer *lexer);
+
+static inline void json_lexer_deinit (struct json_lexer *lexer);
+
+static inline void json_lexer_set (struct json_lexer *lexer,
+ int (*handle_null) (void *),
+ int (*handle_bool) (void *, bool value),
+ int (*handle_number) (void *,
+ double value),
+ int (*handle_string) (void *, size_t len,
+ const char *str),
+ int (*handle_object_start) (void *),
+ int (*handle_object_assoc) (void *),
+ int (*handle_object_stop) (void *),
+ int (*handle_array_start) (void *),
+ int (*handle_array_stop) (void *),
+ int (*handle_next) (void *),
+ int (*handle_syntax_error) (void *),
+ /* Parsing a number stops when a
+ foreign character stops the
+ digit sequence. If the number
+ handler returns non-zero, the
+ foreign character is signalled
+ as a rejected input, so you can
+ resume parsing by pushing it. */
+ int (*handle_rejected_input) (void *,
+ char c),
+ void *context);
+
+static inline int json_lexer_push (struct json_lexer *lexer, char c);
+
+static inline int json_lexer_terminate (struct json_lexer *lexer);
+
+static inline int json_lexer_lex (const struct memory_allocator *allocator,
+ ssize_t (*pull) (void *, size_t request,
+ void *data),
+ void *pull_context,
+ int (*handle_null) (void *),
+ int (*handle_bool) (void *, bool value),
+ int (*handle_number) (void *, double value),
+ int (*handle_string) (void *, size_t len,
+ const char *str),
+ int (*handle_object_start) (void *),
+ int (*handle_object_assoc) (void *),
+ int (*handle_object_stop) (void *),
+ int (*handle_array_start) (void *),
+ int (*handle_array_stop) (void *),
+ int (*handle_next) (void *),
+ int (*handle_syntax_error) (void *),
+ int (*handle_rejected_input) (void *,
+ char c),
+ void *context);
+
+static inline int json_lexer_string (const struct memory_allocator *allocator,
+ size_t n,
+ const char *str,
+ int (*handle_null) (void *),
+ int (*handle_bool) (void *, bool value),
+ int (*handle_number) (void *,
+ double value),
+ int (*handle_string) (void *, size_t len,
+ const char *str),
+ int (*handle_object_start) (void *),
+ int (*handle_object_assoc) (void *),
+ int (*handle_object_stop) (void *),
+ int (*handle_array_start) (void *),
+ int (*handle_array_stop) (void *),
+ int (*handle_next) (void *),
+ int (*handle_syntax_error) (void *),
+ int (*handle_rejected_input) (void *,
+ char c),
+ void *context);
+
+struct json_lexer_stringbuf;
+
+static inline int json_lexer_stringbuf_init (const struct memory_allocator
+ *allocator,
+ struct json_lexer_stringbuf
+ *str);
+
+static inline void json_lexer_stringbuf_deinit (struct json_lexer_stringbuf
+ *str);
+
+static inline void json_lexer_stringbuf_set (struct json_lexer_stringbuf *str,
+ int (*handle_success) (void *,
+ size_t
+ size,
+ const char
+ *str),
+ int (*handle_error) (void *),
+ void *context);
+
+static inline int json_lexer_stringbuf_push (struct json_lexer_stringbuf *str,
+ char c);
+
+# include "../liballoc/allocator.h"
+
+struct json_lexer_stringbuf
+{
+ size_t max;
+ size_t size;
+ char *str;
+ uint8_t escape_size;
+ char escape_sequence[6];
+ int (*handle_success) (void *, size_t, const char *);
+ int (*handle_error) (void *);
+ void *context;
+ const struct memory_allocator allocator;
+};
+
+struct json_lexer_number;
+
+static inline void json_lexer_number_set (struct json_lexer_number *nb,
+ int (*handle_success) (void *,
+ double value,
+ char
+ lookahead),
+ int (*handle_error) (void *),
+ void *context);
+
+static inline int json_lexer_number_push (struct json_lexer_number *nb,
+ char c);
+
+static inline int json_lexer_number_terminate (struct json_lexer_number *nb);
+
+enum json_lexer_number_step
+{
+ LEXER_NUMBER_READ_SIGN,
+ LEXER_NUMBER_READ_INTEGER,
+ LEXER_NUMBER_READ_FRACTIONAL,
+ LEXER_NUMBER_READ_EXPONENT_SIGN,
+ LEXER_NUMBER_READ_EXPONENT
+};
+
+struct json_lexer_number
+{
+ enum json_lexer_number_step step;
+ double sign;
+ double digits;
+ int64_t extra_exponent;
+ int64_t exponent_sign;
+ uint64_t exponent;
+ int (*handle_success) (void *, double, char);
+ int (*handle_error) (void *);
+ void *context;
+};
+
+enum json_lexer_state
+{
+ JSON_LEXER_READING_WHITESPACE = 0,
+ JSON_LEXER_READING_STRING,
+ JSON_LEXER_READING_NUMBER,
+ JSON_LEXER_READING_NU,
+ JSON_LEXER_READING_NUL,
+ JSON_LEXER_READING_NULL,
+ JSON_LEXER_READING_FA,
+ JSON_LEXER_READING_FAL,
+ JSON_LEXER_READING_FALS,
+ JSON_LEXER_READING_FALSE,
+ JSON_LEXER_READING_TR,
+ JSON_LEXER_READING_TRU,
+ JSON_LEXER_READING_TRUE
+};
+
+struct json_lexer
+{
+ const struct memory_allocator allocator;
+ enum json_lexer_state state;
+ struct json_lexer_stringbuf str;
+ struct json_lexer_number nb;
+ int (*handle_null) (void *);
+ int (*handle_bool) (void *, bool value);
+ int (*handle_number) (void *, double value);
+ int (*handle_string) (void *, size_t len, const char *str);
+ int (*handle_object_start) (void *);
+ int (*handle_object_assoc) (void *);
+ int (*handle_object_stop) (void *);
+ int (*handle_array_start) (void *);
+ int (*handle_array_stop) (void *);
+ int (*handle_next) (void *);
+ int (*handle_syntax_error) (void *);
+ int (*handle_rejected_input) (void *, char c);
+ void *context;
+};
+
+# include <assert.h>
+
+static inline int
+json_lexer_init (const struct memory_allocator *allocator,
+ struct json_lexer *lexer)
+{
+ copy_allocator ((struct memory_allocator *) (&(lexer->allocator)),
+ allocator);
+ if (json_lexer_stringbuf_init (allocator, &(lexer->str)) != 0)
+ {
+ return -1;
+ }
+ assert (lexer->allocator.allocate == lexer->str.allocator.allocate);
+ assert (lexer->allocator.reallocate == lexer->str.allocator.reallocate);
+ assert (lexer->allocator.deallocate == lexer->str.allocator.deallocate);
+ return 0;
+}
+
+static inline void
+json_lexer_deinit (struct json_lexer *lexer)
+{
+ assert (lexer->allocator.allocate == lexer->str.allocator.allocate);
+ assert (lexer->allocator.reallocate == lexer->str.allocator.reallocate);
+ assert (lexer->allocator.deallocate == lexer->str.allocator.deallocate);
+ json_lexer_stringbuf_deinit (&(lexer->str));
+}
+
+static inline int json_lexer_handle_stringbuf_success (void *ctx, size_t size,
+ const char *str);
+static inline int json_lexer_handle_stringbuf_error (void *ctx);
+static inline int json_lexer_handle_number_success (void *ctx, double value,
+ char lookahead);
+static inline int json_lexer_handle_number_error (void *ctx);
+
+static inline void
+json_lexer_set (struct json_lexer *lexer,
+ int (*handle_null) (void *),
+ int (*handle_bool) (void *, bool value),
+ int (*handle_number) (void *, double value),
+ int (*handle_string) (void *, size_t len, const char *str),
+ int (*handle_object_start) (void *),
+ int (*handle_object_assoc) (void *),
+ int (*handle_object_stop) (void *),
+ int (*handle_array_start) (void *),
+ int (*handle_array_stop) (void *),
+ int (*handle_next) (void *),
+ int (*handle_syntax_error) (void *),
+ /* Parsing a number stops when a
+ foreign character stops the
+ digit sequence. If the number
+ handler returns non-zero, the
+ foreign character is signalled
+ as a rejected input, so you can
+ resume parsing by pushing it. */
+ int (*handle_rejected_input) (void *, char c), void *context)
+{
+ lexer->state = JSON_LEXER_READING_WHITESPACE;
+ json_lexer_stringbuf_set (&(lexer->str),
+ json_lexer_handle_stringbuf_success,
+ json_lexer_handle_stringbuf_error, lexer);
+ json_lexer_number_set (&(lexer->nb),
+ json_lexer_handle_number_success,
+ json_lexer_handle_number_error, lexer);
+ lexer->handle_null = handle_null;
+ lexer->handle_bool = handle_bool;
+ lexer->handle_number = handle_number;
+ lexer->handle_string = handle_string;
+ lexer->handle_object_start = handle_object_start;
+ lexer->handle_object_assoc = handle_object_assoc;
+ lexer->handle_object_stop = handle_object_stop;
+ lexer->handle_array_start = handle_array_start;
+ lexer->handle_array_stop = handle_array_stop;
+ lexer->handle_next = handle_next;
+ lexer->handle_syntax_error = handle_syntax_error;
+ lexer->handle_rejected_input = handle_rejected_input;
+ lexer->context = context;
+ assert (lexer->allocator.allocate == lexer->str.allocator.allocate);
+ assert (lexer->allocator.reallocate == lexer->str.allocator.reallocate);
+ assert (lexer->allocator.deallocate == lexer->str.allocator.deallocate);
+}
+
+static inline int
+json_lexer_push (struct json_lexer *lexer, char c)
+{
+ assert (lexer->allocator.allocate == lexer->str.allocator.allocate);
+ assert (lexer->allocator.reallocate == lexer->str.allocator.reallocate);
+ assert (lexer->allocator.deallocate == lexer->str.allocator.deallocate);
+ switch (lexer->state)
+ {
+ case JSON_LEXER_READING_WHITESPACE:
+ switch (c)
+ {
+ case ' ':
+ case '\t':
+ case '\r':
+ case '\n':
+ /* Stays that way. */
+ return 0;
+
+ case '"':
+ /* Start reading a string. */
+ lexer->state = JSON_LEXER_READING_STRING;
+ return 0;
+
+ case '-':
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ /* Start reading a number. */
+ lexer->state = JSON_LEXER_READING_NUMBER;
+ if (json_lexer_number_push (&(lexer->nb), c) != 0)
+ {
+ /* Impossible, - or [0-9] always start a number. */
+ assert (0);
+ }
+ return 0;
+
+ case 'n':
+ /* Start reading null */
+ lexer->state = JSON_LEXER_READING_NU;
+ return 0;
+
+ case 'f':
+ /* Start reading false */
+ lexer->state = JSON_LEXER_READING_FA;
+ return 0;
+
+ case 't':
+ /* Start reading true */
+ lexer->state = JSON_LEXER_READING_TR;
+ return 0;
+
+ case '{':
+ return lexer->handle_object_start (lexer->context);
+
+ case ':':
+ return lexer->handle_object_assoc (lexer->context);
+
+ case '}':
+ return lexer->handle_object_stop (lexer->context);
+
+ case '[':
+ return lexer->handle_array_start (lexer->context);
+
+ case ']':
+ return lexer->handle_array_stop (lexer->context);
+
+ case ',':
+ return lexer->handle_next (lexer->context);
+
+ default:
+ return lexer->handle_syntax_error (lexer->context);
+ }
+ break;
+
+ case JSON_LEXER_READING_STRING:
+ return json_lexer_stringbuf_push (&(lexer->str), c);
+
+ case JSON_LEXER_READING_NUMBER:
+ return json_lexer_number_push (&(lexer->nb), c);
+
+ case JSON_LEXER_READING_NU:
+ if (c == 'u')
+ {
+ lexer->state = JSON_LEXER_READING_NUL;
+ return 0;
+ }
+ else
+ {
+ lexer->state = JSON_LEXER_READING_WHITESPACE;
+ return lexer->handle_syntax_error (lexer->context);
+ }
+
+ case JSON_LEXER_READING_NUL:
+ if (c == 'l')
+ {
+ lexer->state = JSON_LEXER_READING_NULL;
+ return 0;
+ }
+ else
+ {
+ lexer->state = JSON_LEXER_READING_WHITESPACE;
+ return lexer->handle_syntax_error (lexer->context);
+ }
+
+ case JSON_LEXER_READING_NULL:
+ if (c == 'l')
+ {
+ lexer->state = JSON_LEXER_READING_WHITESPACE;
+ return lexer->handle_null (lexer->context);
+ }
+ else
+ {
+ lexer->state = JSON_LEXER_READING_WHITESPACE;
+ return lexer->handle_syntax_error (lexer->context);
+ }
+
+ case JSON_LEXER_READING_FA:
+ if (c == 'a')
+ {
+ lexer->state = JSON_LEXER_READING_FAL;
+ return 0;
+ }
+ else
+ {
+ lexer->state = JSON_LEXER_READING_WHITESPACE;
+ return lexer->handle_syntax_error (lexer->context);
+ }
+
+ case JSON_LEXER_READING_FAL:
+ if (c == 'l')
+ {
+ lexer->state = JSON_LEXER_READING_FALS;
+ return 0;
+ }
+ else
+ {
+ lexer->state = JSON_LEXER_READING_WHITESPACE;
+ return lexer->handle_syntax_error (lexer->context);
+ }
+
+ case JSON_LEXER_READING_FALS:
+ if (c == 's')
+ {
+ lexer->state = JSON_LEXER_READING_FALSE;
+ return 0;
+ }
+ else
+ {
+ lexer->state = JSON_LEXER_READING_WHITESPACE;
+ return lexer->handle_syntax_error (lexer->context);
+ }
+
+ case JSON_LEXER_READING_FALSE:
+ if (c == 'e')
+ {
+ lexer->state = JSON_LEXER_READING_WHITESPACE;
+ return lexer->handle_bool (lexer->context, false);
+ }
+ else
+ {
+ lexer->state = JSON_LEXER_READING_WHITESPACE;
+ return lexer->handle_syntax_error (lexer->context);
+ }
+
+ case JSON_LEXER_READING_TR:
+ if (c == 'r')
+ {
+ lexer->state = JSON_LEXER_READING_TRU;
+ return 0;
+ }
+ else
+ {
+ lexer->state = JSON_LEXER_READING_WHITESPACE;
+ return lexer->handle_syntax_error (lexer->context);
+ }
+
+ case JSON_LEXER_READING_TRU:
+ if (c == 'u')
+ {
+ lexer->state = JSON_LEXER_READING_TRUE;
+ return 0;
+ }
+ else
+ {
+ lexer->state = JSON_LEXER_READING_WHITESPACE;
+ return lexer->handle_syntax_error (lexer->context);
+ }
+
+ case JSON_LEXER_READING_TRUE:
+ if (c == 'e')
+ {
+ lexer->state = JSON_LEXER_READING_WHITESPACE;
+ return lexer->handle_bool (lexer->context, true);
+ }
+ else
+ {
+ lexer->state = JSON_LEXER_READING_WHITESPACE;
+ return lexer->handle_syntax_error (lexer->context);
+ }
+ default:
+ assert (false);
+ }
+}
+
+static inline int
+json_lexer_terminate (struct json_lexer *lexer)
+{
+ assert (lexer->allocator.allocate == lexer->str.allocator.allocate);
+ assert (lexer->allocator.reallocate == lexer->str.allocator.reallocate);
+ assert (lexer->allocator.deallocate == lexer->str.allocator.deallocate);
+ switch (lexer->state)
+ {
+ case JSON_LEXER_READING_WHITESPACE:
+ return 0;
+ case JSON_LEXER_READING_NUMBER:
+ return json_lexer_number_terminate (&(lexer->nb));
+ default:
+ return lexer->handle_syntax_error (lexer->context);
+ }
+}
+
+static inline int
+json_lexer_handle_stringbuf_success (void *ctx, size_t size, const char *str)
+{
+ struct json_lexer *lexer = ctx;
+ lexer->state = JSON_LEXER_READING_WHITESPACE;
+ return lexer->handle_string (lexer->context, size, str);
+}
+
+static inline int
+json_lexer_handle_stringbuf_error (void *ctx)
+{
+ struct json_lexer *lexer = ctx;
+ lexer->state = JSON_LEXER_READING_WHITESPACE;
+ return lexer->handle_syntax_error (lexer->context);
+}
+
+static inline int
+json_lexer_handle_number_success (void *ctx, double value, char lookahead)
+{
+ struct json_lexer *lexer = ctx;
+ lexer->state = JSON_LEXER_READING_WHITESPACE;
+ int error = lexer->handle_number (lexer->context, value);
+ if (error == 0)
+ {
+ return json_lexer_push (lexer, lookahead);
+ }
+ else
+ {
+ return lexer->handle_rejected_input (lexer->context, lookahead);
+ }
+}
+
+static inline int
+json_lexer_handle_number_error (void *ctx)
+{
+ struct json_lexer *lexer = ctx;
+ lexer->state = JSON_LEXER_READING_WHITESPACE;
+ return lexer->handle_syntax_error (lexer->context);
+}
+
+static inline int
+json_lexer_stringbuf_init (const struct memory_allocator *allocator,
+ struct json_lexer_stringbuf *str)
+{
+ str->max = 8;
+ copy_allocator ((struct memory_allocator *) (&(str->allocator)), allocator);
+ if (ALLOCATE (str->str, str->max) < 0)
+ {
+ return -1;
+ }
+ return 0;
+}
+
+static inline void
+json_lexer_stringbuf_deinit (struct json_lexer_stringbuf *str)
+{
+ if (str != NULL)
+ {
+ const struct memory_allocator *allocator = &(str->allocator);
+ DEALLOCATE (str->str);
+ }
+}
+
+static inline void
+json_lexer_stringbuf_set (struct json_lexer_stringbuf *str,
+ int (*handle_success) (void *, size_t,
+ const char *),
+ int (*handle_error) (void *), void *context)
+{
+ str->size = 0;
+ str->escape_size = 0;
+ str->handle_success = handle_success;
+ str->handle_error = handle_error;
+ str->context = context;
+}
+
+static inline int json_lexer_stringbuf_push_char (size_t *max,
+ size_t *size,
+ char **str,
+ const struct
+ memory_allocator *allocator,
+ char c);
+
+static inline int
+json_lexer_stringbuf_push (struct json_lexer_stringbuf *str, char c)
+{
+ if (str->escape_size)
+ {
+ assert (str->escape_size < 6);
+ /* Parsing an escape sequence. */
+ str->escape_sequence[str->escape_size++] = c;
+ /* Try to reduce the escape sequence. */
+ switch (str->escape_sequence[1])
+ {
+ case '"':
+ case '\\':
+ case '/':
+ str->escape_size = 0;
+ return json_lexer_stringbuf_push_char (&(str->max), &(str->size),
+ &(str->str),
+ &(str->allocator),
+ str->escape_sequence[1]);
+ case 'b':
+ str->escape_size = 0;
+ return json_lexer_stringbuf_push_char (&(str->max), &(str->size),
+ &(str->str),
+ &(str->allocator), '\b');
+ case 'f':
+ str->escape_size = 0;
+ return json_lexer_stringbuf_push_char (&(str->max), &(str->size),
+ &(str->str),
+ &(str->allocator), '\f');
+ case 'n':
+ str->escape_size = 0;
+ return json_lexer_stringbuf_push_char (&(str->max), &(str->size),
+ &(str->str),
+ &(str->allocator), '\n');
+ case 'r':
+ str->escape_size = 0;
+ return json_lexer_stringbuf_push_char (&(str->max), &(str->size),
+ &(str->str),
+ &(str->allocator), '\r');
+ case 't':
+ str->escape_size = 0;
+ return json_lexer_stringbuf_push_char (&(str->max), &(str->size),
+ &(str->str),
+ &(str->allocator), '\t');
+ case 'u':
+ if (str->escape_size == 6)
+ {
+ uint16_t escape_sequence = 0;
+ static const uint16_t factors[] = { 4096, 256, 16, 0 };
+ for (size_t i = 0; i < 4; i++)
+ {
+ char c = str->escape_sequence[i + 2];
+ if (c >= '0' && c <= '9')
+ {
+ escape_sequence += factors[i] * (c - '0');
+ }
+ else if (c >= 'a' && c <= 'f')
+ {
+ escape_sequence += factors[i] * (c - 'a' + 10);
+ }
+ else if (c >= 'A' && c <= 'F')
+ {
+ escape_sequence += factors[i] * (c - 'A' + 10);
+ }
+ else
+ {
+ str->escape_size = 0;
+ return str->handle_error (str->context);
+ }
+ }
+ str->escape_size = 0;
+ return json_lexer_stringbuf_push_char (&(str->max),
+ &(str->size),
+ &(str->str),
+ &(str->allocator),
+ (uint8_t)
+ escape_sequence);
+ }
+ else
+ {
+ return 0;
+ }
+ default:
+ str->escape_size = 0;
+ return str->handle_error (str->context);
+ }
+ }
+ else if (c == '\\')
+ {
+ str->escape_size = 1;
+ str->escape_sequence[0] = c;
+ return 0;
+ }
+ else if (c == '"')
+ {
+ assert (str->escape_size == 0);
+ int error = str->handle_success (str->context, str->size, str->str);
+ str->size = 0;
+ return error;
+ }
+ else
+ {
+ /* What about control characters? */
+ return json_lexer_stringbuf_push_char (&(str->max), &(str->size),
+ &(str->str), &(str->allocator),
+ c);
+ }
+}
+
+static inline void
+json_lexer_number_set (struct json_lexer_number *nb,
+ int (*handle_success) (void *, double, char),
+ int (*handle_error) (void *), void *context)
+{
+ nb->step = LEXER_NUMBER_READ_SIGN;
+ nb->sign = 1;
+ nb->digits = 0;
+ nb->extra_exponent = 0;
+ nb->exponent_sign = 1;
+ nb->exponent = 0;
+ nb->handle_success = handle_success;
+ nb->handle_error = handle_error;
+ nb->context = context;
+}
+
+static inline int
+json_lexer_number_accept (struct json_lexer_number *nb, char lookahead)
+{
+ const double final_exponent =
+ (((double) (nb->exponent_sign) * (double) (nb->exponent))
+ + (double) (nb->extra_exponent));
+ const double factor = pow (10.0, final_exponent);
+ const double value = nb->sign * nb->digits * factor;
+ nb->sign = +1;
+ nb->digits = 0;
+ nb->extra_exponent = 0;
+ nb->exponent_sign = 1;
+ nb->exponent = 0;
+ nb->step = LEXER_NUMBER_READ_SIGN;
+ return nb->handle_success (nb->context, value, lookahead);
+}
+
+static inline int
+json_lexer_number_push (struct json_lexer_number *nb, char c)
+{
+ /* FIXME: it accepts 0....123 as 0.123, 0345 as 345, ----4 as -4,
+ 123eEeeE4 as 123e4, .4 as 0.4 */
+ switch (nb->step)
+ {
+ case LEXER_NUMBER_READ_SIGN:
+ switch (c)
+ {
+ case '-':
+ nb->sign = -1;
+ nb->step = LEXER_NUMBER_READ_INTEGER;
+ return 0;
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ nb->digits *= 10;
+ nb->digits += (c - '0');
+ nb->step = LEXER_NUMBER_READ_INTEGER;
+ return 0;
+ default:
+ nb->step = LEXER_NUMBER_READ_SIGN;
+ return nb->handle_error (nb->context);
+ }
+ break; /* unreachable */
+ case LEXER_NUMBER_READ_INTEGER:
+ switch (c)
+ {
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ nb->digits *= 10;
+ nb->digits += (c - '0');
+ return 0;
+ case '.':
+ nb->step = LEXER_NUMBER_READ_FRACTIONAL;
+ return 0;
+ case 'e':
+ case 'E':
+ nb->step = LEXER_NUMBER_READ_EXPONENT_SIGN;
+ return 0;
+ default:
+ return json_lexer_number_accept (nb, c);
+ }
+ break; /* unreachable */
+ case LEXER_NUMBER_READ_FRACTIONAL:
+ switch (c)
+ {
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ nb->digits *= 10;
+ nb->digits += (c - '0');
+ nb->extra_exponent -= 1;
+ return 0;
+ case 'e':
+ case 'E':
+ nb->step = LEXER_NUMBER_READ_EXPONENT_SIGN;
+ return 0;
+ default:
+ return json_lexer_number_accept (nb, c);
+ }
+ break; /* unreachable */
+ case LEXER_NUMBER_READ_EXPONENT_SIGN:
+ switch (c)
+ {
+ case '-':
+ nb->exponent_sign = -1;
+ nb->step = LEXER_NUMBER_READ_EXPONENT;
+ return 0;
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ nb->exponent *= 10;
+ nb->exponent += (c - '0');
+ nb->step = LEXER_NUMBER_READ_EXPONENT;
+ return 0;
+ default:
+ return json_lexer_number_accept (nb, c);
+ }
+ break; /* unreachable */
+ case LEXER_NUMBER_READ_EXPONENT:
+ switch (c)
+ {
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ nb->exponent *= 10;
+ nb->exponent += (c - '0');
+ return 0;
+ default:
+ return json_lexer_number_accept (nb, c);
+ }
+ break; /* unreachable */
+ default:
+ assert (false);
+ }
+}
+
+static inline int
+json_lexer_number_terminate (struct json_lexer_number *nb)
+{
+ switch (nb->step)
+ {
+ case LEXER_NUMBER_READ_SIGN:
+ /* No number being parsed. */
+ return 0;
+ default:
+ /* FIXME: what if it’s "1.0E"? Accept as "1.0" for now */
+ return json_lexer_number_push (nb, '\0');
+ }
+}
+
+static inline int
+json_lexer_lex (const struct memory_allocator *allocator,
+ ssize_t (*pull) (void *, size_t request, void *data),
+ void *pull_context,
+ int (*handle_null) (void *),
+ int (*handle_bool) (void *, bool value),
+ int (*handle_number) (void *, double value),
+ int (*handle_string) (void *, size_t len, const char *str),
+ int (*handle_object_start) (void *),
+ int (*handle_object_assoc) (void *),
+ int (*handle_object_stop) (void *),
+ int (*handle_array_start) (void *),
+ int (*handle_array_stop) (void *),
+ int (*handle_next) (void *),
+ int (*handle_syntax_error) (void *),
+ int (*handle_rejected_input) (void *, char c), void *context)
+{
+ struct json_lexer lexer;
+ int error = 0;
+ error = json_lexer_init (allocator, &lexer);
+ if (error != 0)
+ {
+ goto ret;
+ }
+ static const size_t buffer_size = 4096;
+ char *buffer = NULL;
+ if (ALLOCATE (buffer, buffer_size) < 0)
+ {
+ error = -2;
+ goto cleanup;
+ }
+ json_lexer_set (&lexer, handle_null, handle_bool, handle_number,
+ handle_string, handle_object_start,
+ handle_object_assoc, handle_object_stop,
+ handle_array_start, handle_array_stop,
+ handle_next, handle_syntax_error,
+ handle_rejected_input, context);
+ ssize_t n_available;
+ do
+ {
+ assert (lexer.allocator.allocate == lexer.str.allocator.allocate);
+ assert (lexer.allocator.reallocate == lexer.str.allocator.reallocate);
+ assert (lexer.allocator.deallocate == lexer.str.allocator.deallocate);
+ n_available = pull (pull_context, buffer_size, buffer);
+ if (n_available > 0)
+ {
+ assert (n_available >= 0);
+ assert ((size_t) n_available <= buffer_size);
+ for (ssize_t i = 0; i < n_available; i++)
+ {
+ error = json_lexer_push (&lexer, buffer[i]);
+ if (error != 0)
+ {
+ goto cleanup;
+ }
+ }
+ }
+ }
+ while (n_available > 0);
+ error = json_lexer_terminate (&lexer);
+ if (error != 0)
+ {
+ goto cleanup;
+ }
+cleanup:
+ assert (lexer.allocator.allocate == lexer.str.allocator.allocate);
+ assert (lexer.allocator.reallocate == lexer.str.allocator.reallocate);
+ assert (lexer.allocator.deallocate == lexer.str.allocator.deallocate);
+ DEALLOCATE (buffer);
+ json_lexer_deinit (&lexer);
+ret:
+ return error;
+}
+
+struct string_puller
+{
+ size_t size;
+ const char *str;
+};
+
+static inline ssize_t
+json_lexer_pull_from_string (void *ctx, size_t request, void *data)
+{
+ struct string_puller *puller = ctx;
+ ssize_t ret = 0;
+ char *output = data;
+ while (request != 0)
+ {
+ if (puller->size == 0)
+ {
+ return ret;
+ }
+ *output = *puller->str;
+ (puller->size)--;
+ (puller->str)++;
+ request--;
+ output++;
+ }
+ return ret;
+}
+
+static inline int
+json_lexer_string (const struct memory_allocator *allocator,
+ size_t n,
+ const char *str,
+ int (*handle_null) (void *),
+ int (*handle_bool) (void *, bool value),
+ int (*handle_number) (void *, double value),
+ int (*handle_string) (void *, size_t len, const char *str),
+ int (*handle_object_start) (void *),
+ int (*handle_object_assoc) (void *),
+ int (*handle_object_stop) (void *),
+ int (*handle_array_start) (void *),
+ int (*handle_array_stop) (void *),
+ int (*handle_next) (void *),
+ int (*handle_syntax_error) (void *),
+ int (*handle_rejected_input) (void *, char c),
+ void *context)
+{
+ struct string_puller puller = {.size = n,.str = str };
+ return json_lexer_lex (allocator, json_lexer_pull_from_string, &puller,
+ handle_null, handle_bool, handle_number,
+ handle_string, handle_object_start,
+ handle_object_assoc, handle_object_stop,
+ handle_array_start, handle_array_stop,
+ handle_next, handle_syntax_error,
+ handle_rejected_input, context);
+}
+
+static inline int
+json_lexer_stringbuf_push_char (size_t *max,
+ size_t *size,
+ char **str,
+ const struct memory_allocator *allocator,
+ char c)
+{
+ if (*size >= *max)
+ {
+ size_t new_max = 2 * *max;
+ if (new_max == 0)
+ {
+ new_max = 1;
+ }
+ if (REALLOCATE (*str, new_max) < 0)
+ {
+ return -1;
+ }
+ *max = new_max;
+ }
+ (*str)[(*size)++] = c;
+ return 0;
+}
+
+#endif /* NEOAS_JSON_LEXER_H_INCLUDED */