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
Post a Comment