home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
The World of Computer Software
/
World_Of_Computer_Software-02-387-Vol-3of3.iso
/
m
/
m4v05as.zip
/
MACRO.C
< prev
next >
Wrap
C/C++ Source or Header
|
1992-02-22
|
8KB
|
290 lines
/*
* GNU m4 -- A simple macro processor
* Copyright (C) 1989, 1990 Free Software Foundation, Inc.
*
* This program 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 1, or (at your option)
* any later version.
*
* This program 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, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
/*
* MS-DOS port (c) 1990 by Thorsten Ohl, ohl@gnu.ai.mit.edu
* This port is also distributed under the terms of the
* GNU General Public License as published by the
* Free Software Foundation.
*
* Please note that this file is not identical to the
* original GNU release, you should have received this
* code as patch to the official release.
*
* $Header: e:/gnu/m4/RCS/macro.c 0.5.1.0 90/09/28 18:35:07 tho Exp $
*/
/*
* This file contains the functions, that performs the basic argument
* parsing and macro expansion.
*/
#include "m4.h"
#ifdef MSDOS
static enum boolean expand_argument (struct obstack *obs,\
struct token_data *argp);
static void expand_macro (struct symbol *sym);
static void expand_token (struct obstack *obs, enum token_type t,\
struct token_data *td);
#else /* not MSDOS */
static void expand_token();
static void expand_macro();
#endif /* not MSDOS */
/*
* This function read all input, and expands each token, one at a time.
*/
void
expand_input()
{
token_type t;
token_data td;
while ((t = next_token(&td)) != TOKEN_EOF)
expand_token((struct obstack *)nil, t, &td);
}
/*
* Expand one token, according to its type. Potential macro names
* (TOKEN_WORD) are looked up in the symbol table, to see if they have a
* macro definition. If they have, they are expanded as macroes,
* otherwise the text are just opcied to the output.
*/
static void
expand_token(obs, t, td)
struct obstack *obs;
token_type t;
token_data *td;
{
symbol *sym;
switch (t) { /* TOKSW */
case TOKEN_EOF:
case TOKEN_MACDEF:
break;
case TOKEN_SIMPLE:
case TOKEN_STRING:
shipout_text(obs, TOKEN_DATA_TEXT(td));
break;
case TOKEN_WORD:
sym = lookup_symbol(TOKEN_DATA_TEXT(td), SYMBOL_LOOKUP);
if (sym == nil || SYMBOL_TYPE(sym) == TOKEN_VOID)
shipout_text(obs, TOKEN_DATA_TEXT(td));
else
expand_macro(sym);
break;
default:
internal_error("Bad token type in expand_token()");
break;
}
}
/*
* This function parses one argument to a macro call. It expects the
* first left parenthesis, or the separating comma to have been read by
* the caller. It skips leading whitespace, and reads and expands
* tokens, until it finds a comma or an right parenthesis at the same
* level of parentheses. It returns a flag indicating whether the
* argument read are the last for the active macro call. The argument
* are build on the obstack OBS, indirectly through expand_token().
*/
static boolean
expand_argument(obs, argp)
struct obstack *obs;
token_data *argp;
{
token_type t;
token_data td;
char *text;
int paren_level;
TOKEN_DATA_TYPE(argp) = TOKEN_VOID;
/* skip leading white space */
do {
t = next_token(&td);
} while (t == TOKEN_SIMPLE && isspace(*TOKEN_DATA_TEXT(&td)));
paren_level = 0;
while (1) {
switch (t) { /* TOKSW */
case TOKEN_SIMPLE:
text = TOKEN_DATA_TEXT(&td);
if ((*text == ',' || *text == ')') && paren_level == 0) {
/* The argument MUST be finished, whether we want it or not */
obstack_1grow(obs, '\0');
text = obstack_finish(obs);
if (TOKEN_DATA_TYPE(argp) == TOKEN_VOID) {
TOKEN_DATA_TYPE(argp) = TOKEN_TEXT;
TOKEN_DATA_TEXT(argp) = text;
}
return (boolean)(*TOKEN_DATA_TEXT(&td) == ',');
}
if (*text == '(')
paren_level++;
if (*text == ')')
paren_level--;
expand_token(obs, t, &td);
break;
case TOKEN_EOF:
fatal("EOF in argument list");
break;
case TOKEN_WORD:
case TOKEN_STRING:
expand_token(obs, t, &td);
break;
case TOKEN_MACDEF:
if (obstack_object_size(obs) == 0) {
TOKEN_DATA_TYPE(argp) = TOKEN_FUNC;
TOKEN_DATA_FUNC(argp) = TOKEN_DATA_FUNC(&td);
}
break;
default:
internal_error("Bad token type in expand_argument()");
break;
}
t = next_token(&td);
}
}
/*
* The actual macro expansion are handled by expand_macro(). It parses
* the arguments, using expand_argument(), and builds a table of
* pointers to the arguments. The arguments themselves are stored on a
* local obstack. Expand_macro() are passed a symbol SYM, whose type
* are used to call either a builtin function, or the user macro
* expansion function expand_user_macro() (lives in builtin.c). Macro
* tracing are also handled here.
*
* Expand_macro() are potentially recursive, since it calls
* expand_argument(), which might call expand_token(), which might call
* expand_macro().
*/
static void
expand_macro(sym)
symbol *sym;
{
token_data td;
int ch; /* lookahead for ( */
boolean more_args;
struct obstack arguments;
struct obstack argptr;
token_data **argv;
int argc = 0;
struct obstack *expansion;
char *expanded;
boolean traced;
boolean groks_macro_args = SYMBOL_MACRO_ARGS(sym);
static int expansion_level;
expansion_level++;
#ifdef DEBUG_MACRO
fprintf(stderr, "expand_macro(%s)\n", sym->name);
#endif
obstack_init(&argptr);
obstack_init(&arguments);
argv = (token_data **)obstack_base(&argptr);
obstack_blank(&argptr, sizeof(token_data *));
argv[argc] = (token_data *)obstack_alloc(&arguments, sizeof(token_data));
TOKEN_DATA_TYPE(argv[argc]) = TOKEN_TEXT;
TOKEN_DATA_TEXT(argv[argc]) = SYMBOL_NAME(sym);
argc++;
ch = peek_input();
if (ch == '(') {
next_token(&td);
do {
obstack_blank(&argptr, sizeof(token_data *));
argv = (token_data **)obstack_base(&argptr);
argv[argc] = (token_data *)obstack_alloc(&arguments, sizeof(token_data));
more_args = expand_argument(&arguments, argv[argc]);
if (!groks_macro_args && TOKEN_DATA_TYPE(argv[argc]) == TOKEN_FUNC) {
TOKEN_DATA_TYPE(argv[argc]) = TOKEN_TEXT;
TOKEN_DATA_TEXT(argv[argc]) = "";
}
argc++;
} while (more_args);
}
traced = SYMBOL_TRACED(sym);
expansion = push_string_init();
if (traced) {
int i;
fprintf(stderr, "m4 trace (%d): %s", expansion_level, sym->name);
if (argc > 1)
fprintf(stderr, "( %c", lquote);
for (i = 1; i < argc; i++) {
if (i != 1)
fprintf(stderr, "%c, %c", rquote, lquote);
fprintf(stderr, "%s", TOKEN_DATA_TEXT(argv[i]));
}
if (argc > 1)
fprintf(stderr, "%c )", rquote);
}
switch (SYMBOL_TYPE(sym)) {
case TOKEN_FUNC:
(SYMBOL_FUNC(sym))(expansion, argc, argv);
break;
case TOKEN_TEXT:
expand_user_macro(expansion, sym, argc, argv);
break;
default:
internal_error("Bad symbol type in expand_macro()");
break;
}
expanded = push_string_finish();
if (traced) {
if (expanded)
fprintf(stderr, " -> %c%s%c", lquote, expanded, rquote);
fprintf(stderr, "\n");
}
obstack_free(&arguments, 0);
obstack_free(&argptr, 0);
--expansion_level;
#ifdef DEBUG_MACRO
fprintf(stderr, "expand_macro(%s) --- return\n", sym->name);
#endif
}