/*-------------------------------------------------------------------------
 * A stack of automaton states to handle nested conditionals.
 *
 * This file describes a stack of automaton states which
 * allow a manage nested conditionals.
 *
 * It is used by:
 * - "psql" interpreter for handling \if ... \endif
 * - "pgbench" interpreter for handling \if ... \endif
 * - "pgbench" syntax checker to test for proper nesting
 *
 * The stack holds the state of enclosing conditionals (are we in
 * a true branch? in a false branch? have we already encountered
 * a true branch?) so that the interpreter knows whether to execute
 * code and whether to evaluate conditions.
 *
 * Copyright (c) 2000-2021, PostgreSQL Global Development Group
 *
 * src/include/fe_utils/conditional.h
 *
 *-------------------------------------------------------------------------
 */
#ifndef CONDITIONAL_H
#define CONDITIONAL_H

/*
 * Possible states of a single level of \if block.
 */
typedef enum ifState
{
	IFSTATE_NONE = 0,			/* not currently in an \if block */
	IFSTATE_TRUE,				/* currently in an \if or \elif that is true
								 * and all parent branches (if any) are true */
	IFSTATE_FALSE,				/* currently in an \if or \elif that is false
								 * but no true branch has yet been seen, and
								 * all parent branches (if any) are true */
	IFSTATE_IGNORED,			/* currently in an \elif that follows a true
								 * branch, or the whole \if is a child of a
								 * false parent branch */
	IFSTATE_ELSE_TRUE,			/* currently in an \else that is true and all
								 * parent branches (if any) are true */
	IFSTATE_ELSE_FALSE			/* currently in an \else that is false or
								 * ignored */
} ifState;

/*
 * The state of nested \ifs is stored in a stack.
 *
 * query_len is used to determine what accumulated text to throw away at the
 * end of an inactive branch.  (We could, perhaps, teach the lexer to not add
 * stuff to the query buffer in the first place when inside an inactive branch;
 * but that would be very invasive.)  We also need to save and restore some
 * lexer state, such as parenthesis nesting depth, when throwing away text.
 */
struct PsqlScanStateSave;		/* opaque outside lexer */

typedef struct IfStackElem
{
	ifState		if_state;		/* current state, see enum above */
	int			query_len;		/* length of query_buf at last branch start */
	int			paren_depth;	/* (obsolete, not used anymore) */
	struct IfStackElem *next;	/* next surrounding \if, if any */
	struct PsqlScanStateSave *lex_state;	/* lexer state at last branch start */
} IfStackElem;

typedef struct ConditionalStackData
{
	IfStackElem *head;
}			ConditionalStackData;

typedef struct ConditionalStackData *ConditionalStack;


extern ConditionalStack conditional_stack_create(void);

extern void conditional_stack_destroy(ConditionalStack cstack);

extern int	conditional_stack_depth(ConditionalStack cstack);

extern void conditional_stack_push(ConditionalStack cstack, ifState new_state);

extern bool conditional_stack_pop(ConditionalStack cstack);

extern ifState conditional_stack_peek(ConditionalStack cstack);

extern bool conditional_stack_poke(ConditionalStack cstack, ifState new_state);

extern bool conditional_stack_empty(ConditionalStack cstack);

extern bool conditional_active(ConditionalStack cstack);

extern void conditional_stack_set_query_len(ConditionalStack cstack, int len);

extern int	conditional_stack_get_query_len(ConditionalStack cstack);

extern void conditional_stack_set_lex_state(ConditionalStack cstack,
											struct PsqlScanStateSave *lex_state);

extern struct PsqlScanStateSave *conditional_stack_get_lex_state(ConditionalStack cstack);

extern void conditional_stack_set_paren_depth(ConditionalStack cstack, int depth);

extern int	conditional_stack_get_paren_depth(ConditionalStack cstack);

#endif							/* CONDITIONAL_H */
