diff options
Diffstat (limited to 'agen5/expState.c')
-rw-r--r-- | agen5/expState.c | 819 |
1 files changed, 819 insertions, 0 deletions
diff --git a/agen5/expState.c b/agen5/expState.c new file mode 100644 index 0000000..871fbbe --- /dev/null +++ b/agen5/expState.c @@ -0,0 +1,819 @@ + +/** + * @file expState.c + * + * This module implements expression functions that + * query and get state information from AutoGen data. + * + * Time-stamp: "2012-04-07 09:50:32 bkorb" + * + * 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/>. + */ + +#ifdef SCM_HAVE_T_UINT64 + typedef uint64_t ver_type_t; +# define VER_UNIT_SHIFT 16ULL +# if ((SCM_MAJOR_VERSION * 100) + SCM_MINOR_VERSION) >= 108 +# define SCM_FROM(v) scm_from_uint64(v) +# else +# define SCM_FROM(v) gh_ulong2scm((unsigned long)v) +# endif + +#else + typedef uint32_t ver_type_t; +# define VER_UNIT_SHIFT 8 +# ifdef HAVE_SCM_FROM_UINT32 +# define SCM_FROM(v) scm_from_uint32(v) +# else +# define SCM_FROM(v) gh_ulong2scm((unsigned long)v) +# endif +#endif + +/* = = = START-STATIC-FORWARD = = = */ +static int +entry_length(char* name); + +static int +count_entries(char* name); + +static SCM +find_entry_value(SCM op, SCM obj, SCM test); + +static ver_type_t +str2int_ver(char* pz); + +static SCM +do_tpl_file_line(int line_delta, char const * fmt); +/* = = = END-STATIC-FORWARD = = = */ + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * EXPRESSION EVALUATION SUPPORT ROUTINES + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +static int +entry_length(char* name) +{ + def_ent_t** papDefs = find_def_ent_list(name); + int res = 0; + + if (papDefs == NULL) + return 0; + + for (;;) { + def_ent_t* pDE = *(papDefs++); + if (pDE == NULL) + break; + if (pDE->de_type == VALTYP_TEXT) + res += strlen(pDE->de_val.dvu_text); + else + res++; + } + return res; +} + + +static int +count_entries(char* name) +{ + def_ent_t** papDefs = find_def_ent_list(name); + int res = 0; + + if (papDefs == NULL) + return 0; + + for (;;) { + def_ent_t* pDE = *(papDefs++); + if (pDE == NULL) + break; + res++; + } + return res; +} + +/** + * Find a definition with a specific value + */ +static SCM +find_entry_value(SCM op, SCM obj, SCM test) +{ + bool isIndexed; + def_ent_t* pE; + char* pzField; + + { + char * name = ag_scm2zchars(obj, "find name"); + + if (OPT_VALUE_TRACE >= TRACE_EXPRESSIONS) + fprintf(trace_fp, TRACE_FIND_ENT, name); + + pzField = strchr(name, name_sep_ch); + if (pzField != NULL) + *(pzField++) = NUL; + + pE = find_def_ent(name, &isIndexed); + } + + /* + * No such entry? return FALSE + */ + if (pE == NULL) { + if (OPT_VALUE_TRACE >= TRACE_EXPRESSIONS) + fputs(FIND_ENT_FAIL, trace_fp); + return SCM_BOOL_F; + } + + /* + * No subfield? Check the values + */ + if (pzField == NULL) { + SCM result; + SCM field; + if (pE->de_type != VALTYP_TEXT) { + if (OPT_VALUE_TRACE >= TRACE_EXPRESSIONS) + fputs(FIND_ENT_FAIL, trace_fp); + return SCM_BOOL_F; /* Cannot match string -- not a text value */ + } + + field = AG_SCM_STR02SCM(pE->de_val.dvu_text); + result = AG_SCM_APPLY2(op, field, test); + if (! isIndexed) + while (result == SCM_BOOL_F) { + + pE = pE->de_twin; + if (pE == NULL) + break; + + field = AG_SCM_STR02SCM(pE->de_val.dvu_text); + result = AG_SCM_APPLY2(op, field, test); + } + + if (OPT_VALUE_TRACE >= TRACE_EXPRESSIONS) + fputs((result == SCM_BOOL_T) ? FIND_ENT_SUCC : FIND_ENT_FAIL, + trace_fp); + return result; + } + + /* + * a subfield for a text macro? return FALSE + */ + if (pE->de_type == VALTYP_TEXT) { + if (OPT_VALUE_TRACE >= TRACE_EXPRESSIONS) + fputs(FIND_ENT_FAIL, trace_fp); + return SCM_BOOL_F; + } + + /* + * Search the members for what we want. + */ + pzField[-1] = name_sep_ch; + { + SCM field = AG_SCM_STR02SCM(pzField); + SCM result; + def_ctx_t ctx = curr_def_ctx; + + curr_def_ctx.dcx_prev = &ctx; + curr_def_ctx.dcx_defent = pE->de_val.dvu_entry; + + result = find_entry_value(op, field, test); + + if (! isIndexed) + while (result == SCM_BOOL_F) { + + pE = pE->de_twin; + if (pE == NULL) + break; + + curr_def_ctx.dcx_defent = pE->de_val.dvu_entry; + result = find_entry_value(op, field, test); + } + + curr_def_ctx = ctx; + return result; + } +} + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * EXPRESSION ROUTINES + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +/*=gfunc base_name + * + * what: base output name + * + * doc: Returns a string containing the base name of the output file(s). + * Generally, this is also the base name of the definitions file. +=*/ +SCM +ag_scm_base_name(void) +{ + return AG_SCM_STR02SCM((char*)(void*)OPT_ARG(BASE_NAME)); +} + +/*=gfunc version_compare + * + * what: compare two version numbers + * general_use: + * + * exparg: op, comparison operator + * exparg: v1, first version + * exparg: v2, compared-to version + * + * doc: Converts v1 and v2 strings into 64 bit values and returns the + * result of running 'op' on those values. It assumes that the version + * is a 1 to 4 part dot-separated series of numbers. Suffixes like, + * "5pre4" or "5-pre4" will be interpreted as two numbers. The first + * number ("5" in this case) will be decremented and the number after + * the "pre" will be added to 0xC000. (Unless your platform is unable + * to support 64 bit integer arithmetic. Then it will be added to 0xC0.) + * Consequently, these yield true: + * @example + * (version-compare > "5.8.5" "5.8.5-pre4") + * (version-compare > "5.8.5-pre10" "5.8.5-pre4") + * @end example +=*/ +static ver_type_t +str2int_ver(char* pz) +{ + char* pzStr = pz; + ver_type_t val = 0; + int ix = 4; + + while (--ix >= 0) { + unsigned int v; + val <<= VER_UNIT_SHIFT; + pz = SPN_WHITESPACE_CHARS(pz); + + next_number: + if (! IS_DEC_DIGIT_CHAR(*pz)) break; + v = (unsigned int)strtoul(pz, &pz, 0) & ((1 << VER_UNIT_SHIFT) - 1); + if (pz == NULL) + break; + val += v; + if (*pz == '-') pz++; + + switch (*pz) { + case 'p': + if ((pz[1] == 'r') && (pz[2] == 'e')) { + pz += 3; + val = (val << 2) - 1; + val <<= (VER_UNIT_SHIFT - 2); + if (--ix < 0) goto leave_str2int_ver; + goto next_number; + } + /* FALLTHROUGH */ + + default: + goto leave_str2int_ver; + + case '.': + if (! IS_DEC_DIGIT_CHAR(*(++pz))) + goto leave_str2int_ver; + break; + } + } leave_str2int_ver: ; + + while (--ix >= 0) val <<= VER_UNIT_SHIFT; + if (OPT_VALUE_TRACE >= TRACE_EXPRESSIONS) + fprintf(trace_fp, TRACE_VER_CONVERT, (long long)val, pzStr); + return val; +} + +/** + * Convert version number strings into a binary representation and compare. + */ +SCM +ag_scm_version_compare(SCM op, SCM v1, SCM v2) +{ + ver_type_t val1 = str2int_ver(ag_scm2zchars(v1, "ver")); + ver_type_t val2 = str2int_ver(ag_scm2zchars(v2, "ver")); + v1 = SCM_FROM(val1); + v2 = SCM_FROM(val2); + return scm_apply(op, v1, scm_cons(v2, AG_SCM_LISTOFNULL())); +} + +/*=gfunc count + * + * what: definition count + * + * exparg: ag-name, name of AutoGen value + * + * doc: Count the number of entries for a definition. + * The input argument must be a string containing the name + * of the AutoGen values to be counted. If there is no + * value associated with the name, the result is an SCM + * immediate integer value of zero. +=*/ +SCM +ag_scm_count(SCM obj) +{ + int ent_len = count_entries(ag_scm2zchars(obj, "ag object")); + + return AG_SCM_INT2SCM(ent_len); +} + + +/*=gfunc def_file + * + * what: definitions file name + * + * doc: Get the name of the definitions file. + * Returns the name of the source file containing the AutoGen + * definitions. +=*/ +SCM +ag_scm_def_file(void) +{ + return AG_SCM_STR02SCM((char*)(void*)base_ctx->scx_fname); +} + + +/*=gfunc exist_p + * + * what: test for value name + * + * exparg: ag-name, name of AutoGen value + * + * doc: return SCM_BOOL_T iff a specified name has an AutoGen value. + * The name may include indexes and/or member names. + * All but the last member name must be an aggregate definition. + * For example: + * @example + * (exist? "foo[3].bar.baz") + * @end example + * will yield true if all of the following is true: + * @* + * There is a member value of either group or string type + * named @code{baz} for some group value @code{bar} that + * is a member of the @code{foo} group with index @code{3}. + * There may be multiple entries of @code{bar} within + * @code{foo}, only one needs to contain a value for @code{baz}. +=*/ +SCM +ag_scm_exist_p(SCM obj) +{ + bool x; + SCM res; + + if (find_def_ent(ag_scm2zchars(obj, "ag object"), &x) == NULL) + res = SCM_BOOL_F; + else res = SCM_BOOL_T; + + return res; +} + + +/*=gfunc ag_function_p + * + * what: test for function + * + * exparg: ag-name, name of AutoGen macro + * + * doc: return SCM_BOOL_T if a specified name is a user-defined AutoGen + * macro, otherwise return SCM_BOOL_F. +=*/ +SCM +ag_scm_ag_function_p(SCM obj) +{ + SCM res; + + if (find_tpl(ag_scm2zchars(obj, "ag user macro")) == NULL) + res = SCM_BOOL_F; + else res = SCM_BOOL_T; + + return res; +} + + +/*=gfunc match_value_p + * + * what: test for matching value + * + * exparg: op, boolean result operator + * exparg: ag-name, name of AutoGen value + * exparg: test-str, string to test against + * + * doc: This function answers the question, "Is there an AutoGen value named + * @code{ag-name} with a value that matches the pattern @code{test-str} + * using the match function @code{op}?" Return SCM_BOOL_T iff at least + * one occurrence of the specified name has such a value. The operator + * can be any function that takes two string arguments and yields a + * boolean. It is expected that you will use one of the string matching + * functions provided by AutoGen. + * @* + * The value name must follow the same rules as the + * @code{ag-name} argument for @code{exist?} (@pxref{SCM exist?}). +=*/ +SCM +ag_scm_match_value_p(SCM op, SCM obj, SCM test) +{ + if ( (! AG_SCM_IS_PROC(op)) + || (! AG_SCM_STRING_P(obj)) ) + return SCM_UNDEFINED; + + if (OPT_VALUE_TRACE >= TRACE_EXPRESSIONS) + fprintf(trace_fp, TRACE_MATCH_VAL, ag_scm2zchars(test, "test val")); + + return find_entry_value(op, obj, test); +} + + +/*=gfunc get + * + * what: get named value + * + * exparg: ag-name, name of AutoGen value + * exparg: alt-val, value if not present, optional + * + * doc: + * Get the first string value associated with the name. + * It will either return the associated string value (if + * the name resolves), the alternate value (if one is provided), + * or else the empty string. +=*/ +SCM +ag_scm_get(SCM agName, SCM altVal) +{ + def_ent_t* pE; + bool x; + + pE = (! AG_SCM_STRING_P(agName)) ? NULL : + find_def_ent(ag_scm2zchars(agName, "ag value"), &x); + + if ((pE == NULL) || (pE->de_type != VALTYP_TEXT)) { + if (AG_SCM_STRING_P(altVal)) + return altVal; + return AG_SCM_STR02SCM(zNil); + } + + return AG_SCM_STR02SCM(pE->de_val.dvu_text); +} + + +/*=gfunc get_c_name + * + * what: get named value, mapped to C name syntax + * + * exparg: ag-name, name of AutoGen value + * + * doc: + * + * Get the first string value associated with the name. It will either + * return the associated string value (if the name resolves), the alternate + * value (if one is provided), or else the empty string. The result is + * passed through "string->c-name!". +=*/ +SCM +ag_scm_get_c_name(SCM agName) +{ + return ag_scm_string_to_c_name_x( + ag_scm_get(agName, SCM_UNDEFINED)); +} + + +/*=gfunc get_up_name + * + * what: get upper cased named value, mapped to C name syntax + * + * exparg: ag-name, name of AutoGen value + * + * doc: + * + * Get the first string value associated with the name. It will either + * return the associated string value (if the name resolves), the alternate + * value (if one is provided), or else the empty string. The result is + * passed through "string->c-name!" and "string->up-case!". +=*/ +SCM +ag_scm_get_up_name(SCM agName) +{ + return ag_scm_string_upcase_x(ag_scm_get_c_name(agName)); +} + + +/*=gfunc get_down_name + * + * what: get lower cased named value, mapped to C name syntax + * + * exparg: ag-name, name of AutoGen value + * + * doc: + * + * Get the first string value associated with the name. It will either + * return the associated string value (if the name resolves), the alternate + * value (if one is provided), or else the empty string. The result is + * passed through "string->c-name!" and "string->down-case!". +=*/ +SCM +ag_scm_get_down_name(SCM agName) +{ + return ag_scm_string_downcase_x(ag_scm_get_c_name(agName)); +} + + +/*=gfunc high_lim + * + * what: get highest value index + * + * exparg: ag-name, name of AutoGen value + * + * doc: + * + * Returns the highest index associated with an array of definitions. + * This is generally, but not necessarily, one less than the + * @code{count} value. (The indexes may be specified, rendering a + * non-zero based or sparse array of values.) + * + * This is very useful for specifying the size of a zero-based array + * of values where not all values are present. For example: + * + * @example + * tMyStruct myVals[ [+ (+ 1 (high-lim "my-val-list")) +] ]; + * @end example +=*/ +SCM +ag_scm_high_lim(SCM obj) +{ + def_ent_t* pE; + bool isIndexed; + + pE = find_def_ent(ag_scm2zchars(obj, "ag value"), &isIndexed); + + /* + * IF we did not find the entry we are looking for + * THEN return zero + * ELSE search the twin list for the high entry + */ + if (pE == NULL) + return AG_SCM_INT2SCM(0); + + if (isIndexed) + return AG_SCM_INT2SCM((int)pE->de_index); + + if (pE->de_etwin != NULL) + pE = pE->de_etwin; + + return AG_SCM_INT2SCM((int)pE->de_index); +} + + +/*=gfunc len + * + * what: get count of values + * + * exparg: ag-name, name of AutoGen value + * + * doc: If the named object is a group definition, then "len" is + * the same as "count". Otherwise, if it is one or more text + * definitions, then it is the sum of their string lengths. + * If it is a single text definition, then it is equivalent to + * @code{(string-length (get "ag-name"))}. +=*/ +SCM +ag_scm_len(SCM obj) +{ + int len = entry_length(ag_scm2zchars(obj, "ag value")); + + return AG_SCM_INT2SCM(len); +} + + +/*=gfunc low_lim + * + * what: get lowest value index + * + * exparg: ag-name, name of AutoGen value + * + * doc: Returns the lowest index associated with an array of definitions. +=*/ +SCM +ag_scm_low_lim(SCM obj) +{ + def_ent_t* pE; + bool x; + + pE = find_def_ent(ag_scm2zchars(obj, "ag value"), &x); + + /* + * IF we did not find the entry we are looking for + * THEN return zero + * ELSE we have the low index. + */ + if (pE == NULL) + return AG_SCM_INT2SCM(0); + + return AG_SCM_INT2SCM((int)pE->de_index); +} + + +/*=gfunc set_option + * + * what: Set a command line option + * + * exparg: opt, AutoGen option name + its argument + * + * doc: The text argument must be an option name followed by any needed + * option argument. Returns SCM_UNDEFINED. +=*/ +SCM +ag_scm_set_option(SCM opt) +{ + optionLoadLine(&autogenOptions, ag_scm2zchars(opt, "opt + arg")); + return SCM_UNDEFINED; +} + + +/*=gfunc suffix + * + * what: get the current suffix + * + * doc: + * Returns the current active suffix (@pxref{pseudo macro}). +=*/ +SCM +ag_scm_suffix(void) +{ + return AG_SCM_STR02SCM((char*)curr_sfx); +} + + +/*=gfunc tpl_file + * + * what: get the template file name + * + * exparg: full_path, include full path to file, optonal + * + * doc: Returns the name of the current template file. + * If @code{#t} is passed in as an argument, then the template + * file is hunted for in the template search path. Otherwise, + * just the unadorned name. +=*/ +SCM +ag_scm_tpl_file(SCM full) +{ + if (AG_SCM_BOOL_P(full) && AG_SCM_NFALSEP(full)) { + static char const * const sfx[] = { TPL_FILE_TPL, NULL }; + + char z[AG_PATH_MAX]; + if (SUCCESSFUL(find_file(tpl_fname, z, sfx, NULL))) + return AG_SCM_STR02SCM(z); + } + + return AG_SCM_STR02SCM((char*)(void*)tpl_fname); +} + +/** + * guts of the template file/line functions + */ +static SCM +do_tpl_file_line(int line_delta, char const * fmt) +{ + void * args[2] = { + [0] = (void*)current_tpl->td_file, + [1] = (void*)((long)cur_macro->md_line + line_delta) + }; + char * buf = strrchr(args[0], DIRCH); + if (buf != NULL) + args[0] = buf + 1; + + { + size_t sz = strlen(fmt) + strlen(args[0]) + 24; + buf = ag_scribble(sz); + } + + sprintfv(buf, fmt, (snv_constpointer*)args); + return AG_SCM_STR02SCM(buf); +} + +/*=gfunc tpl_file_line + * + * what: get the template file+line number + * + * exparg: msg-fmt, formatting for line message, optional + * + * doc: + * Returns the file and line number of the current template macro using + * either the default format, "from %s line %d", or else the format you + * supply. For example, if you want to insert a "C" language file-line + * directive, you would supply the format "# %2$d \"%1$s\"", but that + * is also already supplied with the scheme variable + * @xref{SCM c-file-line-fmt}. You may use it thus: + * @example + * (tpl-file-line c-file-line-fmt) + * @end example + * + * It is also safe to use the formatting string, "%2$d". AutoGen uses + * an argument vector version of printf: @xref{snprintfv}, + * and it does not need to know the types of each argument in order to + * skip forward to the second argument. +=*/ +SCM +ag_scm_tpl_file_line(SCM fmt) +{ + char const * pzFmt = TPL_FILE_LINE_FMT; + if (AG_SCM_STRING_P(fmt)) + pzFmt = ag_scm2zchars(fmt, "f/l fmt"); + + return do_tpl_file_line(0, pzFmt); +} + +/*=gfunc tpl_file_next_line + * + * what: get the template file plus next line number + * + * exparg: msg-fmt, formatting for line message, optional + * + * doc: + * This is almost the same as @xref{SCM tpl-file-line}, except that + * the line referenced is the next line, per C compiler conventions, and + * consequently defaults to the format: # <line-no+1> "<file-name>" +=*/ +SCM +ag_scm_tpl_file_next_line(SCM fmt) +{ + char const * pzFmt = TPL_FILE_NEXT_LINE_FMT; + if (AG_SCM_STRING_P(fmt)) + pzFmt = ag_scm2zchars(fmt, "f/l fmt"); + + return do_tpl_file_line(1, pzFmt); +} + +/*=gfunc def_file_line + * + * what: get a definition file+line number + * + * exparg: ag-name, name of AutoGen value + * exparg: msg-fmt, formatting for line message, optional + * + * doc: + * Returns the file and line number of a AutoGen defined value, using + * either the default format, "from %s line %d", or else the format you + * supply. For example, if you want to insert a "C" language file-line + * directive, you would supply the format "# %2$d \"%1$s\"", but that + * is also already supplied with the scheme variable + * @xref{SCM c-file-line-fmt}. You may use it thus: + * + * @example + * (def-file-line "ag-def-name" c-file-line-fmt) + * @end example + * + * It is also safe to use the formatting string, "%2$d". AutoGen uses + * an argument vector version of printf: @xref{snprintfv}. +=*/ +SCM +ag_scm_def_file_line(SCM obj, SCM fmt) +{ + char const * pzFmt = DEF_FILE_LINE_FMT; + char * buf; + bool x; + + def_ent_t * pE = find_def_ent(ag_scm2zchars(obj, "ag value"), &x); + + /* + * IF we did not find the entry we are looking for + * THEN return UNDEFINED + */ + if (pE == NULL) + return SCM_UNDEFINED; + + if (AG_SCM_STRING_P(fmt)) + pzFmt = ag_scm2zchars(fmt, "f/l fmt"); + + { + void * args[2] = { + (void*)pE->de_file, + (void*)(long)pE->de_line + }; + size_t maxlen; + + buf = strrchr(args[0], DIRCH); + if (buf != NULL) + args[0] = buf + 1; + + maxlen = strlen(args[0]) + strlen(pzFmt) + LOG10_2to32 + 1; + buf = ag_scribble(maxlen); + sprintfv(buf, pzFmt, (snv_constpointer*)args); + } + + return AG_SCM_STR02SCM(buf); +} +/* + * Local Variables: + * mode: C + * c-file-style: "stroustrup" + * indent-tabs-mode: nil + * End: + * end of agen5/expState.c */ |