数式の評価を原始的に #11

字句とは、1個以上の数字列(非負整数)か、数字と' 'と'\t'以外の1個の文字か、入力の終端か、である。
入力の終端は文字そのものではなく、これ以上入力がないことを示すためのシンボルである。
この字句の定義に合わせてTokenの定義を変更する。

#define TOKEN_TYPE_EOF 256
#define TOKEN_TYPE_INTEGER 257

typedef struct {
    int type;
    char *value;
} Token;

入力の一文字読み戻し用の情報を加えるためにLexerも少し変更。

typedef struct {
    Token token;
    size_t buffer_size;
    char *buffer;
    Reader *reader;
    int internal_prepared_reader;
    int pre_read;
    int has_pre_read;
} Lexer;

その初期化のための一文を追加。

void lexer_init_with_reader(Lexer *lexer, Reader *reader)
{
    lexer->buffer = malloc(LEXER_BUFFER_INIT_SIZE);
    if (lexer->buffer == NULL) {
        perror("fail to allocate Lexer internal buffer");
        exit(1);
    }
    lexer->buffer_size = LEXER_BUFFER_INIT_SIZE;
    lexer->reader = reader;
    lexer->internal_prepared_reader = 0;
    lexer->has_pre_read = 0;
}

そして字句切り出しの関数を変更。

Token *lexer_next_token(Lexer *lexer)
{
    for (;;) {
        int c;
        if (lexer->has_pre_read) {
            c = lexer->pre_read;
            lexer->has_pre_read = 0;
        } else {
            c = lexer->reader->any.get(lexer->reader);
        }
        if (c == EOF) {
            lexer->token.type = TOKEN_TYPE_EOF;
            break;
        }
        if (c == ' ' || c == '\t') continue;
        if (isdigit(c)) {
            size_t i = 0;
            do {
                lexer->buffer[i++] = c;
                if (i == lexer->buffer_size - 1) {
                    size_t s = lexer->buffer_size + LEXER_BUFFER_INCREASE;
                    char *b = realloc(lexer->buffer, s);
                    if (b == NULL) {
                        perror("fail to re-allocate Lexer internal buffer");
                        exit(1);
                    }
                    lexer->buffer_size = s;
                    lexer->buffer = b;
                }
                c = lexer->reader->any.get(lexer->reader);
            } while (isdigit(c));
            lexer->buffer[i] = '\0';
            lexer->token.type = TOKEN_TYPE_INTEGER;
            lexer->token.value = lexer->buffer;
            lexer->pre_read = c;
            lexer->has_pre_read = 1;
            break;
        } else {
            lexer->token.type = c;
            break;
        }
    }
    return &lexer->token;
}

最後にLexerを使って字句切り出しの結果を表示するmain関数。

int main(int argc, char *argv[])
{
    Lexer lexer;
    Reader reader;
    int eof = 0;

    if (argc > 1) {
        strreader_init(&reader, argv[1]);
        lexer_init_with_reader(&lexer, &reader);
    } else {
        lexer_init(&lexer);
    }
    while (! eof) {
        Token *token = lexer_next_token(&lexer);
        switch (token->type) {
        case TOKEN_TYPE_EOF:
            puts("[type=EOF]");
            eof = 1;
            break;
        case TOKEN_TYPE_INTEGER:
            printf("[type=INTEGER,value=%s]\n", token->value);
            break;
        default:
            if (isprint(token->type)) {
                printf("[type=%c]\n", token->type);
            } else {
                printf("[type=0x%02x]\n", token->type);
            }
        }
    }
    lexer_fin(&lexer);
    return 0;
}