/* * $Id: funcDef.c,v 4.19 2007/07/04 20:51:12 bkorb Exp $ * * Time-stamp: "2007-07-04 11:26:09 bkorb" * Last Committed: $Date: 2007/07/04 20:51:12 $ * * This module implements the DEFINE text function. * * This file is part of AutoGen. * AutoGen copyright (c) 1992-2007 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 int (tCmpProc)( const void*, const void* ); typedef struct def_list tDefList; struct def_list { tDefEntry de; char* pzExpr; }; tSCC zNoResolution[] = "Could not resolve macro name: ``%s''"; tSCC zTplInvoked[] = "Template macro %s invoked with %d args\n"; static tDefList* linkTwins( tDefList* pDL, tDefList* pNext, int* pCt ); /* = = = START-STATIC-FORWARD = = = */ /* static forward declarations maintained by :mkfwd */ static int orderDefList( const void* p1, const void* p2 ); static tDefList* linkTwins( tDefList* pDL, tDefList* pNext, int* pCt ); static void prepInvokeArgs( tMacro* pMac ); static void build_defs( int defCt, tDefList* pList ); /* = = = END-STATIC-FORWARD = = = */ tMacro* mLoad_Debug( tTemplate* pT, tMacro* pMac, tCC** ppzScan ); static int orderDefList( const void* p1, const void* p2 ) { tDefEntry* pDL1 = (tDefEntry*)p1; tDefEntry* pDL2 = (tDefEntry*)p2; int cmp = streqvcmp( pDL1->pzDefName, pDL2->pzDefName ); /* * IF the names are the same, then we order them * based on which name appears first. We do _not_ * want the entries reordered within the same name! */ if (cmp == 0) cmp = (int)(pDL1->pzDefName - pDL2->pzDefName); return cmp; } static tDefList* linkTwins( tDefList* pDL, tDefList* pNext, int* pCt ) { tDefList* pN; int ct = *pCt; int idx = 1; pDL->de.pTwin = &(pNext->de); pNext->de.pPrevTwin = &(pDL->de); for (;;) { pNext->de.index = idx++; pN = pNext + 1; /* We return this, valid or not */ if (--ct <= 0) /* count each successive twin */ break; if (streqvcmp( pNext->de.pzDefName, pN->de.pzDefName ) != 0) break; /* * We have found another twin. Link it in and advance */ pNext->de.pTwin = &(pN->de); pN->de.pPrevTwin = &(pNext->de); pNext = pN; } pDL->de.pEndTwin = &(pNext->de); pNext->de.pTwin = NULL; /* NULL terminated list */ pDL->de.pPrevTwin = NULL; /* NULL terminated list */ *pCt = ct; pDL->de.pNext = NULL; /* in case ct == 0 */ return pN; /* If ct is zero, then this is invalid */ } /* * parseMacroArgs * * This routine is called just before the first call to mFunc_Define * for a particular macro invocation. It scans the text of the invocation * for name-value assignments that are only to live for the duration * of the processing of the user defined macro. */ LOCAL void parseMacroArgs( tTemplate* pT, tMacro* pMac ) { char* pzScan = pT->pzTemplText + pMac->ozText; u_int ct = 0; tDefList* pDL; tDefList* pN; /* * If there is no argument text, then the arg count is zero. */ if (pMac->ozText == 0) { pMac->res = 0; return; } /* * Count the number of named values in the argument list */ while (*pzScan != NUL) { ct++; if (! isalpha( *pzScan )) { fprintf( stderr, "On macro argument # %d:\n%s\n", ct, pzScan ); AG_ABEND_IN( pT, pMac, "no macro arg name" ); } while (ISNAMECHAR( *pzScan )) pzScan++; while (isspace( *pzScan )) pzScan++; if (*pzScan != '=') continue; while (isspace( *++pzScan )) ; pzScan = (char*)skipExpression( pzScan, strlen( pzScan )); while (isspace( *pzScan )) pzScan++; } /* * The result is zero if we don't have any */ pMac->sibIndex = ct; if (ct == 0) { pMac->ozText = 0; pMac->res = 0; return; } /* * Allocate the array of definition descriptors */ pzScan = pT->pzTemplText + pMac->ozText; pDL = (tDefList*)AGALOC( ct * sizeof( tDefList ), "array of def desc" ); memset( (void*)pDL, 0, ct * sizeof( tDefList )); pMac->res = (long)pDL; /* * Fill in the array of value assignments */ for (;; pDL++ ) { pDL->de.pzDefName = pzScan; while (ISNAMECHAR( *pzScan )) pzScan++; switch (*pzScan) { case NUL: pDL->de.val.pzText = (char*)zNil; goto fill_in_array_done; default: AG_ABEND_IN( pT, pMac, "name not followed by '='" ); case ' ': case '\t': case '\n': case '\f': *(pzScan++) = NUL; while (isspace( *pzScan )) pzScan++; /* * The name was separated by space, but has no value */ if (*pzScan != '=') { pDL->de.val.pzText = (char*)zNil; if (*pzScan == NUL) goto fill_in_array_done; goto fill_in_array_continue; } /* FALLTHROUGH */ case '=': *(pzScan++) = NUL; } /* * When we arrive here, we have just clobbered a '=' char. * Now we have gather up the assigned value. */ while (isspace( *pzScan )) pzScan++; strtransform( pDL->de.pzDefName, pDL->de.pzDefName ); pDL->pzExpr = pzScan; pDL->de.valType = VALTYP_TEXT; pzScan = (char*)skipExpression( pzScan, strlen( pzScan )); /* * Figure out what kind of expression we have */ switch (*pDL->pzExpr) { case ';': case '(': /* * These expressions will need evaluation */ break; case '`': { char* pz; /* * Process the quoted string, but leave a '`' marker, too */ AGDUPSTR( pz, pDL->pzExpr, "macro arg expr" ); spanQuote( pz ); strcpy( pDL->pzExpr+1, pz ); AGFREE( (void*)pz ); break; } case '"': case '\'': /* * Process the quoted strings now */ if ((pzScan - pDL->pzExpr) < 24) { char* pz = (char*)AGALOC( 24, "quoted string" ); memcpy((void*)pz, pDL->pzExpr, (size_t)(pzScan - pDL->pzExpr)); pDL->pzExpr = pz; manageAllocatedData( pz ); } spanQuote( pDL->pzExpr ); /* FALLTHROUGH */ default: /* * Default: the raw sequence of characters is the value */ pDL->de.val.pzText = pDL->pzExpr; pDL->pzExpr = NULL; } /* * IF the next char is NUL, we are done. * OTHERWISE, the next character must be a space */ if (*pzScan == NUL) break; if (! isspace( *pzScan )) AG_ABEND_IN( pT, pMac, "no space separating entries" ); /* * Terminate the string value and skip over any additional space */ *(pzScan++) = NUL; while (isspace( *pzScan )) pzScan++; fill_in_array_continue:; } fill_in_array_done:; if (ct > 1) { /* * There was more than one value assignment. * Sort them just so we know the siblings are together. * Order is preserved by comparing string addresses, * if the strings compare equal. */ pDL = (tDefList*)pMac->res; qsort( (void*)pDL, (size_t)ct, (size_t)sizeof(tDefList), orderDefList ); /* * Now, link them all together. Singly linked list. */ for (;;) { if (--ct == 0) { pDL->de.pNext = NULL; break; } pN = pDL + 1; /* * IF the next entry has the same name, * THEN it is a "twin". Link twins on the twin list. */ if (streqvcmp( pDL->de.pzDefName, pN->de.pzDefName ) == 0) { pN = linkTwins( pDL, pN, (int*)&ct ); if (ct <= 0) break; /* pN is now invalid */ } pDL->de.pNext = &(pN->de); pDL = pN; } } } static void prepInvokeArgs( tMacro* pMac ) { char* pzText; tTemplate* pT = pCurTemplate; if (pMac->ozText == 0) AG_ABEND_IN( pT, pMac, "The INVOKE macro requires a name" ); pMac->ozName = pMac->ozText; pzText = pT->pzTemplText + pMac->ozText; pzText = (char*)skipExpression( pzText, strlen( pzText )); /* * IF there is no more text, * THEN there are no arguments */ if (*pzText == NUL) { pMac->ozText = 0; pMac->res = 0; } /* * OTHERWISE, skip to the start of the text and process * the arguments to the macro */ else { if (! isspace( *pzText )) AG_ABEND_IN( pT, pMac, "The INVOKE macro name not space separated" ); *pzText = NUL; while (isspace( *++pzText )) ; pMac->ozText = pzText - pT->pzTemplText; parseMacroArgs( pT, pMac ); pCurTemplate = pT; } } #ifdef DEBUG_ENABLED /*=macfunc DEBUG, ifdef DEBUG_ENABLED * * handler_proc: * what: Provide break point spots * desc: * By inserting [+DEBUG n+] into your template, you can set * a breakpoint on the #n case element below and step through * the processing of interesting parts of your template. =*/ tMacro* mFunc_Debug( tTemplate* pT, tMacro* pMac ) { static int dummy = 0; if (OPT_VALUE_TRACE > TRACE_NOTHING) fprintf( pfTrace, " -- DEBUG %s -- FOR index %d\n", pT->pzTemplText + pMac->ozText, (forInfo.fi_depth <= 0) ? -1 : forInfo.fi_data[ forInfo.fi_depth - 1].for_index ); /* * The case element values were chosen to thwart most * optimizers that might be too bright for its own good. * (`dummy' is write-only and could be ignored) */ switch (atoi( pT->pzTemplText + pMac->ozText )) { case 0: dummy = 'A'; break; case 1: dummy = 'u'; break; case 2: dummy = 't'; break; case 4: dummy = 'o'; break; case 8: dummy = 'G'; break; case 16: dummy = 'e'; break; case 32: dummy = 'n'; break; case 64: dummy = 'X'; break; case 128: dummy = 'Y'; break; case 256: dummy = 'Z'; break; case 512: dummy = '.'; break; default: dummy++; } return pMac+1; } #endif /* * build_defs * * Build up a definition context created by passed-in macro arguments */ static void build_defs( int defCt, tDefList* pList ) { tDefEntry* pDefs = &(pList->de); currDefCtx.pDefs = pDefs; /* * FOR each definition, evaluate the associated expression * and set the text value to it. */ do { if (pList->pzExpr == NULL) continue; retryExpression: switch (*(pList->pzExpr)) { case ';': { char* pz = strchr( pList->pzExpr, '\n' ); if (pz != NULL) { while (isspace( *++pz )) ; pList->pzExpr = pz; goto retryExpression; } /* FALLTHROUGH */ } case NUL: pList->pzExpr = NULL; pList->de.val.pzText = (char*)zNil; break; case '(': { SCM res; /* * It is a scheme expression. Accept only string * and number results. */ if (OPT_VALUE_TRACE >= TRACE_EXPRESSIONS) { fprintf( pfTrace, "Scheme eval for arg %d:\n\t`%s'\n", pCurMacro->sibIndex - defCt, pList->pzExpr ); } res = ag_eval( pList->pzExpr ); if (AG_SCM_STRING_P( res )) { AGDUPSTR( pList->de.val.pzText, ag_scm2zchars(res, "eval res"), "dup eval res" ); } else if (AG_SCM_NUM_P( res )) { pList->de.val.pzText = AGALOC( 16, "number buf" ); snprintf(pList->de.val.pzText, (size_t)16, "%ld", gh_scm2ulong( res )); } else AGDUPSTR( pList->de.val.pzText, zNil, "empty string" ); break; } case '`': if (OPT_VALUE_TRACE >= TRACE_EXPRESSIONS) { fprintf( pfTrace, "shell eval for arg %d:\n\t`%s'\n", pCurMacro->sibIndex - defCt, pList->pzExpr+1 ); } pList->de.val.pzText = runShell( pList->pzExpr+1 ); break; } } while (pList++, --defCt > 0); } /*=macfunc DEFINE * * what: Define a user AutoGen macro * cindex: define macro * handler_proc: * load_proc: * unload-proc: * * desc: * * This function will define a new macro. You must provide a name for the * macro. You do not specify any arguments, though the invocation may * specify a set of name/value pairs that are to be active during the * processing of the macro. * * @example * [+ define foo +] * ... macro body with macro functions ... * [+ enddef +] * ... [+ foo bar='raw text' baz=<> +] * @end example * * Once the macro has been defined, this new macro can be invoked by * specifying the macro name as the first token after the start macro marker. * Alternatively, you may make the invocation explicitly invoke a defined * macro by specifying @code{INVOKE} (@pxref{INVOKE}) in the macro * invocation. If you do that, the macro name can be computed with an * expression that gets evaluated every time the INVOKE macro is encountered. * * Any remaining text in the macro invocation will be used to create new * name/value pairs that only persist for the duration of the processing of * the macro. The expressions are evaluated the same way basic * expressions are evaluated. @xref{expression syntax}. * * The resulting definitions are handled much like regular * definitions, except: * * @enumerate * @item * The values may not be compound. That is, they may not contain * nested name/value pairs. * @item * The bindings go away when the macro is complete. * @item * The name/value pairs are separated by whitespace instead of * semi-colons. * @item * Sequences of strings are not concatenated. * @end enumerate * * @quotation * @strong{NB:} The macro is extracted from the template as the template is * scanned. You cannot conditionally define a macro by enclosing it in an * @code{IF}/@code{ENDIF} (@pxref{IF}) macro pair. If you need to dynamically * select the format of a @code{DEFINE}d macro, then put the flavors into * separate template files that simply define macros. @code{INCLUDE} * (@pxref{INCLUDE}) the appropriate template when you have computed which * you need. * @end quotation * * Due to this, it is acceptable and even a good idea to place all the * @code{DEFINE} macros at the end of the template. That puts the main * body of the template at the beginning of the file. =*/ /*=macfunc ENDDEF * * what: Ends a macro definition. * in-context: * * desc: * This macro ends the @code{DEFINE} function template block. * For a complete description @xref{DEFINE}. =*/ /* * mFunc_Define * * This routine runs the invocation. */ tMacro* mFunc_Define( tTemplate* pT, tMacro* pMac ) { tDefList* pList = (tDefList*)pMac->res; int defCt = pMac->sibIndex; tDefCtx ctx; pT = (tTemplate*)pMac->funcPrivate; if (OPT_VALUE_TRACE > TRACE_NOTHING) { fprintf( pfTrace, zTplInvoked, pT->pzTplName, defCt ); if (OPT_VALUE_TRACE < TRACE_EVERYTHING) fprintf( pfTrace, zFileLine, pCurTemplate->pzTplFile, pMac->lineNo ); } /* * IF we have no special definitions, then do not nest definitions */ if (defCt != 0) { ctx = currDefCtx; currDefCtx.pPrev = &ctx; build_defs( defCt, pList ); } { tTemplate* pOldTpl = pCurTemplate; pCurTemplate = pT; generateBlock( pT, pT->aMacros, pT->aMacros + pT->macroCt ); pCurTemplate = pOldTpl; } if (defCt != 0) currDefCtx = ctx; if ((defCt = pMac->sibIndex) > 0) { pList = (tDefList*)pMac->res; while (defCt-- > 0) { if (pList->pzExpr != NULL) { AGFREE( (void*)pList->de.val.pzText ); pList->de.val.pzText = NULL; } pList++; } } return pMac+1; } void mUnload_Define( tMacro* pMac ) { void* p = (void*)(pMac->res); if (p != NULL) AGFREE(p); } /*=macfunc INVOKE * * handler_proc: * what: Invoke a User Defined Macro * * desc: * * User defined macros may be invoked explicitly or implicitly. * If you invoke one implicitly, the macro must begin with the * name of the defined macro. Consequently, this may @strong{not} * be a computed value. If you explicitly invoke a user defined macro, * the macro begins with the macro name @code{INVOKE} followed by * a @i{basic expression} that must yield a known user defined macro. * A macro name _must_ be found, or AutoGen will issue a diagnostic * and exit. * * Arguments are passed to the invoked macro by name. * The text following the macro name must consist of a series of * names each of which is followed by an equal sign (@code{=}) and * a @i{basic expression} that yields a string. * * The string values may contain template macros that are parsed * the first time the macro is processed and evaluated again every * time the macro is evaluated. =*/ tMacro* mFunc_Invoke( tTemplate* pT, tMacro* pMac ) { char* pzText; SCM macName; tTemplate* pInv; /* * IF this is the first time through, * THEN separate the name from the rest of the arguments. */ if (pMac->ozName == 0) { prepInvokeArgs( pMac ); /* * IF the name is constant and not an expression, * THEN go find the template now and bind the macro call * to a particular template macro */ if (isalpha( pT->pzTemplText[ pMac->ozName ])) { pMac->funcCode = FTYP_DEFINE; pMac->funcPrivate = (void*)findTemplate( pT->pzTemplText + pMac->ozName ); if (pMac->funcPrivate == NULL) { pzText = aprf( zNoResolution, pT->pzTemplText + pMac->ozName ); AG_ABEND_IN( pT, pMac, pzText ); /* NOTREACHED */ } return mFunc_Define( pT, pMac ); } } /* * Call `eval' to determine the macro name. Every invocation * may be different!! */ macName = eval( pT->pzTemplText + pMac->ozName ); pzText = ag_scm2zchars( macName, "macro name" ); pInv = findTemplate( pzText ); if (pInv == NULL) { pzText = aprf( zNoResolution, pzText ); AG_ABEND_IN( pT, pMac, pzText ); /* NOTREACHED */ } pMac->funcPrivate = (void*)pInv; return mFunc_Define( pT, pMac ); } /* Load Debug * * what: Loads the debug function so you can set breakpoints * at load time, too :-) =*/ tMacro* mLoad_Debug( tTemplate* pT, tMacro* pMac, tCC** ppzScan ) { return mLoad_Unknown( pT, pMac, ppzScan ); } tMacro* mLoad_Define( tTemplate* pT, tMacro* pMac, tCC** ppzScan ) { tSCC zNameNeeded[] = "DEFINE requires a name"; char* pzCopy; /* next text dest */ tTemplate* pNewT; /* * Save the global macro loading mode */ tpLoadProc* papLP = papLoadProc; static tpLoadProc apDefineLoad[ FUNC_CT ] = { NULL }; if (pMac->ozText == 0) AG_ABEND_IN( pT, pMac, zNameNeeded ); /* * IF this is the first time here, * THEN set up the "DEFINE" block callout table. * It is the standard table, except entries are inserted * for functions that are enabled only while processing * a DEFINE block (viz. "ENDDEF" and removing "DEFINE"). */ if (apDefineLoad[0] == NULL) { memcpy( (void*)apDefineLoad, apLoadProc, sizeof( apLoadProc )); apDefineLoad[ FTYP_ENDDEF ] = &mLoad_Ending; apDefineLoad[ FTYP_DEFINE ] = &mLoad_Bogus; } papLoadProc = apDefineLoad; { char const* pzScan = *ppzScan; /* text after macro */ char const* pzSrc = (char const*)pMac->ozText; /* macro text */ int macCt = pT->macroCt - (pMac - pT->aMacros); size_t alocSize = sizeof( *pNewT ) + (macCt * sizeof( tMacro )) + strlen( pzScan ) + 0x100; alocSize &= ~0x0F; /* * Allocate a new template block that is much larger than needed. */ pNewT = (tTemplate*)AGALOC( alocSize, "AG macro definition" ); memset( (void*)pNewT, 0, alocSize ); pNewT->magic = pT->magic; pNewT->descSize = alocSize; pNewT->macroCt = macCt; pNewT->pzTplFile = strdup(pT->pzTplFile); pzCopy = pNewT->pzTplName = (void*)(pNewT->aMacros + macCt); if (! isalpha( *pzSrc )) AG_ABEND_IN( pT, pMac, zNameNeeded ); while (ISNAMECHAR(*pzSrc)) *(pzCopy++) = *(pzSrc++); } if (OPT_VALUE_TRACE >= TRACE_BLOCK_MACROS) fprintf( pfTrace, "Defining macro %s from %s\n", pNewT->pzTplName, pNewT->pzTplFile ); *(pzCopy++) = NUL; pNewT->pzTemplText = pzCopy; pNewT->pNext = pzCopy+1; strcpy( pNewT->zStartMac, pT->zStartMac ); strcpy( pNewT->zEndMac, pT->zEndMac ); pCurTemplate = pNewT; { tMacro* pMacEnd = parseTemplate( pNewT->aMacros, ppzScan ); int ct; /* * Make sure all of the input string was *NOT* scanned. */ if (*ppzScan == NULL) AG_ABEND_IN( pNewT, pNewT->aMacros, "parse ended unexpectedly" ); ct = pMacEnd - pNewT->aMacros; /* * IF there are empty macro slots, * THEN pack the text */ if (ct < pNewT->macroCt) { int delta = sizeof(tMacro) * (pNewT->macroCt - ct); void* data = (pNewT->pzTplName == NULL) ? pNewT->pzTemplText : pNewT->pzTplName; size_t size = pNewT->pNext - (char*)data; memmove( (void*)pMacEnd, data, size ); pNewT->pzTemplText -= delta; pNewT->pNext -= delta; pNewT->pzTplName -= delta; pNewT->macroCt = ct; } } /* * Adjust the sizes. Remove absolute pointers. Reallocate to the correct * size. Restore the offsets to pointer values. */ { size_t sz = pNewT->pNext - (char*)pNewT; if (sz < pNewT->descSize) { pNewT->descSize = sz; pNewT->pzTplName -= (long)pNewT; pNewT->pzTemplText -= (long)pNewT; pNewT = (tTemplate*)AGREALOC( (void*)pNewT, pNewT->descSize, "resize AG macro definition" ); if (pNewT == NULL) AG_ABEND( "failed to resize AG macro" ); pNewT->pzTplName += (long)pNewT; pNewT->pzTemplText += (long)pNewT; } } #if defined( DEBUG_ENABLED ) if (HAVE_OPT( SHOW_DEFS )) { tSCC zSum[] = "loaded %d macros from %s\n" "\tBinary template size: 0x%X\n\n"; fprintf( pfTrace, zSum, pNewT->macroCt, pNewT->pzTplFile, pNewT->descSize ); } #endif pNewT->pNext = (char*)pNamedTplList; pNamedTplList = pNewT; papLoadProc = papLP; memset( (void*)pMac, 0, sizeof(*pMac) ); pCurTemplate = pT; return pMac; } /* * Local Variables: * mode: C * c-file-style: "stroustrup" * indent-tabs-mode: nil * End: * end of agen5/funcDef.c */