Seeker's Memo

個人的で技術的かもしれないメモがメインのブログです。http://seekers.hatenablog.jp/about

C(C++)で何かを字句解析してみる(?) その2

3※なお、C++11の機能を使って書いてるのであしからず。
前回は、整数と文字列、空白文字(タブ、改行、スペース)だけを認識するというプログラムを書いた。
今回は少し拡張し、空白文字を細かく認識するようにし、今後のための些末な改良も加えた。

//Start. 2017/08/24
#include <cstdio>
#include <cctype>
#include <cstring>

const int MAX_TOKEN_STR = 1024 + 1;

enum KIND_OF_TOKEN {SPACE, TAB, NEWLINE, NUMBER, STRING, OTHER};

struct TOKEN{
    KIND_OF_TOKEN kind;
    int count = 0;//何文字読み込んだかを保存
    union{
        int  number;
        char str[MAX_TOKEN_STR];
    };
};

bool ishyphen(char c){
    if(c == '-'){
        return true;
    }
    else{
        return false;
    }
}

struct TOKEN tokenize_number(char* str){
    int offset = 0;
    //追加した処理
    if(ishyphen(str[0])){
        offset = 1;
    }
    int i = 0;
    int num = 0;
    while(std::isdigit(str[i+offset])){
        if(i > 0){
            num *= 10;
        }
        num += str[i+offset] - '0';
        ++i;
    }
    if(offset == 1){
        num = 0 - num;
    }
    TOKEN token;
    token.kind = KIND_OF_TOKEN::NUMBER;
    token.number = num;
    token.count = i + offset;
    return token;
}

struct TOKEN tokenize_string(char* str){
    TOKEN token;
    int i = 0;
    while(std::isalpha(str[i])){
        token.str[i] = str[i];
        ++i;
    }
    token.kind = KIND_OF_TOKEN::STRING;
    token.str[i] = '\0';
    token.count = i;
    return token;
}

void tokenize(char* str){
    int pos = 0;
    while(str[pos] != '\0'){
        if(str[pos] == ' '){
            TOKEN token;
            token.kind  = KIND_OF_TOKEN::SPACE;
            token.count = 1;
            std::printf("[pos:%d]:[Space]\n", pos);
            pos++;
        }
        else if(str[pos] == '\r' || str[pos] == '\n'){
            TOKEN token;
            token.kind = KIND_OF_TOKEN::NEWLINE;
            token.count = 1;
            std::printf("[pos:%d]:[NewLine]\n", pos);
            pos++;
        }
        else if(str[pos] == '\t'){
            TOKEN token;
            token.kind = KIND_OF_TOKEN::TAB;
            token.count = 1;
            std::printf("[pos:%d]:[Tab]\n", pos);
            pos++;
        }
        else if(std::isdigit(str[pos]) ||  str[pos] == '-' || str[pos] == '+'){
            struct TOKEN token = tokenize_number(&str[pos]);
            std::printf("[pos:%d-%d]:[Number]\n", pos, pos+token.count-1);
            pos += token.count;
        }
        else if(std::isalpha(str[pos])){
            TOKEN token = tokenize_string(str+pos);
            std::printf("[pos:%d-%d]:[Alpha]\n", pos, pos+token.count-1);
            pos += token.count;
        }
        else{
            TOKEN token;
            token.kind = KIND_OF_TOKEN::OTHER;
            token.count = 1;
            std::printf("[pos:%d]: unrecognize token\n", pos);
            pos++;  
        }
        
    }
}

/*
**  main
*/
int main(int argc, char **argv){
    char str[40] = "0 12 -3\n\t4.56 i abc;\n";
    std::printf("source:%s\n", str);
    tokenize(str);
    return 0;
}

ソースコードをカット&ペーストするのも面倒なんで、
GitHub - seekerkrt/compiler
コレに置いとく。