|
|
DataMuseum.dkPresents historical artifacts from the history of: DKUUG/EUUG Conference tapes |
This is an automatic "excavation" of a thematic subset of
See our Wiki for more about DKUUG/EUUG Conference tapes Excavated with: AutoArchaeologist - Free & Open Source Software. |
top - metrics - downloadIndex: T m
Length: 12268 (0x2fec)
Types: TextFile
Names: »macro.c«
└─⟦a0efdde77⟧ Bits:30001252 EUUGD11 Tape, 1987 Spring Conference Helsinki
└─⟦this⟧ »EUUGD11/euug-87hel/sec1/prep/macro.c«
/* MACRO.c
*
* The routines in this file support the macro processing facilities
* of PREP. The style is similar to that of c #define macros, except
* that : is used instead of #define and ; terminates the macro.
* This is done to allow both PREP macros and ratfor macros in the
* same program.
* Recursive definitions are permitted, but will cause an abort
* (and possibley a memory allocation error) on expansion. For each
* line submitted to expand_macros, a count of is kept for each
* stored macro indicating how many times it has been expanded in
* the current line. When this exceeds MAX_CALLS, the program
* assumes a macro definition is recursive and stops. Macros
* are expanded starting with the one with the longest name, so that
* if the definitions
*
* : >= .ge. ;
* : > .gt. ;
*
* are in effect, >= will be changed to .ge. rather than .gt.=. This
* is only a potential problem when macro names are not fully
* alphanumeric, since "arg" will not be flagged if "r" is defined.
*
* 11/4/86 P.R.OVE
*/
#include "macro.h"
/* Macro processor.
*
* This routine defines and expands macros. The definition phase
* is invoked when a leading : is found in the record. Text is
* then taken until the terminating ; is found. Text following the
* ; is ignored. Multiline macros are permitted: they will be
* converted to at least as many lines in the fortran program.
* Failure to have a terminating ; will define the entire program
* to be a macro.
* A NULL pointer is returned if a macro has been defined. Otherwise
* a pointer to the buffer with the expanded text is returned (even if
* no macros have been expanded). The buffer is temporary and should
* be eliminated by the caller.
*/
char *mac_proc()
{
int i, j, size ;
char *text, *def ;
/* see if this is a definition (look for leading :) */
for ( i=0, text=NULL; in_buff[i] != NULL; i++ ) {
if ( in_buff[i] == BLANK | in_buff[i] == TAB ) continue ;
if ( in_buff[i] == ':' ) text = &in_buff[i] ;
break ;
}
if ( text == NULL ) {
/* expand macro if not a definition */
if ( defined_macros == 0 ) {
GET_MEM( text, strlen(in_buff) ) ;
strcpy( text, in_buff ) ;
return( text ) ;
}
else return( expand_macros( in_buff ) ) ;
}
else {
/* macro definition, get characters until ; */
GET_MEM( def, strlen(text)+10 ) ;
strcpy( def, text ) ;
for ( j=1;; j++ ) {
switch ( def[j] ) {
case ';' :{ def[j+1] = NULL ;
define_macro( def ) ;
free( def ) ;
return( NULL ) ;
}
case NULL :{
def[j] = '\n' ;
def[j+1] = NULL ;
if ( NULL == get_rec() )
abort("MACRO: EOF in macro def") ;
size = strlen(def) + strlen(in_buff) + 10 ;
if ( NULL == (def=realloc(def,size)) )
abort("MACRO: realloc error") ;
strcat( def, in_buff ) ;
}
}
}
}
}
/* Process the macro definition in the argument string.
* A macro has the form:
*
* : name( parm1, parm2, ... ) text with parms ;
*
* In a definition the delimeter must follow the name
* without whitespace. In the source code this requirement is
* relaxed. Alphanumeric macros must be not be next to an alpha or
* number character or they will not be recognized.
*
* This routine puts the macro string into a more easily handled
* structure, replacing parms in the text with n, where n is a
* binary value (128 to 128+MAX_TOKENS).
*
* The macro is placed in a structure of the form:
*
* struct mac {
* char *name ; macro id tag
* char *text ; encoded macro text
* int parmcount ; number of arguments
* int callcount ; recursion check
* } macro[MAX_MACROS] ;
*
* where the text string has binary symbols where the parms were.
* Returns the macro index. The number of macros defined is stored
* in global variable defined_macros.
*
* The macros are entered in order of their name length, so that
* the macro expander will expand those with long names first.
*/
int define_macro(string)
char *string ;
{
char *pntr, *pntr1, *name, *parms[MAX_TOKENS], *parm, *text,
*open_parens, *close_parens ;
int i, j, l, parmcount ;
/* macrop is a pointer to the macro structure that will be used */
if ( defined_macros >= MAX_MACROS ) {
sprintf(errline,"DEFINE_MACRO: too many macros: %s",string);
abort( errline ) ;
}
macrop = ¯o[defined_macros] ;
defined_macros++ ;
/* get the name */
name = strtokp( string, ":; \n\t(" ) ; /* pointer to the name */
GET_MEM( macrop->name, strlen(name) ) ;
strcpy( macrop->name, name ) ;
/* get the parameters */
for ( i=0; i<MAX_TOKENS; i++ ) parms[i] = NULL ;
open_parens = strmatch(string,name) + strlen(name) ;
if ( NULL == line_end( open_parens ) ) {
sprintf( errline, "DEFINE_MACRO: unterminated: %s", string ) ;
abort( errline ) ;
}
/* get the text storage here to avoid memory allocation tangles */
text = open_parens ;
GET_MEM( macrop->text, strlen(text) ) ;
if ( strchr( "([{\'\"", *open_parens ) ) {
if ( NULL == ( close_parens = mat_del( open_parens ) ) ) {
sprintf(errline,"DEFINE_MACRO: missing delimeter: %s",
string ) ;
abort( errline ) ;
}
text = close_parens + 1 ;
i = (int)(close_parens - open_parens) - 1 ;
pntr = open_parens + 1 ;
*close_parens = NULL ;
for ( i=0, pntr1 = pntr; i<MAX_TOKENS; i++, pntr1 = NULL ) {
if ( NULL == ( parm = strtokp( pntr1, ", \t" ) ) )
break ;
GET_MEM( parms[i], strlen(parm) ) ;
strcpy( parms[i], parm ) ;
}
}
/* get the text, plugging in binary codes for parameters */
/* remove leading whitespace */
if ( NULL == (text=line_end( text )) ) {
sprintf( errline, "DEFINE_MACRO: unterminated: %s", string ) ;
abort( errline ) ;
}
/* remove the trailing ';' but NOT whitespace */
for ( i=strlen(text)-1; i>=0; i-- ) {
if ( text[i] == ';' ) { text[i] = NULL ; break ; }
}
strcpy( macrop->text, text ) ;
text = macrop->text ;
for ( i=0; i<MAX_TOKENS & NULL != (parm = parms[i]); i++ ) {
/* replace parm by code, if not next to an alpha or number */
l = strlen(parm) ;
for ( pntr=text;NULL != (pntr1=strmatch(pntr,parm));
pntr=pntr1+1 ) {
if ( !( isalnum(*(pntr1-1)) && isalnum(*pntr1) ) &
!( isalnum(*(pntr1+l-1)) && isalnum(*(pntr1+l)))) {
*pntr1 = i + 128 ;
strcpy( pntr1 + 1, pntr1 + strlen(parm) ) ;
}
}
}
/* count parms and free up temporary storage */
macrop->parmcount = 0 ;
for ( i=0; i<MAX_TOKENS & NULL != parms[i]; i++ ) {
free( parms[i] ) ;
macrop->parmcount++ ;
}
/* rearrange the macro table so it is sorted by name length */
name = macrop->name ;
text = macrop->text ;
parmcount = macrop->parmcount ;
l = strlen( name ) ;
for ( i=0; i<defined_macros-1; i++ ) {
if ( l < strlen( macro[i].name ) ) {
for ( j=defined_macros-1; j>i; j-- ) {
macro[j].name = macro[j-1].name ;
macro[j].text = macro[j-1].text ;
macro[j].parmcount = macro[j-1].parmcount ;
}
macro[i].name = name ;
macro[i].text = text ;
macro[i].parmcount = parmcount ;
break ;
}
}
/* return the index of the new macro */
return(i) ;
}
/* Expand the macros in the argument string. Returns a pointer
* to the expanded string, which is likely to be huge. The memory
* should be freed as soon as possible. The macros are expanded
* starting with the one with the highest index. Recursive macro
* definitions will be flagged, but may cause a termination due to
* allocation failure before doing so. Caution must be exercised
* to avoid accidental recursive definitions involving
* more than one macro:
* : h i+x ;
* : i(y) func(y) ;
* : func h ;
* This will generate the successive strings (from a = func(x)):
* a = h(x)
* a = i+x(x)
* a = func()+x(x)
* a = h()+x(x) .... and so on. Beware.
* The string is deallocated by this routine.
*/
/* macros to check for being next to an alpha */
#define FIRSTCHAR ( (pntr1!=text) && (isalnum(*(pntr1-1))&&isalnum(*pntr1)) )
#define LASTCHAR ( isalnum(*(pntr1+l-1)) && isalnum(*(pntr1+l)) )
#define NEXT_TO_ALPHA ( FIRSTCHAR || LASTCHAR )
char *expand_macros(string)
char *string ;
{
char *pntr, *pntr1, *name, *text ;
int i, hit, l ;
/* Allocate some initial storage */
GET_MEM( text, strlen(string) ) ;
strcpy( text, string ) ;
/* clear the recursion check counters */
for ( i=0; i<defined_macros; i++ ) macro[i].callcount = 0 ;
/* search for macros */
do {
for ( i=defined_macros-1, hit=0; i>=0; i-- ) {
/* See if macro[i] is in the present string. If the "edges"
* of the macro name are alphanumeric, don't accept the string
* if the adjacent character is also alphanumeric. This avoids
* having variables such as "sin" flagged if "s" is defined.
* Potential macros are also rejected if quoted with '.
*/
name = macro[i].name ;
l = strlen(name) ;
for ( pntr=text; NULL != (pntr1=strmatch(pntr,name));
pntr=pntr1+1 ) {
if ( !quoted( pntr1, text ) && !NEXT_TO_ALPHA ) {
hit = 1 ; /* got one */
text = mac_expand( text, pntr1, i ) ;
break ;
}
}
if ( hit != 0 ) break ; /* start over if one was found */
}
} while( hit != 0 ) ;
return( text ) ;
}
/* Expand a single macro in a text string, freeing the old storage
* and returning * a pointer to the new string. Name points to the
* macro in the string and index is the macro index.
*/
char *mac_expand( text, name, index )
char *text, *name ;
int index ;
{
char *pntr, *newtext, *parm, *parms[MAX_TOKENS], *temp,
*open_parens, *close_parens, *rest_of_text ;
int i, j, size ;
unsigned char c ;
macrop = ¯o[index] ;
if ( macrop->callcount++ > MAX_CALLS ) {
sprintf( errline,
"MAC_EXPAND: possible recursion involving: \'%s\' in\n%s",
macrop->name, in_buff ) ;
abort( errline ) ;
}
/* get the parameters if there are any for this macro */
for ( i=0; i<MAX_TOKENS; i++ ) parms[i] = NULL ;
rest_of_text = &name[ strlen( macrop->name ) ] ;
if ( macrop->parmcount != 0 ) {
open_parens = &rest_of_text[ strspn( rest_of_text, " \t" ) ] ;
if ( (NULL != strchr( "([{\'\"", *open_parens )) &
(NULL != *open_parens )) {
if (NULL == (close_parens=mat_del(open_parens)) ) {
sprintf( errline,
"MAC_EXPAND: missing delimeter: %s", in_buff ) ;
abort( errline ) ;
}
i = (int)(close_parens - open_parens) - 1 ;
pntr = open_parens + 1 ;
c = *close_parens ; /* save *close_parens */
*close_parens = NULL ; /* make parm block a string */
i = tokenize( pntr, parms ) ; /* break out the parms */
*close_parens = (char)c ; /* restore text */
rest_of_text = close_parens + 1 ;
}
}
/* find out how much memory we will need, then allocate */
size = strlen(text) ;
if ( NULL != ( pntr = macrop->text ) ) size += strlen(pntr) ;
for ( i=0; NULL != (c=pntr[i]); i++ ) {
if ( c > 127 & parms[c-128] != NULL )
size += strlen(parms[c-128]) ;
}
GET_MEM( newtext, size ) ;
/* copy up to macro verbatim */
*name = NULL ;
strcpy( newtext, text ) ;
/* expand the macro itself if there is text */
if ( NULL != (pntr = macrop->text) ) {
for ( i=0, j=strlen(newtext); NULL != (c=pntr[i]); i++, j++ ) {
if ( c > 127 ) {
if ( parms[c-128] != NULL ) {
strcat( newtext, parms[c-128] ) ;
j += strlen( parms[c-128] ) - 1 ;
}
else j-- ;
}
else { /* keep null terminated */
newtext[j] = c ;
newtext[j+1] = NULL ;
}
}
}
/* finish off trailing text */
strcat( newtext, rest_of_text ) ;
/* free up temporary storage and return pointer to new allocation */
for ( i=0; i<MAX_TOKENS & NULL != parms[i]; i++ ) free( parms[i] ) ;
free( text ) ;
return( newtext ) ;
}
/* isalnum: returns nonzero value if the character argument belongs to the
* sets { a-z, A-Z, 0-9 }.
*/
int isalnum( c )
char c ;
{
if ( c >= 97 & c <= 122 ) return (1) ; /* a-z */
if ( c >= 65 & c <= 90 ) return (2) ; /* A-Z */
if ( c >= 48 & c <= 57 ) return (3) ; /* 0-9 */
return(0) ; /* miss */
}
/* Return TRUE is the pointer is quoted in the string (pntr marks
* a position in the string). The quote character the apostrophe.
* If pntr is not in the the result will be meaningless.
*/
int quoted( pntr, string )
char *pntr, *string ;
{
int i, quote=FALSE ;
for ( i=0; NULL != string[i] && &string[i] < pntr; i++ )
if ( string[i] == '\'' ) quote = !quote ;
return( quote ) ;
}