diff options
Diffstat (limited to 'agen5/funcFor.c')
-rw-r--r-- | agen5/funcFor.c | 918 |
1 files changed, 918 insertions, 0 deletions
diff --git a/agen5/funcFor.c b/agen5/funcFor.c new file mode 100644 index 0000000..2d6b219 --- /dev/null +++ b/agen5/funcFor.c @@ -0,0 +1,918 @@ + +/** + * @file funcFor.c + * + * Time-stamp: "2012-04-07 09:13:53 bkorb" + * + * This module implements the FOR text macro. + * + * 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/>. + */ + +#define ENTRY_END INT_MAX +#define UNASSIGNED 0x7BAD0BAD + +/* = = = START-STATIC-FORWARD = = = */ +static for_state_t * +find_for_state(SCM which_scm); + +static bool +next_def(bool invert, def_ent_t** de_lst); + +static int +for_by_step(templ_t * pT, macro_t * pMac, def_ent_t * found); + +static int +for_each(templ_t * tpl, macro_t * mac, def_ent_t * def_ent); + +static void +load_for_in(char const * pzSrc, size_t srcLen, templ_t * pT, macro_t * pMac); +/* = = = END-STATIC-FORWARD = = = */ + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * + * Operational Functions */ + +static for_state_t * +find_for_state(SCM which_scm) +{ + ivk_info_t * srch = curr_ivk_info; + char const * which = AG_SCM_STRING_P(which_scm) + ? ag_scm2zchars(which_scm, "which for") : NULL; + + for (; srch != NULL; srch = srch->ii_prev) { + int ix; + + if (srch->ii_for_depth <= 0) + continue; + + /* + * If "which" is not specified, then accept first found + */ + ix = srch->ii_for_depth - 1; + if (which == NULL) + return srch->ii_for_data + ix; + + for (; ix >= 0; ix--) { + for_state_t * fs = srch->ii_for_data + ix; + if (strcmp(fs->for_name, which) == 0) + return fs; + } + } + return NULL; +} + +/*=gfunc first_for_p + * + * what: detect first iteration + * exparg: for_var, which for loop, opt + * doc: + * + * Returns @code{SCM_BOOL_T} if the named FOR loop (or, if not named, the + * current innermost loop) is on the first pass through the data. Outside + * of any @code{FOR} loop, it returns @code{SCM_UNDEFINED}, @pxref{FOR}. +=*/ +SCM +ag_scm_first_for_p(SCM which) +{ + for_state_t * p = find_for_state(which); + if (p == NULL) + return SCM_UNDEFINED; + + if (! AG_SCM_STRING_P(which)) + return (for_state->for_isfirst) ? SCM_BOOL_T : SCM_BOOL_F; + + which = (p->for_isfirst ? SCM_BOOL_T : SCM_BOOL_F); + return which; +} + + +/*=gfunc last_for_p + * + * what: detect last iteration + * exparg: for_var, which for loop, opt + * doc: Returns SCM_BOOL_T if the named FOR loop (or, if not named, the + * current innermost loop) is on the last pass through the data. + * Outside of any FOR loop, it returns SCM_UNDEFINED. + * @xref{FOR}. +=*/ +SCM +ag_scm_last_for_p(SCM which) +{ + for_state_t * p = find_for_state(which); + if (p == NULL) + return SCM_UNDEFINED; + + which = (p->for_islast ? SCM_BOOL_T : SCM_BOOL_F); + return which; +} + + +/*=gfunc for_index + * + * what: get current loop index + * exparg: for_var, which for loop, opt + * doc: + * + * Returns the current index for the named @code{FOR} loop. + * If not named, then the index for the innermost loop. + * Outside of any FOR loop, it returns @code{SCM_UNDEFINED}, @xref{FOR}. +=*/ +SCM +ag_scm_for_index(SCM which) +{ + for_state_t * p = find_for_state(which); + if (p == NULL) + return SCM_UNDEFINED; + + which = AG_SCM_INT2SCM(p->for_index); + return which; +} + + +/*=gfunc for_from + * + * what: set initial index + * exparg: from, the initial index for the AutoGen FOR macro + * + * doc: This function records the initial index information + * for an AutoGen FOR function. + * Outside of the FOR macro itself, this function will emit an error. + * @xref{FOR}. +=*/ +SCM +ag_scm_for_from(SCM from) +{ + if ((! for_state->for_loading) || (! AG_SCM_NUM_P(from))) + return SCM_UNDEFINED; + + for_state->for_from = AG_SCM_TO_INT(from); + return SCM_BOOL_T; +} + + +/*=gfunc for_to + * + * what: set ending index + * exparg: to, the final index for the AutoGen FOR macro + * + * doc: This function records the terminating value information + * for an AutoGen FOR function. + * Outside of the FOR macro itself, this function will emit an error. + * @xref{FOR}. +=*/ +SCM +ag_scm_for_to(SCM to) +{ + if ((! for_state->for_loading) || (! AG_SCM_NUM_P(to))) + return SCM_UNDEFINED; + + for_state->for_to = AG_SCM_TO_INT(to); + return SCM_BOOL_T; +} + + +/*=gfunc for_by + * + * what: set iteration step + * exparg: by, the iteration increment for the AutoGen FOR macro + * + * doc: This function records the "step by" information + * for an AutoGen FOR function. + * Outside of the FOR macro itself, this function will emit an error. + * @xref{FOR}. +=*/ +SCM +ag_scm_for_by(SCM by) +{ + if ((! for_state->for_loading) || (! AG_SCM_NUM_P(by))) + return SCM_UNDEFINED; + + for_state->for_by = AG_SCM_TO_INT(by); + return SCM_BOOL_T; +} + +/*=gfunc for_sep + * + * what: set loop separation string + * exparg: separator, the text to insert between the output of + * each FOR iteration + * + * doc: This function records the separation string that is to be inserted + * between each iteration of an AutoGen FOR function. This is often + * nothing more than a comma. + * Outside of the FOR macro itself, this function will emit an error. +=*/ +SCM +ag_scm_for_sep(SCM obj) +{ + if ((! for_state->for_loading) || (! AG_SCM_STRING_P(obj))) + return SCM_UNDEFINED; + + AGDUPSTR(for_state->for_sep_str, ag_scm2zchars(obj, "sep"), "seps"); + return SCM_BOOL_T; +} + +/** + * search for matching definition entry. + * Only the current level is traversed, via the "de_twin" link. + * + * @param[in] invert invert the sense of the search + * ("FOR" is running backwards) + * @param[in,out] de_lst linked list of definitions + */ +static bool +next_def(bool invert, def_ent_t** de_lst) +{ + bool haveMatch = false; + def_ent_t* ent = *de_lst; + + while (ent != NULL) { + /* + * Loop until we find or pass the current index value + * + * IF we found an entry for the current index, + * THEN break out and use it + */ + if (ent->de_index == for_state->for_index) { + haveMatch = true; + break; + } + + /* + * IF the next definition is beyond our current index, + * (that is, the current index is inside of a gap), + * THEN we have no current definition and will use + * only the set passed in. + */ + if ((invert) + ? (ent->de_index < for_state->for_index) + : (ent->de_index > for_state->for_index)) { + + /* + * When the "by" step is zero, force syncronization. + */ + if (for_state->for_by == 0) { + for_state->for_index = ent->de_index; + haveMatch = true; + } + break; + } + + /* + * The current index (for_state->for_index) is past the current value + * (pB->de_index), so advance to the next entry and test again. + */ + ent = (invert) ? ent->de_ptwin : ent->de_twin; + } + + /* + * Save our restart point and return the find indication + */ + *de_lst = ent; + return haveMatch; +} + +static int +for_by_step(templ_t * pT, macro_t * pMac, def_ent_t * found) +{ + int loopCt = 0; + def_ent_t textDef; + bool invert = (for_state->for_by < 0) ? true : false; + t_word loopLimit = OPT_VALUE_LOOP_LIMIT; + def_ctx_t ctx = curr_def_ctx; + macro_t * end_mac = pT->td_macros + pMac->md_end_idx; + + /* + * IF the for-from and for-to values have not been set, + * THEN we set them from the indices of the first and last + * entries of the twin set. + */ + { + def_ent_t* pLast = (found->de_etwin != NULL) + ? found->de_etwin : found; + + if (for_state->for_from == UNASSIGNED) + for_state->for_from = (invert) ? pLast->de_index : found->de_index; + + if (for_state->for_to == UNASSIGNED) + for_state->for_to = (invert) ? found->de_index : pLast->de_index; + + /* + * "loopLimit" is intended to catch runaway ending conditions. + * However, if you really have a gazillion entries, who am I + * to stop you? + */ + if (loopLimit < pLast->de_index - found->de_index) + loopLimit = (pLast->de_index - found->de_index) + 1; + } + + /* + * Make sure we have some work to do before we start. + */ + if (invert) { + if (for_state->for_from < for_state->for_to) + return 0; + } else { + if (for_state->for_from > for_state->for_to) + return 0; + } + + for_state->for_index = for_state->for_from; + + /* + * FROM `from' THROUGH `to' BY `by', + * DO... + */ + for (;;) { + int next_ix; + bool have_next = next_def(invert, &found); + + if (loopLimit-- < 0) { + fprintf(trace_fp, TRACE_FOR_STEP_TOO_FAR, + pT->td_name, pMac->md_line); + fprintf(trace_fp, TRACE_FOR_BY_STEP, + pT->td_text + pMac->md_txt_off, + for_state->for_from, for_state->for_to, for_state->for_by, + (int)OPT_VALUE_LOOP_LIMIT); + break; + } + + if (for_state->for_by != 0) { + next_ix = for_state->for_index + for_state->for_by; + + } else if (invert) { + next_ix = (found->de_ptwin == NULL) + ? for_state->for_to - 1 /* last iteration !! */ + : found->de_ptwin->de_index; + + } else { + next_ix = (found->de_twin == NULL) + ? for_state->for_to + 1 /* last iteration !! */ + : found->de_twin->de_index; + } + + /* + * IF we have a non-base definition, use the old def context + */ + if (! have_next) + curr_def_ctx = ctx; + + /* + * ELSE IF this macro is a text type + * THEN create an un-twinned version of it to be found first + */ + else if (found->de_type == VALTYP_TEXT) { + textDef = *found; + textDef.de_next = textDef.de_twin = NULL; + + curr_def_ctx.dcx_defent = &textDef; + curr_def_ctx.dcx_prev = &ctx; + } + + /* + * ELSE the current definitions are based on the block + * macro's values + */ + else { + curr_def_ctx.dcx_defent = found->de_val.dvu_entry; + curr_def_ctx.dcx_prev = &ctx; + } + + for_state->for_islast = (invert) + ? ((next_ix < for_state->for_to) ? true : false) + : ((next_ix > for_state->for_to) ? true : false); + + switch (call_gen_block(for_state->for_env, pT, pMac+1, end_mac)) { + case LOOP_JMP_OKAY: + case LOOP_JMP_NEXT: + break; + + case LOOP_JMP_BREAK: + goto leave_loop; + } + + loopCt++; + for_state = curr_ivk_info->ii_for_data + + (curr_ivk_info->ii_for_depth - 1); + + if (for_state->for_islast) + break; + + if ( (for_state->for_sep_str != NULL) + && (*for_state->for_sep_str != NUL)) + fputs(for_state->for_sep_str, cur_fpstack->stk_fp); + + fflush(cur_fpstack->stk_fp); + for_state->for_isfirst = false; + for_state->for_index = next_ix; + } leave_loop: + + curr_def_ctx = ctx; /* Restore the def context */ + return loopCt; +} + +static int +for_each(templ_t * tpl, macro_t * mac, def_ent_t * def_ent) +{ + int loopCt = 0; + def_ctx_t ctx = curr_def_ctx; + macro_t * end_mac = tpl->td_macros + mac->md_end_idx; + + curr_def_ctx.dcx_prev = &ctx; + mac++; + + for (;;) { + def_ent_t txt_def_ent; + + /* + * IF this loops over a text macro, + * THEN create a definition that will be found *before* + * the repeated (twinned) copy. That way, when it + * is found as a macro invocation, the current value + * will be extracted, instead of the value list. + */ + if (def_ent->de_type == VALTYP_TEXT) { + txt_def_ent = *def_ent; + txt_def_ent.de_next = txt_def_ent.de_twin = NULL; + + curr_def_ctx.dcx_defent = &txt_def_ent; + } else { + curr_def_ctx.dcx_defent = def_ent->de_val.dvu_entry; + } + + /* + * Set the global current index + */ + for_state->for_index = def_ent->de_index; + + /* + * Advance to the next twin + */ + def_ent = def_ent->de_twin; + if (def_ent == NULL) + for_state->for_islast = true; + + switch (call_gen_block(for_state->for_env, tpl, mac, end_mac)) { + case LOOP_JMP_OKAY: + case LOOP_JMP_NEXT: + break; + + case LOOP_JMP_BREAK: + goto leave_loop; + } + + loopCt++; + for_state = curr_ivk_info->ii_for_data + + (curr_ivk_info->ii_for_depth - 1); + + if (def_ent == NULL) + break; + for_state->for_isfirst = false; + + /* + * Emit the iteration separation + */ + fputs(for_state->for_sep_str, cur_fpstack->stk_fp); + fflush(cur_fpstack->stk_fp); + } leave_loop:; + + curr_def_ctx = ctx; /* Restore the def context */ + return loopCt; +} + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +static void +load_for_in(char const * pzSrc, size_t srcLen, templ_t * pT, macro_t * pMac) +{ + char* pzName = pT->td_text + pMac->md_name_off; + int ix = 0; + char* pz; + def_ent_t * prev_ent = NULL; + + /* + * Find the first text value + */ + pz = SPN_WHITESPACE_CHARS(pzSrc + 3); + if (*pz == NUL) + AG_ABEND_IN(pT, pMac, FOR_IN_LISTLESS); + srcLen -= pz - pzSrc; + pzSrc = pz; + + { + size_t nmlen = strlen(pzName); + + pz = AGALOC(srcLen + 2 + nmlen, "copy FOR text"); + strcpy(pz, pzName); + pzName = pz; + pz += nmlen + 1; + } + + memcpy(pz, pzSrc, srcLen); + pz[srcLen] = NUL; + + do { + def_ent_t* pDef = new_def_ent(); + + pDef->de_name = pzName; + pDef->de_index = ix++; + pDef->de_type = VALTYP_TEXT; + pDef->de_val.dvu_text = pz; + + switch (*pz) { + case '\'': + case '"': + pz = span_quote(pz); + /* + * Clean up trailing commas + */ + pz = SPN_WHITESPACE_CHARS(pz); + if (*pz == ',') + pz++; + break; + + default: + for (;;) { + char ch = *(pz++); + switch (ch) { + case ' ': case TAB: case '\f': case '\v': case NL: + pz[-1] = NUL; + if (*pz != ',') + break; + pz++; + /* FALLTHROUGH */ + + case ',': + pz[-1] = NUL; + break; + + case NUL: + pz--; + break; + + default: + continue; + } + break; + } + break; + } + + /* + * Clean up trailing white space + */ + pz = SPN_WHITESPACE_CHARS(pz); + + /* + * IF there is a previous entry, link its twin to this one. + * OTHERWISE, it is the head of the twin list. + * Link to md_pvt. + */ + if (prev_ent != NULL) + prev_ent->de_twin = pDef; + else + pMac->md_pvt = pDef; + + prev_ent = pDef; + } while (*pz != NUL); + + pMac->md_txt_off = 0; +} + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +/*=macfunc FOR + * + * what: Emit a template block multiple times + * cindex: looping, for + * cindex: for loop + * handler_proc: + * load_proc: + * + * desc: + * This macro has a slight variation on the standard syntax: + * @example + * FOR <value-name> [ <separator-string> ] + * + * FOR <value-name> (...Scheme expression list) + * + * FOR <value-name> IN "string" [ ... ] + * @end example + * + * Other than for the last form, the first macro argument must be the name of + * an AutoGen value. If there is no value associated with the name, the + * @code{FOR} template block is skipped entirely. The scope of the @code{FOR} + * macro extends to the corresponding @code{ENDFOR} macro. The last form will + * create an array of string values named @code{<value-name>} that only exists + * within the context of this @code{FOR} loop. With this form, in order to + * use a @code{separator-string}, you must code it into the end of the + * template block using the @code{(last-for?)} predicate function + * (@pxref{SCM last-for?}). + * + * If there are any arguments after the @code{value-name}, the initial + * characters are used to determine the form. If the first character is + * either a semi-colon (@code{;}) or an opening parenthesis (@code{(}), then + * it is presumed to be a Scheme expression containing the FOR macro specific + * functions @code{for-from}, @code{for-by}, @code{for-to}, and/or + * @code{for-sep}. @xref{AutoGen Functions}. If it consists of an '@code{i}' + * an '@code{n}' and separated by white space from more text, then the + * @code{FOR x IN} form is processed. Otherwise, the remaining text is + * presumed to be a string for inserting between each iteration of the loop. + * This string will be emitted one time less than the number of iterations of + * the loop. That is, it is emitted after each loop, excepting for the last + * iteration. + * + * If the from/by/to functions are invoked, they will specify which copies of + * the named value are to be processed. If there is no copy of the named + * value associated with a particular index, the @code{FOR} template block + * will be instantiated anyway. The template must use methods for detecting + * missing definitions and emitting default text. In this fashion, you can + * insert entries from a sparse or non-zero based array into a dense, zero + * based array. + * + * @strong{NB:} the @code{for-from}, @code{for-to}, @code{for-by} and + * @code{for-sep} functions are disabled outside of the context of the + * @code{FOR} macro. Likewise, the @code{first-for}, @code{last-for} + * and @code{for-index} functions are disabled outside of the range + * of a @code{FOR} block. + * + * @strong{Also:} the @code{<value-name>} must be a single level name, + * not a compound name (@pxref{naming values}). + * + * @example + * [+FOR var (for-from 0) (for-to <number>) (for-sep ",") +] + * ... text with @code{var}ious substitutions ...[+ + * ENDFOR var+] + * @end example + * + * @noindent + * this will repeat the @code{... text with @code{var}ious + * substitutions ...} <number>+1 times. Each repetition, + * except for the last, will have a comma @code{,} after it. + * + * @example + * [+FOR var ",\n" +] + * ... text with @code{var}ious substitutions ...[+ + * ENDFOR var +] + * @end example + * + * @noindent + * This will do the same thing, but only for the index + * values of @code{var} that have actually been defined. +=*/ +/*=macfunc ENDFOR + * + * what: Terminates the @code{FOR} function template block + * in-context: + * + * desc: + * This macro ends the @code{FOR} function template block. + * For a complete description @xref{FOR}. +=*/ +macro_t * +mFunc_For(templ_t * tpl, macro_t * mac) +{ + macro_t * ret_mac = tpl->td_macros + mac->md_end_idx; + def_ent_t * def; + int lp_ct; + + /* + * "md_pvt" is used by the "FOR x IN ..." variation of the macro. + * When parsed, a chain of text definitions are hung off of "md_pvt". + * "for_each()" will then chase through the linked list of text + * values. This winds up being identical to [+ FOR var +] where + * a list of "var" values has been set. + */ + if (mac->md_pvt != NULL) + def = mac->md_pvt; + else { + bool indexed; + def = find_def_ent(tpl->td_text + mac->md_name_off, &indexed); + if (def == NULL) { + if (OPT_VALUE_TRACE >= TRACE_BLOCK_MACROS) { + fprintf(trace_fp, TRACE_FN_FOR_SKIP, + tpl->td_text + mac->md_name_off); + + if (OPT_VALUE_TRACE == TRACE_EVERYTHING) + fprintf(trace_fp, TAB_FILE_LINE_FMT, + tpl->td_file, mac->md_line); + } + + return ret_mac; + } + } + + for_state = new_for_context(); + for_state->for_name = tpl->td_text + mac->md_name_off; + + if (OPT_VALUE_TRACE >= TRACE_BLOCK_MACROS) + fprintf(trace_fp, TRACE_FN_FOR, tpl->td_text + mac->md_name_off, + tpl->td_file, mac->md_line); + + /* + * Check for a FOR iterating based on scheme macros + */ + if (tpl->td_text[ mac->md_txt_off ] == '(') { + for_state->for_from = \ + for_state->for_to = UNASSIGNED; + + for_state->for_loading = true; + (void) eval(tpl->td_text + mac->md_txt_off); + for_state->for_loading = false; + lp_ct = for_by_step(tpl, mac, def); + + } else { + /* + * The FOR iterates over a list of definitions + */ + AGDUPSTR(for_state->for_sep_str, tpl->td_text + mac->md_txt_off, "ss"); + lp_ct = for_each(tpl, mac, def); + } + + if (for_state->for_sep_str != NULL) + AGFREE(for_state->for_sep_str); + + free_for_context(false); + + if (OPT_VALUE_TRACE >= TRACE_BLOCK_MACROS) { + fprintf(trace_fp, TRACE_FN_FOR_REPEAT, tpl->td_text + mac->md_name_off, + lp_ct); + + if (OPT_VALUE_TRACE == TRACE_EVERYTHING) + fprintf(trace_fp, TAB_FILE_LINE_FMT, tpl->td_file, mac->md_line); + } + + return ret_mac; +} + +/** + * allocate and zero out a FOR macro context. + * + * @returns the top of thecurr_ivk_info stack of contexts. + */ +LOCAL for_state_t * +new_for_context(void) +{ + for_state_t * res; + + /* + * Push the current FOR context onto a stack. + */ + if (++(curr_ivk_info->ii_for_depth) > curr_ivk_info->ii_for_alloc) { + void * dta = curr_ivk_info->ii_for_data; + size_t sz = (curr_ivk_info->ii_for_alloc += 5) * sizeof(for_state_t); + + if (dta == NULL) + dta = AGALOC(sz, "Init FOR sate"); + else dta = AGREALOC(dta, sz, "FOR state"); + curr_ivk_info->ii_for_data = dta; + } + + res = curr_ivk_info->ii_for_data + (curr_ivk_info->ii_for_depth - 1); + memset((void*)res, 0, sizeof(for_state_t)); + res->for_isfirst = true; + return res; +} + +/** + * allocate and zero out a FOR macro context. + * + * @returns the top of thecurr_ivk_info stack of contexts. + */ +LOCAL void +free_for_context(bool everything) +{ + if ((--(curr_ivk_info->ii_for_depth) == 0) || everything) { + AGFREE(curr_ivk_info->ii_for_data); + curr_ivk_info->ii_for_data = NULL; + curr_ivk_info->ii_for_alloc = 0; + curr_ivk_info->ii_for_depth = 0; + } +} + +/** + * Function to initiate loading of FOR block. + * It must replace the dispatch table to handle the ENDFOR function, + * which will unload the dispatch table. + */ +macro_t * +mLoad_For(templ_t * tpl, macro_t * mac, char const ** p_scan) +{ + char * scan_out = tpl->td_scan; /* next text dest */ + char const * scan_in = (char const*)mac->md_txt_off; /* macro text */ + size_t in_len = (size_t)mac->md_res; /* macro len */ + + /* + * Save the global macro loading mode + */ + load_proc_p_t const * save_load_procs = load_proc_table; + + static load_proc_p_t load_for_macro_procs[ FUNC_CT ] = { NULL }; + + load_proc_table = load_for_macro_procs; + + /* + * IF this is the first time here, THEN set up the "FOR" mode callout + * table. It is the standard table, except entries are inserted for + * functions that are enabled only while processing a FOR macro + */ + if (load_for_macro_procs[0] == NULL) { + memcpy((void*)load_for_macro_procs, base_load_table, + sizeof(base_load_table)); + load_for_macro_procs[ FTYP_ENDFOR ] = &mLoad_Ending; + } + + if (in_len == 0) + AG_ABEND_IN(tpl, mac, LD_FOR_NAMELESS); + + /* + * src points to the name of the iteration "variable" + * Special hack: if the name is preceeded by a `.', + * then the lookup is local-only and we will accept it. + */ + mac->md_name_off = tpl->td_scan - tpl->td_text; + if (*scan_in == '.') { + *(scan_out++) = *(scan_in++); + if (! IS_VAR_FIRST_CHAR(*scan_in)) + scan_out--; /* force an error */ + } + + while (IS_VALUE_NAME_CHAR(*scan_in)) *(scan_out++) = *(scan_in++); + *(scan_out++) = NUL; + + if (tpl->td_text[ mac->md_name_off ] == NUL) + AG_ABEND_IN(tpl, mac, LD_FOR_INVALID_VAR); + + /* + * Skip space to the start of the text following the iterator name + */ + scan_in = SPN_WHITESPACE_CHARS(scan_in); + in_len -= scan_in - (char*)mac->md_txt_off; + + /* * * * * + * + * No source -> zero offset to text + */ + if ((ssize_t)in_len <= 0) { + mac->md_txt_off = 0; + } + + /* * * * * + * + * FOR foo IN ... -> no text, but we create an array of text values + */ + else if ( (strneqvcmp(scan_in, LD_FOR_IN, 2) == 0) + && IS_WHITESPACE_CHAR(scan_in[2])) { + load_for_in(scan_in, in_len, tpl, mac); + } + + /* * * * * + * + * *EITHER* a: FOR foo "<<separator>>" + * *OR* FOR foo (scheme ...) ... + */ + else { + mac->md_txt_off = scan_out - tpl->td_text; + memmove(scan_out, scan_in, in_len); + scan_out[in_len] = NUL; + if (IS_QUOTE_CHAR(*scan_out)) + span_quote(scan_out); + scan_out += in_len + 1; + } + /* + * * * * */ + + tpl->td_scan = scan_out; + { + macro_t * next_mac = parse_tpl(mac + 1, p_scan); + if (*p_scan == NULL) + AG_ABEND_IN(tpl, mac, LD_FOR_NO_ENDFOR); + + mac->md_end_idx = mac->md_sib_idx = next_mac - tpl->td_macros; + + load_proc_table = save_load_procs; + return next_mac; + } +} +/* + * Local Variables: + * mode: C + * c-file-style: "stroustrup" + * indent-tabs-mode: nil + * End: + * end of agen5/funcFor.c */ |