| 1 | /*------------------------------------------------------------------------- |
| 2 | * A stack of automaton states to handle nested conditionals. |
| 3 | * |
| 4 | * This file describes a stack of automaton states which |
| 5 | * allow a manage nested conditionals. |
| 6 | * |
| 7 | * It is used by: |
| 8 | * - "psql" interpreter for handling \if ... \endif |
| 9 | * - "pgbench" interpreter for handling \if ... \endif |
| 10 | * - "pgbench" syntax checker to test for proper nesting |
| 11 | * |
| 12 | * The stack holds the state of enclosing conditionals (are we in |
| 13 | * a true branch? in a false branch? have we already encountered |
| 14 | * a true branch?) so that the interpreter knows whether to execute |
| 15 | * code and whether to evaluate conditions. |
| 16 | * |
| 17 | * Copyright (c) 2000-2019, PostgreSQL Global Development Group |
| 18 | * |
| 19 | * src/include/fe_utils/conditional.h |
| 20 | * |
| 21 | *------------------------------------------------------------------------- |
| 22 | */ |
| 23 | #ifndef CONDITIONAL_H |
| 24 | #define CONDITIONAL_H |
| 25 | |
| 26 | /* |
| 27 | * Possible states of a single level of \if block. |
| 28 | */ |
| 29 | typedef enum ifState |
| 30 | { |
| 31 | IFSTATE_NONE = 0, /* not currently in an \if block */ |
| 32 | IFSTATE_TRUE, /* currently in an \if or \elif that is true |
| 33 | * and all parent branches (if any) are true */ |
| 34 | IFSTATE_FALSE, /* currently in an \if or \elif that is false |
| 35 | * but no true branch has yet been seen, and |
| 36 | * all parent branches (if any) are true */ |
| 37 | IFSTATE_IGNORED, /* currently in an \elif that follows a true |
| 38 | * branch, or the whole \if is a child of a |
| 39 | * false parent branch */ |
| 40 | IFSTATE_ELSE_TRUE, /* currently in an \else that is true and all |
| 41 | * parent branches (if any) are true */ |
| 42 | IFSTATE_ELSE_FALSE /* currently in an \else that is false or |
| 43 | * ignored */ |
| 44 | } ifState; |
| 45 | |
| 46 | /* |
| 47 | * The state of nested \ifs is stored in a stack. |
| 48 | * |
| 49 | * query_len is used to determine what accumulated text to throw away at the |
| 50 | * end of an inactive branch. (We could, perhaps, teach the lexer to not add |
| 51 | * stuff to the query buffer in the first place when inside an inactive branch; |
| 52 | * but that would be very invasive.) We also need to save and restore the |
| 53 | * lexer's parenthesis nesting depth when throwing away text. (We don't need |
| 54 | * to save and restore any of its other state, such as comment nesting depth, |
| 55 | * because a backslash command could never appear inside a comment or SQL |
| 56 | * literal.) |
| 57 | */ |
| 58 | typedef struct IfStackElem |
| 59 | { |
| 60 | ifState if_state; /* current state, see enum above */ |
| 61 | int query_len; /* length of query_buf at last branch start */ |
| 62 | int paren_depth; /* parenthesis depth at last branch start */ |
| 63 | struct IfStackElem *next; /* next surrounding \if, if any */ |
| 64 | } IfStackElem; |
| 65 | |
| 66 | typedef struct ConditionalStackData |
| 67 | { |
| 68 | IfStackElem *head; |
| 69 | } ConditionalStackData; |
| 70 | |
| 71 | typedef struct ConditionalStackData *ConditionalStack; |
| 72 | |
| 73 | |
| 74 | extern ConditionalStack conditional_stack_create(void); |
| 75 | |
| 76 | extern void conditional_stack_destroy(ConditionalStack cstack); |
| 77 | |
| 78 | extern int conditional_stack_depth(ConditionalStack cstack); |
| 79 | |
| 80 | extern void conditional_stack_push(ConditionalStack cstack, ifState new_state); |
| 81 | |
| 82 | extern bool conditional_stack_pop(ConditionalStack cstack); |
| 83 | |
| 84 | extern ifState conditional_stack_peek(ConditionalStack cstack); |
| 85 | |
| 86 | extern bool conditional_stack_poke(ConditionalStack cstack, ifState new_state); |
| 87 | |
| 88 | extern bool conditional_stack_empty(ConditionalStack cstack); |
| 89 | |
| 90 | extern bool conditional_active(ConditionalStack cstack); |
| 91 | |
| 92 | extern void conditional_stack_set_query_len(ConditionalStack cstack, int len); |
| 93 | |
| 94 | extern int conditional_stack_get_query_len(ConditionalStack cstack); |
| 95 | |
| 96 | extern void conditional_stack_set_paren_depth(ConditionalStack cstack, int depth); |
| 97 | |
| 98 | extern int conditional_stack_get_paren_depth(ConditionalStack cstack); |
| 99 | |
| 100 | #endif /* CONDITIONAL_H */ |
| 101 | |