K&Rを読もう(20) 演習 1-24 括弧の釣合(4)

さて、最終段階に入ります。更なる抽象化を進めるためには高階関数を使うしかない!!

1章の最後なので気合いを入れて挑みます。

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

typedef struct {
    char *stack;
    int   index;
} Stack;

void err_exit(const char s[])
{
    fprintf(stderr, s);
    exit(EXIT_FAILURE);
}

void next(int *cp, int *np)
{
    *cp = *np;
    *np = getchar();
}

/* ぐるぐる回す */
void iter(int (*f)(int *, int *, void *), int *cp, int *np, void *data)
{
    while (*cp != EOF) {
        if (!f(cp, np, data))
            return;
        next(cp, np);
    }

}

/* クオート */
int quote_f(int *cp, int *np, void *q)
{
    if (*cp == '\\')
        next(cp, np);
    else if (*cp == (int) q) {
        next(cp, np);
        return 0;
    }
    return 1;
}

inline void skip_quote(int q, int *cp, int *np)
{
    iter(quote_f, cp, np , (void *) q);
}

/* コメント */
int comment_f(int *cp, int *np, void *data)
{
    if (*cp == '*' && *np == '/') {
        next(cp, np);
        next(cp, np);
        return 0;
    }
    return 1;
}

inline void skip_comment(int *cp, int *np)
{
    iter(comment_f, cp, np , NULL);
}

/* 閉じてるかチェック */
int check_f(int *cp, int *np, void *stack) {
    int d;
    Stack *s = (Stack *) stack;
    
    /* 不要なものを読み飛ばす。 */
    if (*cp == '/' && *np == '*') {
        next(cp, np);
        next(cp, np);
        skip_comment(cp, np);
    } else if (*cp == '\'') {
        next(cp, np);
        skip_quote('\'', cp, np);
    } else if (*cp == '\"') {
        next(cp, np);
        skip_quote('\"', cp, np);
    }
    
    /* TODO: 再帰にしたい!! */
    if (*cp == '(' || *cp == '{' || *cp == '[')
        s->stack[s->index++] = *cp;
    else if (*cp == ')' || *cp == '}' || *cp == ']') {
        if (s->index <= 0)
            err_exit("閉じすぎですよ。\n");

        d = s->stack[--s->index];
        printf("level %2d : [%c:%c]\n", s->index + 1, d, *cp);
        if ( d == '(' && *cp != ')' ||
             d == '{' && *cp != '}' ||
             d == '[' && *cp != ']')
            err_exit("同じので閉じよう。\n");
    }
    return 1;
}

int check(int *cp, int *np)
{ 
    Stack s;
    const int stack_size = 1024;
    char stack_array[stack_size];

    s.stack = stack_array;
    s.index = 0;

    iter(check_f, cp, np , (void *) &s);

    if (s.index != 0)
       err_exit("たぶん開きすぎです。\n");
    else
        printf("OK\n");
}

int main(void)
{
    int c, n = EOF; /* current, next */

    /* 先読み(ずらしていく) */
    next(&c, &n); /* c = EOF, n = '#'  */
    next(&c, &n); /* c = '#', n = 'i' */

    check(&c, &n);

    exit(EXIT_SUCCESS);
}

先読みアルゴリズムを抽象化したことで、更なる抽象化が期待できます。

長いエントリになりましたが満足な結果が得られるまでになりました。いぇぃ!!

括弧の釣合どころか・・・。

追記

  • 先読み方法を変更し、読み飛ばす動作に変えた。