/* nanojson - very small JSON event parser with push API - token level

   This nanolib is written by Tibor 'Igor2' Palinkas in 2023 and 2024 and
   is licensed under the Creative Common CC0 (or is placed in the Public Domain
   where it is permitted by the law). */

#ifndef LIBNANOJSON_H
#define LIBNANOJSON_H

typedef enum njson_ev_e {
	/* if any of these is returned a new token has been read succesfully and
	   more characters shall be pushed */
	NJSON_EV_OBJECT_BEGIN,
	NJSON_EV_OBJECT_END,
	NJSON_EV_ARRAY_BEGIN,
	NJSON_EV_ARRAY_END,

	/* value parsed; if ctx->is_name is 1, this is the name of a field
	   within an object */
	NJSON_EV_NAME,              /* name in ctx->value.string */
	NJSON_EV_STRING,            /* value in ctx->value.string */
	NJSON_EV_NUMBER,            /* value in ctx->value.number */
	NJSON_EV_TRUE,
	NJSON_EV_FALSE,
	NJSON_EV_NULL,

	NJSON_EV_error,             /* ran into an unrecoverable error - don't push more */
	NJSON_EV_eof,               /* reached eof - don't push more */
	NJSON_EV_more               /* token not complete, please push more */
} njson_ev_t;

/* internal state */
typedef enum njson_state_e {
	NJSON_ST_IDLE = 0,
	NJSON_ST_NUMBER,
	NJSON_ST_STRING,
	NJSON_ST_STRING_ESC,  /* in string, right after \ */
	NJSON_ST_STRING_HEX,  /* in string, reading a \u hex value */
	NJSON_ST_KEYWORD,     /* true, false, null */
	NJSON_ST_WANT_EOF,
	NJSON_ST_GOT_EOF,
	NJSON_ST_ERROR
} njson_state_t;

/* buffer for internal use */
typedef struct njson_buf_t {
	char *arr;
	long used, alloced;
} njson_buf_t;

typedef struct njson_ctx_s {
	union {
		const char *string;
		double number;
	} value;

	const char *error; /* error message */

	void *user_data; /* the caller is free to use this, njson won't touch it */

	char stack_top; /* either '{' or '[', depending on which type of subtree is being processed */

	long lineno, col; /* location; both counting from 0 */

	/*** internal ***/
	unsigned num_has_dot:1;
	unsigned num_has_exp:1;
	unsigned has_name:1;
	unsigned after_exp:1;

	njson_buf_t str;
	njson_buf_t stack; /* for { and [ opens */

	char ahead; /* read-ahead 1 char for token termination */
	char hex[5];
	int hex_used;

	njson_state_t state;
} njson_ctx_t;

/* Push the next character into the parser. chr is -1 for EOF. Return value
   indicates if a token completed and/or if more characters should be pushed.
   ctx should be all zero before the first call. */
njson_ev_t njson_push(njson_ctx_t *ctx, int chr);

/* Free all memory of ctx and reset it to all-zero */
void njson_uninit(njson_ctx_t *ctx);

#endif
