Exercise 6-6 (#define preprocessor)

Chapter_6     Exercise_6-5 ch6-Exercises     Chapter_7







Exercise 6-6     K&R, p. 145


Exercise 6-6. 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.




Notes:

We use a test file for macro expansion (test.c). We compile and run it, saving the output to a text file (out1.txt).
As an alternative, we use our own program for macro expansion.
First, we use nocomment from Exercise_1-23, Chapter_1, to remove comments from the test file, creating the file copy1.c.
Then we use the program define presented below (define.c) to read the macros and then to expand them in the text, in main(), creating the file copy2.c (preprocessing).
We compile and run the result C file (copy2.c), saving the output to a text file (out2.txt).
At the end, we compare the two output text files (out1.txt and out2.txt) for differences.




CONTENTS:     test.c     out1.txt     define.c     copy2.c




test.c         download


#include <stdio.h> // for printf()

#define HASHSIZE 101 // hash size
#define ARRAYSIZE 100 // array size
#define MAXLINE 1000 // max line length
#define MAXWORD 100 // max word length

#define FALSE 0
#define TRUE 1

#define FUNCTION "()"
#define BRACKETS "[]"
#define BRACES "{}"
#define COMMA ','
#define ARGSEP COMMA

#define GREETING "hello"

int main()
{
char word[MAXWORD] = GREETING;

printf("HASHSIZE: %d\n", HASHSIZE);
printf("ARRAYSIZE: %d\n", ARRAYSIZE);
printf("MAXLINE: %d\n", MAXLINE);
printf("GREETING: %s\n", word);
printf("ARGSEP: %c\n", ARGSEP);
printf("FUNCTION: %s\n", FUNCTION);
printf("BRACKETS: %s\n", BRACKETS);
printf("BRACES: %s\n", BRACES);

if (TRUE == FALSE) {printf("True Lies\n");}

return 0;
}
/*
gcc test.c -o test
./test > out1.txt

gcc nocomment.c -o nocomment
./nocomment < test.c > copy1.c

gcc define.c -o define
./define < copy1.c > copy2.c

gcc copy2.c -o copy2
./copy2 > out2.txt

diff -s out1.txt out2.txt
// Files out1.txt and out2.txt are identical
meld out1.txt out2.txt
// Files are identical

rm copy* out* // clean
*/











out1.txt         download


HASHSIZE: 101
ARRAYSIZE: 100
MAXLINE: 1000
GREETING: hello
ARGSEP: ,
FUNCTION: ()
BRACKETS: []
BRACES: {}











define.c         download


#include <stdio.h> // for getchar(), putchar(), printf(), EOF, NULL
#include <string.h> // for strcmp(), strlen(), strcpy()
#include <stdlib.h> // for malloc(), free()
#include <ctype.h> // for isspace(), isalpha(), isalnum()

#define MAXWORD 100 // max word length

struct nlist // hash table entry
{
struct nlist *next; // next entry in the chain

char *name; // defined name
char *defn; // replacement text
};

#define HASHSIZE 101 // array size

static struct nlist *
hashtab[HASHSIZE]; // pointer hash table
// all values of hashtab[] are initialized to NULL

unsigned hash(char *); // return hash value for string

struct nlist *
lookup(char *); // look for string in hashtab[]

struct nlist * // put (name, defn) in hashtab[]
install(char *name, char *defn);

int main()
{
int i, c1, c2;
char word[MAXWORD], name[MAXWORD], defn[MAXWORD];
struct nlist *np;

while ((c1 = getchar()) != EOF)
{
if (c1 == '#') // include or define
{
c2 = getchar(); // ungetch()
if (c2 == 'i') // include
{
putchar(c1);
putchar(c2);
while((c1 = getchar()) != '\n' && c1 != EOF)
{putchar(c1);}
putchar(c1); // putchar('\n');
}
else // define
{
i = 0;
word[i++] = c1;
word[i++] = c2;
while((c1 = getchar()) != ' ' && c1 != '\t')
{word[i++] = c1;}
word[i] = '\0'; // end string

if(strcmp(word, "#define") == 0)
{ // don't print line
while((c1 = getchar()) == ' ' || c1 == '\t')
{} // skip whitespace
i = 0;
name[i++] = c1;
while(!isspace(c1 = getchar())) // ' '. '\t', '\n'
{name[i++] = c1;}
name[i] = '\0'; // end string

while((c1 = getchar()) == ' ' || c1 == '\t')
{} // skip whitespace
i = 0;
defn[i++] = c1;
while(!isspace(c1 = getchar())) // ' '. '\t', '\n'
{defn[i++] = c1;}
defn[i] = '\0'; // end string

np = lookup(defn);
if (np != NULL) // we should do this repeatedly
{ // until np->defn is no longer in hashtab[]
install(name, np->defn);
}
else {install(name, defn);}

if (c1 != '\n' && c1 != EOF)
{
while((c1 = getchar()) != '\n' && c1 != EOF)
{} // don't print the rest of line
}
}
else // print line
{
printf("%s", word);
putchar(c1);
if (c1 != '\n' && c1 != EOF)
{
while((c1 = getchar()) != '\n' && c1 != EOF)
{putchar(c1);}
putchar(c1); // putchar('\n');
}
}
}
}
else if (c1 == '\"') // skip string (don't process it)
{ // print string
putchar(c1); // putchar('\"');
while ((c1 = getchar()) != '\"')
{putchar(c1);} // here c1 == '\"'
putchar(c1); // putchar('\"');
}
else if (isalpha(c1) || c1 == '_')
{ // possible macro expansion
i = 0;
word[i++] = c1;
while (isalnum(c1 = getchar()))
{word[i++] = c1;}
word[i] = '\0'; // end string

np = lookup(word);
if(np!= NULL)
{printf("%s", np->defn);}
else {printf("%s", word);}

putchar(c1);
}
else {putchar(c1);}
}

return 0;
}

unsigned hash(char *s) // return hash value for string s
{
unsigned hashval;

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

return hashval % HASHSIZE;
}

struct nlist *
lookup(char *s) // look for string s in hashtab[]
{
struct nlist *np;

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

return NULL; // not found
}
// strdup() is declared by string.h
char * strDup(char *); // duplicate string

struct nlist * // put (name, defn) in hashtab[]
install(char *name, char *defn)
{
struct nlist *np;
unsigned hashval;

if ((np = lookup(name)) == NULL) // not found
{
np = (struct nlist *) malloc(sizeof(*np)); // sizeof(struct nlist)

if (np == NULL || (np->name = strDup(name)) == NULL)
{return NULL;}

hashval = hash(name);
np->next = hashtab[hashval]; // old value (previous pointer or NULL)
hashtab[hashval] = np; // update (insert new value into linked list)
} // np is now the beginning of the linked list
else // already there
{free((void *)np->defn);} // free previous defn

if((np->defn = strDup(defn)) == NULL) // update (replace) defn
{return NULL;}

return np;
}

char * strDup(char *s) // make a duplicate of s
{
char *p;
// we assume a char is stored on a byte
p = (char *) malloc (strlen(s) + 1); // +1 for ending '\0'

if (p != NULL)
{strcpy(p, s);}

return p;
}

/*
gcc define.c -o define

gcc test.c -o test
./test > out1.txt

gcc nocomment.c -o nocomment
./nocomment < test.c > copy1.c

./define < copy1.c > copy2.c

gcc copy2.c -o copy2
./copy2 > out2.txt

diff -s out1.txt out2.txt
// Files out1.txt and out2.txt are identical
meld out1.txt out2.txt
// Files are identical

rm copy* out* // clean
*/





Note:  We did not do much error checking in define.c to keep the program shorter and easier to understand.











copy2.c         download


#include <stdio.h>

int main()
{
char word[100] = "hello";

printf("HASHSIZE: %d\n", 101);
printf("ARRAYSIZE: %d\n", 100);
printf("MAXLINE: %d\n", 1000);
printf("GREETING: %s\n", word);
printf("ARGSEP: %c\n", ',');
printf("FUNCTION: %s\n", "()");
printf("BRACKETS: %s\n", "[]");
printf("BRACES: %s\n", "{}");

if (1 == 0) {printf("True Lies\n");}

return 0;
}





Notes:

We removed the extra lines between include and main() to keep the file shorter.
Note the double macro expansion for ARGSEP, replaced by COMMA, replaced by ','.
The file copy2.c compiles as it is and produces the output out2.txt, identical with out1.txt.









Chapter_6     Exercise_6-5 BACK_TO_TOP ch6-Exercises     Chapter_7



Comments

Popular posts from this blog

Contents

Blogger Page Margins in Contempo