演習6-6 K&R プログラミング言語C

演習6-6

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

#define MAXWORD 100
#define BUFSIZE 100
#define HASHSIZE 101

char buf[BUFSIZE];  /* ungetch 用のバッファ */
int bufp = 0;       /* buf 中の次の空き位置 */

struct nlist { /* テーブルの項目 */
    struct nlist *next; /* チェインの中の次の項目 */
    char *name;         /* 定義された名前 */
    char *defn;         /* 置換テキスト */
};

static struct nlist *hashtab[HASHSIZE]; /* ポインタのテーブル */

unsigned hash(char *);
struct nlist *lookup(char *);
struct nlist *install(char *, char *);
char *my_strdup(char *);
char *search_defn(char *);
void undef(char *);
int getword(char *, int);
int is_legal_char(char);

enum { LABEL, NAME, DEFINITION };

int main(void)
{
    char word[MAXWORD];
    int cond = LABEL;
    char *name;
    char *defn;

    while (getword(word, MAXWORD) != EOF) {
        if (is_legal_char(word[0])) {
            if (cond == LABEL && strcmp(word, "#define") == 0) {
                cond = NAME;
            } else if (cond == NAME) {
                name = strdup(word);
                cond = DEFINITION;
            } else if (cond == DEFINITION) {
                defn = strdup(word);
                cond = LABEL;
                install(name, defn);
            }

            if (cond == LABEL) {
                printf("%s is %s\n", name, search_defn(name));
            }
        }
    }

    return 0;
}

/* undef : ハッシュテーブルから名前と定義を削除する */
void undef(char *s)
{
    struct nlist *np1, *np2; /* np1 は削除対象のポインタ */
    unsigned hashval = hash(s);

    for (np1 = np2 = hashtab[hashval]; np1 != NULL; np2 = np1, np1 = np1->next) {
        if (strcmp(s, np1->name) == 0) { /* s がハッシュテーブルに見つかった */
            if (np1 == np2) { /* リストの先頭の場合 */
                hashtab[hashval] = np1->next;
            } else { /* np2 が np1 の前の場合 */
                np2->next = np1->next;
            }
            free(np1->name);
            free(np1->defn);
            free(np1);
            break;
        }
    }

}

/* search_defn : 名前文字列 s からハッシュに登録されている定義文字列を探す */
char *search_defn(char *s)
{
    struct nlist *np;

    if ((np = lookup(s)) != NULL) { /* 見つかった */
        return np->defn;
    } else {
        return "not defined.";
    }
}

/* hash : 文字列 s に対しハッシュの値を求める */
unsigned hash(char *s)
{
    unsigned hashval;

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

/* lookup : hashtab の中で s を探す */
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 : hashtab の中に (name, defn) を置く */
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 = my_strdup(name)) == NULL) {
            return NULL;
        }
        hashval = hash(name);
        np->next = hashtab[hashval];
        hashtab[hashval] = np;
    } else { /* すでにある */
        free((void *) np->defn); /* 以前の defn を解放する */
    }
    if ((np->defn = my_strdup(defn)) == NULL) {
        return NULL;
    }
    return np;
}

/* my_strdup : s の複製を作る */
char *my_strdup(char *s)
{
    char *p;

    p = (char *) malloc(strlen(s)+1); /* + 1 for '\0' */
    if (p != NULL) {
        strcpy(p, s);
    }
    return p;
}

/* #define に利用できる文字かチェックする */
int is_legal_char(char c)
{
    if (isalnum(c)) {
        return 1;
    } else if (c == '_') {
        return 1;
    } else if (c == '#') {
        return 1;
    } else if (c == '"') {
        return 1;
    } else if (c == '\'') {
        return 1;
    } else {
        return 0;
    }
}

/* getword : 入力から次の語または文字を求める */
int getword(char *word, int lim)
{
    int c, getch(void);
    void ungetch(int);
    char *w = word;

    while (isspace(c = getch()))
        ;
    if (c != EOF) {
        *w++ = c;
    }
    if (!is_legal_char(c)) {
        *w = '\0';
        return c;
    }
    for ( ; --lim > 0; w++) {
        if (!is_legal_char(*w = getch())) {
            ungetch(*w);
            break;
        }
    }
    *w = '\0';
    return word[0];
}

int getch(void) /* (押し戻された可能性もある) 1文字をとってくる */
{
    return (bufp > 0) ? buf[--bufp] : getchar();
}

void ungetch(int c) /* 文字を入力に押し戻す */
{
    if (bufp > BUFSIZE)
        fprintf(stderr, "ungetch : too many characters\n");
    else
        buf[bufp++] = c;
}

実行結果

$ cat sample.txt
#define MAX 1000
#define MIN 100
#define MSG "hello"
#define IS_WORD 1
$ ./ex6-6 <sample.txt
MAX is 1000
MIN is 100
MSG is "hello"
IS_WORD is 1
プログラミング言語C 第2版 ANSI規格準拠
B.W. カーニハン D.M. リッチー
共立出版
売り上げランキング: 9726
«
»