The C Programming Language

Chapter 6 - Structures 6

mmresult 2017. 12. 22. 10:48

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.

Solution by Akil Adeshwar

 

 

/*
   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<stdlib.h>
#include<string.h>

#define HASHSIZE 101
#define MAXLEN 200
#define BUFSIZE 1000
#define STATE_OUT 321
#define STATE_IN_NO_NAME 322
#define STATE_IN_WITH_NAME 323


static int state = STATE_OUT; // Initial state


char buf[BUFSIZE];
int bufp = 0;


int getch(FILE *fp){
 return (bufp > 0)? buf[--bufp] : fgetc(fp);
}

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

// Data structure to store the definitions
typedef struct list{
 struct list *next;
 char *name;
 char *defn;
}nlist;

static nlist *hashtab[HASHSIZE]; // Pointer Table


// Hash
unsigned hash(char *s){

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

 return hashval%HASHSIZE;
}


//Lookup

nlist *lookup(char *s){

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

// Install

nlist *install(char *name,char *defn){

 nlist *np;
 unsigned hashval;
 hashval = hash(name);

 if((np = lookup(name))== NULL){

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

 if((np->defn = strdup(defn))==NULL)
  return NULL;
 return np;
}


// Print the Lookup Table
void print_all_def(){
 int i=0;
 for(;i<HASHSIZE;i++){
  if(hashtab[i]!=NULL){
   nlist *p = hashtab[i];
   while(p!=NULL){
    printf("LABEL: %s DEFN: %s\n",p->name,p->defn);
    p = p->next;
   }
  }
 }
}

int getword(char *word,int lim,FILE *fp){
 
 int c;
 char *w = word;

 while(isspace(c = getch(fp)));
 if(c==EOF){
  if(state == STATE_OUT)
   return -1;
  else{
   puts("Error: Incorrect definition\n");
   return -1;
  }
 }
 // Word should be identifier in STATE_IN_WITH_NAME to name the definition
 if(isalpha(c) || c=='_' || (state == STATE_OUT) || state==STATE_IN_WITH_NAME)
  *w++=c;
 if(!isalpha(c) && c!='_' && state!=STATE_OUT && state!=STATE_IN_WITH_NAME){
  *w = '\0';
  return w-word;
 }
 for(; --lim>0; w++){
  *w = getch(fp);
  if(state!=STATE_IN_WITH_NAME){
   // Name of definition must be a valid identifier
   if((!isalnum(*w) && *w!='_')){
    ungetch(*w);
    break;
   }
  }
  else
   if(isspace(*w)) // Definition can be any character
    break;
 }
 *w = '\0';
 return w-word;
}

int main(void){
 puts("\nCheck t6.txt to understand the below output:\n"); 
 char line[MAXLEN];
 FILE *fp = fopen("t6.txt","r");
 char *name,*defn,*p;
 int len;
 if(fp!=NULL){
  while((len = getword(line,MAXLEN,fp)>0)){
   switch(state){

    case STATE_OUT:
     if(strcmp(line,"#define")==0)
      state = STATE_IN_NO_NAME;
     else{
      /*
         Check if line is present in lookup table,
         if yes, substitute the definition, else print as it is.
         */
      nlist *np;
      if((np=lookup(line))==NULL)
       printf("%s ",line);
      else
       printf("%s ",np->defn);
     } 
     break;
    case STATE_IN_NO_NAME:
     // Received name for definition
     name = (char *) malloc(len);
     strcpy(name,line);
     state = STATE_IN_WITH_NAME;
     break;
    case STATE_IN_WITH_NAME:
     // Received defn for name
     defn = (char *) malloc(len);
     strcpy(defn,line);
     // Update the lookup table
     if(install(name,defn)==NULL){
      puts("Insert Error");
      return -1;
     }
     state = STATE_OUT;
     break;
   }
  }
  // Print the Lookup Table
  printf("\nLookup Table Now: \n");
  print_all_def();
  fclose(fp);
 }
 return 0;
}

 

 


Solution by Barrett Drawdy
/*
 * K&R2 exercise 6-6.  By: Barrett Drawdy
 * "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 <stdlib.h>
#include <string.h>
#include <ctype.h>

#define HASHSIZE 101
#define MAXWORD 1000

/* nlist:  From K&R2 page 144.  Points to a search text and
 * a replacement text */
struct nlist
{
 struct nlist *next;
 char *name;
 char *defn;
};

static struct nlist *hashtab[HASHSIZE];

struct nlist *lookup(char *);
struct nlist *install(char *, char *);
int process(void);
int getch(void);
void ungetch(int);
int preproc(void);
int backslash(void);
int comment(void);
int literal(void);
int readword(void);

int main(void)
{
 int c;
 const int done = 0;     /* just a dummy for the infinite loop */
 int status = 1; /* 1 at line begin, 2 for failure, 0 otherwise */
 
 while(!done) {
  while(isspace(c = getch())) {
   putchar(c);
   if(c == '\n')
    status = 1;
  }
  
  if(c == '#' && status == 1)
   status = preproc();
  
  else if(c == '\\')
   status = backslash();
  
  else if(c == '/')
   status = comment();
  
  else if(c == '\"')
   status = literal();
  
  else if(c == EOF)
   return 0;
  
  else if(!isalpha(c) && c != '_') {
   putchar(c);
   status = 0;
  }
  
  else {
   ungetch(c);
   status = readword();
  }
  
  if(status == 2)
   return 1;
 }
 return 0;
} /* end main() */

/*
 * preproc:  Called when a '#' is found at the beginning of a line.
 * If a #define statement is found, it stores add the search and replacement
 * text to the hash table.  Otherwise it just prints what it reads.
 * The line a #define statement is found on is replaced with a blank line.
 */
int preproc(void)
{
 int c;
 char name[MAXWORD+1], defn[MAXWORD+1];
 char *n, *d;
 
 /* Read what comes after the '#' */
 for(n = name; isalpha(c = getch()) && n - name < MAXWORD; ++n)
  *n = c;
 *n = '\0';
 
 /* If it's a #define... */
 if(strcmp(name, "define") == 0) {
  
  /* ignore whitespace */
  while(isspace(c)) {
   if(c == '\n') { /* a #define followed by a '\n' is discarded */
    putchar(c);
    return 1;
   }
   c = getch();
  }
  
  /* Read search text */
  for(n = name; (isalnum(c) || c == '_') && n - name < MAXWORD; ++n) {
   *n = c;
   c = getch();
  }
  *n = '\0';
  
  /* Ignore whitespace */
  while(isspace(c)) {
   if(c == '\n')
    *defn = '\0'; /* If there is no replacement text. */
   c = getch();
  }
  
  /* Read replacement text. */
  for(d = defn; (isalnum(c) || c == '_') && d - defn < MAXWORD; ++d) {
   *d = c;
   c = getch();
  }
  *d = '\0';
  
  /* Add to hash table. */
  if(install(name, defn) == NULL)
   return 2;
 }
 else {                    /* not a #define statement */
  putchar('#');
  printf("%s", name);
 }
 
 /* finish line reading/printing line */
 while(c != '\n') {
  if(c == EOF)
   return EOF;
  putchar(c);
  c = getch();
 }
 putchar(c);
 return 1;
} /* end preproc() */

/*
 * backslash:  Makes sure that if a /, *, or " is preceded by a \ it is
 * just printed, not used in a comment or string literal.
 */
int backslash(void)
{
 int c, slash = 1;
 putchar('\\');
 while((c = getch()) == '\\') {
  slash = !slash;
  putchar(c);
 }
 
 if(slash)
  putchar(c);
 else
  ungetch(c);
 
 return 0;
} /* end backslash() */

/*
 * comment:  Prints comments without changes.
 */
int comment(void)
{
 int c;
 int after_star = 0;
 
 putchar('/');
 
 if((c = getch()) == '*') {   /* comment begin */
  putchar(c);
  c = getch();
  
  while(c != EOF) {
   if(c == '\\') {
    backslash();
    after_star = 0;
   }
   else if(c == '*') {
    after_star = 1;
    putchar(c);
   }
   else if(c == '/' && after_star) {
    putchar(c);
    return 0;
   }
   else {
    after_star = 0;
    putchar(c);
   }
   c = getch();
  }
  
  if(c == EOF)
   return EOF;
  
  putchar(c);
  return 0;
 }
 else {
  ungetch(c);
  return 0;
 }
} /* end comment() */

/*
 * literal:  Prints string literals without changes.
 */
int literal(void)
{
 int c;
 putchar('\"');
 c = getch();
 
 while(c != '\"' && c != EOF) {
  if(c == '\\')
   backslash();
  else
   putchar(c);
  c = getch();
 }
 
 if(c == EOF)
  return EOF;
 
 putchar(c);
 return 0;
} /* end literal() */

/*
 * readwood:  Reads an identifier and looks for it in the hash table.  If
 * it's found, it's replaced with the replacement text.  Otherwise, it is
 * printed as is.
 */
int readword(void)
{
 int c;
 char word[MAXWORD];
 char *w;
 struct nlist *node;
 
 c = getch();
 for(w = word; (isalnum(c) || c == '_') && c != EOF; ++w) {
  *w = c;
  c = getch();
 }
 *w = '\0';
 node = lookup(word);
 if(node == NULL)
  printf("%s", word);
 else
  printf("%s", node->defn);
 
 if(c == EOF)
  return EOF;
 
 ungetch(c);
 return 0;
} /* end readword() */

/***************************************************************************
 *                              From K&R2                                  *
 ***************************************************************************/

unsigned hash(char *);
char *strdup(char *);

/* lookup: From K&R2 page 145. Look for s in hashtab. */
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;
}

/* install: From K&R2 page 145. Put (name, defn) in hashtab. */
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;
}

/* hash: From K&R2 page 144.  Form hash value for string s. */
unsigned hash(char *s)
{
 unsigned hashval;
 for(hashval = 0; *s != '\0'; ++s)
  hashval = *s + 31 * hashval;
 return hashval % HASHSIZE;
}

/* strdup: From K&R2 page 143. Makes a duplicate of s. */
char *strdup(char *s)
{
 char *p;
 p = (char *) malloc(strlen(s) + 1);
 if(p != NULL)
  strcpy(p, s);
 return p;
}

#define BUFSIZE 100

char buf[BUFSIZE];  /* buffer for ungetch() */
int bufp = 0;       /* next free position in buf */

int getch(void) { /* get a (possibly pushed back) character */
 return (bufp > 0) ? buf[--bufp] : getchar();
}

void ungetch(int c) {  /* push character back on input */
 if(bufp >= BUFSIZE)
  printf("ungetch: too many characters\n");
 else
  buf[bufp++] = c;
 return;
}

 

 

'The C Programming Language' 카테고리의 다른 글

Chapter 7 - Input and Output 2  (0) 2017.12.22
Chapter 7 - Input and Output 1  (0) 2017.12.22
Chapter 6 - Structures 5  (0) 2017.12.22
Chapter 6 - Structures 4  (0) 2017.12.22
Chapter 6 - Structures 3  (0) 2017.12.22