【C++】インタプリタを初めから丁寧に(第02回)

前回はインタプリタの概要についてを行いました。
今回は字句解析プログラムの内容について考えてみようと思います。

前回の字句解析で出てきた文字について整理します。

  • 演算子
  • 制御文判別子
  • 区切り記号
  • 関数判別子
  • 識別子
  • 整数
  • 文字列

今回は判断が簡単な5, 6, 7について考えて見ます。

5.識別子

識別子は int a; などとあるときに a にあたる部分のことを指します。
c言語などでは最初の文字がアルファベットもしくはアンダーバーで始まるものを識別子として認識されます。
ここでも同様に考えるとすると以下の手順を踏む必要があります。

  • 最初の文字はアルファベットかアンダーバーであるか
  • 使用できない文字列が含まれていないか

これだけですので実装は簡単です


std::string str; // スクリプトの内容
static int Pos; // スクリプトの位置
bool ReadIdentify() {
int n = Pos; // 何文字目を読んでいるか
std::string identify; // 識別子の名前
str = ReadLine(); // 1行読み出します。
if(!(IsAlpha(str[n]) ||  str[n] == '_'))
return false:  // アルファベットかアンダーバー以外ならfalse
n++;
// 識別子に許される文字である限り読み続けます。
while(1) {
if(IsDig(str[n] || IsAlpha(str[n]) || str[n] == '_')
n++;
else if(str[n] == ' ')
break;
else
return false;
}
identify = str.substr(Pos, Pos - n); // identifyの中に識別子の単語(int test なら test)が入ります
Pos = n;
return true;
}
// アルファベットであるかどうか
bool IsAlpha(char c) {
return 'a' <= c <= 'z' || 'A' <= c <= 'Z';
}
// 数字かどうか
bool IsDig(char c) {
return '0' <= c <= '9';
}

6, 7はこれと同様であるのですぐに実装ができます。


bool ReadInt() {
int n = Pos; // 何文字目を読んでいるか
int value;    // 整数の値
std::string integer; // 整数の名前
// 数字でなかった場合falseを返します
if(!IsDig(str[n]))
return false;
n++;
while(1) {
if(!(IsDig(str[n])) return false;
else if(str[n] == ' ') break;
else return false;
n++;
}
// 数字の値とその名前を登録します。 ( i = 12345 の場合 名前が "12345"であり、値が12345です)
integer = str.substr(Pos, n - Pos);
value = to_i(integer);
}
// string型をint型に直して返します。
int to_i(string s) {
int i, n, ret = 0;
if((n=(int)s.length()) == 0) exit(-1); // 送られてきた数字がまったくなかった場合はエラー
for(i=0; i<n; i++) {
if(s[i] <'0' || '9' < s[i]) exit(-1); // それが数字でなかった場合はエラー
ret = ret*10 + (s[i] - '0');
}
return ret;
}
bool ReadStr() {
int n = Pos; // 何文字目を読んでいるか
std::string strvalue; // 文字列の名前
if(str[n] != '"' ) return false; // ダブルクォーテーションで始まっていなければ文字列ではない
n++;
while(1) {
// もし日本語文字を読み込む場合2バイトずつ読まなければなりません。
// 文字が1バイト目の文字であるかどうかを調べ、もし1バイト目の文字であった場合
// 2バイト分読み進める必要があります。
if( 0x81 <= (unsigned char) str[pos] && (unsigned char) str[pos]<= 0x9f ||
(0xe0 <= (unsigned char) str[pos] && (unsigned char) str[pos]<= 0xfc))
n += 2;
else if(str[pos] == "\\")
n += 2;
else
n++;
if(str[n] != '"' || str[n] != '\0')
return false;
}
strvalue = str.substr(Pos, n-Pos);
}

これで ( 123 , abc, "mozi") (整数, 識別子, 文字列)の判断が可能となりました。
その他の文字については定型文字列にマッチしているかどうかといった内容で同様にチェックが可能となります。
つまり演算子 = とは文字列 "="にマッチしているかどうかといった判断により行われます。
これらの実装については第3回で行います。

そして今回のマトメとして読み出した内容を格納する必要があります。 格納しなければならない情報は以下の通りです

  • 1.この文字列は何であったか
  • この文字列の値は何か
  • この文字列はなんと書かれていたか

となります。
では実装を行います。

class Token() {
private:
int id;  // この文字列が何かを判別するidです
int value;  // 文字列の値です、今回はint型のみをサポートします。
strin str;  // 文字列の名前です
public:
Token(int id, int value, std::string str);
}
vector <Token> Tokens;
// 例で100というinteger文字列を格納する場合、以下のようにします。
Tokens.push_back(Token(integer, 100, "100"));

以上で取り出した値を格納する準備ができました。

では今回はこのあたりで。