/*
 * fy-parse.h - YAML parser internal header file
 *
 * Copyright (c) 2019 Pantelis Antoniou <pantelis.antoniou@konsulko.com>
 *
 * SPDX-License-Identifier: MIT
 */
#ifndef FY_PARSE_H
#define FY_PARSE_H

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include <stdint.h>
#include <stdbool.h>
#include <stdio.h>

#include <libfyaml.h>

#include "fy-ctype.h"
#include "fy-utf8.h"
#include "fy-list.h"
#include "fy-typelist.h"
#include "fy-types.h"
#include "fy-diag.h"
#include "fy-dump.h"
#include "fy-atom.h"
#include "fy-input.h"
#include "fy-ctype.h"
#include "fy-token.h"
#include "fy-event.h"
#include "fy-docstate.h"
#include "fy-doc.h"
#include "fy-docbuilder.h"
#include "fy-emit.h"
#include "fy-accel.h"
#include "fy-emit-accum.h"
#include "fy-path.h"
#include "fy-composer.h"

struct fy_parser;
struct fy_input;

/* for the event */
FY_PARSE_TYPE_DECL_ALLOC(eventp);

enum fy_flow_type {
	FYFT_NONE,
	FYFT_MAP,
	FYFT_SEQUENCE,
};

struct fy_flow {
	struct list_head node;
	enum fy_flow_type flow;
	int pending_complex_key_column;
	struct fy_mark pending_complex_key_mark;
	int parent_indent;
};
FY_PARSE_TYPE_DECL(flow);

struct fy_indent {
	struct list_head node;
	int indent;
	int indent_line;
	bool generated_block_map : 1;
};
FY_PARSE_TYPE_DECL(indent);

struct fy_token;

struct fy_simple_key {
	struct list_head node;
	struct fy_mark mark;
	struct fy_mark end_mark;
	struct fy_token *token;	/* associated token */
	int flow_level;
	bool required : 1;
	bool implicit_complex : 1;
};
FY_PARSE_TYPE_DECL(simple_key);

struct fy_document_state;

enum fy_parser_state {
	/** none state */
	FYPS_NONE,
	/** Expect STREAM-START. */
	FYPS_STREAM_START,
	/** Expect the beginning of an implicit document. */
	FYPS_IMPLICIT_DOCUMENT_START,
	/** Expect DOCUMENT-START. */
	FYPS_DOCUMENT_START,
	/** Expect the content of a document. */
	FYPS_DOCUMENT_CONTENT,
	/** Expect DOCUMENT-END. */
	FYPS_DOCUMENT_END,
	/** Expect a block node. */
	FYPS_BLOCK_NODE,
	/** Expect the first entry of a block sequence. */
	FYPS_BLOCK_SEQUENCE_FIRST_ENTRY,
	/** Expect an entry of a block sequence. */
	FYPS_BLOCK_SEQUENCE_ENTRY,
	/** Expect an entry of an indentless sequence. */
	FYPS_INDENTLESS_SEQUENCE_ENTRY,
	/** Expect the first key of a block mapping. */
	FYPS_BLOCK_MAPPING_FIRST_KEY,
	/** Expect a block mapping key. */
	FYPS_BLOCK_MAPPING_KEY,
	/** Expect a block mapping value. */
	FYPS_BLOCK_MAPPING_VALUE,
	/** Expect the first entry of a flow sequence. */
	FYPS_FLOW_SEQUENCE_FIRST_ENTRY,
	/** Expect an entry of a flow sequence. */
	FYPS_FLOW_SEQUENCE_ENTRY,
	/** Expect a key of an ordered mapping. */
	FYPS_FLOW_SEQUENCE_ENTRY_MAPPING_KEY,
	/** Expect a value of an ordered mapping. */
	FYPS_FLOW_SEQUENCE_ENTRY_MAPPING_VALUE,
	/** Expect the and of an ordered mapping entry. */
	FYPS_FLOW_SEQUENCE_ENTRY_MAPPING_END,
	/** Expect the first key of a flow mapping. */
	FYPS_FLOW_MAPPING_FIRST_KEY,
	/** Expect a key of a flow mapping. */
	FYPS_FLOW_MAPPING_KEY,
	/** Expect a value of a flow mapping. */
	FYPS_FLOW_MAPPING_VALUE,
	/** Expect an empty value of a flow mapping. */
	FYPS_FLOW_MAPPING_EMPTY_VALUE,
	/** Expect only stream end */
	FYPS_SINGLE_DOCUMENT_END,
	/** Expect nothing. */
	FYPS_END
};

struct fy_parse_state_log {
	struct list_head node;
	enum fy_parser_state state;
};
FY_PARSE_TYPE_DECL(parse_state_log);

struct fy_streaming_alias {
	struct list_head node;
	struct fy_token *anchor;
	bool collecting;
	long mapping_nest;
	long sequence_nest;
	struct fy_eventp_list events;
};
FY_PARSE_TYPE_DECL(streaming_alias);

struct fy_streaming_alias_state {
	struct fy_streaming_alias *fysa;
	struct fy_eventp *next;
};

/* 2 bits per collection tracking state
 * pos: * 2
 * bit 0 - 0 map, 1 seq
 * bit 1 - 0 key, 1 val (if map)
 */
#define FYCTS_SCALAR  0
#define FYCTS_SEQ     1
#define FYCTS_MAP     2
#define FYCTS_MAP_KEY 2
#define FYCTS_MAP_VAL 3
#define FYCTS_MAP_TOGGLE 1

struct fy_parser {
	struct fy_parse_cfg cfg;

	struct fy_input_list queued_inputs;	/* all the inputs queued */
	struct fy_reader builtin_reader;	/* the builtin reader */
	struct fy_reader *reader;		/* the external reader, or ptr to builtin_reader */

	struct fy_version default_version;
	struct fy_version stream_version;	/* last version set in stream */

	union {
		struct {
			bool suppress_recycling : 1;
			bool stream_start_produced : 1;
			bool stream_end_produced : 1;
			bool stream_end_reached : 1;
			bool simple_key_allowed : 1;
			bool tab_used_for_ws : 1;
			bool stream_error : 1;
			bool generated_block_map : 1;
			bool last_was_comma : 1;
			bool document_has_content : 1;
			bool document_first_content_token : 1;
			bool bare_document_only : 1;		/* no document start indicators allowed, no directives */
			bool stream_has_content : 1;
			bool parse_flow_only : 1;	/* document is in flow form, and stop parsing at the end */
			bool parse_block_only : 1;	/* document is in embedded block form, and stop parsing at the end */
			bool colon_follows_colon : 1;	/* "foo"::bar -> "foo": :bar */
			bool had_directives : 1;	/* document had directives */
			bool is_checkpoint : 1;		/* a parser checkpoint (not a real parser) */
		};
		unsigned int parser_bitflags;
	};

	int flow_level;
	int pending_complex_key_column;
	struct fy_mark pending_complex_key_mark;
	int last_block_mapping_key_line;
	struct fy_mark last_comma_mark;
	struct fy_mark last_tab_used_for_ws_mark;

	/* copy of stream_end token */
	struct fy_token *stream_end_token;

	/* produced tokens, but not yet consumed */
	struct fy_token_list queued_tokens;
	int token_activity_counter;

	/* last comment */
	struct fy_atom last_comment;
	/* next comment */
	struct fy_atom override_comment;

	/* indent stack */
	struct fy_indent_list indent_stack;
	int indent;
	int parent_indent;
	int indent_line;
	int starting_indent;
	/* simple key stack */
	struct fy_simple_key_list simple_keys;
	/* state stack */
	enum fy_parser_state state;
	struct fy_parse_state_log_list state_stack;

	/* current parse document */
	struct fy_document_state *current_document_state;
	struct fy_document_state *default_document_state;
	bool next_single_document;

	/* flow stack */
	enum fy_flow_type flow;
	struct fy_flow_list flow_stack;

	/* recycling lists */
	struct fy_indent_list recycled_indent;
	struct fy_simple_key_list recycled_simple_key;
	struct fy_parse_state_log_list recycled_parse_state_log;
	struct fy_flow_list recycled_flow;
	struct fy_streaming_alias_list recycled_streaming_alias;

	struct fy_eventp_list recycled_eventp;
	struct fy_token_list recycled_token;

	struct fy_eventp_list *recycled_eventp_list;
	struct fy_token_list *recycled_token_list;

	/* the diagnostic object */
	struct fy_diag *diag;

	int err_term_width;
	int err_term_height;

	/* for when using the built-in document builder */
	struct fy_document_builder *fydb;

	/* when using the composer interface */
	struct fy_composer *fyc;
	fy_parse_composer_cb fyc_cb;
	void *fyc_userdata;

	/* last generated event atom */
	struct fy_atom last_event_handle;

	struct fy_streaming_alias_list streaming_aliases;
	/* streaming alias state */
	struct {
		int alloc;
		int top;
		struct fy_streaming_alias_state *stack;
		struct fy_streaming_alias_state local[8];
	} sas;
	/* collection tracking state */
	struct {
		int alloc;
		int top;
		uint32_t *stack;
		uint32_t local[8];	/* 8 * 32 = 256 / 2 = 128 levels before needing dynamic allocation */
	} cts;
	/* merge key state */
	struct {
		bool active;
		struct fy_eventp_list args;
		struct fy_document *fyd;
	} mks;
	struct fy_eventp *fyep_peek;
};

static inline struct fy_input *
fyp_current_input(const struct fy_parser *fyp)
{
	assert(fyp);
	return fy_reader_current_input(fyp->reader);
}

static inline uint64_t
fyp_current_input_generation(const struct fy_parser *fyp)
{
	assert(fyp);
	return fy_reader_current_input_generation(fyp->reader);
}

static inline int fyp_column(const struct fy_parser *fyp)
{
	assert(fyp);
	return fy_reader_column(fyp->reader);
}

static inline int fyp_line(const struct fy_parser *fyp)
{
	return fy_reader_line(fyp->reader);
}

static inline int fyp_tabsize(const struct fy_parser *fyp)
{
	return fy_reader_tabsize(fyp->reader);
}

static inline bool fyp_json_mode(const struct fy_parser *fyp)
{
	assert(fyp);
	return fy_reader_json_mode(fyp->reader);
}

static inline enum fy_lb_mode fyp_lb_mode(const struct fy_parser *fyp)
{
	assert(fyp);
	return fy_reader_lb_mode(fyp->reader);
}

static inline enum fy_flow_ws_mode fyp_fws_mode(const struct fy_parser *fyp)
{
	assert(fyp);
	return fy_reader_flow_ws_mode(fyp->reader);
}

static inline bool fyp_directive0_mode(const struct fy_parser *fyp)
{
	assert(fyp);
	return fy_reader_directive0_mode(fyp->reader);
}

static inline bool fyp_block_mode(struct fy_parser *fyp)
{
	return !fyp_json_mode(fyp) && !fyp->flow_level;
}

static inline bool fyp_is_lb(const struct fy_parser *fyp, int c)
{
	assert(fyp);
	return fy_reader_is_lb(fyp->reader, c);
}

static inline bool fyp_is_lbz(const struct fy_parser *fyp, int c)
{
	assert(fyp);
	return fy_reader_is_lbz(fyp->reader, c);
}

static inline bool fyp_is_blankz(const struct fy_parser *fyp, int c)
{
	assert(fyp);
	return fy_reader_is_blankz(fyp->reader, c);
}

static inline bool fyp_is_generic_lb(const struct fy_parser *fyp, int c)
{
	assert(fyp);
	return fy_reader_is_generic_lb(fyp->reader, c);
}

static inline bool fyp_is_generic_lbz(const struct fy_parser *fyp, int c)
{
	assert(fyp);
	return fy_reader_is_generic_lbz(fyp->reader, c);
}

static inline bool fyp_is_generic_blankz(const struct fy_parser *fyp, int c)
{
	assert(fyp);
	return fy_reader_is_generic_blankz(fyp->reader, c);
}

static inline bool fyp_is_flow_ws(const struct fy_parser *fyp, int c)
{
	assert(fyp);
	return fy_reader_is_flow_ws(fyp->reader, c);
}

static inline bool fyp_is_flow_blank(const struct fy_parser *fyp, int c)
{
	assert(fyp);
	return fy_reader_is_flow_blank(fyp->reader, c);
}

static inline bool fyp_is_flow_blankz(const struct fy_parser *fyp, int c)
{
	assert(fyp);
	return fy_reader_is_flow_blankz(fyp->reader, c);
}

static inline const void *
fy_ensure_lookahead_slow_path(struct fy_parser *fyp, size_t size, size_t *leftp)
{
	assert(fyp);
	return fy_reader_ensure_lookahead_slow_path(fyp->reader, size, leftp);
}

/* only allowed if input does not update */
static inline void
fy_get_mark(struct fy_parser *fyp, struct fy_mark *fym)
{
	assert(fyp);
	fy_reader_get_mark(fyp->reader, fym);
}

static inline const void *
fy_ptr(struct fy_parser *fyp, size_t *leftp)
{
	assert(fyp);
	return fy_reader_ptr(fyp->reader, leftp);
}

static inline const void *
fy_ensure_lookahead(struct fy_parser *fyp, size_t size, size_t *leftp)
{
	assert(fyp);
	return fy_reader_ensure_lookahead(fyp->reader, size, leftp);
}

/* advance the given number of ascii characters, not utf8 */
static inline void
fy_advance_octets(struct fy_parser *fyp, size_t advance)
{
	assert(fyp);
	fy_reader_advance_octets(fyp->reader, advance);
}

/* compare string at the current point (n max) */
static inline int
fy_parse_strncmp(struct fy_parser *fyp, const char *str, size_t n)
{
	assert(fyp);
	return fy_reader_strncmp(fyp->reader, str, n);
}

static FY_ALWAYS_INLINE inline int
fy_parse_peek_at_offset_width(struct fy_parser *fyp, size_t offset, int *wp)
{
	assert(fyp);
	return fy_reader_peek_at_offset_width(fyp->reader, offset, wp);
}

static FY_ALWAYS_INLINE inline int
fy_parse_peek_at_offset(struct fy_parser *fyp, size_t offset)
{
	assert(fyp);
	return fy_reader_peek_at_offset(fyp->reader, offset);
}

static FY_ALWAYS_INLINE inline int
fy_parse_peek_at_width_internal(struct fy_parser *fyp, int pos, ssize_t *offsetp, int *wp)
{
	assert(fyp);
	return fy_reader_peek_at_width_internal(fyp->reader, pos, offsetp, wp);
}

static FY_ALWAYS_INLINE inline int
fy_parse_peek_at_internal(struct fy_parser *fyp, int pos, ssize_t *offsetp)
{
	assert(fyp);
	return fy_reader_peek_at_internal(fyp->reader, pos, offsetp);
}

static inline bool
fy_is_blank_at_offset(struct fy_parser *fyp, size_t offset)
{
	assert(fyp);
	return fy_is_blank(fy_reader_peek_at_offset(fyp->reader, offset));
}

static inline bool
fy_is_blankz_at_offset(struct fy_parser *fyp, size_t offset)
{
	assert(fyp);
	return fy_reader_is_blankz(fyp->reader, fy_reader_peek_at_offset(fyp->reader, offset));
}

static inline bool
fy_is_generic_blankz_at_offset(struct fy_parser *fyp, size_t offset)
{
	assert(fyp);
	return fy_reader_is_generic_blankz(fyp->reader, fy_reader_peek_at_offset(fyp->reader, offset));
}

static FY_ALWAYS_INLINE inline int
fy_parse_peek_at_width(struct fy_parser *fyp, int pos, int *wp)
{
	assert(fyp);
	return fy_reader_peek_at_width(fyp->reader, pos, wp);
}

static FY_ALWAYS_INLINE inline int
fy_parse_peek_at(struct fy_parser *fyp, int pos)
{
	assert(fyp);
	return fy_reader_peek_at(fyp->reader, pos);
}

static FY_ALWAYS_INLINE inline int
fy_parse_peek_width(struct fy_parser *fyp, int *wp)
{
	assert(fyp);
	return fy_reader_peek_width(fyp->reader, wp);
}

static FY_ALWAYS_INLINE inline int
fy_parse_peek(struct fy_parser *fyp)
{
	assert(fyp);
	return fy_reader_peek(fyp->reader);
}

static FY_ALWAYS_INLINE inline void
fy_advance(struct fy_parser *fyp, int c)
{
	assert(fyp);
	fy_reader_advance(fyp->reader, c);
}

static FY_ALWAYS_INLINE inline void
fy_advance_ws(struct fy_parser *fyp, int c)
{
	assert(fyp);
	fy_reader_advance_ws(fyp->reader, c);
}

static FY_ALWAYS_INLINE inline void
fy_advance_space(struct fy_parser *fyp)
{
	assert(fyp);
	fy_reader_advance_space(fyp->reader);
}

static FY_ALWAYS_INLINE inline int
fy_parse_get(struct fy_parser *fyp)
{
	assert(fyp);
	return fy_reader_get(fyp->reader);
}

static FY_ALWAYS_INLINE inline int
fy_advance_by(struct fy_parser *fyp, int count)
{
	assert(fyp);
	return fy_reader_advance_by(fyp->reader, count);
}

/* compare string at the current point */
static inline bool
fy_parse_strcmp(struct fy_parser *fyp, const char *str)
{
	assert(fyp);
	return fy_reader_strcmp(fyp->reader, str);
}

static inline void
fy_fill_atom_start(struct fy_parser *fyp, struct fy_atom *handle)
{
	assert(fyp);
	fy_reader_fill_atom_start(fyp->reader, handle);
}

static inline void
fy_fill_atom_end_at(struct fy_parser *fyp, struct fy_atom *handle, struct fy_mark *end_mark)
{
	assert(fyp);
	fy_reader_fill_atom_end_at(fyp->reader, handle, end_mark);
}

static inline void
fy_fill_atom_end(struct fy_parser *fyp, struct fy_atom *handle)
{
	assert(fyp);
	fy_reader_fill_atom_end(fyp->reader, handle);
}

static inline struct fy_atom *
fy_fill_atom(struct fy_parser *fyp, int advance, struct fy_atom *handle)
{
	assert(fyp);
	return fy_reader_fill_atom(fyp->reader, advance, handle);
}

static inline struct fy_atom *
fy_fill_atom_mark(struct fy_parser *fyp, const struct fy_mark *start_mark,
		  const struct fy_mark *end_mark, struct fy_atom *handle)
{
	assert(fyp);
	return fy_reader_fill_atom_mark(fyp->reader, start_mark, end_mark, handle);
}

static inline struct fy_atom *
fy_fill_atom_at(struct fy_parser *fyp, int advance, int count, struct fy_atom *handle)
{
	assert(fyp);
	return fy_reader_fill_atom_at(fyp->reader, advance, count, handle);
}

static inline void
fy_parser_set_reader(struct fy_parser *fyp, struct fy_reader *fyr)
{
	if (!fyp)
		return;
	fyp->reader = fyr ? fyr : &fyp->builtin_reader;
}

static inline void
fy_parser_set_flow_only_mode(struct fy_parser *fyp, bool flow_only_mode)
{
	fyp->parse_flow_only = flow_only_mode;
}

static inline void
fy_parser_set_block_only_mode(struct fy_parser *fyp, bool block_only_mode)
{
	fyp->parse_block_only = block_only_mode;
}

#define fy_fill_atom_a(_fyp, _advance) \
	fy_fill_atom((_fyp), (_advance), alloca(sizeof(struct fy_atom)))

struct fy_token *fy_token_vqueue(struct fy_parser *fyp, enum fy_token_type type, va_list ap);
struct fy_token *fy_token_queue(struct fy_parser *fyp, enum fy_token_type type, ...);

struct fy_token *
fy_token_vqueue_internal(struct fy_parser *fyp, struct fy_token_list *fytl,
			 enum fy_token_type type, va_list ap);
struct fy_token *
fy_token_queue_internal(struct fy_parser *fyp, struct fy_token_list *fytl,
			enum fy_token_type type, ...);

int fy_parse_setup(struct fy_parser *fyp, const struct fy_parse_cfg *cfg);
void fy_parse_cleanup(struct fy_parser *fyp);

int fy_parse_input_append(struct fy_parser *fyp, const struct fy_input_cfg *fyic);
ssize_t fy_parse_estimate_queued_input_size(struct fy_parser *fyp);

struct fy_eventp *fy_parse_private(struct fy_parser *fyp);

extern const char *fy_event_type_txt[];

enum fy_parse_cfg_flags fy_parser_get_cfg_flags(const struct fy_parser *fyp);

extern const struct fy_tag * const fy_default_tags[];

extern const struct fy_version fy_default_version;	/* usually highest stable */

bool fy_tag_handle_is_default(const char *handle, size_t handle_size);
bool fy_tag_is_default_internal(const char *handle, size_t handle_size,
				const char *prefix, size_t prefix_size);
bool fy_token_tag_directive_is_overridable(struct fy_token *fyt_td);

int fy_parser_set_default_document_state(struct fy_parser *fyp,
					 struct fy_document_state *fyds);
void fy_parser_set_next_single_document(struct fy_parser *fyp);

void *fy_alloc_default(void *userdata, size_t size);
void fy_free_default(void *userdata, void *ptr);
void *fy_realloc_default(void *userdata, void *ptr, size_t size);

int fy_reader_fetch_flow_scalar_handle(struct fy_reader *fyr, int c, int indent, struct fy_atom *handle, bool sloppy_indent);
int fy_reader_fetch_plain_scalar_handle(struct fy_reader *fyr, int c, int indent, int flow_level, struct fy_atom *handle);

void fy_reader_skip_ws_cr_nl(struct fy_reader *fyr);

void fy_reader_skip_ws(struct fy_reader *fyr);
void fy_reader_skip_space(struct fy_reader *fyr);

static inline int fy_document_state_version_compare(struct fy_document_state *fyds, const struct fy_version *vb)
{
	return fy_version_compare(fy_document_state_version(fyds), vb);
}

int fy_parse_set_composer(struct fy_parser *fyp, fy_parse_composer_cb cb, void *userdata);

struct fy_streaming_alias *
fy_parse_streaming_alias_create(struct fy_parser *fyp, struct fy_token *fyt_anchor);
void fy_parse_streaming_alias_clean(struct fy_parser *fyp, struct fy_streaming_alias *fysa);
void fy_parse_streaming_aliases_reset(struct fy_parser *fyp);

struct fy_eventp *fy_parser_parse_resolve_prolog(struct fy_parser *fyp);
struct fy_eventp *fy_parser_parse_resolve_epilog(struct fy_parser *fyp, struct fy_eventp *fyep);

struct fy_parser_checkpoint {
	struct fy_parser fyp_checkpoint;
	int queued_token_count;
	struct fy_token **queued_tokens;
	int queued_input_count;
	struct fy_input **queued_inputs;
	bool diag_on_error;
};

/* diagnostics */

static inline bool
fyp_debug_log_level_is_enabled(struct fy_parser *fyp, enum fy_error_module module)
{
	return fyp && fy_diag_log_level_is_enabled(fyp->diag, FYET_DEBUG, module);
}

int fy_parser_vdiag(struct fy_parser *fyp, unsigned int flags,
		    const char *file, int line, const char *func,
		    const char *fmt, va_list ap);

int fy_parser_diag(struct fy_parser *fyp, unsigned int flags,
		   const char *file, int line, const char *func,
		   const char *fmt, ...)
	FY_FORMAT(printf, 6, 7);

void fy_parser_diag_vreport(struct fy_parser *fyp,
			    const struct fy_diag_report_ctx *fydrc,
			    const char *fmt, va_list ap);
void fy_parser_diag_report(struct fy_parser *fyp,
			   const struct fy_diag_report_ctx *fydrc,
			   const char *fmt, ...)
	FY_FORMAT(printf, 3, 4);

#ifdef FY_DEVMODE

#define fyp_debug(_fyp, _module, _fmt, ...) \
	do { \
		struct fy_parser *__fyp = (_fyp); \
		enum fy_error_module __module = (_module); \
		\
		if (fyp_debug_log_level_is_enabled(__fyp, __module)) \
			fy_parser_diag(__fyp, FYET_DEBUG | FYDF_MODULE(__module), \
					__FILE__, __LINE__, __func__, \
					(_fmt) , ## __VA_ARGS__); \
	} while(0)
#else

#define fyp_debug(_fyp, _module, _fmt, ...) \
	do { } while(0)

#endif

#define fyp_info(_fyp, _fmt, ...) \
	fy_parser_diag((_fyp), FYET_INFO, __FILE__, __LINE__, __func__, \
			(_fmt) , ## __VA_ARGS__)
#define fyp_notice(_fyp, _fmt, ...) \
	fy_parser_diag((_fyp), FYET_NOTICE, __FILE__, __LINE__, __func__, \
			(_fmt) , ## __VA_ARGS__)
#define fyp_warning(_fyp, _fmt, ...) \
	fy_parser_diag((_fyp), FYET_WARNING, __FILE__, __LINE__, __func__, \
			(_fmt) , ## __VA_ARGS__)
#define fyp_error(_fyp, _fmt, ...) \
	fy_parser_diag((_fyp), FYET_ERROR, __FILE__, __LINE__, __func__, \
			(_fmt) , ## __VA_ARGS__)

#define fyp_scan_debug(_fyp, _fmt, ...) \
	fyp_debug((_fyp), FYEM_SCAN, (_fmt) , ## __VA_ARGS__)
#define fyp_parse_debug(_fyp, _fmt, ...) \
	fyp_debug((_fyp), FYEM_PARSE, (_fmt) , ## __VA_ARGS__)
#define fyp_doc_debug(_fyp, _fmt, ...) \
	fyp_debug((_fyp), FYEM_DOC, (_fmt) , ## __VA_ARGS__)

#define fyp_error_check(_fyp, _cond, _label, _fmt, ...) \
	do { \
		if (!(_cond)) { \
			fyp_error((_fyp), _fmt, ## __VA_ARGS__); \
			goto _label ; \
		} \
	} while(0)

#define _FYP_TOKEN_DIAG(_fyp, _fyt, _type, _module, _fmt, ...) \
	do { \
		struct fy_diag_report_ctx _drc; \
		memset(&_drc, 0, sizeof(_drc)); \
		_drc.type = (_type); \
		_drc.module = (_module); \
		_drc.fyt = (_fyt); \
		fy_parser_diag_report((_fyp), &_drc, (_fmt) , ## __VA_ARGS__); \
	} while(0)

#define FYP_TOKEN_DIAG(_fyp, _fyt, _type, _module, _fmt, ...) \
	_FYP_TOKEN_DIAG(_fyp, fy_token_ref(_fyt), _type, _module, _fmt, ## __VA_ARGS__)

#define FYP_PARSE_DIAG(_fyp, _adv, _cnt, _type, _module, _fmt, ...) \
	_FYP_TOKEN_DIAG(_fyp, \
		fy_token_create(FYTT_INPUT_MARKER, \
			fy_fill_atom_at((_fyp), (_adv), (_cnt), \
			alloca(sizeof(struct fy_atom)))), \
		_type, _module, _fmt, ## __VA_ARGS__)

#define FYP_MARK_DIAG(_fyp, _sm, _em, _type, _module, _fmt, ...) \
	_FYP_TOKEN_DIAG(_fyp, \
		fy_token_create(FYTT_INPUT_MARKER, \
			fy_fill_atom_mark(((_fyp)), (_sm), (_em), \
				alloca(sizeof(struct fy_atom)))), \
		_type, _module, _fmt, ## __VA_ARGS__)

#define FYP_NODE_DIAG(_fyp, _fyn, _type, _module, _fmt, ...) \
	_FYP_TOKEN_DIAG(_fyp, fy_node_token(_fyn), _type, _module, _fmt, ## __VA_ARGS__)

#define FYP_TOKEN_ERROR(_fyp, _fyt, _module, _fmt, ...) \
	FYP_TOKEN_DIAG(_fyp, _fyt, FYET_ERROR, _module, _fmt, ## __VA_ARGS__)

#define FYP_PARSE_ERROR(_fyp, _adv, _cnt, _module, _fmt, ...) \
	FYP_PARSE_DIAG(_fyp, _adv, _cnt, FYET_ERROR, _module, _fmt, ## __VA_ARGS__)

#define FYP_MARK_ERROR(_fyp, _sm, _em, _module, _fmt, ...) \
	FYP_MARK_DIAG(_fyp, _sm, _em, FYET_ERROR, _module, _fmt, ## __VA_ARGS__)

#define FYP_NODE_ERROR(_fyp, _fyn, _module, _fmt, ...) \
	FYP_NODE_DIAG(_fyp, _fyn, FYET_ERROR, _module, _fmt, ## __VA_ARGS__)

#define FYP_TOKEN_ERROR_CHECK(_fyp, _fyt, _module, _cond, _label, _fmt, ...) \
	do { \
		if (!(_cond)) { \
			FYP_TOKEN_ERROR(_fyp, _fyt, _module, _fmt, ## __VA_ARGS__); \
			goto _label; \
		} \
	} while(0)

#define FYP_PARSE_ERROR_CHECK(_fyp, _adv, _cnt, _module, _cond, _label, _fmt, ...) \
	do { \
		if (!(_cond)) { \
			FYP_PARSE_ERROR(_fyp, _adv, _cnt, _module, _fmt, ## __VA_ARGS__); \
			goto _label; \
		} \
	} while(0)

#define FYP_MARK_ERROR_CHECK(_fyp, _sm, _em, _module, _cond, _label, _fmt, ...) \
	do { \
		if (!(_cond)) { \
			FYP_MARK_ERROR(_fyp, _sm, _em, _module, _fmt, ## __VA_ARGS__); \
			goto _label; \
		} \
	} while(0)

#define FYP_NODE_ERROR_CHECK(_fyp, _fyn, _module, _cond, _label, _fmt, ...) \
	do { \
		if (!(_cond)) { \
			FYP_NODE_ERROR(_fyp, _fyn, _module, _fmt, ## __VA_ARGS__); \
			goto _label; \
		} \
	} while(0)

#define FYP_TOKEN_WARNING(_fyp, _fyt, _module, _fmt, ...) \
	FYP_TOKEN_DIAG(_fyp, _fyt, FYET_WARNING, _module, _fmt, ## __VA_ARGS__)

#define FYP_PARSE_WARNING(_fyp, _adv, _cnt, _module, _fmt, ...) \
	FYP_PARSE_DIAG(_fyp, _adv, _cnt, FYET_WARNING, _module, _fmt, ## __VA_ARGS__)

#define FYP_MARK_WARNING(_fyp, _sm, _em, _module, _fmt, ...) \
	FYP_MARK_DIAG(_fyp, _sm, _em, FYET_WARNING, _module, _fmt, ## __VA_ARGS__)

#define FYP_NODE_WARNING(_fyp, _fyn, _type, _module, _fmt, ...) \
	FYP_NODE_DIAG(_fyp, _fyn, FYET_WARNING, _module, _fmt, ## __VA_ARGS__)

#endif
