Boost Spirit Classic における計算機の例

by taiji at 2013/04/04 12:50

Boost Spirit のサンプルにはいくつもの計算機の例があるが、四則演算以外をサポートした簡潔な好例がなく、ここで実装例を紹介する価値はありそうである。

手始めに復習を兼ねて Spirit Classic での例を紹介していく。

Spirit Classic の基本形

Spirit Classic の基本形は以下のようになる。

#include <iostream>
#include <string>
#include <boost/spirit/include/classic.hpp>

using namespace boost::spirit::classic;

namespace client {
  struct a_parser : public grammar<a_parser> {
    template <typename S>
    struct definition {
      rule<S> top;
      definition(const a_parser &self)
      {
        top = *~ch_p('\t'); // ここにパーサ規則を書く
      }
      const rule<S> &start() const { return top; }
    };
  };
}
int main()
{
  client::a_parser a_parser;
  std::string str;
  while (std::getline(std::cin, str)) {
    parse_info<std::string::iterator> r =
      parse(str.begin(), str.end(), a_parser, ~anychar_p);
    if (r.full && r.stop == str.end()) {
      std::cout << "succeeded" << std::endl;
    }
    else {
      std::cout << "failed:\t" << std::string(r.stop, str.end()) << std::endl;
      return 1;
    }
  }
  return 0;
}

計算機でサポートする演算子

以下の演算子をサポートする実装例を段階的に示そう。

  1. 単項演算子の単項正負(+ -)、単項論理否定&ビット否定(! ~)、二項演算子の二項加減乗除法(+ - * / %)、関係等号不等号(< <= > >= == !=)、ビットシフト(<< >>)、ビット演算子(& ^ |)、論理積和(&& ||)
  2. 三項演算子の三項条件(?:)
  3. 変数のサポートおよび単項演算子の前後置増減分(++ --)、二項演算子の単純代入(=)、複合代入(+= -+ *= /= %= &= ^= +=)およびカンマ演算子(,)

また、演算は任意の数値型で行えるように実装する。

ここで、int型における1.と2.の演算についてはiexpr、3.についてはicalcというプログラム名をつけるものとする。

ここでは敢えてAST(Abstract Syntax Tree)による例は別稿にゆずり、セマンティックアクションで計算機を実装する例を紹介する。セマンティックアクションでは構文解析しながら演算を行うので、例えば、以下のような代入と三項条件の組み合わせについては困難を伴うので、サポートしないものとする。

i<j ? i++ : i=j

iexpr_classic.cc

int型における1.と2.の演算を行う計算機を Spirit Classic で実装すると以下のようになるだろう。

#include <iostream>
#include <iomanip>
#include <string>
#include <stack>
#include <functional>
#include "more_functional"
#include <boost/spirit/include/classic.hpp>

using namespace boost::spirit::classic;

namespace client {
  template <typename T>
  struct popper {
    popper(std::stack<T> &vs_) : vs(vs_) {}
    void operator()(const T v=0) const { (void)vs.pop(); }
    std::stack<T> &vs;
  };

  template <typename T>
  struct pusher {
    pusher(std::stack<T> &vs_) : vs(vs_) {}
    void operator()(T v) const { vs.push(v); }
    std::stack<T> &vs;
  };

  template <typename T>
  struct top_popper {
    top_popper(std::stack<T> &vs_) : vs(vs_) {}
    T operator()() const { T a = vs.top(); (popper<T>(vs))(); return a; }
    std::stack<T> &vs;
  };

  template <typename F, typename T>
  struct unary_f_pusher {
    unary_f_pusher(F const &f_, std::stack<T> &vs_) : f(f_), vs(vs_) {}
    void operator()(char const *, char const *) const
    {
      T lhs = (top_popper<T>(vs))();
      (pusher<T>(vs))(f(lhs));
    }
    F f;
    std::stack<T> &vs;
  };

  template <class F, typename T>
  unary_f_pusher<F, T> unary_f_doer(F const &f, std::stack<T> &vs)
  {
    return unary_f_pusher<F, T>(f, vs);
  }

  template <typename F, typename T>
  struct binary_f_pusher {
    binary_f_pusher(F const &f_, std::stack<T> &vs_) : f(f_), vs(vs_) {}
    void operator()(char const *, char const *) const
    {
      T rhs = (top_popper<T>(vs))(), lhs = (top_popper<T>(vs))();
      (pusher<T>(vs))(f(lhs, rhs));
    }
    F f;
    std::stack<T> &vs;
  };

  template <class F, typename T>
  binary_f_pusher<F, T> binary_f_doer(F const &f, std::stack<T> &vs)
  {
    return binary_f_pusher<F, T>(f, vs);
  }

#if !0 /* ternary operator */
  template <typename T>
  struct ternary_condition_doer {
    ternary_condition_doer(std::stack<T> &vs_) : vs(vs_) {}
    void operator()(char const *, char const *) const
    {
      T c = (top_popper<T>(vs))(), b = (top_popper<T>(vs))(), a = (top_popper<T>(vs))();
      (pusher<T>(vs))(a ? b : c);
    }
    std::stack<T> &vs;
  };
#endif

  template <typename T>
  struct calc : public grammar<calc<T> > {
    calc(std::stack<T> &vs_) : vs(vs_) {}
    template <typename S>
    struct definition {
      rule<S> expr, t[9], term, fctr;
      definition(calc const &self)
      {
#if !0 /* ternary operator */
        expr = t[0] >> *('?'    >> expr >>
                         ':'    >> expr)[ternary_condition_doer<T>(self.vs)];
#else
        expr = t[0];
#endif
        t[0] = t[1] >> *("||"   >> t[1][binary_f_doer(std::logical_or<T>(), self.vs)]);
        t[1] = t[2] >> *("&&"   >> t[2][binary_f_doer(std::logical_and<T>(), self.vs)]);
        t[2] = t[3] >> *('|'    >> t[3][binary_f_doer(std::bitwise_or<T>(), self.vs)]);
        t[3] = t[4] >> *('^'    >> t[4][binary_f_doer(std::bitwise_xor<T>(), self.vs)]);
        t[4] = t[5] >> *('&'    >> t[5][binary_f_doer(std::bitwise_and<T>(), self.vs)]);
        t[5] = t[6] >> *("<<"   >> t[6][binary_f_doer(std::bitshift_left<T>(), self.vs)] |
                         ">>"   >> t[6][binary_f_doer(std::bitshift_right<T>(), self.vs)]);
        t[6] = t[7] >> *('<'    >> t[7][binary_f_doer(std::less<T>(), self.vs)] |
                         '>'    >> t[7][binary_f_doer(std::greater<T>(), self.vs)] |
                         "<="   >> t[7][binary_f_doer(std::less_equal<T>(), self.vs)] |
                         ">="   >> t[7][binary_f_doer(std::greater_equal<T>(), self.vs)]);
        t[7] = t[8] >> *("=="   >> t[8][binary_f_doer(std::equal_to<T>(), self.vs)] |
                         "!="   >> t[8][binary_f_doer(std::not_equal_to<T>(), self.vs)]);
        t[8] = term >> *('+'    >> term[binary_f_doer(std::plus<T>(), self.vs)] |
                         '-'    >> term[binary_f_doer(std::minus<T>(), self.vs)]);
        term = fctr >> *('*'    >> fctr[binary_f_doer(std::multiplies<T>(), self.vs)] |
                         '/'    >> fctr[binary_f_doer(std::divides<T>(), self.vs)] |
                         '%'    >> fctr[binary_f_doer(std::modulus<T>(), self.vs)]);
        fctr = "0x" >> hex_p[pusher<T>(self.vs)] |
          '0' >> oct_p[pusher<T>(self.vs)] |
          uint_p[pusher<T>(self.vs)] |
          '(' >> expr >> ')' |
          '-' >> fctr[unary_f_doer(std::negate<T>(), self.vs)] |
          '+' >> fctr |
          '!' >> fctr[unary_f_doer(std::logical_not<T>(), self.vs)] |
          '~' >> fctr[unary_f_doer(std::bitwise_not<T>(), self.vs)];
      }
      rule<S> const &start() const { return expr; }
    };
    mutable T *var;
    std::stack<T> &vs;
  };
}

int main()
{
  int rv = 0;

  typedef int value_type;
  std::stack<value_type> st;
  client::calc<value_type> calc(st);
  std::string str;
  while (std::getline(std::cin, str)) {
    parse_info<> r = parse(str.c_str(), calc, space_p);
    if (r.full && r.stop == str.c_str()+str.size()) {
      std::cout << "succeeded:\t" << std::endl;
      std::cout << calc.vs.top() << std::endl;
    }
    else {
      std::cout << "failed:\t" << std::string(r.stop, str.c_str()+str.size()) << std::endl;
      rv = !0;
    }
    calc.vs = std::stack<value_type>();
  }
  return rv;
}

もし unsigned int 型の演算を行いたければ、main 関数の冒頭を typedef unsigned int value_type; に書き換えれば良い。

ちなみに、ここでは別項で使用した more_functional を使用していることに注意。

icalc_classic.cc

さて、iexpr_classic.cc に変数のサポートと代入演算子を導入すると以下のようになるだろう。

#include <iostream>
#include <iomanip>
#include <string>
#include <stack>
#include <functional>
#include "more_functional"
#include <boost/spirit/include/classic.hpp>

using namespace boost::spirit::classic;

namespace client {
  template <typename T>
  struct popper {
    popper(std::stack<T> &vs_) : vs(vs_) {}
    void operator()(const T v=0) const { (void)vs.pop(); }
    std::stack<T> &vs;
  };

  template <typename T>
  struct pusher {
    pusher(std::stack<T> &vs_) : vs(vs_) {}
    void operator()(T v) const { vs.push(v); }
    std::stack<T> &vs;
  };

  template <typename T>
  struct swapper {
   swapper(std::stack<T> &vs_) : vs(vs_) {}
    void operator()(T v) const { (popper<T>(vs))(); (pusher<T>(vs))(v); }
    std::stack<T> &vs;
  };

  template <typename T>
  struct top_popper {
    top_popper(std::stack<T> &vs_) : vs(vs_) {}
    T operator()() const { T a = vs.top(); (popper<T>(vs))(); return a; }
    std::stack<T> &vs;
  };

  template <typename F, typename T>
  struct unary_f_pusher {
    unary_f_pusher(F const &f_, std::stack<T> &vs_) : f(f_), vs(vs_) {}
    void operator()(char const *, char const *) const
    {
      T lhs = (top_popper<T>(vs))();
      (pusher<T>(vs))(f(lhs));
    }
    F f;
    std::stack<T> &vs;
  };

  template <class F, typename T>
  unary_f_pusher<F, T> unary_f_doer(F const &f, std::stack<T> &vs)
  {
    return unary_f_pusher<F, T>(f, vs);
  }

  template <typename F, typename T>
  struct binary_f_pusher {
    binary_f_pusher(F const &f_, std::stack<T> &vs_) : f(f_), vs(vs_) {}
    void operator()(char const *, char const *) const
    {
      T rhs = (top_popper<T>(vs))(), lhs = (top_popper<T>(vs))();
      (pusher<T>(vs))(f(lhs, rhs));
    }
    F f;
    std::stack<T> &vs;
  };

  template <class F, typename T>
  binary_f_pusher<F, T> binary_f_doer(F const &f, std::stack<T> &vs)
  {
    return binary_f_pusher<F, T>(f, vs);
  }

#if !0 /* ternary operator */
  template <typename T>
  struct ternary_condition_doer {
    ternary_condition_doer(std::stack<T> &vs_) : vs(vs_) {}
    void operator()(char const *, char const *) const
    {
      T c = (top_popper<T>(vs))(), b = (top_popper<T>(vs))(), a = (top_popper<T>(vs))();
      (pusher<T>(vs))(a ? b : c);
    }
    std::stack<T> &vs;
  };
#endif

  template <typename T>
  struct lvar_pusher {
    lvar_pusher(symbols<T> &vars_, T *&var_, std::stack<T> &vs_) : vars(vars_), var(var_), vs(vs_) {}
    void operator()(char const *first, const char *last) const
    {
      if (!(var = find(vars, std::string(first, last).c_str()))) {
        vars.add(first, last, T(0));
        var = find(vars, std::string(first, last).c_str());
      }
      (pusher<T>(vs))(*var);
    }
    symbols<T> &vars;
    T *&var;
    std::stack<T> &vs;
  };

  template <typename T>
  struct postfix_incrementer {
    postfix_incrementer(T *&var_, std::stack<T> &vs_) : var(var_), vs(vs_) {}
    void operator()(char const *, char const *) const
    {
      (popper<T>(vs))();
      (pusher<T>(vs))((*var)++); 
    }
    T *&var;
    std::stack<T> &vs;
  };

  template <typename T>
  struct postfix_decrementer {
    postfix_decrementer(T *&var_, std::stack<T> &vs_) : var(var_), vs(vs_) {}
    void operator()(char const *, char const *) const
    {
      (popper<T>(vs))();
      (pusher<T>(vs))((*var)--);
    }
    T *&var;
    std::stack<T> &vs;
  };

  template <typename T>
  struct prefix_incrementer {
    prefix_incrementer(T *&var_, std::stack<T> &vs_) : var(var_), vs(vs_) {}
    void operator()(char const *, char const *) const
    {
      (pusher<T>(vs))(++(*var));
    }
    T *&var;
    std::stack<T> &vs;
  };

  template <typename T>
  struct prefix_decrementer {
    prefix_decrementer(T *&var_, std::stack<T> &vs_) : var(var_), vs(vs_) {}
    void operator()(char const *first, char const *last) const
    {
      (pusher<T>(vs))(--(*var));
    }
    T *&var;
    std::stack<T> &vs;
  };

  template <typename F, typename T>
  struct unary_f_var_pusher {
    unary_f_var_pusher(F const &f_, T *&var_, std::stack<T> &vs_) : f(f_), var(var_), vs(vs_) {}
    void operator()(char const *, char const *) const
    {
      (swapper<T>(vs))((*var=f((top_popper<T>(vs))())));
    }
    F f;
    T *&var;
    std::stack<T> &vs;
  };

  template <class F, typename T>
  unary_f_var_pusher<F, T> unary_f_assignor(F const &f, T *&var, std::stack<T> &vs)
  {
    return unary_f_var_pusher<F, T>(f, var, vs);
  }

  template <typename F, typename T>
  struct binary_f_var_pusher {
    binary_f_var_pusher(F const &f_, T *&var_, std::stack<T> &vs_) : f(f_), var(var_), vs(vs_) {}
    void operator()(char const *, char const *) const
    {
      T rhs = (top_popper<T>(vs))();
      (pusher<T>(vs))((*var=f((top_popper<T>(vs))(), rhs)));
    }
    F f;
    T *&var;
    std::stack<T> &vs;
  };

  template <class F, typename T>
  binary_f_var_pusher<F, T> binary_f_assignor(F const &f, T *&var, std::stack<T> &vs)
  {
    return binary_f_var_pusher<F, T>(f, var, vs);
  }


  template <typename T>
  struct calc : public grammar<calc<T> > {
    calc(symbols<T> &vars_, std::stack<T> &vs_) : vars(vars_), vs(vs_) {}
    template <typename S>
    struct definition {
      rule<S> vars, e[7], expr, t[9], term, fctr;
      definition(calc const &self)
      {
        vars = lexeme_d[(alpha_p >> *(alnum_p | '_'))][lvar_pusher<T>(self.vars, self.var, self.vs)];
        expr = e[0] % ch_p(',')[popper<T>(self.vs)];
        e[0] = e[1] | e[2] | e[3] | e[4] | e[5] | e[6];
        e[1] = vars >> ('='     >> e[0][unary_f_assignor(std::identify<T>(), self.var, self.vs)]);
        e[2] = vars >> ("+="    >> e[0][binary_f_assignor(std::plus<T>(), self.var, self.vs)] |
                        "-="    >> e[0][binary_f_assignor(std::minus<T>(), self.var, self.vs)]);
        e[3] = vars >> ("*="    >> e[0][binary_f_assignor(std::multiplies<T>(), self.var, self.vs)] |
                        "/="    >> e[0][binary_f_assignor(std::divides<T>(), self.var, self.vs)] |
                        "%="    >> e[0][binary_f_assignor(std::modulus<T>(), self.var, self.vs)]);
        e[4] = vars >> ("<<="   >> e[0][binary_f_assignor(std::bitshift_left<T>(), self.var, self.vs)] |
                        ">>="   >> e[0][binary_f_assignor(std::bitshift_right<T>(), self.var, self.vs)]);
        e[5] = vars >> ("&="    >> e[0][binary_f_assignor(std::bitwise_and<T>(), self.var, self.vs)] |
                        "^="    >> e[0][binary_f_assignor(std::bitwise_xor<T>(), self.var, self.vs)] |
                        "|="    >> e[0][binary_f_assignor(std::bitwise_or<T>(), self.var, self.vs)]);
#if !0 /* ternary operator */
        e[6] = t[0] >> *('?'    >> e[0] >>
                         ':'    >> e[0])[ternary_condition_doer<T>(self.vs)];
#else
        e[6] = t[0];
#endif
        t[0] = t[1] >> *("||"   >> t[1][binary_f_doer(std::logical_or<T>(), self.vs)]);
        t[1] = t[2] >> *("&&"   >> t[2][binary_f_doer(std::logical_and<T>(), self.vs)]);
        t[2] = t[3] >> *('|'    >> t[3][binary_f_doer(std::bitwise_or<T>(), self.vs)]);
        t[3] = t[4] >> *('^'    >> t[4][binary_f_doer(std::bitwise_xor<T>(), self.vs)]);
        t[4] = t[5] >> *('&'    >> t[5][binary_f_doer(std::bitwise_and<T>(), self.vs)]);
        t[5] = t[6] >> *("<<"   >> t[6][binary_f_doer(std::bitshift_left<T>(), self.vs)] |
                         ">>"   >> t[6][binary_f_doer(std::bitshift_right<T>(), self.vs)]);
        t[6] = t[7] >> *('<'    >> t[7][binary_f_doer(std::less<T>(), self.vs)] |
                         '>'    >> t[7][binary_f_doer(std::greater<T>(), self.vs)] |
                         "<="   >> t[7][binary_f_doer(std::less_equal<T>(), self.vs)] |
                         ">="   >> t[7][binary_f_doer(std::greater_equal<T>(), self.vs)]);
        t[7] = t[8] >> *("=="   >> t[8][binary_f_doer(std::equal_to<T>(), self.vs)] |
                         "!="   >> t[8][binary_f_doer(std::not_equal_to<T>(), self.vs)]);
        t[8] = term >> *('+'    >> term[binary_f_doer(std::plus<T>(), self.vs)] |
                         '-'    >> term[binary_f_doer(std::minus<T>(), self.vs)]);
        term = fctr >> *('*'    >> fctr[binary_f_doer(std::multiplies<T>(), self.vs)] |
                         '/'    >> fctr[binary_f_doer(std::divides<T>(), self.vs)] |
                         '%'    >> fctr[binary_f_doer(std::modulus<T>(), self.vs)]);
        fctr = "0x" >> hex_p[pusher<T>(self.vs)] |
          '0' >> oct_p[pusher<T>(self.vs)] |
          uint_p[pusher<T>(self.vs)] |
          "++" >> vars[prefix_incrementer<T>(self.var, self.vs)] |
          "--" >> vars[prefix_decrementer<T>(self.var, self.vs)] |
          vars >> str_p("++")[postfix_incrementer<T>(self.var, self.vs)] |
          vars >> str_p("--")[postfix_decrementer<T>(self.var, self.vs)] |
          vars |
          '(' >> expr >> ')' |
          '-' >> fctr[unary_f_doer(std::negate<T>(), self.vs)] |
          '+' >> fctr |
          '!' >> fctr[unary_f_doer(std::logical_not<T>(), self.vs)] |
          '~' >> fctr[unary_f_doer(std::bitwise_not<T>(), self.vs)];
      }
      rule<S> const &start() const { return expr; }
    };
    symbols<T> &vars;
    mutable T *var;
    std::stack<T> &vs;
  };
}

int main()
{
  int rv = 0;

  typedef int value_type;
  symbols<value_type> vars;
  std::stack<value_type> st;
  client::calc<value_type> calc(vars, st);
  std::string str;
  while (std::getline(std::cin, str)) {
    parse_info<> r = parse(str.c_str(), calc, space_p);
    if (r.full && r.stop == str.c_str()+str.size()) {
      std::cout << "succeeded:\t" << std::endl;
      std::cout << calc.vs.top() << std::endl;
    }
    else {
      std::cout << "failed:\t" << std::string(r.stop, str.c_str()+str.size()) << std::endl;
      rv = !0;
    }
    calc.vs = std::stack<value_type>();
  }
  return rv;
}

同様に、もし unsigned int 型の演算を行いたければ、main 関数の冒頭を typedef unsigned int value_type; に書き換えれば良い。

ちなみに、この実装では代入時に未使用の変数を新たに使用することができ、変数の参照時に未使用の変数を使用すると零が代入されているものとする。

diff -u iexpr_classic.cc icalc_classic.cc

上述の二つのプログラムリストの掲示だけでは、どのように変数のサポートと代入演算子を導入したかが分かり辛いので、これらの Unified diff 形式をみてみよう。

--- iexpr_classic.cc
+++ icalc_classic.cc
@@ -24,6 +24,13 @@
   };
 
+  template <typename T>
+  struct swapper {
+   swapper(std::stack<T> &vs_) : vs(vs_) {}
+    void operator()(T v) const { (popper<T>(vs))(); (pusher<T>(vs))(v); }
+    std::stack<T> &vs;
+  };
+
   template <typename T>
   struct top_popper {
     top_popper(std::stack<T> &vs_) : vs(vs_) {}
     T operator()() const { T a = vs.top(); (popper<T>(vs))(); return a; }
@@ -80,18 +87,132 @@
 #endif
 
+  template <typename T>
+  struct lvar_pusher {
+    lvar_pusher(symbols<T> &vars_, T *&var_, std::stack<T> &vs_) : vars(vars_), var(var_), vs(vs_) {}
+    void operator()(char const *first, const char *last) const
+    {
+      if (!(var = find(vars, std::string(first, last).c_str()))) {
+        vars.add(first, last, T(0));
+        var = find(vars, std::string(first, last).c_str());
+      }
+      (pusher<T>(vs))(*var);
+    }
+    symbols<T> &vars;
+    T *&var;
+    std::stack<T> &vs;
+  };
+
+  template <typename T>
+  struct postfix_incrementer {
+    postfix_incrementer(T *&var_, std::stack<T> &vs_) : var(var_), vs(vs_) {}
+    void operator()(char const *, char const *) const
+    {
+      (popper<T>(vs))();
+      (pusher<T>(vs))((*var)++); 
+    }
+    T *&var;
+    std::stack<T> &vs;
+  };
+
+  template <typename T>
+  struct postfix_decrementer {
+    postfix_decrementer(T *&var_, std::stack<T> &vs_) : var(var_), vs(vs_) {}
+    void operator()(char const *, char const *) const
+    {
+      (popper<T>(vs))();
+      (pusher<T>(vs))((*var)--);
+    }
+    T *&var;
+    std::stack<T> &vs;
+  };
+
+  template <typename T>
+  struct prefix_incrementer {
+    prefix_incrementer(T *&var_, std::stack<T> &vs_) : var(var_), vs(vs_) {}
+    void operator()(char const *, char const *) const
+    {
+      (pusher<T>(vs))(++(*var));
+    }
+    T *&var;
+    std::stack<T> &vs;
+  };
+
+  template <typename T>
+  struct prefix_decrementer {
+    prefix_decrementer(T *&var_, std::stack<T> &vs_) : var(var_), vs(vs_) {}
+    void operator()(char const *first, char const *last) const
+    {
+      (pusher<T>(vs))(--(*var));
+    }
+    T *&var;
+    std::stack<T> &vs;
+  };
+
+  template <typename F, typename T>
+  struct unary_f_var_pusher {
+    unary_f_var_pusher(F const &f_, T *&var_, std::stack<T> &vs_) : f(f_), var(var_), vs(vs_) {}
+    void operator()(char const *, char const *) const
+    {
+      (swapper<T>(vs))((*var=f((top_popper<T>(vs))())));
+    }
+    F f;
+    T *&var;
+    std::stack<T> &vs;
+  };
+
+  template <class F, typename T>
+  unary_f_var_pusher<F, T> unary_f_assignor(F const &f, T *&var, std::stack<T> &vs)
+  {
+    return unary_f_var_pusher<F, T>(f, var, vs);
+  }
+
+  template <typename F, typename T>
+  struct binary_f_var_pusher {
+    binary_f_var_pusher(F const &f_, T *&var_, std::stack<T> &vs_) : f(f_), var(var_), vs(vs_) {}
+    void operator()(char const *, char const *) const
+    {
+      T rhs = (top_popper<T>(vs))();
+      (pusher<T>(vs))((*var=f((top_popper<T>(vs))(), rhs)));
+    }
+    F f;
+    T *&var;
+    std::stack<T> &vs;
+  };
+
+  template <class F, typename T>
+  binary_f_var_pusher<F, T> binary_f_assignor(F const &f, T *&var, std::stack<T> &vs)
+  {
+    return binary_f_var_pusher<F, T>(f, var, vs);
+  }
+
+
   template <typename T>
   struct calc : public grammar<calc<T> > {
-    calc(std::stack<T> &vs_) : vs(vs_) {}
+    calc(symbols<T> &vars_, std::stack<T> &vs_) : vars(vars_), vs(vs_) {}
     template <typename S>
     struct definition {
-      rule<S> expr, t[9], term, fctr;
+      rule<S> vars, e[7], expr, t[9], term, fctr;
       definition(calc const &self)
       {
+        vars = lexeme_d[(alpha_p >> *(alnum_p | '_'))][lvar_pusher<T>(self.vars, self.var, self.vs)];
+        expr = e[0] % ch_p(',')[popper<T>(self.vs)];
+        e[0] = e[1] | e[2] | e[3] | e[4] | e[5] | e[6];
+        e[1] = vars >> ('='     >> e[0][unary_f_assignor(std::identify<T>(), self.var, self.vs)]);
+        e[2] = vars >> ("+="    >> e[0][binary_f_assignor(std::plus<T>(), self.var, self.vs)] |
+                        "-="    >> e[0][binary_f_assignor(std::minus<T>(), self.var, self.vs)]);
+        e[3] = vars >> ("*="    >> e[0][binary_f_assignor(std::multiplies<T>(), self.var, self.vs)] |
+                        "/="    >> e[0][binary_f_assignor(std::divides<T>(), self.var, self.vs)] |
+                        "%="    >> e[0][binary_f_assignor(std::modulus<T>(), self.var, self.vs)]);
+        e[4] = vars >> ("<<="   >> e[0][binary_f_assignor(std::bitshift_left<T>(), self.var, self.vs)] |
+                        ">>="   >> e[0][binary_f_assignor(std::bitshift_right<T>(), self.var, self.vs)]);
+        e[5] = vars >> ("&="    >> e[0][binary_f_assignor(std::bitwise_and<T>(), self.var, self.vs)] |
+                        "^="    >> e[0][binary_f_assignor(std::bitwise_xor<T>(), self.var, self.vs)] |
+                        "|="    >> e[0][binary_f_assignor(std::bitwise_or<T>(), self.var, self.vs)]);
 #if !0 /* ternary operator */
-        expr = t[0] >> *('?'    >> expr >>
-                         ':'    >> expr)[ternary_condition_doer<T>(self.vs)];
+        e[6] = t[0] >> *('?'    >> e[0] >>
+                         ':'    >> e[0])[ternary_condition_doer<T>(self.vs)];
 #else
-        expr = t[0];
+        e[6] = t[0];
 #endif
         t[0] = t[1] >> *("||"   >> t[1][binary_f_doer(std::logical_or<T>(), self.vs)]);
         t[1] = t[2] >> *("&&"   >> t[2][binary_f_doer(std::logical_and<T>(), self.vs)]);
@@ -114,6 +235,11 @@
         fctr = "0x" >> hex_p[pusher<T>(self.vs)] |
           '0' >> oct_p[pusher<T>(self.vs)] |
           uint_p[pusher<T>(self.vs)] |
+          "++" >> vars[prefix_incrementer<T>(self.var, self.vs)] |
+          "--" >> vars[prefix_decrementer<T>(self.var, self.vs)] |
+          vars >> str_p("++")[postfix_incrementer<T>(self.var, self.vs)] |
+          vars >> str_p("--")[postfix_decrementer<T>(self.var, self.vs)] |
+          vars |
           '(' >> expr >> ')' |
           '-' >> fctr[unary_f_doer(std::negate<T>(), self.vs)] |
           '+' >> fctr |
@@ -122,6 +248,7 @@
       }
       rule<S> const &start() const { return expr; }
     };
+    symbols<T> &vars;
     mutable T *var;
     std::stack<T> &vs;
   };
@@ -132,8 +259,9 @@
   int rv = 0;
 
   typedef int value_type;
+  symbols<value_type> vars;
   std::stack<value_type> st;
-  client::calc<value_type> calc(st);
+  client::calc<value_type> calc(vars, st);
   std::string str;
   while (std::getline(std::cin, str)) {
     parse_info<> r = parse(str.c_str(), calc, space_p);

lvar_pusher 関数オブジェクトにより、変数の新規使用のときは symbols<value_type> vars に登録され、既に登録されている変数ならば symbols<value_type> vars がもつ数値が使用される。


C++ functional, Boost Bind and Boost Phoenix

by taiji at 2013/02/26 15:36

Boost Phoenix はとても便利ですが、もしその使用が許されない場面で標準の functional で代替する場合の対応表を演算子の優先順位で書いてみました。

演算子 Name functional Boost Phoenixでの例
. ドット(Element selection by reference) mem_fun_ref transform(a.begin(),a.end(),b.begin(),mem_fun_ref(&string::size)) bind(&string::size,_1)
-> アロー(Element selection through pointer) mem_fun transform(a.begin(),a.end(),b.begin(),mem_fun(&string::size)) bind(&string::size,_1)
- 単項負(Unary minus) negate<T> transform(a.begin(),a.end(),b.begin(),negate<int>()) -_1
& 単項アドレス(Address-of) ptr_fun transform(a.begin(),a.end(),b.begin(),ptr_fun(&atoi)) bind(&atoi,_1)
! 単項論理否定(Unary Logical NOT) not1 count_if(a.begin(),a.end(),not1(bind2nd(modulus<int>(),2))) !(_1 % 2)
! 単項論理否定(Unary Logical NOT) logical_not<T> transform(a.begin(),a.end(),b.begin(),logical_not<bool>()) !_1
~ 単項ビット否定(Unary Bitwise NOT) bitwise_not<T> transform(a.begin(),a.end(),b.begin(),bitwise_not<int>()) ~_1
* 二項乗(Multiplication) multiplies<T> transform(a.begin(),a.end(),b.begin(),c.begin(),multiplies<int>()) _1 * _2
/ 二項除(Division) divides<T> transform(a.begin(),a.end(),b.begin(),c.begin(),divides<int>()) _1 / _2
% 二項法(Modulus) modulus<T> transform(a.begin(),a.end(),b.begin(),c.begin(),modulus<int>()) _1 % _2
+ 二項加(Addition) plus<T> transform(a.begin(),a.end(),b.begin(),c.begin(),plus<int>()) _1 + _2
- 二項減(Subtraction) minus<T> transform(a.begin(),a.end(),b.begin(),c.begin(),minus<int>()) _1 - _2
<< ビット左シフト(Bitwise left shift) bitshift_left<T> transform(a.begin(),a.end(),b.begin(),c.begin(),bitshift_left<int>()) _1 << _2
>> ビット右シフト(Bitwise right shift) bitshift_right<T> transform(a.begin(),a.end(),b.begin(),c.begin(),bitshift_right<int>()) _1 >> _2
< 関係不等号(Relational "less than") less<T> sort(a.begin(),a.end(),less<int>()) _1 < _2
<= 関係不等号(Relational "less than or equal to") less_equal<T> count_if(a.begin(),a.end(),bind2nd(less_equal<int>(),4)) _1 <= 4
> 関係不等号(Relational "greater than") greater<T> sort(a.begin(),a.end(),greater<int>()) _1 > _2
>= 関係不等号(Relational "greater than or equal to") greater_equal<T> count_if(a.begin(),a.end(),bind2nd(greater_equal<int>(),4)) _1 >= 4
== 関係等号(Relational "equal to") equal_to<T> count_if(a.begin(),a.end(),bind2nd(equal_to<int>(),4)) _1 == 4
!= 関係不等号(Relational "not equal to") not_equal_to<T> count_if(a.begin(),a.end(),bind2nd(not_equal_to<int>(),4)) _1 != 4
!= 関係不等号(Relational "not equal to") not2 mismatch(a.begin(),a.end(),b.begin(),not2(equal_to<int>())) !(_1 == _2)
& ビットAND(Bitwise AND) bitwise_and<T> transform(a.begin(),a.end(),b.begin(),c.end(),bitwise_and<int>()) _1 & _2
^ ビットXOR(Bitwise XOR) bitwise_xor<T> transform(a.begin(),a.end(),b.begin(),c.end(),bitwise_xor<int>()) _1 ^ _2
| ビットOR(Bitwise OR) bitwise_or<T> transform(a.begin(),a.end(),b.begin(),c.end(),bitwise_or<int>()) _1 | _2
&& 論理AND(Logical AND) logical_and<T> transform(a.begin(),a.end(),b.begin(),c.end(),logical_and<bool>()) _1 && _2
|| 論理OR(Logical OR) logical_or<T> transform(a.begin(),a.end(),b.begin(),c.end(),logical_or<bool>()) _1 || _2

但し、表中のビット演算子は functional にはなぜか存在しませんので、more_functional で拵えました。

また、Boost Bind では、論理演算子については直感的に使えるようオーバーロードされているので、Boost Phoenix との対比を兼ねて表にしてみました。

演算子 Boost Bind Boost Phoenixでの例
. bind(〜,_1) transform(a.begin(),a.end(),b.begin(),bind(&string::size,_1)) bind(&string::size,_1)
-> bind(〜,_1) transform(a.begin(),a.end(),b.begin(),bind(&string::size,_1)) bind(&string::size,_1)
& bind(〜,_1) transform(a.begin(),a.end(),b.begin(),bind(&atoi,_1)) bind(&atoi,_1)
! !bind(〜) transform(a.begin(),a.end(),b.begin(),!bind(modulus<int>(),_1,2)) !(_1 % 2)
< bind(〜) < bind(〜) transform(a.begin(),a.end(),b.begin(),bind(modulus<int>(),_1,2) < bind(modulus<int>(),_1,3)) _1 % 2 < _1 % 3
<= bind(〜) <= bind(〜) transform(a.begin(),a.end(),b.begin(),bind(modulus<int>(),_1,2) <= bind(modulus<int>(),_1,3)) _1 % 2 <= _1 % 3
> bind(〜) > bind(〜) transform(a.begin(),a.end(),b.begin(),bind(modulus<int>(),_1,2) > bind(modulus<int>(),_1,3)) _1 % 2 > _1 % 3
>= bind(〜) >= bind(〜) transform(a.begin(),a.end(),b.begin(),bind(modulus<int>(),_1,2) >= bind(modulus<int>(),_1,3)) _1 % 2 >= _1 % 3
== bind(〜) == bind(〜) transform(a.begin(),a.end(),b.begin(),bind(modulus<int>(),_1,2) == bind(modulus<int>(),_1,3)) _1 % 2 == _1 % 3
!= bind(〜) != bind(〜) transform(a.begin(),a.end(),b.begin(),bind(modulus<int>(),_1,2) != bind(modulus<int>(),_1,3)) _1 % 2 != _1 % 3
&& bind(〜) && bind(〜) transform(a.begin(),a.end(),b.begin(),bind(modulus<int>(),_1,2) && bind(modulus<int>(),_1,3)) _1 % 2 && _1 % 3
|| bind(〜) || bind(〜) transform(a.begin(),a.end(),b.begin(),bind(modulus<int>(),_1,2) || bind(modulus<int>(),_1,3)) _1 % 2 || _1 % 3

さて、more_functional で拵えたビット演算子ですが、これが有用なのは Hacker's Delight 第2章 BASICS「Manipulating Rightmost Bits」で紹介されている公式を保持できるようになるからですが、それを Boost Phoenix & Bind に対比させた形で以下にまとめておきます。

formula Phoenix Boost Bind 説明
\(x \& x - 1\) _1 & _1-1 bind(bitwise_and<T>(),_1,bind(minus<T>(),_1,1)) 最右1のビットをオフにする
\(x | x + 1\) _1 | _1+1 bind(bitwise_or<T>(),_1,bind(plus<T>(),_1,1)) 最右0のビットをオンにする
\(x \& x + 1\) _1 & _1+1 bind(bitwise_and<T>(),_1,bind(plus<T>(),_1,1)) 最右0のビットと末尾まで続く1をオフにする
\(x | x - 1\) _1 | _1-1 bind(bitwise_or<T>(),_1,bind(minus<T>(),_1,1)) 最右1のビットを末尾まで満たす
\(¬x \& x + 1\) ~_1 & _1+1 bind(bitwise_and<T>(),bind(bitwise_not<T>(),_1),bind(plus<T>(),_1,1)) 最右0のビットを1のビットとして取り出す
\(¬x | x - 1\) ~1 | _1-1 bind(bitwise_or<T>(),bind(bitwise_not<T>(),_1),bind(minus<T>(),_1,1)) 最右1のビットを0のビットとして取り出す
\(¬x \& x - 1\) ~_1 & _1-1 bind(bitwise_and<T>(),bind(bitwise_not<T>(),_1),bind(minus<T>(),_1,1)) 末尾まで続く0を1のビット列として取り出す
\(¬x | x + 1\) ~_1 | _1+1 bind(bitwise_or<T>(),bind(bitwise_not<T>(),_1),bind(plus<T>(),_1,1)) 末尾まで続く1を0のビット列として取り出す
\(x \& -x\) _1 & -_1 bind(bitwise_and<T>(),_1,bind(negate<T>(),_1)) 最右1のビットを1のビットとして取り出す
\(x ⊕ x - 1\) _1 ^ _1-1 bind(bitwise_xor<T>(),_1,bind(minus<T>(),_1,1)) 最右1のビットと末尾まで続く0を1のビット列として取り出す
\(x ⊕ x + 1\) _1 ^ _1+1 bind(bitwise_xor<T>(),_1,bind(plus<T>(),_1,1)) 最右0のビットと末尾まで続く1を1ビット列として取り出す

つーか、記事の表組の中で MathJax+Unicode を試したかっただけですけど。


Boost Spirit V2 の演算子

by taiji at 2013/02/20 20:12

以前、Boost の Spirit (Classic) について書きましたが、新しい Boost Spirit V2 で使用されている演算子を優先順位でまとめてみました。

Spirit V2 Name EBNF SpiritのAttribute Description
&a And Predicate なし unused_type,A aにマッチする。
-a Optional なし optional<A> 0回または1回のマッチ
+a Plus a+ vector<A> 1回以上のマッチ
*a Kleene a* vector<A> 0回以上のマッチ
!a Not Predicate なし unused_type,A aにマッチしない。
~a Negation [^...] - C-aにマッチ(Cは文字集合全体)
a % b List a (b a)* vector<A> 1回以上の b 区切り a のリストのマッチ。a>>*(b>>a) と同義
a - b Difference a - b A a にマッチかつ b にマッチしない。
a >> b,a << b Sequence a b tupple<A,B> a,b の並びにマッチ
a > b Expect a b tupple<A,B> a,b の並びにマッチしなければ例外
a ^ b Permutation a - b | b - a tuple<optional<A>,optional<B> > a か b にマッチし両方にはマッチしない。a-b|b-a と同義
a | b Alternative a | b variant<A,B> a か b にマッチ
a || b Sequential Or (a b) | a | b,(a b?) | b tuple<optional<A>,optional<B> > a,b の並びもしくはどちらかにマッチ。a>>b|a|b,(a>>-b)|b と同義

&」Intersection,「&&」Sequential-and がなくなり「!」Optional が「-」に変わっているようです。


Tokyo Promenade meets MathJax

by taiji at 2013/02/17 14:32

Tokyo Promenade に MathJax を組み込んでみた。

この記事はその実験です。オイラーの公式を(ちょっと変えて)以下に記してみる。

\[e^{\imath \vartheta} = \cos(\vartheta) + \imath\sin(\vartheta)\]

おお!出来た!Javascript オフのときは TeX コマンドそのまま出力されるのもよいですね。

promenade.tmpl</head> 行が一箇所あるので、その直前に

<script type="text/javascript" src="http://cdn.mathjax.org/mathjax/latest/MathJax.js?config=TeX-AMS-MML_HTMLorMML"></script>

という行を挿入すればよい。

Tokyo Promenade 自体を改造して数式が必要なときのみ MathJax を使うようにすべきかもしれない。

それに Tokyo Promenade が数式として認識しているわけではないので、一行で書かないとうまくいかない。


ics-tools - iCalendar 形式のファイルのためのツール群

by taiji at 2011/12/08 19:23

Mac OS X の iCal.app で読み込める iCalendar 形式のファイル、例えば、http://www.aihara.co.jp/~taiji/Holidays.ja.ics を生成するために、以下のツール群を作成した。

このツール群には以下のプログラムが含まれる。

  • tsv2ics
  • ics2tsv
  • ics-view
  • ics-merge

tsv2ics - タブ区切りデータファイルから iCalendar 形式のファイルを生成する。

例えば、同梱されている「春分・秋分の日」に関する equin.tsv ファイルから equin.ics ファイルを生成するには、以下のように実行する。

$ tsv2ics --ical equin.tsv > equin.ics

タブ区切りデータファイルの形式は、他には次の ics2tsv が生成する形式などに対応する。

ics2tsv - iCalendar 形式のファイルからタブ区切りデータファイルを生成する。

例えば、同梱されている「国民の休日」に関する citholid.ics ファイルから citholid.tsv ファイルを生成するには、以下のように実行する。

$ ics2tsv citholid.ics > citholid.tsv

これは、デフォルトで VEVENT 構成要素のみを処理する。もし、VTODO 構成要素のみを処理したいなら「--todo」オプションを指定する。ちなみに、VALARM 構成要素や EXRULE 構成要素には対応していない。RRULE 構成要素に加えて、RDATE, EXDATE 構成要素には対応しているが、iCal は RDATE には対応していないようである。

ics-view - iCalendar 形式のファイルを less 等の PAGER で見やすくする。

例えば、以下のように実行する。

$ ics-view *.ics | less

ics-merge - 複数の iCalendar 形式のファイルを結合して、ひとつの iCalendar 形式のファイルを生成する。

例えば、http://www.asahi-net.or.jp/~ci5m-nmr/iCal/ で配布されている holid110.zip を展開して得られる複数の iCalendar 形式のファイルをひとつにするには以下のように実行する。

$ ics-merge holid110/*.ICS > holid110.ics

ついでに、繰り返し規則にて12ヶ月の整数倍は1ヶ年の整数倍に修正したり、終日を VALUE=DATE で修正したりする。ちなみに、VCALENDAR 構成要素の諸情報は最初の入力ファイルのみが使用される。

その他

http://www.asahi-net.or.jp/~ci5m-nmr/iCal/ で配布されている holid110.zip は2007年の法改正に対応しておらず、また equin201.zip の「秋分の日」の4年毎の1951年から1979年の日付が間違っているように思われる。よって、以下のパッチを作成した。

また、http://www.aihara.co.jp/~taiji/Holidays.ja.ics を生成するにあたり、iPhone の iOS におけるカレンダーでは終点をもつ長期の繰り返し規則が無視されることがあるので、「春分・秋分の日」はせいぜい2年先までとしている。さらに、「国民の休日」や「振替休日」など holid110.zip では対応していない祝祭日を含めた。