diff options
Diffstat (limited to 'agen5/funcIf.c')
-rw-r--r-- | agen5/funcIf.c | 514 |
1 files changed, 514 insertions, 0 deletions
diff --git a/agen5/funcIf.c b/agen5/funcIf.c new file mode 100644 index 0000000..ddf3aa9 --- /dev/null +++ b/agen5/funcIf.c @@ -0,0 +1,514 @@ + +/** + * @file funcIf.c + * + * Time-stamp: "2012-04-07 09:59:54 bkorb" + * + * This module implements the _IF text function. + * + * This file is part of AutoGen. + * AutoGen Copyright (c) 1992-2012 by Bruce Korb - all rights reserved + * + * AutoGen is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * AutoGen is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +typedef struct if_stack tIfStack; +struct if_stack { + macro_t* pIf; + macro_t* pElse; +}; + +static tIfStack current_if; +static load_proc_t mLoad_Elif, mLoad_Else; + +/* = = = START-STATIC-FORWARD = = = */ +static bool +eval_true(void); + +static macro_t* +mLoad_Elif(templ_t * pT, macro_t * pMac, char const ** ppzScan); + +static macro_t * +mLoad_Else(templ_t * pT, macro_t * pMac, char const ** ppzScan); +/* = = = END-STATIC-FORWARD = = = */ + +/* + * eval_true - should a string be interpreted as TRUE? + * + * It is always true unless: + * + * 1. it is the empty string + * 2. it starts with a digit and the number evaluates to zero + * 3. it starts with either "#f" or "#F" + * 4. For its length or its first five characters (whichever is less) + * it matches the string "false" + */ +static bool +eval_true(void) +{ + bool needFree; + bool res = true; + char const * pz = eval_mac_expr(&needFree); + + if (IS_DEC_DIGIT_CHAR(*pz)) + res = (atoi(pz) == 0) ? false : true; + + else switch (*pz) { + case NUL: + res = false; + break; + + case '#': + if ((pz[1] == 'f') || (pz[1] == 'F')) + res = false; + break; + + case 'f': + case 'F': + { + int len = strlen(pz); + if (len > 5) + len = 5; + if (strneqvcmp(EVAL_TRUE_FALSE_STR, pz, len) == 0) + res = false; + break; + } + } + + if (needFree) + AGFREE(pz); + + return res; +} + + +/*=macfunc IF + * + * what: Conditionally Emit a Template Block + * cindex: conditional emit + * cindex: if test + * handler_proc: + * load_proc: + * + * desc: + * Conditional block. Its arguments are evaluated (@pxref{EXPR}) and + * if the result is non-zero or a string with one or more bytes, + * then the condition is true and the text from that point + * until a matched @code{ELIF}, @code{ELSE} or @code{ENDIF} is emitted. + * @code{ELIF} introduces a conditional alternative if the @code{IF} + * clause evaluated FALSE and @code{ELSE} introduces an unconditional + * alternative. + * + * @example + * [+IF <full-expression> +] + * emit things that are for the true condition[+ + * + * ELIF <full-expression-2> +] + * emit things that are true maybe[+ + * + * ELSE "This may be a comment" +] + * emit this if all but else fails[+ + * + * ENDIF "This may *also* be a comment" +] + * @end example + * + * @noindent + * @code{<full-expression>} may be any expression described in the + * @code{EXPR} expression function, including the use of apply-codes + * and value-names. If the expression yields an empty string, it + * is interpreted as @i{false}. +=*/ +/*=macfunc ENDIF + * + * what: Terminate the @code{IF} Template Block + * in-context: + * + * desc: + * This macro ends the @code{IF} function template block. + * For a complete description @xref{IF}. +=*/ +macro_t* +mFunc_If(templ_t* pT, macro_t* pMac) +{ + macro_t* pRet = pT->td_macros + pMac->md_end_idx; + macro_t* pIf = pMac; + + do { + /* + * The current macro becomes the 'ELIF' or 'ELSE' macro + */ + cur_macro = pMac; + + /* + * 'ELSE' is equivalent to 'ELIF true' + */ + if ( (pMac->md_code == FTYP_ELSE) + || eval_true()) { + + if (OPT_VALUE_TRACE >= TRACE_BLOCK_MACROS) { + fprintf(trace_fp, TRACE_FN_IF, (pMac->md_code == FTYP_ELSE) + ? FN_IF_ELSE : pT->td_text + pMac->md_txt_off, + pMac->md_line); + + if (OPT_VALUE_TRACE == TRACE_EVERYTHING) + fprintf(trace_fp, TAB_FILE_LINE_FMT, + current_tpl->td_file, pIf->md_line); + } + + gen_block(pT, pMac+1, pT->td_macros + pMac->md_sib_idx); + break; + } + pMac = pT->td_macros + pMac->md_sib_idx; + } while (pMac < pRet); + + if ((OPT_VALUE_TRACE >= TRACE_BLOCK_MACROS) && (pMac >= pRet)) { + fprintf(trace_fp, TRACE_FN_IF_NOTHING, + current_tpl->td_text + cur_macro->md_txt_off); + + if (OPT_VALUE_TRACE == TRACE_EVERYTHING) + fprintf(trace_fp, TAB_FILE_LINE_FMT, + current_tpl->td_file, pIf->md_line); + } + + return pRet; +} + + +/*=macfunc WHILE + * + * what: Conditionally loop over a Template Block + * cindex: conditional emit + * cindex: while test + * handler_proc: + * load_proc: + * + * desc: + * Conditionally repeated block. Its arguments are evaluated (@pxref{EXPR}) + * and as long as the result is non-zero or a string with one or more bytes, + * then the condition is true and the text from that point + * until a matched @code{ENDWHILE} is emitted. + * + * @example + * [+WHILE <full-expression> +] + * emit things that are for the true condition[+ + * + * ENDWHILE +] + * @end example + * + * @noindent + * @code{<full-expression>} may be any expression described in the + * @code{EXPR} expression function, including the use of apply-codes + * and value-names. If the expression yields an empty string, it + * is interpreted as @i{false}. +=*/ +/*=macfunc ENDWHILE + * + * what: Terminate the @code{WHILE} Template Block + * in-context: + * + * desc: + * This macro ends the @code{WHILE} function template block. + * For a complete description @xref{WHILE}. +=*/ +macro_t* +mFunc_While(templ_t* pT, macro_t* pMac) +{ + macro_t * end = pT->td_macros + pMac->md_end_idx; + int ct = 0; + + if (OPT_VALUE_TRACE >= TRACE_BLOCK_MACROS) + fprintf(trace_fp, TRACE_FN_WHILE_START, + current_tpl->td_text + cur_macro->md_txt_off, + pT->td_file, pMac->md_line); + + for (;;) { + jmp_buf jbuf; + + current_tpl = pT; + cur_macro = pMac; + + if (! eval_true()) + break; + ct++; + if (call_gen_block(jbuf, pT, pMac+1, end) == LOOP_JMP_BREAK) + break; + } + + if (OPT_VALUE_TRACE >= TRACE_BLOCK_MACROS) { + fprintf(trace_fp, TRACE_FN_WHILE_END, ct); + + if (OPT_VALUE_TRACE == TRACE_EVERYTHING) + fprintf(trace_fp, TAB_FILE_LINE_FMT, pT->td_file, pMac->md_line); + } + + return end; +} + +/*=macfunc ELIF + * + * what: Alternate Conditional Template Block + * in-context: + * + * desc: + * This macro must only appear after an @code{IF} function, and + * before any associated @code{ELSE} or @code{ENDIF} functions. + * It denotes the start of an alternate template block for the + * @code{IF} function. Its expression argument is evaluated as are + * the arguments to @code{IF}. For a complete description @xref{IF}. +=*/ +static macro_t* +mLoad_Elif(templ_t * pT, macro_t * pMac, char const ** ppzScan) +{ + if ((int)pMac->md_res == 0) + AG_ABEND_IN(pT, pMac, NO_IF_EXPR); + /* + * Load the expression + */ + (void)mLoad_Expr(pT, pMac, ppzScan); + + current_if.pElse->md_sib_idx = pMac - pT->td_macros; + current_if.pElse = pMac; + return pMac + 1; +} + + +/*=macfunc ELSE + * + * what: Alternate Template Block + * in-context: + * + * desc: + * This macro must only appear after an @code{IF} function, + * and before the associated @code{ENDIF} function. + * It denotes the start of an alternate template block for + * the @code{IF} function. For a complete description @xref{IF}. +=*/ +static macro_t * +mLoad_Else(templ_t * pT, macro_t * pMac, char const ** ppzScan) +{ + /* + * After processing an "ELSE" macro, + * we have a special handler function for 'ENDIF' only. + */ + static load_proc_p_t load_for_if_after_else_procs[ FUNC_CT ] = { NULL }; + (void)ppzScan; + + if (load_for_if_after_else_procs[0] == NULL) { + memcpy((void*)load_for_if_after_else_procs, base_load_table, + sizeof(base_load_table)); + load_for_if_after_else_procs[ FTYP_ENDIF ] = &mLoad_Ending; + } + + load_proc_table = load_for_if_after_else_procs; + + current_if.pElse->md_sib_idx = pMac - pT->td_macros; + current_if.pElse = pMac; + pMac->md_txt_off = 0; + + return pMac+1; +} + + +/** + * End any of the block macros. It ends @code{ENDDEF}, + * @code{ENDFOR}, @code{ENDIF}, @code{ENDWHILE} and @code{ESAC}. It + * leaves no entry in the dispatch tables for itself. By returning + * NULL, it tells the macro parsing loop to return. + * + * @param tpl ignored + * @param[out] mac zeroed out for re-use + * @param scan ignored + */ +macro_t * +mLoad_Ending(templ_t * tpl, macro_t * mac, char const ** scan) +{ + (void) tpl; + (void) scan; + memset((void*)mac, 0, sizeof(*mac)); + return NULL; +} + +/** + * Load template macros until matching @code{ENDIF} is found. + * + * @param[in,out] tpl Template we are filling in with macros + * @param[in,out] mac Linked into the "if" macro segments + * @param[in,out] scan pointer to scanning pointer. We advance it + * past the ending @code{ENDIF} macro. + * + * @returns the address of the next macro slot for insertion. + */ +macro_t * +mLoad_If(templ_t * tpl, macro_t * mac, char const ** ppzScan) +{ + size_t srcLen = (size_t)mac->md_res; /* macro len */ + tIfStack save_stack = current_if; + load_proc_p_t const * papLP = load_proc_table; + macro_t * pEndifMac; + + /* + * While processing an "IF" macro, + * we have handler functions for 'ELIF', 'ELSE' and 'ENDIF' + * Otherwise, we do not. Switch the callout function table. + */ + static load_proc_p_t apIfLoad[ FUNC_CT ] = { NULL }; + + /* + * IF there is no associated text expression + * THEN woops! what are we to case on? + */ + if (srcLen == 0) + AG_ABEND_IN(tpl, mac, NO_IF_EXPR); + + if (apIfLoad[0] == NULL) { + memcpy((void*)apIfLoad, base_load_table, sizeof(base_load_table)); + apIfLoad[ FTYP_ELIF ] = &mLoad_Elif; + apIfLoad[ FTYP_ELSE ] = &mLoad_Else; + apIfLoad[ FTYP_ENDIF ] = &mLoad_Ending; + } + + load_proc_table = apIfLoad; + + /* + * We will need to chain together the 'IF', 'ELIF', and 'ELSE' + * macros. The 'ENDIF' gets absorbed. + */ + current_if.pIf = current_if.pElse = mac; + + /* + * Load the expression + */ + (void)mLoad_Expr(tpl, mac, ppzScan); + + /* + * Now, do a nested parse of the template. + * When the matching 'ENDIF' macro is encountered, + * the handler routine will cause 'parse_tpl()' + * to return with the text scanning pointer pointing + * to the remaining text. + */ + pEndifMac = parse_tpl(mac+1, ppzScan); + if (*ppzScan == NULL) + AG_ABEND_IN(tpl, mac, LD_IF_NO_ENDIF); + + current_if.pIf->md_end_idx = \ + current_if.pElse->md_sib_idx = pEndifMac - tpl->td_macros; + + /* + * Restore the context of any encompassing block macros + */ + current_if = save_stack; + load_proc_table = papLP; + return pEndifMac; +} + +/** + * load the @code{WHILE} macro. Sets up the while loop parsing table, which + * is a copy of the global "base_load_table" with added entries for + * @code{ENDWHILE}, @code{NEXT} and @code{BREAK}. + * + * @param[in,out] tpl Template we are filling in with macros + * @param[in,out] mac Linked into the "if" macro segments + * @param[in,out] scan pointer to scanning pointer. We advance it + * past the ending @code{ENDWHILE} macro. + * + * @returns the address of the next macro slot for insertion. + */ +macro_t * +mLoad_While(templ_t * pT, macro_t * mac, char const ** p_scan) +{ + /* + * While processing a "WHILE" macro, + * we have handler a handler function for ENDWHILE, NEXT and BREAK. + */ + static load_proc_p_t while_tbl[ FUNC_CT ] = { NULL }; + + + load_proc_p_t const * lpt = load_proc_table; //!< save current table + + /* + * IF there is no associated text expression + * THEN woops! what are we to case on? + */ + if ((size_t)mac->md_res == 0) + AG_ABEND_IN(pT, mac, LD_WHILE_NO_EXPR); + + if (while_tbl[0] == NULL) { + memcpy((void*)while_tbl, base_load_table, sizeof(base_load_table)); + while_tbl[ FTYP_ENDWHILE ] = &mLoad_Ending; + } + + load_proc_table = while_tbl; + + /* + * Load the expression + */ + (void)mLoad_Expr(pT, mac, p_scan); + + /* + * Now, do a nested parse of the template. When the matching 'ENDWHILE' + * macro is encountered, the handler routine will cause 'parse_tpl()' + * to return with the text scanning pointer pointing to the remaining + * text. + */ + { + macro_t * end = parse_tpl(mac+1, p_scan); + if (*p_scan == NULL) + AG_ABEND_IN(pT, mac, LD_WHILE_NO_ENDWHILE); + + mac->md_sib_idx = \ + mac->md_end_idx = end - pT->td_macros; + + load_proc_table = lpt; // restore context + return end; + } +} + +/*=gfunc set_writable + * + * what: Make the output file be writable + * + * exparg: + set? + boolean arg, false to make output non-writable + opt + * + * doc: This function will set the current output file to be writable + * (or not). This is only effective if neither the @code{--writable} + * nor @code{--not-writable} have been specified. This state + * is reset when the current suffix's output is complete. +=*/ +SCM +ag_scm_set_writable(SCM set) +{ + switch (STATE_OPT(WRITABLE)) { + case OPTST_DEFINED: + case OPTST_PRESET: + fprintf(trace_fp, SET_WRITE_WARN, current_tpl->td_file, + cur_macro->md_line); + break; + + default: + if (AG_SCM_BOOL_P(set) && (set == SCM_BOOL_F)) + CLEAR_OPT(WRITABLE); + else + SET_OPT_WRITABLE; + } + + return SCM_UNDEFINED; +} +/* + * Local Variables: + * mode: C + * c-file-style: "stroustrup" + * indent-tabs-mode: nil + * End: + * end of agen5/funcIf.c */ |