ゲームに簡単なプログラムを組み込むためにスクリプト言語とそれを実行するインタープリタをC++で作ってしまおうというコーナーです。
今回から本格的にスクリプトを作り始めます。
ただし、この回の内容は第17回で見直す予定です。
何でもかんでも値を持つ
第2回で式は値をもつと書きましたが、いっそのこと何でもかんでも値をもつようにします。
10とか$a+$bなどはもちろんのこと、whileとかifなんかも値を持ちます。
どうしても割り当てたい値が無いときは未定義値を割り当てておけばいいんですから、あれは値を割り当てているか、これは割り当てていないかとか、考えなくて済むようになります。
当然のことながら、何も書いていないプログラムも値を持ち、その値は未定義値となります。
なぜ何でもかんでも値を持つようにしたかというと、逆に値だけでもプログラムが成り立つようにしたかったのです。
例えばスクリプトを呼ぶプログラム側から変数$aの値がほしくなった場合は、変数のリストへ直接アクセスして'$a'という変数の値を取り出す方法もあるのですが、「$a」というスクリプトを実行してその結果を得るという方法もあるのです。
前置きが長くなりましたが、つまり、「10」とだけ書かれたスクリプトをインタープリタに渡すと10という数値が得られますよという話です。
こんな簡単なものであれば、解析すべき構文を持ったプログラムではないので構文解析は省いて字句解析だけを作れます。
つまり、今回の趣旨は字句解析を作ろうという話。
スクリプト内の値を格納するクラス
つまり、スクリプト内にあるいろんな値をインタープリタ側ではどんな形で保持しておこうかという話です。
値と一口に言っても、第2回に書いてあるように5種類もあり、場合によっては別の型への変換が必要になるわけですから、「値」とひとくくりでありなおかつ5種類の内容をカバーできるように作らなきゃいけません。
かといって一つの値に同時に5つの内容を持たせておくのもなんだか馬鹿げてます(「10」が配列の情報までも持っているのは変ですよね?)。
今回はC++で作ることにしているので、全ての値を表現できるクラスを親玉として、その派生クラスとしてそれぞれの種類の値を用意しましょう。
// 値の型 enum EMSValueType { eMSNullValue, eMSNumericalValue, eMSStringValue, eMSArrayValue, eMSErrorValue, }; // スクリプト内の値を格納するクラスの親玉 class CMSValue { public: CMSValue() {} virtual ~CMSValue() {} virtual EMSValueType GetType() = 0; virtual double GetNumber() { return 0.0; } virtual std::string GetString() { return ""; } virtual bool GetBoolean() { return false; } }; // 値なし class CMSNullValue : public CMSValue { public: CMSNullValue() {} virtual ~CMSNullValue() {} EMSValueType GetType() { return eMSNullValue; } }; // 数値 class CMSNumericalValue : public CMSValue { private: double m_dValue; public: CMSNumericalValue() { m_dValue = 0.0;} CMSNumericalValue(double dValue) { m_dValue = dValue;} virtual ~CMSNumericalValue() {} EMSValueType GetType() { return eMSNumericalValue; } double GetNumber() { return m_dValue; } std::string GetString() { char ret[100]; sprintf(ret,"%f",m_dValue); return ret; } virtual bool GetBoolean() { return m_dValue!=0.0; } }; // 文字列 class CMSStringValue : public CMSValue { private: std::string m_strValue; public: CMSStringValue() {} CMSStringValue(std::string strValue) { m_strValue = strValue; } virtual ~CMSStringValue() {} EMSValueType GetType() { return eMSStringValue; } double GetNumber() {} std::string GetString() { return m_strValue; } virtual bool GetBoolean() { return m_strValue!=""; } }; // 配列 class CMSArrayValue : public CMSValue { public: CMSArrayValue() {} virtual ~CMSArrayValue() {} EMSValueType GetType() { return eMSArrayValue; } }; // エラー class CMSErrorValue : public CMSValue { public: CMSErrorValue() {} virtual ~CMSErrorValue() {} EMSValueType GetType() { return eMSErrorValue; } };
とりあえず今すぐ使えそうな型だけは中身も実装してみました。
今回までのソース(VC++6)