Page 145

Implement a simple version of the #define processor (i.e., no arguments) suitable for use with C programs, based on the routines of this section. You may also find getch and ungetch helpful.

#include <stdio.h>
#include <ctype.h>

#define IDENTIFIER "#define"
#define MAXBUF 1000

struct nlist {
	struct nlist *next;
	char *name;
	char *defn;
};

struct nlist *install(char *name, char *defn);
struct nlist *lookup(char *name);

int main()
{
	enum state { NONE, DECLARATION, NAME, VALUE };

	int c, i = 0, j = 0, getch(void);
	void ungetch(int);
	char buf[MAXBUF], name[MAXBUF], val[MAXBUF];
	enum state state = NONE;
	struct nlist *res;

	while ((c = buf[i++] = getch()) != EOF) {
		switch (state) {
		case NONE:
			if (c == IDENTIFIER[0]) {
				state = DECLARATION;
				j = 1;
			} else if (!isalnum(c)) {
				buf[--i] = '\0';

				res = lookup(buf);
				printf("%s%c", res == NULL ? buf : res->defn,
				       c);
				i = 0;
			}
			break;
		case DECLARATION:
			if (IDENTIFIER[j] == '\0') {
				ungetch(c);
				i--;
				state = NAME;
				j = 0;
			} else if (c != IDENTIFIER[j]) {
				state = NONE;
				j = 0;
			} else {
				j++;
			}
			break;
		case NAME:
			if (!isspace(c)) {
				name[j++] = c;
			} else if (j > 0) {
				ungetch(c);
				i--;
				name[j] = '\0';
				j = 0;
				state = VALUE;
			}
			break;
		case VALUE:
			if (!isspace(c)) {
				val[j++] = c;
			} else if (j > 0) {
				val[j] = '\0';
				install(name, val);
				j = 0;
				ungetch(c);
				i--;
				state = NONE;
			}
			break;
		}
	}
}

#define HASHSIZE 101
#include <string.h>
#include <stdlib.h>
unsigned hash(char *s)
{
	unsigned hashval;

	for (hashval = 0; *s != '\0'; s++) {
		hashval = *s + 31 * hashval;
	}
	return hashval % HASHSIZE;
}

static struct nlist *hashtab[HASHSIZE];
struct nlist *lookup(char *s)
{
	struct nlist *np;

	for (np = hashtab[hash(s)]; np != NULL; np = np->next) {
		if (strcmp(s, np->name) == 0) {
			return np;
		}
	}
	return NULL;
}

struct nlist *install(char *name, char *defn)
{
	struct nlist *np;
	unsigned hashval;

	if ((np = lookup(name)) == NULL) {
		np = (struct nlist *)malloc(sizeof(*np));
		if (np == NULL || (np->name = strdup(name)) == NULL) {
			return NULL;
		}
		hashval = hash(name);
		np->next = hashtab[hashval];
		hashtab[hashval] = np;
	} else {
		free((void *)np->defn);
	}
	if ((np->defn = strdup(defn)) == NULL) {
		return NULL;
	}
	return np;
}

#define BUFSIZE 100
char buf[BUFSIZE];
int bufp = 0;

int getch(void)
{
	return (bufp > 0) ? buf[--bufp] : getchar();
}

void ungetch(int c)
{
	if (bufp >= BUFSIZE) {
		printf("ungetch: too many characters\n");
	} else {
		buf[bufp++] = c;
	}
}