home *** CD-ROM | disk | FTP | other *** search
- /* macros - skeleton of macro expansion algorithm
- * Each token is one char (letter a-z).
- * Token-buffers are modeled by plain old char strings.
- * Each such buffer has another same-size buffer printed just under it,
- * which shows the "hide-set" associated with the token above it.
- * (The "hide-set" contains all the tokens that are non-replaceable.)
- * #define is #d (no space before or after #).
- * No attempt to model details of whitespace handling.
- * Two tokens, when catenated, produce "second token plus one".
- * "Stringize" is crudely stubbed; two quotes "" replace the string.
- * Does not handle empty actuals gracefully.
- * Does not span newlines in matching actuals.
- * Revisions:
- * 88/10/01: Identify the "replacement" buffer as "R".
- * 88/10/03: Handle the hide-set of actuals properly.
- * 88/10/03: Indicate where to refine for the exact (unspecified)
- * semantics of interactions of hide-sets in catenation
- */
- /*
- * First version written by Thomas Plum, Plum Hall Inc.
- * Subsequently revised by members of X3J11, the
- * ANSI C Standards Committee.
- * Permission is granted to reproduce and use this program,
- * for all purposes, provided that this notice is included.
- */
-
- /* First, 300+ lines of inelegant support routines, to about 363 ... */
-
- #include <stdio.h>
- #include <ctype.h>
- #include <string.h>
- #define TRACE(x) 0 /* or, printf x , if needed for debugging */
- #define OUT(x) printf x
- #define cpy_nam(p, q) (p[0] = *(q), p[1] = '\0', ++(q))
- #define eobuf(p) (*(p) == '\0')
- #define next(p, s) (strncmp(p, s, (length_matched = strlen(s))) == 0)
- #define advance(p) (p += length_matched)
- #define is_obj(p) in_set(obj.nam, *(p))
- #define is_fn(p) in_set(fn.nam, *(p))
- #define obj_def(p) obj.def[obj_num(p)][0]
- #define obj_num(p) (strchr(obj.nam, *(p)) - obj.nam)
- #define fn_def(p) fn.def[fn_num(p)][0]
- #define fn_num(p) (strchr(fn.nam, *(p)) - fn.nam)
- #define fn_parm_count(p) strlen(fn.parms[fn_num(p)])
- #define fn_parm_index(p, q) \
- (strchr(fn.parms[fn_num(p)], *(q)) - fn.parms[fn_num(p)])
- #define is_parm_name(p, q) in_set(fn.parms[fn_num(p)], *(q))
- #define in_set(p, c) (strchr(p, c) != 0)
- #define hide_set(p) *((char*)p + L)
- #define A 9 /* max # of macro args */
- #define D 26 /* max # of macro defs */
- #define L 100 /* max length of an input line or of a macro def */
- int length_matched;
- void diagram(), expand(), expand_fn(), set_hide(), listcpy();
- void add_hide(), lower_case();
- char *h_set();
- char arg_patterns[10][24] =
- {
- "()",
- "(_)",
- "(_,_)",
- "(_,_,_)",
- "(_,_,_,_)",
- "(_,_,_,_,_)",
- "(_,_,_,_,_,_)",
- "(_,_,_,_,_,_,_)",
- "(_,_,_,_,_,_,_,_)",
- "(_,_,_,_,_,_,_,_,_)",
- };
- char next_token(p) char *p;
- {
- length_matched = 0;
- ++p;
- for (;;)
- {
- if (*p == ' ')
- ++p, ++length_matched;
- else if (*p == '\0')
- {
- printf("treating end-of-buffer as end-of-file\n");
- return *p;
- }
- else
- return *p;
- }
- }
-
- void strip_blanks(p) char *p;
- {
- char *q = p;
-
- while (*p == ' ')
- ++p;
- /* now at first non-blank in p */
- while (*p != '\0') /* can't use strcpy because overlap */
- hide_set(q) = hide_set(p), *q++ = *p++;
- while (*--q == ' ')
- ;
- *++q = '\0', hide_set(q) = '\0';
- }
-
- struct obj { int n; char nam[D]; char def[D][2][L]; }
- obj = {0};
- struct fn { int n; char nam[D]; char def[D][2][L]; char parms[D][A]; }
- fn = {0};
-
- void install_obj(nam, def) char *nam; char *def;
- {
- obj.nam[obj.n] = nam[0];
- strcpy(obj.def[obj.n][0], def);
- strip_blanks(obj.def[obj.n][0]);
- set_hide(obj.def[obj.n][0], nam);
- printf("obj: nam=<%s> def=<%s>\n", nam, obj.def[obj.n][0]);
- printf(" <%s>\n", obj.def[obj.n][1]);
- ++obj.n;
- }
-
- void install_fn(nam, def, parms) char *nam; char *def; char *parms;
- {
- fn.nam[fn.n] = nam[0];
- strcpy(fn.def[fn.n][0], def);
- strcpy(fn.parms[fn.n], parms);
- strip_blanks(fn.def[fn.n][0]);
- set_hide(fn.def[fn.n][0], nam);
- printf("fn: nam=<%s> parms=<%s> def=<%s>\n", nam, parms, fn.def[fn.n][0]);
- printf(" %*s <%s>\n",
- strlen(parms), "", fn.def[fn.n][1]);
- ++fn.n;
- }
-
- char *parse_parms(p, parms) char *p; char *parms;
- {
- int i = 0;
-
- while (*p != ')')
- {
- if (*p != ' ' && *p != ',')
- parms[i++] = *p;
- ++p;
- }
- ++p;
- parms[i] = '\0';
- return p;
- }
-
-
-
- void replace(level, suf, buf, p, p2, def) char *level, *suf, *buf, *p, *p2, *def;
- {
- char hold[2][L];
- char old_hide[2];
-
- listcpy(hold[0], p2);
- old_hide[0] = hide_set(p), old_hide[1] = '\0';
- listcpy(p, def);
- add_hide(p, old_hide);
- listcpy(p + strlen(def), hold[0]);
- diagram(level, buf, suf);
- }
- char *match_actuals(p, actual, n) char *p; char actual[A][2][L]; int n;
- {
- int parens = 1;
- int i = 0;
- int j = 0;
-
- TRACE(("match_actuals(<%s>, actual)\n", p));
- p += 1;
- /* past the '(' */
-
- for ( ;; )
- {
- if (*p == '(')
- {
- ++parens;
- actual[i][1][j] = p[L];
- actual[i][0][j++] = *p++;
- }
- else if (*p == ')')
- {
- --parens;
- if (parens == 0)
- break;
- actual[i][1][j] = p[L];
- actual[i][0][j++] = *p++;
- }
- else if (*p == ',' && parens == 1)
- {
- actual[i][1][j] = '\0';
- actual[i][0][j] = '\0';
- j = 0;
- ++i;
- ++p;
- }
- else
- {
- actual[i][1][j] = p[L];
- actual[i][0][j++] = *p++;
- }
- }
- actual[i][1][j] = '\0';
- actual[i][0][j] = '\0';
- /* should strip blanks on each actual, to be sure non-empty */
- if (i == 0 && n == 0)
- ; /* ok, no args */
- else if (i != n-1)
- {
- printf("wrong number of actuals\n");
- exit(2);
- }
- for (i = 0; i < n; ++i)
- TRACE(("actual[%d] = <%s>\n", i, actual[i][0]));
- return p+1;
- }
-
- char *stringize(s) char *s;
- {
- static char string_buf[2][L] = {"\"\"", " "};
-
- printf("string \"%s\" --> \"\"\n", s);
- return string_buf[0];
- }
-
- char *catenate(p, q) char *p, *q;
- {
- static char cat_buf[2][L];
- char old_hide[2];
-
- cat_buf[0][0] = q[0] + 1;
- cat_buf[0][1] = '\0';
- set_hide(cat_buf[0], " ");
- old_hide[0] = hide_set(p), old_hide[1] = '\0';
- add_hide(cat_buf[0], old_hide);
- old_hide[0] = hide_set(q), old_hide[1] = '\0';
- add_hide(cat_buf[0], old_hide); /* NOTE: The other "unspecified" */
- /* choice is to intersect(!) the */
- /* two hide-sets, as in Prosser */
- /* 86-196. */
- printf("catenate %c%c --> %c\n", p[0], q[0], cat_buf[0][0]);
- return cat_buf[0];
- }
-
- void listcpy(p, q) char *p, *q;
- {
- strcpy(p, q);
- strcpy(&hide_set(p), &hide_set(q));
- }
-
- void diagram(level, s, suf) char *level, *s, *suf;
- {
- char prefix[L];
-
- if (level[0] == '\0')
- strcpy(prefix, "0");
- else
- strcpy(prefix, level+1);
- printf("%s%s: %s\n", prefix, suf, s);
- printf("%*s%.*s\n", strlen(prefix)+strlen(suf)+2, ": ",
- strlen(s), &hide_set(s));
- }
-
- int charcmp(s, t) char *s, *t;
- {
- return *s - *t;
- }
-
- void set_hide(def, nam) char *def, *nam;
- {
- memset(&hide_set(def), nam[0], strlen(def));
- def[L+strlen(def)] = '\0';
- }
- char hide_sets[10][10] = {0};
- int n_hide_sets = 0;
- void add_hide(p, h) char *p, *h;
- {
- int i, lim;
-
- lim = strlen(p);
- for (i = 0; i < lim; ++i)
- {
- char c = p[L+i];
- char old_h[2];
-
- old_h[0] = c, old_h[1] = '\0';
- TRACE(("c=<%c>, h=<%c>\n", c, h[0]));
- if (h[0] == ' ')
- ;
- else if (c == ' ')
- p[L+i] = h[0];
- else if (h[0] == c)
- ;
- else if (in_set(h_set(old_h), h[0]))
- ;
- else
- {
- char new_hide[10];
- int n = n_hide_sets;
- int j;
- int found = 0;
-
- strcpy(new_hide, h_set(old_h));
- strcat(new_hide, h_set(h));
- qsort(new_hide, strlen(new_hide), 1, charcmp);
- TRACE(("new_hide=<%s>\n", new_hide));
- for (j = 0; j < n_hide_sets; ++j)
- {
- if (strcmp(new_hide, hide_sets[j]) == 0)
- {
- p[L+i] = j + '0';
- found = 1;
- }
- }
- if (!found)
- {
- if (n > 9)
- {
- printf("too many hide-sets\n");
- exit(2);
- }
- strcpy(hide_sets[n], new_hide);
- p[L+i] = n + '0';
- qsort(hide_sets[n], strlen(hide_sets[n]), 1, charcmp);
- printf("hide-set #%d = {%s}\n", n, hide_sets[n]);
- ++n_hide_sets;
- }
- }
- }
- }
-
- char *h_set(s) char *s;
- {
- char c = s[0];
-
- if (isdigit(c))
- return hide_sets[c - '0'];
- else
- return s;
- }
-
- int is_hidden(p) char *p;
- {
- if (!islower(*p))
- return 0;
- else if (*p == p[L])
- return 1;
- else if (isdigit(p[L]) && strchr(hide_sets[p[L] - '0'], *p) != 0)
- return 1;
- else
- return 0;
- }
-
- void mark_non_replace(p) char *p;
- {
- *p = toupper(*p);
- }
-
- void lower_case(p) char *p;
- {
- for ( ; *p != '\0'; ++p)
- *p = tolower(*p);
- }
-
-
- /* Now: Here's the actual macro algorithm ... */
-
- void preproc(p) char *p;
- {
- char nam[2];
- char parms[A];
-
- if (next(p, "#d "))
- { /* starting a #define */
- advance(p);
- TRACE(("p=<%s>\n", p));
- cpy_nam(nam, p);
- if (next(p, "(")) /* a fn-like macro */
- {
- advance(p);
- p = parse_parms(p, parms);
- install_fn(nam, p, parms);
- }
- else /* an object-like macro */
- install_obj(nam, p);
- } /* end of #define */
- else if (next(p, "#u "))
- { /* starting a #undef */
- } /* stub */
- else
- {
- expand(p, "");
- lower_case(p);
- set_hide(p, " ");
- diagram("", p, "");
- }
- }
- void expand(buf, level) char *buf; char *level;
- {
- char *p = buf;
-
- TRACE(("expand(<%s>, %s)\n", buf, level));
- diagram(level, buf, "");
- while (!eobuf(p))
- {
- if (is_hidden(p))
- {
- mark_non_replace(p);
- ++p;
- diagram(level, buf, "");
- }
- else if (is_obj(p)) /* instance of object-like macro */
- replace(level, "", buf, p, p+1, obj_def(p));
- else if (is_fn(p) && next_token(p) == '(')
- expand_fn(buf, p, level); /* instance of fn-like macro */
- else
- ++p; /* ordinary token */
- } /* end while !eobuf */
- } /* end expand() */
-
-
-
-
-
-
- void expand_fn(buf, p, level) char *buf, *p, *level;
- {
- char actual[A][2][L], expandeds[A][2][L];
- char repl[2][L];
- char nlevel[20];
- char fn_nam[2];
- char invocation[2][L];
- char *start_invok, *q;
- int i_parm, num_parms;
-
- start_invok = p;
- cpy_nam(fn_nam, p);
- advance(p); /* past any blanks skipped in next_token */
- num_parms = fn_parm_count(fn_nam);
- p = match_actuals(p, actual, num_parms);
- for (i_parm = 0; i_parm < num_parms; ++i_parm)
- {
- listcpy(expandeds[i_parm][0], actual[i_parm][0]);
- sprintf(nlevel, "%s.%d", level, i_parm+1);
- expand(expandeds[i_parm][0], nlevel);
- }
- sprintf(invocation[0], "%s%s", fn_nam, arg_patterns[num_parms]);
- set_hide(invocation[0], " ");
- diagram(level, invocation[0], "R");
- listcpy(repl[0], fn_def(fn_nam));
- diagram(level, repl[0], "R");
- TRACE(("subst parms in repl:<%s>\n", repl));
- for (q = repl[0]; !eobuf(q); )
- {
- TRACE(("repl-token <%c>\n", *q));
- if (q[1] == '#' && q[2] == '#' && !eobuf(q+3))
- {
- replace(level, "R", repl[0], q, q+4,
- catenate(q, q+3));
- q += 1; /* advance past new "token" */
- }
- else if (q[0] == '#' && is_parm_name(fn_nam, q+1))
- {
- i_parm = fn_parm_index(fn_nam, q+1);
- replace(level, "R", repl[0], q, q+2,
- stringize(actual[i_parm][0]));
- q += 2; /* advance past "" */
- }
- else if (is_parm_name(fn_nam, q))
- {
- i_parm = fn_parm_index(fn_nam, q);
- replace(level, "R", repl[0], q, q+1,
- expandeds[i_parm][0]);
- q += strlen(expandeds[i_parm][0]); /* advance past expansion */
- }
- else /* ordinary token */
- ++q;
- }
- replace(level, "", buf, start_invok, p, repl[0]);
- }
-
-
-
-
- main()
- {
- char line[BUFSIZ];
-
- while (gets(line))
- {
- set_hide(line, " ");
- preproc(line);
- }
- } /* end main */