/**
* @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 .
*/
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 +]
* emit things that are for the true condition[+
*
* ELIF +]
* 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{} 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 +]
* emit things that are for the true condition[+
*
* ENDWHILE +]
* @end example
*
* @noindent
* @code{} 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 */