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