Seeker's Memo

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

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

というわけで、まずは単純愚直に1文字ずつ判定するところから。
テスト用なので関数に分けてあるが、そのまま標準出力しているし、メタ的な結果を返すことをしていない。

#include <cstdio>
#include <cctype>

void tokenize(char* str){
    int pos = 0;
    while(*str != '\0'){
        if(std::isspace(*str)){
            std::printf("[pos:%d]:[Space]\n", pos);
        }
        else if(std::isdigit(*str)){
            std::printf("[pos:%d]:%c is [Digit]\n", pos, *str);
        }
        else if(isalpha(*str)){
            std::printf("[pos:%d]:%c is [Alpha]\n", pos, *str);
        }
        else{
            std::printf("[pos:%d]: unrecognize token\n", pos);
        }
        pos++;
        str++;  
    }
}

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

実行結果は

source:0 12 -3 5.67 i abc;
[pos:0]:0 is [Digit]
[pos:1]:[Space]
[pos:2]:1 is [Digit]
[pos:3]:2 is [Digit]
[pos:4]:[Space]
[pos:5]: unrecognize token
[pos:6]:3 is [Digit]
[pos:7]:[Space]
[pos:8]:5 is [Digit]
[pos:9]: unrecognize token
[pos:10]:6 is [Digit]
[pos:11]:7 is [Digit]
[pos:12]:[Space]
[pos:13]:i is [Alpha]
[pos:14]:[Space]
[pos:15]:a is [Alpha]
[pos:16]:b is [Alpha]
[pos:17]:c is [Alpha]
[pos:18]: unrecognize token

という感じに。
次はコレを拡張して、とりあえず、「正の整数」と「文字列」のように認識できるようにする。

#include <cstdio>
#include <cctype>
#include <cstring>

void tokenize(char* str){
    int pos = 0;
    while(str[pos] != '\0'){
        if(std::isspace(str[pos])){
            std::printf("[pos:%d]:[Space]\n", pos);
            pos++;
        }
        else if(std::isdigit(str[pos])){
            char buf[256];
            int i = 0;
            int num = 0;
            while(std::isdigit(str[pos+i])){
                buf[i] = str[pos+i];
                if(i > 0){
                    num *= 10;
                }
                num += str[pos+i] - '0';
                ++i;
            }
            buf[i] = '\0'; 
            std::printf("[pos:%d]:%s is [Number] = %d\n", pos, buf, num);
            pos += i;
        }
        else if(std::isalpha(str[pos])){
            char buf[1024];
            int i = 0;
            while(std::isalpha(str[pos+i])){
                buf[i] = str[pos+i];
                ++i;
            }
            buf[i] = '\0';
            std::printf("[pos:%d]:%s is [Alpha]\n", pos, buf);
            pos += i;
        }
        else{
            std::printf("[pos:%d]: unrecognize token\n", pos);
            pos++;  
        }
        
    }
}
/*
**  main
*/
int main(int argc, char **argv){
    char str[] = "0 12 -3 4.56 i abc;";
    std::printf("source:%s\n", str);
    tokenize(str);
    return 0;
}

ource:0 12 -3 4.56 i abc;
[pos:0]:0 is [Number] = 0
[pos:1]:[Space]
[pos:2]:12 is [Number] = 12
[pos:4]:[Space]
[pos:5]: unrecognize token
[pos:6]:3 is [Number] = 3
[pos:7]:[Space]
[pos:8]:4 is [Number] = 4
[pos:9]: unrecognize token
[pos:10]:56 is [Number] = 56
[pos:12]:[Space]
[pos:13]:i is [Alpha]
[pos:14]:[Space]
[pos:15]:abc is [Alpha]
[pos:18]: unrecognize token

こんな感じ。

そして次は、整数として正負を認識できるように。
今度は余計な出力を取っ払い、なおかつ整数の判定などを関数に分けた。

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

bool isnumber(char* str){
    int cnt = 0;
    while(std::isdigit(*str)){
        ++cnt;
    }
    return cnt > 0 ? true:false;
}

bool ishyphen(char c){
    if(c == '-'){
        return true;
    }
    else{
        return false;
    }
}
//なん文字すっ飛ばしたかを戻り値で返す
int 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;
    }
    return i + offset;
}

void tokenize(char* str){
    int pos = 0;
    while(str[pos] != '\0'){
        if(std::isspace(str[pos])){
            std::printf("[pos:%d]:[Space]\n", pos);
            pos++;
        }
        else if(std::isdigit(str[pos]) ||  str[pos] == '-' || str[pos] == '+'){
            int cnt = tokenize_number(&str[pos]);
            std::printf("[pos:%d-%d]:[Number]\n", pos, pos+cnt-1);
            pos += cnt;
        }
        else if(std::isalpha(str[pos])){
            int i = 0;
            while(std::isalpha(str[pos+i])){
                ++i;
            }
            std::printf("[pos:%d-%d]:[Alpha]\n", pos, pos+i-1);
            pos += i;
        }
        else{
            std::printf("[pos:%d]: unrecognize token\n", pos);
            pos++;  
        }
        
    }
}
/*
**  main
*/
int main(int argc, char **argv){
    char str[] = "0 12 -3 4.56 i abc;";
    std::printf("source:%s\n", str);
    tokenize(str);
    return 0;
}

出力はこんな感じ。まだ実数などの認識ができていない。

source:0 12 -3 4.56 i abc;
[pos:0-0]:[Number]
[pos:1]:[Space]
[pos:2-3]:[Number]
[pos:4]:[Space]
[pos:5-6]:[Number]
[pos:7]:[Space]
[pos:8-8]:[Number]
[pos:9]: unrecognize token
[pos:10-11]:[Number]
[pos:12]:[Space]
[pos:13-13]:[Alpha]
[pos:14]:[Space]
[pos:15-17]:[Alpha]
[pos:18]: unrecognize token

次回は、実数の判定をすっ飛ばして、字句解析のプログラムっぽく大幅に色々実装する予定。