忍者ブログ
ゲーム関連の話題に絞っていましたが、全然書かないのでいろいろ解禁してみたり。
[14]  [13]  [12]  [11]  [10]  [9]  [8]  [7]  [6]  [5]  [4
×

[PR]上記の広告は3ヶ月以上新規記事投稿のないブログに表示されています。新しい記事を書く事で広告が消えます。

というわけで本日の最終回。
早速いってみましょうっ♪

四則演算+変数の使用+変数の中身チェックな対話モードインタプリタを作ってみました。

コードはこちら。


#pragma warning(disable:4100) //引数は関数の本体部で 1 度も参照されません。
#pragma warning(disable:4512) //代入演算子を生成できません。

#include <iostream>
#include <map>
#include <string>
#include <stack>

#include <boost/spirit/home/qi.hpp>
#include <boost/spirit/home/phoenix.hpp>

class VariableManager{
public:
 void Set(std::string name, int value){
  container[name] = value;
 }

 int Get(std::string name){
  return container[name];
 }

private:
 typedef std::map<std::string, int> Container;
 Container container;
};

template<typename String>
struct Calc : boost::spirit::qi::grammar<typename String::const_iterator, boost::spirit::ascii::space_type>{
 typedef typename String::const_iterator Iterator;

 Calc() : Calc::base_type(line){

  using namespace boost::spirit;
  using namespace boost::spirit::arg_names;

  // 構文定義

  //数値演算
  group  = '(' >> expression[_val = _1] >> ')';
  factor  = lexeme[
        int_[_val = _1]  //数字
       ]
     | group[_val = _1]    //部分式の演算結果
     | refVariable[_val = _1];  //変数
  term  = factor[_val = _1] >> *(('*' >> factor[_val *= _1])
           |('/' >> factor[_val /= _1]));
  expression = term[_val = _1] >> *(('+' >> term[_val += _1])
          |  ('-' >> term[_val -= _1]));

  //変数
  variable = lexeme[
        ascii::alpha[_val += _1] >> +ascii::alnum[_val += _1]
       ];

  //refVariable = variable[_val = vm.Get(_1)];
  //refVariable = variable[_val = boost::bind(&Calc::GET_VAL, this, _1)];
  refVariable = variable[_val = boost::phoenix::bind(&Calc::GET_VAL, this, _1)];

  //inVariable = (variable >> expression)[vm.Set(_1, _2)];
  //inVariable = (variable >> "=" >> expression)[boost::bind(&Calc::SET_VAL, this, _1, _2)];
  inVariable = (variable >> "=" >> expression)[boost::phoenix::bind(&Calc::SET_VAL, this, _1, _2)];

  //行
  //line  = expression[boost::phoenix::bind(&Calc::PUSH, this, _1)];
  //   | inVariable
  line  = inVariable
     | expression[boost::phoenix::bind(&Calc::PUSH, this, _1)];
 };

 boost::spirit::qi::rule<Iterator, int(), boost::spirit::ascii::space_type> expression;
 boost::spirit::qi::rule<Iterator, int(), boost::spirit::ascii::space_type> term;
 boost::spirit::qi::rule<Iterator, int(), boost::spirit::ascii::space_type> factor;
 boost::spirit::qi::rule<Iterator, int(), boost::spirit::ascii::space_type> group;

 boost::spirit::qi::rule<Iterator, String(), boost::spirit::ascii::space_type> variable;
 boost::spirit::qi::rule<Iterator, int(), boost::spirit::ascii::space_type> refVariable;
 boost::spirit::qi::rule<Iterator, boost::spirit::ascii::space_type> inVariable;

 boost::spirit::qi::rule<Iterator, boost::spirit::ascii::space_type> line;


 std::stack<int> stack;
 VariableManager vm;
 
 bool isResult(){
  return !stack.empty();
 }

 int GetResult(){
  int i = stack.top();
  Reset();
  return i;
 }

 void Reset(){
  std::stack<int> empty;
  std::swap(empty, stack);
 }

 void PUSH(int i){
  stack.push(i);
 }

 void SET_VAL(String str, int i){
  vm.Set(str, i);
 }

 int GET_VAL(String str){
  return vm.Get(str);
 }

};


int main(void)
{
 Calc<std::string> calc;

 std::string str;
 while (getline(std::cin, str))
 {
  if (str.empty() || str[0] == 'q' || str[0] == 'Q')
   break;

  //boost::algorithm::trim(str);
  std::string::const_iterator it = str.begin();
  std::string::const_iterator end = str.end();

  bool result = phrase_parse(it, end, calc, boost::spirit::ascii::space);

  if (result && it == end)
  {
   std::cout << "-------------------------\n";
   std::cout << "Parsing succeeded\n";
   std::cout << str << " Parses OK: " << std::endl;
   if(calc.isResult()) std::cout << calc.GetResult() << std::endl;
  }
  else if(result)
  {
   std::cout << "-------------------------\n";
   std::cout << "Parsing succeeded\n";
   std::cout << std::string(str.begin(), it) << " Parses OK: " << std::endl;
   std::cout << std::string(it, end) << " amari" << std::endl;
   if(calc.isResult()) std::cout << calc.GetResult() << std::endl;
  }
  else
  {
   std::cout << "-------------------------\n";
   std::cout << "Parsing failed\n";
   std::cout << "-------------------------\n";
   calc.Reset();
  }
 }

 return 0;
}

ポイントはここです。

#include <boost/spirit/home/phoenix.hpp>

……えーっと、phoenixで何が必要になるかわからないのでもう全部インクルードしちゃえっ!って発想です。
たぶんこれで全部インクルード……のおかげでビルド時間ものびのび。
必要なヘッダだけ後で探した方がよさげなのですorz
で、一番重要なのはここ。

  line  = inVariable
     | expression[boost::phoenix::bind(&Calc::PUSH, this, _1)];

なんと、phoenixがすでにbindを持っていました。
しかもphoenixの_1を普通に使えるので、今までの苦労は何だったのか……って感じに。
使い方は普通のbindと変わらないみたいです。
boost::bindをインクルードしなくなったので、大手を振ってphoenixの_1をそのままかけるようになってコードもすっきり。
あともう一つ。

refVariable = variable[_val = boost::phoenix::bind(&Calc::GET_VAL, this, _1)];

変数マネージャへの変数の参照をphoenixなbindで記述して、そのままphoenixなlambdaで_valに代入なんて無茶が出来ちゃいます。
これ、これを探していたんですっ。感涙物なのです……
ちなみに、前後でコメントアウトしているのは試行錯誤の軌跡です。笑ってやってください。

なにげに重要そうなのがここ。

  //line  = expression[boost::phoenix::bind(&Calc::PUSH, this, _1)];
  //   | inVariable
  line  = inVariable
     | expression[boost::phoenix::bind(&Calc::PUSH, this, _1)];

inVariable(変数への代入)の方を先にしないと、たとえば

hoge=10

というスクリプト文がそのままinVariableではなく、hogeの部分だけでexpression→term→factor→refVariable……と解釈されてhogeの中身参照(代入できないので常に0)、=10が余分……と構文解析されます。
最後にここ。

  inVariable = (variable >> "=" >> expression)[boost::phoenix::bind(&Calc::SET_VAL, this, _1, _2)];

4の時に見つけた複数の引数をとる方法がそのまま使えるのです。

これで何とか色々出来そうな気分になってきました。

//2009-10-25追記
コードの修正(mutableとconstの勘違い)

PR
この記事にコメントする
お名前
タイトル
文字色
メールアドレス
URL
コメント
パスワード   Vodafone絵文字 i-mode絵文字 Ezweb絵文字
この記事へのトラックバック
この記事にトラックバックする:
Twitter
Twitter Update
ブログ内検索
カレンダー
05 2025/06 07
S M T W T F S
1 2 3 4 5 6 7
8 9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27 28
29 30
最新コメント
最新トラックバック
プロフィール
HN:
100poisha
性別:
非公開
アクセス解析
Copyright © 100poishaのブログ All Rights Reserved.
Designed by north sound
Powered by Ninja Blog

忍者ブログ [PR]