Exercise 6-2 (Groups of names)

Chapter_6     Exercise_6-1     tree Exercise_6-3







Exercise 6-2     K&R, p. 143


Exercise 6-2. Write a program that reads a C program and prints in alphabetical order each group of variable names that are identical in the first 6 characters, but different somewhere thereafter. Don't count words within strings and comments. Make 6 a parameter that can be set from the command line.




CONTENTS:     nocomment.c     nokeywords.c     groups.c




Note:  We use nocomment from Exercise_6-1 (this time we remove preprocessor control lines), then we remove C keywords with the program nokeywords below, and then we extract the words from the output file.




nocomment.c         download


#include <stdio.h> // for getchar(), putchar(), printf(), EOF

// test multiline preprocessor command:
#define FALSE \
0
#define TRUE 1

// testing a \
multiline \
C++ style \
comment

int main()
{
int c1, c2;
int insideComment = FALSE; // for C style comments

while ((c1 = getchar()) != EOF)
{
if (c1 == '\\') // escape character or sequence
{ /* skip \', \", \\ */ // \ continues a C++ style comment on the next line
c2 = getchar();
if (c2 == EOF)
{ // \ newline is part of a multiline preprocessor command or C++ comment
printf("\nEscape character not ended properly\n"); // Error message
return 1; // return from main(), end program with an error message
}
if (c2 == '\n') // not an escape, but a multiline command or comment
{
putchar(c1); // '\\'
putchar(c2); // '\n'
}
// else remove escape (print nothing)
}
else if (c1 == '\'') // character literal
{ // skip '\\', '"', '\"', '*', '#'
// remove character literal (print nothing)
while ((c1 = getchar()) != '\'')
{
if (c1 == EOF || c1 == '\n')
{
printf("\nCharacter literal not ended properly\n"); // Error message
return 1; // end program with an error message
}
// else
if (c1 == '\\') // escape char or sequence
{
// print nothing
c1 = getchar();
if (c1 == EOF)
{ // \ newline is part of a multiline preprocessor command or C++ comment
printf("\nEscape character not ended properly\n"); // Error message
return 1; // end program with an error message
}
// else skip char
}
// else skip char
} // here c1 == '\''
}
else if (c1 == '\"') // strings are on a single line
{ // Skip strings: "/*", "*/", "//", "'", "\"", "\'", "\\", "#"
// skip char (print nothing)
while ((c1 = getchar()) != '\"')
{
if (c1 == EOF || c1 == '\n')
{
printf("\nQuoted string not ended properly\n"); // Error message
return 1; // end program with an error message
}
else if (c1 == '\\') // escape char or sequence
{
// skip char (print nothing)
c1 = getchar();
if (c1 == EOF || c1 == '\n')
{
printf("\nEscape character not ended properly\n"); // Error message
return 1; // end program with an error message
}
// else skip char
}
// else skip char
} // here c1 == '\"'
} // continue with getchar() in outer while() to skip last '\"'
else if (c1 == '/')
{
c2 = /* testing */ getchar();
if (c2 == '/') // beginning of a C++ style comment
{
while ((c2 = getchar()) != EOF)
{ // \ continues a C++ style comment on the next line
if (c1 != '\\' && c2 == '\n')
{break;} // out of inner while()
c1 = c2; // c1 holds the penultimate char read
} // here c2 == '\n' or EOF, end of C++ style comment
if (c2 == EOF)
{return 0;} // end program normally, exit main()
putchar(c2); // putchar('\n'); // C++ style comment ends with '\n'
}
else if (c2 == '*') // beginning of a C style comment
{
insideComment = TRUE;
while (insideComment) // insideComment == 1 (insideComment != 0)
{
while ((c1 = getchar()) != '*')
{
if (c1 == EOF)
{
printf("\nC style comment not closed properly\n"); // Error message
return 1; // end program with an error message
}
// else skip chars till '*'
}
c2 = getchar();
if (c2 == EOF)
{
printf("\nC style comment not closed properly\n"); // Error message
return 1; // end program with an error message
}
else if (c2 == '/') // end of C style comment
{
insideComment = FALSE; // reset
break; // out of while(insideComment)
}
// else continue; // C style comment continues
} // end of while (insideComment)
}
else // no comment, false alarm
{
putchar(c1);
if (c2 != EOF) {putchar(c2);}
else {return 0;} // end program normally, exit main()
}
}
else if (c1 == '#') // skip preprocessor control lines
{
while ((c2 = getchar()) != EOF)
{ // \ continues a preprocessor command on the next line
if (c1 != '\\' && c2 == '\n')
{break;} // out of inner while()
c1 = c2; // c1 holds the penultimate char read
} // here c2 == '\n' or EOF, end of preprocessor command
if (c2 == EOF)
{return 0;} // end program normally
// else skip line (don't print newline)
}
else {putchar(c1);} // no comment
} // end of outer while()

return 0;
}
/*
gcc nocomment.c -o nocomment
./nocomment < groups.c > copy1.c

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

rm copy1.c copy2.c // clean
*/











nokeywords.c         download


#include <stdio.h> // for getchar(), putchar(), printf(), EOF
#include <string.h> // for strcmp()
#include <ctype.h> // for isspace(), isalpha(), isalnum()

#define MAXWORD 100
#define NKEYS (sizeof (keytab) / sizeof(keytab[0]))

char * keytab[] =
{"auto", "break", "case", "char", "const", "continue",
"default", "define", "do", "double", "else", "enum",
"extern", "float", "for", "goto", "if", "include",
"int", "long", "register", "return", "short", "signed",
"sizeof", "static", "struct", "switch", "typedef",
"union", "unsigned", "void", "volatile", "while"};

// get next word or character from input, print whitespace
int getword(char *, int);

int binsearch(char *, char *[], int); // find C keywords

int main()
{
int c, n;
char word[MAXWORD];

while((c = getword(word, MAXWORD)) != EOF)
{
if (isalpha(c) || c == '_')
{
if ((n = binsearch(word, keytab, NKEYS)) < 0)
{printf("%s", word);}
}
else {printf("%s", word);}
}

return 0;
}


// find word in tab[0] ... tab[n-1]
int binsearch(char *word, char * tab[], int n)
{
int cond;
int low, high, mid;

low = 0;
high = n-1;

while (low <= high)
{
mid = (low + high) / 2;

if ((cond = strcmp(word, tab[mid])) < 0)
{high = mid - 1;}
else if (cond > 0)
{low = mid + 1;}
else {return mid;} // found it
}

return -1; // not found
}

int getch(void);
void ungetch(int);

// get next word or character from input, print whitespace
int getword(char *word, int lim)
{
int c;
char *w = word;

while (isspace(c = getch()))
{putchar(c);} // print beginning whitespace

if (c != EOF)
{*w++ = c;}

if (!isalpha(c) && c != '_')
{
*w = '\0';
return c;
}

for ( ; --lim > 0; w++)
{
if (!isalnum(*w = getch()) && *w != '_')
{
ungetch(*w);
break; // out of for(), w++ is not executed
}
}

*w = '\0';
return word[0];
}

// buffer for ungetch():
int buf = EOF-1; // not a real character, not even EOF

int getch(void) // get a (possibly pushed-back) character
{
if (buf < EOF)
{
return getchar();
}

int temp = buf; // buf >= EOF
buf = EOF-1; // reset buf

return temp;
}
// push character back on input (make it available for the next getch()):
void ungetch(int c)
{
buf = c;
}

/*
gcc nokeywords.c -o nokeywords

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

./nokeywords < copy1.c > copy2.c

rm copy1.c copy2.c // clean
*/











groups.c         download


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

#define MAXWORD 100 // max word length
#define MAXLENGTH 10 // for words comparison

enum bool {FALSE, TRUE};

struct tnode // tree node
{
char *word; // points to the text
enum bool lastnode; // used for printing
struct tnode *left; // left child (smaller)
struct tnode *right; // right child (bigger)
};

struct tree
{
struct tnode *root;
struct tree *left; // left child (smaller)
struct tree *right; // right child (bigger)
};

struct tnode * // add node to the tree
addtnode (struct tnode *, char *);

struct tree *
addtree (struct tree *, char *, int);

int count; // used for printing
void treeprint(struct tnode *); // start with the root node
void printalltrees(struct tree *); // start with the root tree

int getword(char *, int);

// print alphabetically groups of words similar in the first n chars
int main(int argc, char *argv[])
{
if (argc > 2)
{
printf("Usage: ./groups n\n");
printf("0 <= n <= %d\n", MAXLENGTH);
return 1; // end program, signalling error
}

int n = 0; // default length for words comp. (compare first n chars)

if (argc == 2)
{
n = atoi(argv[1]);

if (n < 0 || n > 10)
{
printf("Usage: ./groups n\n");
printf("0 <= n <= %d\n", MAXLENGTH);
return 1; // end program, signalling error
}
}

struct tree *rtree; // root tree
char word[MAXWORD];

rtree = NULL; // initialize

int c;
while ((c = getword(word, MAXWORD)) != EOF)
{
if (isalpha(c) || c == '_')
{rtree = addtree(rtree, word, n);}
}

printalltrees(rtree);

return 0;
}

struct tree *
talloc(void); // allocate tree

struct tnode *
nalloc(void); // allocate node

// duplicate string:
char * strDup(char *); // strdup() is declared by string.h

struct tree *
addtree(struct tree *p, char *w, int n)
{
int cond;

if (p == NULL) // a new word has arrived
{
p = talloc(); // make a new tree
p->root = nalloc(); // make a new node
p->root->word = strDup(w);
p->root->lastnode = FALSE;
p->root->left = p->root->right = NULL;
p->left = p->right = NULL;
}
else if ((cond = strncmp(w, p->root->word, n)) == 0)
{ // same type of word
addtnode(p->root, w); // add to the current tree
}
else if (cond < 0) // less than into left subforest
{p->left = addtree(p->left, w, n);}
else // greater than into right subforest
{p->right = addtree(p->right, w, n);}

return p;
}

struct tnode *
addtnode(struct tnode *p, char *w)
{
int cond;

if (p == NULL) // a new word has arrived
{
p = nalloc(); // make a new node
p->word = strDup(w);
p->lastnode = FALSE;
p->left = p->right = NULL;
}
else if ((cond = strcmp(w, p->word)) < 0) // less than into left subtree
{p->left = addtnode(p->left, w);}
else if (cond > 0) // greater than into right subtree
{p->right = addtnode(p->right, w);}
// else // repeated word, do nothing
return p;
}

struct tree *
talloc(void) // allocate space for a tree node
{ // make a tnode
return (struct tree *) malloc (sizeof(struct tree));
}

struct tnode *
nalloc(void) // allocate space for a tree node
{ // make a tnode
return (struct tnode *) malloc (sizeof(struct tnode));
}

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;
}

// in-order print of tree with root p (left, node, right)
void treeprint (struct tnode *p)
{
if (p != NULL)
{
treeprint(p->left);
if (p->lastnode == TRUE)
{printf("%s", p->word);}
else
{
printf("%s,%s", p->word, (count % 5 == 4)? "\n" : " ");
count++;
treeprint(p->right);
}
}
}

// in-order print of trees with root tree p (left, node, right)
void printalltrees(struct tree *p) // start with the root tree
{
struct tnode *t;
if (p != NULL)
{
printalltrees(p->left);
putchar('(');
count = 0; // reset
t = p->root;
while (t->right != NULL)
{t = t->right;}
t->lastnode = TRUE;
treeprint(p->root);
putchar(')'), putchar('\n');
printalltrees(p->right);
}
}

int getch(void);
void ungetch(int);

// get next word or character from input
int getword(char *word, int lim)
{
int c;
char *w = word;

while (isspace(c = getch()))
{} // skip beginning whitespace

if (c != EOF)
{*w++ = c;}

if (!isalpha(c) && c != '_')
{
*w = '\0';
return c;
}

for ( ; --lim > 0; w++)
{
if (!isalnum(*w = getch()) && *w != '_')
{
ungetch(*w);
break; // out of for(), w++ is not executed
}
}

*w = '\0';
return word[0];
}

// buffer for ungetch():
int buf = EOF-1; // not a real character, not even EOF

int getch(void) // get a (possibly pushed-back) character
{
if (buf < EOF)
{
return getchar();
}

int temp = buf; // buf >= EOF
buf = EOF-1; // reset buf

return temp;
}
// push character back on input (make it available for the next getch()):
void ungetch(int c)
{
buf = c;
}
/*
gcc groups.c -o groups

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

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

./groups < copy2.c
./groups 0 < copy2.c
(EOF, FALSE, MAXLENGTH, MAXWORD, NULL,
TRUE, addtnode, addtree, argc, argv,
atoi, bool, buf, c, cond,
count, getch, getchar, getword, isalnum,
isalpha, isspace, lastnode, left, lim,
main, malloc, n, nalloc, p,
printalltrees, printf, putchar, right, root,
rtree, s, strDup, strcmp, strcpy,
strlen, strncmp, t, talloc, temp,
tnode, tree, treeprint, ungetch, w,
word)

./groups -1
Usage: ./groups n
0 <= n <= 10

./groups 11
Usage: ./groups n
0 <= n <= 10

./groups 1 < copy2.c
(EOF)
(FALSE)
(MAXLENGTH, MAXWORD)
(NULL)
(TRUE)
(addtnode, addtree, argc, argv, atoi)
(bool, buf)
(c, cond, count)
(getch, getchar, getword)
(isalnum, isalpha, isspace)
(lastnode, left, lim)
(main, malloc)
(n, nalloc)
(p, printalltrees, printf, putchar)
(right, root, rtree)
(s, strDup, strcmp, strcpy, strlen,
strncmp)
(t, talloc, temp, tnode, tree,
treeprint)
(ungetch)
(w, word)

./groups 3 < copy2.c

./groups 6 < copy2.c

rm copy1.c copy2.c // clean
*/









Chapter_6     Exercise_6-1     tree BACK_TO_TOP Exercise_6-3



Comments

Popular posts from this blog

Contents

Blogger Page Margins in Contempo