Boost Spirit V2+Universal Tree における計算機の例

ID: 24
creation date: 2013/04/04 12:54
modification date: 2013/04/04 12:54
owner: taiji
tags: C++, Boost, Spirit, Spirit V2, Spirit utree, AST

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

本稿では Spirit V2+Universal Tree での例を紹介していく。

Spirit V2+Universal Tree の基本形

Spirit V2+Universal Tree の基本形は以下のようになる。

#include <iostream>
#include <string>
//#define BOOST_SPIRIT_DEBUG
#include <boost/spirit/include/support_utree.hpp>
#include <boost/spirit/include/qi.hpp>

namespace qi = boost::spirit::qi;
namespace spirit = boost::spirit;

namespace client {
  template <typename Iterator>
  struct a_parser : qi::grammar<Iterator, spirit::utree()> {
    qi::rule<Iterator, spirit::utree()> top;
    a_parser() : a_parser::base_type(top)
    {
      top = *~qi::lit('\t'); // ここにパーサ規則を書く

      BOOST_SPIRIT_DEBUG_NODE(top);
    }
  };
}
int main()
{
  client::a_parser<std::string::const_iterator> a_parser;
  std::string str;
  while (std::getline(std::cin, str)) {
    std::string::const_iterator it = str.begin(), end = str.end();
    spirit::utree ut;
    bool r = phrase_parse(it, end, a_parser, qi::ascii::space, ut);
    if (r && it == end) {
      std::cout << "succeeded:\t" << ut << std::endl;
    }
    else {
      std::cout << "failed:\t" << std::string(it, end) << std::endl;
      return 1;
    }
  }
  return 0;
}

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

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

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

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

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

ここではセマンティックアクションは使わずに、Universal Treeを用いた AST(Abstract Syntax Tree) の実装を紹介する。

よって、変数のサポートがなされた次節の AST では以下のような代入と三項条件の組み合わせがサポートされる。

i<j ? i++ : i=j

iexpr+utree.cc

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

#include <libgen.h>
#include <iostream>
#include <iomanip>
#include <string>
#include <boost/spirit/include/support_utree.hpp>
#include <boost/spirit/include/phoenix_operator.hpp>
#include <boost/spirit/include/qi.hpp>

namespace client {
  namespace qi = boost::spirit::qi;
  namespace ascii = boost::spirit::ascii;
  namespace spirit = boost::spirit;
  namespace phoenix = boost::phoenix;

  template <typename T>
  struct unary_op {
    template <typename T1, typename T2 = void, typename T3 = void>
    struct result { typedef void type; };
    unary_op(unsigned int op) : op(op) {}
    void operator()(T &val, T const &rhs) const
    {
      val.clear();
      val.push_back(op);
      val.push_back(rhs);
    }
    unsigned int op;
  };
  template <typename T>
  struct binary_op : public unary_op<T> {
    binary_op(unsigned int op) : unary_op<T>(op) {}
    void operator()(T &val, T const &rhs) const
    {
      T lhs;
      lhs.swap(val);
      val.push_back(unary_op<T>::op);
      val.push_back(lhs);
      val.push_back(rhs);
    }
  };
  template <typename T>
  struct ternary_op : public unary_op<T> {
    ternary_op(unsigned int op) : unary_op<T>(op) {}
    void operator()(T &val, T const &a, T const &b) const
    {
      T c;
      c.swap(val);
      val.push_back(unary_op<T>::op);
      val.push_back(c);
      val.push_back(a);
      val.push_back(b);
    }
  };
  enum code_type {
    code_negate = 0,
    code_logical_not,
    code_bitwise_not,
    code_plus,
    code_minus,
    code_multiplies,
    code_divides,
    code_modulus,
    code_bitshift_left,
    code_bitshift_right,
    code_less,
    code_less_equal,
    code_greater,
    code_greater_equal,
    code_equal_to,
    code_not_equal_to,
    code_logical_or,
    code_logical_and,
    code_bitwise_or,
    code_bitwise_xor,
    code_bitwise_and,
    code_conditional,
  };
  phoenix::function<unary_op<spirit::utree> > const
    unary_negate(code_negate),
    unary_logical_not(code_logical_not),
    unary_bitwise_not(code_bitwise_not);
  phoenix::function<binary_op<spirit::utree> > const
    binary_plus(code_plus),
    binary_minus(code_minus),
    binary_multiplies(code_multiplies),
    binary_divides(code_divides),
    binary_modulus(code_modulus),
    binary_bitshift_left(code_bitshift_left),
    binary_bitshift_right(code_bitshift_right),
    binary_less(code_less),
    binary_less_equal(code_less_equal),
    binary_greater(code_greater),
    binary_greater_equal(code_greater_equal),
    binary_equal_to(code_equal_to),
    binary_not_equal_to(code_not_equal_to),
    binary_logical_or(code_logical_or),
    binary_logical_and(code_logical_and),
    binary_bitwise_or(code_bitwise_or),
    binary_bitwise_xor(code_bitwise_xor),
    binary_bitwise_and(code_bitwise_and);
  phoenix::function<ternary_op<spirit::utree> > const
    ternary_conditional(code_conditional);

  template <typename Iterator, typename T>
  struct calculator : qi::grammar<Iterator, ascii::space_type, T()> {
    qi::rule<Iterator, ascii::space_type, T()> expr, t[9], term, fctr;
    calculator() : calculator::base_type(expr)
    {
      using qi::uint_;
      using qi::_val;
      using qi::_1;
      using qi::_2;
      using qi::hex;
      using qi::oct;

      expr = t[0][_val = _1] >> *('?' >> expr >>
                                  ':' >> expr)[ternary_conditional(_val, _1, _2)];
      t[0] = t[1][_val = _1] >> *("||"  >> t[1][binary_logical_or(_val, _1)]);
      t[1] = t[2][_val = _1] >> *("&&"  >> t[2][binary_logical_and(_val, _1)]);
      t[2] = t[3][_val = _1] >> *('|'   >> t[3][binary_bitwise_or(_val, _1)]);
      t[3] = t[4][_val = _1] >> *('^'   >> t[4][binary_bitwise_xor(_val, _1)]);
      t[4] = t[5][_val = _1] >> *('&'   >> t[5][binary_bitwise_and(_val, _1)]);
      t[5] = t[6][_val = _1] >> *("<<"  >> t[6][binary_bitshift_left(_val, _1)] |
                                  ">>"  >> t[6][binary_bitshift_right(_val, _1)]);
      t[6] = t[7][_val = _1] >> *('<'   >> t[7][binary_less(_val, _1)] |
                                  '>'   >> t[7][binary_greater(_val, _1)] |
                                  "<="  >> t[7][binary_less_equal(_val, _1)] |
                                  "<="  >> t[7][binary_greater_equal(_val, _1)]);
      t[7] = t[8][_val = _1] >> *("=="  >> t[8][binary_equal_to(_val, _1)] |
                                  "!="  >> t[8][binary_not_equal_to(_val, _1)]);
      t[8] = term[_val = _1] >> *('+'   >> term[binary_plus(_val, _1)] |
                                  '-'   >> term[binary_minus(_val, _1)]);
      term = fctr[_val = _1] >> *('*'   >> fctr[binary_multiplies(_val, _1)] |
                                  '/'   >> fctr[binary_divides(_val, _1)] |
                                  '%'   >> fctr[binary_modulus(_val, _1)]);
      fctr = "0x" >> hex[_val = _1] |
        '0' >> oct[_val = _1] |
        uint_[_val = _1] |
        '(' >> expr[_val = _1] >> ')' |
        '-' >> fctr[unary_negate(_val, _1)] |
        '+' >> fctr[_val = _1] |
        '!' >> fctr[unary_logical_not(_val, _1)] |
        '~' >> fctr[unary_bitwise_not(_val, _1)];
    }
  };
  template <typename T>
  struct calculator_utree_machine {
    calculator_utree_machine() {}
    T execute(const spirit::utree &ut) const
    {
      if (ut.which() != spirit::utree_type::list_type)
        return ut.get<T>();
      typename spirit::utree::const_iterator it=ut.begin();
      switch (it->get<T>()) {
      case code_negate:
        return -execute(*++it);
      case code_logical_not:
        return !execute(*++it);
      case code_bitwise_not:
        return ~execute(*++it);
      case code_plus:
        return execute(*++it) + execute(*++it);
      case code_minus:
        return execute(*++it) - execute(*++it);
      case code_multiplies:
        return execute(*++it) * execute(*++it);
      case code_divides:
        return execute(*++it) / execute(*++it);
      case code_modulus:
        return execute(*++it) % execute(*++it);
      case code_bitshift_left:
        return execute(*++it) << execute(*++it);
      case code_bitshift_right:
        return execute(*++it) >> execute(*++it);
      case code_less:
        return execute(*++it) < execute(*++it);
      case code_less_equal:
        return execute(*++it) <= execute(*++it);
      case code_greater:
        return execute(*++it) > execute(*++it);
      case code_greater_equal:
        return execute(*++it) >= execute(*++it);
      case code_equal_to:
        return execute(*++it) == execute(*++it);
      case code_not_equal_to:
        return execute(*++it) != execute(*++it);
      case code_logical_or:
        return execute(*++it) || execute(*++it);
      case code_logical_and:
        return execute(*++it) && execute(*++it);
      case code_bitwise_or:
        return execute(*++it) | execute(*++it);
      case code_bitwise_xor:
        return execute(*++it) ^ execute(*++it);
      case code_bitwise_and:
        return execute(*++it) & execute(*++it);
      case code_conditional: default:
        if (!execute(*++it)) ++it;
        return execute(*++it);
      }
    }
  };
}

int main(int argc, char *argv[])
{
  int rv = 0;

  typedef std::string::const_iterator iterator_type;
  typedef int value_type;
  client::calculator<iterator_type, boost::spirit::utree> calc;
  std::string str;
  while (std::getline(std::cin, str)) {
    iterator_type it = str.begin(), end = str.end();
    boost::spirit::utree ut;
    bool r = phrase_parse(it, end, calc, boost::spirit::ascii::space, ut);
    client::calculator_utree_machine<value_type> vmachine;
    if (r && it == end) {
      std::cout << "succeeded:\t" << ut << std::endl;
      std::cout << vmachine.execute(ut) << std::endl;
    }
    else {
      std::cout << "failed:\t" << std::string(it, end) << std::endl;
      rv = !0;
    }
  }
  return rv;
}

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

クラス utree が使用され、その内部のリスト構造によって AST が構成されていることがわかるだろう。そしてその AST を calculator_utree_machine::execute によって再帰的に演算している。

icalc+utree.cc

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

#include <libgen.h>
#include <iostream>
#include <iomanip>
#include <string>
#include <map>
#include <boost/spirit/include/support_utree.hpp>
#include <boost/spirit/include/phoenix_operator.hpp>
#include <boost/spirit/include/qi.hpp>

namespace client {
  namespace qi = boost::spirit::qi;
  namespace ascii = boost::spirit::ascii;
  namespace spirit = boost::spirit;
  namespace phoenix = boost::phoenix;

  template <typename T>
  struct unary_op {
    template <typename T1, typename T2 = void, typename T3 = void>
    struct result { typedef void type; };
    unary_op(unsigned int op) : op(op) {}
    void operator()(T &val, T const &rhs) const
    {
      val.clear();
      val.push_back(op);
      val.push_back(rhs);
    }
    unsigned int op;
  };
  template <typename T>
  struct binary_op : public unary_op<T> {
    binary_op(unsigned int op) : unary_op<T>(op) {}
    void operator()(T &val, T const &rhs) const
    {
      T lhs;
      lhs.swap(val);
      val.push_back(unary_op<T>::op);
      val.push_back(lhs);
      val.push_back(rhs);
    }
  };
  template <typename T>
  struct ternary_op : public unary_op<T> {
    ternary_op(unsigned int op) : unary_op<T>(op) {}
    void operator()(T &val, T const &a, T const &b) const
    {
      T c;
      c.swap(val);
      val.push_back(unary_op<T>::op);
      val.push_back(c);
      val.push_back(a);
      val.push_back(b);
    }
  };
  enum code_type {
    code_negate = 0,
    code_logical_not,
    code_bitwise_not,
    code_plus,
    code_minus,
    code_multiplies,
    code_divides,
    code_modulus,
    code_bitshift_left,
    code_bitshift_right,
    code_less,
    code_less_equal,
    code_greater,
    code_greater_equal,
    code_equal_to,
    code_not_equal_to,
    code_logical_or,
    code_logical_and,
    code_bitwise_or,
    code_bitwise_xor,
    code_bitwise_and,
    code_assignment_direct,
    code_assignment_summation,
    code_assignment_difference,
    code_assignment_product,
    code_assignment_quotient,
    code_assignment_remainder,
    code_assignment_bitshift_left,
    code_assignment_bitshift_right, 
    code_assignment_bitwise_and,
    code_assignment_bitwise_xor,
    code_assignment_bitwise_or,
    code_prefix_increment,
    code_prefix_decrement,
    code_postfix_increment,
    code_postfix_decrement,
    code_conditional,
    code_binary_comma,
  };
  phoenix::function<unary_op<spirit::utree> > const
    unary_negate(code_negate),
    unary_logical_not(code_logical_not),
    unary_bitwise_not(code_bitwise_not);
  phoenix::function<binary_op<spirit::utree> > const
    binary_plus(code_plus),
    binary_minus(code_minus),
    binary_multiplies(code_multiplies),
    binary_divides(code_divides),
    binary_modulus(code_modulus),
    binary_bitshift_left(code_bitshift_left),
    binary_bitshift_right(code_bitshift_right),
    binary_less(code_less),
    binary_less_equal(code_less_equal),
    binary_greater(code_greater),
    binary_greater_equal(code_greater_equal),
    binary_equal_to(code_equal_to),
    binary_not_equal_to(code_not_equal_to),
    binary_logical_or(code_logical_or),
    binary_logical_and(code_logical_and),
    binary_bitwise_or(code_bitwise_or),
    binary_bitwise_xor(code_bitwise_xor),
    binary_bitwise_and(code_bitwise_and);
  phoenix::function<ternary_op<spirit::utree> > const
    ternary_conditional(code_conditional);
  phoenix::function<binary_op<spirit::utree> > const
    assignment_direct(code_assignment_direct),
    assignment_summation(code_assignment_summation),
    assignment_difference(code_assignment_difference),
    assignment_product(code_assignment_product),
    assignment_quotient(code_assignment_quotient),
    assignment_remainder(code_assignment_remainder),
    assignment_bitshift_left(code_assignment_bitshift_left),
    assignment_bitshift_right(code_assignment_bitshift_right), 
    assignment_bitwise_and(code_assignment_bitwise_and),
    assignment_bitwise_xor(code_assignment_bitwise_xor),
    assignment_bitwise_or(code_assignment_bitwise_or),
    binary_comma(code_binary_comma);
  phoenix::function<unary_op<spirit::utree> > const
    prefix_increment(code_prefix_increment),
    prefix_decrement(code_prefix_decrement),
    postfix_increment(code_postfix_increment),
    postfix_decrement(code_postfix_decrement);

  template <typename Iterator, typename T>
  struct calculator : qi::grammar<Iterator, ascii::space_type, T()> {
    qi::rule<Iterator, ascii::space_type, T()> vars, e[7], expr, t[9], term, fctr;
    calculator() : calculator::base_type(expr)
    {
      using qi::uint_;
      using qi::_val;
      using qi::_1;
      using qi::_2;
      using qi::hex;
      using qi::oct;
      using qi::alpha;
      using qi::alnum;
      using qi::lexeme;
      using qi::as_string;

      vars = as_string[lexeme[(alpha >> *(alnum | '_'))]];
      expr = e[0][_val = _1] >> *(','   >> e[0][binary_comma(_val, _1)]);
      e[0] = e[1] | e[2] | e[3] | e[4] | e[5] | e[6];
      e[1] = vars[_val = _1] >> ('='    >> e[0][assignment_direct(_val, _1)]);
      e[2] = vars[_val = _1] >> ("+="   >> e[0][assignment_summation(_val, _1)] |
                                 "-="   >> e[0][assignment_difference(_val, _1)]);
      e[3] = vars[_val = _1] >> ("*="   >> e[0][assignment_product(_val, _1)] |
                                 "/="   >> e[0][assignment_quotient(_val, _1)] |
                                 "%="   >> e[0][assignment_remainder(_val, _1)]);
      e[4] = vars[_val = _1] >> ("<<="  >> e[0][assignment_bitshift_left(_val, _1)] |
                                 ">>="  >> e[0][assignment_bitshift_right(_val, _1)]);
      e[5] = vars[_val = _1] >> ("&="   >> e[0][assignment_bitwise_and(_val, _1)] |
                                 "^="   >> e[0][assignment_bitwise_xor(_val, _1)] |
                                 "|="   >> e[0][assignment_bitwise_or(_val, _1)]);
      e[6] = t[0][_val = _1] >> *('?' >> e[0] >>
                                  ':' >> e[0])[ternary_conditional(_val, _1, _2)];
      t[0] = t[1][_val = _1] >> *("||"  >> t[1][binary_logical_or(_val, _1)]);
      t[1] = t[2][_val = _1] >> *("&&"  >> t[2][binary_logical_and(_val, _1)]);
      t[2] = t[3][_val = _1] >> *('|'   >> t[3][binary_bitwise_or(_val, _1)]);
      t[3] = t[4][_val = _1] >> *('^'   >> t[4][binary_bitwise_xor(_val, _1)]);
      t[4] = t[5][_val = _1] >> *('&'   >> t[5][binary_bitwise_and(_val, _1)]);
      t[5] = t[6][_val = _1] >> *("<<"  >> t[6][binary_bitshift_left(_val, _1)] |
                                  ">>"  >> t[6][binary_bitshift_right(_val, _1)]);
      t[6] = t[7][_val = _1] >> *('<'   >> t[7][binary_less(_val, _1)] |
                                  '>'   >> t[7][binary_greater(_val, _1)] |
                                  "<="  >> t[7][binary_less_equal(_val, _1)] |
                                  "<="  >> t[7][binary_greater_equal(_val, _1)]);
      t[7] = t[8][_val = _1] >> *("=="  >> t[8][binary_equal_to(_val, _1)] |
                                  "!="  >> t[8][binary_not_equal_to(_val, _1)]);
      t[8] = term[_val = _1] >> *('+'   >> term[binary_plus(_val, _1)] |
                                  '-'   >> term[binary_minus(_val, _1)]);
      term = fctr[_val = _1] >> *('*'   >> fctr[binary_multiplies(_val, _1)] |
                                  '/'   >> fctr[binary_divides(_val, _1)] |
                                  '%'   >> fctr[binary_modulus(_val, _1)]);
      fctr = "0x" >> hex[_val = _1] |
        '0' >> oct[_val = _1] |
        uint_[_val = _1] |
        "++" >> vars[prefix_increment(_val, _1)] |
        "--" >> vars[prefix_decrement(_val, _1)] |
        vars[postfix_increment(_val, _1)] >> "++" |
        vars[postfix_decrement(_val, _1)] >> "--" |
        vars[_val = _1] |
        '(' >> expr[_val = _1] >> ')' |
        '-' >> fctr[unary_negate(_val, _1)] |
        '+' >> fctr[_val = _1] |
        '!' >> fctr[unary_logical_not(_val, _1)] |
        '~' >> fctr[unary_bitwise_not(_val, _1)];
    }
  };
  template <typename T>
  struct calculator_utree_machine {
    std::map<std::string, T> &vars;
    calculator_utree_machine(std::map<std::string, T> &vars) : vars(vars) {}
    T execute(const spirit::utree &ut)
    {
      spirit::utf8_string_range_type r;
      if (ut.which() != spirit::utree_type::list_type) {
        if (ut.which() != spirit::utree_type::int_type) {
          r = ut.get<spirit::utf8_string_range_type>();
          if (vars.find(std::string(r.begin(), r.end())) == vars.end())
            vars[std::string(r.begin(), r.end())] = 0;
          return vars[std::string(r.begin(), r.end())];
        }
        return ut.get<T>();
      }
      typename spirit::utree::const_iterator it=ut.begin();
      switch (it->get<T>()) {
      case code_negate:
        return -execute(*++it);
      case code_logical_not:
        return !execute(*++it);
      case code_bitwise_not:
        return ~execute(*++it);
      case code_plus:
        return execute(*++it) + execute(*++it);
      case code_minus:
        return execute(*++it) - execute(*++it);
      case code_multiplies:
        return execute(*++it) * execute(*++it);
      case code_divides:
        return execute(*++it) / execute(*++it);
      case code_modulus:
        return execute(*++it) % execute(*++it);
      case code_bitshift_left:
        return execute(*++it) << execute(*++it);
      case code_bitshift_right:
        return execute(*++it) >> execute(*++it);
      case code_less:
        return execute(*++it) < execute(*++it);
      case code_less_equal:
        return execute(*++it) <= execute(*++it);
      case code_greater:
        return execute(*++it) > execute(*++it);
      case code_greater_equal:
        return execute(*++it) >= execute(*++it);
      case code_equal_to:
        return execute(*++it) == execute(*++it);
      case code_not_equal_to:
        return execute(*++it) != execute(*++it);
      case code_logical_or:
        return execute(*++it) || execute(*++it);
      case code_logical_and:
        return execute(*++it) && execute(*++it);
      case code_bitwise_or:
        return execute(*++it) | execute(*++it);
      case code_bitwise_xor:
        return execute(*++it) ^ execute(*++it);
      case code_bitwise_and:
        return execute(*++it) & execute(*++it);
      case code_assignment_direct:
        r = (*++it).get<spirit::utf8_string_range_type>();
        return (vars[std::string(r.begin(), r.end())] = execute(*++it));
      case code_assignment_summation:
        r = (*++it).get<spirit::utf8_string_range_type>();
        return (vars[std::string(r.begin(), r.end())] += execute(*++it));
      case code_assignment_difference:
        r = (*++it).get<spirit::utf8_string_range_type>();
        return (vars[std::string(r.begin(), r.end())] -= execute(*++it));
      case code_assignment_product:
        r = (*++it).get<spirit::utf8_string_range_type>();
        return (vars[std::string(r.begin(), r.end())] *= execute(*++it));
      case code_assignment_quotient:
        r = (*++it).get<spirit::utf8_string_range_type>();
        return (vars[std::string(r.begin(), r.end())] /= execute(*++it));
      case code_assignment_remainder:
        r = (*++it).get<spirit::utf8_string_range_type>();
        return (vars[std::string(r.begin(), r.end())] %= execute(*++it));
      case code_assignment_bitshift_left:
        r = (*++it).get<spirit::utf8_string_range_type>();
        return (vars[std::string(r.begin(), r.end())] <<= execute(*++it));
      case code_assignment_bitshift_right:
        r = (*++it).get<spirit::utf8_string_range_type>();
        return (vars[std::string(r.begin(), r.end())] >>= execute(*++it));
      case code_assignment_bitwise_and:
        r = (*++it).get<spirit::utf8_string_range_type>();
        return (vars[std::string(r.begin(), r.end())] &= execute(*++it));
      case code_assignment_bitwise_xor:
        r = (*++it).get<spirit::utf8_string_range_type>();
        return (vars[std::string(r.begin(), r.end())] ^= execute(*++it));
      case code_assignment_bitwise_or:
        r = (*++it).get<spirit::utf8_string_range_type>();
        return (vars[std::string(r.begin(), r.end())] |= execute(*++it));
      case code_prefix_increment:
        r = (*++it).get<spirit::utf8_string_range_type>();
        return ++vars[std::string(r.begin(), r.end())];
      case code_prefix_decrement:
        r = (*++it).get<spirit::utf8_string_range_type>();
        return --vars[std::string(r.begin(), r.end())];
      case code_postfix_increment:
        r = (*++it).get<spirit::utf8_string_range_type>();
        return vars[std::string(r.begin(), r.end())]++;
      case code_postfix_decrement:
        r = (*++it).get<spirit::utf8_string_range_type>();
        return vars[std::string(r.begin(), r.end())]--;
      case code_binary_comma:
        return execute(*++it), execute(*++it);
      case code_conditional: default:
        if (!execute(*++it)) ++it;
        return execute(*++it);
      }
    }
  };
}

int main(int argc, char *argv[])
{
  int rv = 0;

  typedef std::string::const_iterator iterator_type;
  typedef int value_type;
  client::calculator<iterator_type, boost::spirit::utree> calc;
  std::map<std::string, value_type> vars;
  std::string str;
  while (std::getline(std::cin, str)) {
    iterator_type it = str.begin(), end = str.end();
    boost::spirit::utree ut;
    bool r = phrase_parse(it, end, calc, boost::spirit::ascii::space, ut);
    client::calculator_utree_machine<value_type> vmachine(vars);
    if (r && it == end) {
      std::cout << "succeeded:\t" << ut << std::endl;
      std::cout << vmachine.execute(ut) << std::endl;
    }
    else {
      std::cout << "failed:\t" << std::string(it, end) << std::endl;
      rv = !0;
    }
  }
  return rv;
}

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

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

diff -u iexpr+utree.cc icalc+utree.cc

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

--- iexpr+utree.cc
+++ icalc+utree.cc
@@ -2,6 +2,7 @@
 #include <iostream>
 #include <iomanip>
 #include <string>
+#include <map>
 #include <boost/spirit/include/support_utree.hpp>
 #include <boost/spirit/include/phoenix_operator.hpp>
 #include <boost/spirit/include/qi.hpp>
@@ -72,7 +73,23 @@
     code_bitwise_or,
     code_bitwise_xor,
     code_bitwise_and,
+    code_assignment_direct,
+    code_assignment_summation,
+    code_assignment_difference,
+    code_assignment_product,
+    code_assignment_quotient,
+    code_assignment_remainder,
+    code_assignment_bitshift_left,
+    code_assignment_bitshift_right, 
+    code_assignment_bitwise_and,
+    code_assignment_bitwise_xor,
+    code_assignment_bitwise_or,
+    code_prefix_increment,
+    code_prefix_decrement,
+    code_postfix_increment,
+    code_postfix_decrement,
     code_conditional,
+    code_binary_comma,
   };
   phoenix::function<unary_op<spirit::utree> > const
   unary_negate(code_negate),
@@ -99,10 +116,28 @@
     binary_bitwise_and(code_bitwise_and);
   phoenix::function<ternary_op<spirit::utree> > const
   ternary_conditional(code_conditional);
+  phoenix::function<binary_op<spirit::utree> > const
+    assignment_direct(code_assignment_direct),
+    assignment_summation(code_assignment_summation),
+    assignment_difference(code_assignment_difference),
+    assignment_product(code_assignment_product),
+    assignment_quotient(code_assignment_quotient),
+    assignment_remainder(code_assignment_remainder),
+    assignment_bitshift_left(code_assignment_bitshift_left),
+    assignment_bitshift_right(code_assignment_bitshift_right), 
+    assignment_bitwise_and(code_assignment_bitwise_and),
+    assignment_bitwise_xor(code_assignment_bitwise_xor),
+    assignment_bitwise_or(code_assignment_bitwise_or),
+    binary_comma(code_binary_comma);
+  phoenix::function<unary_op<spirit::utree> > const
+    prefix_increment(code_prefix_increment),
+    prefix_decrement(code_prefix_decrement),
+    postfix_increment(code_postfix_increment),
+    postfix_decrement(code_postfix_decrement);
 
   template <typename Iterator, typename T>
   struct calculator : qi::grammar<Iterator, ascii::space_type, T()> {
-    qi::rule<Iterator, ascii::space_type, T()> expr, t[9], term, fctr;
+    qi::rule<Iterator, ascii::space_type, T()> vars, e[7], expr, t[9], term, fctr;
     calculator() : calculator::base_type(expr)
     {
       using qi::uint_;
@@ -111,9 +146,27 @@
       using qi::_2;
       using qi::hex;
       using qi::oct;
+      using qi::alpha;
+      using qi::alnum;
+      using qi::lexeme;
+      using qi::as_string;
 
-      expr = t[0][_val = _1] >> *('?' >> expr >>
-                                  ':' >> expr)[ternary_conditional(_val, _1, _2)];
+      vars = as_string[lexeme[(alpha >> *(alnum | '_'))]];
+      expr = e[0][_val = _1] >> *(','   >> e[0][binary_comma(_val, _1)]);
+      e[0] = e[1] | e[2] | e[3] | e[4] | e[5] | e[6];
+      e[1] = vars[_val = _1] >> ('='    >> e[0][assignment_direct(_val, _1)]);
+      e[2] = vars[_val = _1] >> ("+="   >> e[0][assignment_summation(_val, _1)] |
+                                 "-="   >> e[0][assignment_difference(_val, _1)]);
+      e[3] = vars[_val = _1] >> ("*="   >> e[0][assignment_product(_val, _1)] |
+                                 "/="   >> e[0][assignment_quotient(_val, _1)] |
+                                 "%="   >> e[0][assignment_remainder(_val, _1)]);
+      e[4] = vars[_val = _1] >> ("<<="  >> e[0][assignment_bitshift_left(_val, _1)] |
+                                 ">>="  >> e[0][assignment_bitshift_right(_val, _1)]);
+      e[5] = vars[_val = _1] >> ("&="   >> e[0][assignment_bitwise_and(_val, _1)] |
+                                 "^="   >> e[0][assignment_bitwise_xor(_val, _1)] |
+                                 "|="   >> e[0][assignment_bitwise_or(_val, _1)]);
+      e[6] = t[0][_val = _1] >> *('?' >> e[0] >>
+                                  ':' >> e[0])[ternary_conditional(_val, _1, _2)];
       t[0] = t[1][_val = _1] >> *("||"  >> t[1][binary_logical_or(_val, _1)]);
       t[1] = t[2][_val = _1] >> *("&&"  >> t[2][binary_logical_and(_val, _1)]);
       t[2] = t[3][_val = _1] >> *('|'   >> t[3][binary_bitwise_or(_val, _1)]);
@@ -135,6 +188,11 @@
       fctr = "0x" >> hex[_val = _1] |
         '0' >> oct[_val = _1] |
         uint_[_val = _1] |
+        "++" >> vars[prefix_increment(_val, _1)] |
+        "--" >> vars[prefix_decrement(_val, _1)] |
+        vars[postfix_increment(_val, _1)] >> "++" |
+        vars[postfix_decrement(_val, _1)] >> "--" |
+        vars[_val = _1] |
         '(' >> expr[_val = _1] >> ')' |
         '-' >> fctr[unary_negate(_val, _1)] |
         '+' >> fctr[_val = _1] |
@@ -144,11 +202,20 @@
   };
   template <typename T>
   struct calculator_utree_machine {
-    calculator_utree_machine() {}
-    T execute(const spirit::utree &ut) const
+    std::map<std::string, T> &vars;
+    calculator_utree_machine(std::map<std::string, T> &vars) : vars(vars) {}
+    T execute(const spirit::utree &ut)
     {
-      if (ut.which() != spirit::utree_type::list_type)
+      spirit::utf8_string_range_type r;
+      if (ut.which() != spirit::utree_type::list_type) {
+        if (ut.which() != spirit::utree_type::int_type) {
+          r = ut.get<spirit::utf8_string_range_type>();
+          if (vars.find(std::string(r.begin(), r.end())) == vars.end())
+            vars[std::string(r.begin(), r.end())] = 0;
+          return vars[std::string(r.begin(), r.end())];
+        }
         return ut.get<T>();
+      }
       typename spirit::utree::const_iterator it=ut.begin();
       switch (it->get<T>()) {
       case code_negate:
@@ -193,6 +260,53 @@
         return execute(*++it) ^ execute(*++it);
       case code_bitwise_and:
         return execute(*++it) & execute(*++it);
+      case code_assignment_direct:
+        r = (*++it).get<spirit::utf8_string_range_type>();
+        return (vars[std::string(r.begin(), r.end())] = execute(*++it));
+      case code_assignment_summation:
+        r = (*++it).get<spirit::utf8_string_range_type>();
+        return (vars[std::string(r.begin(), r.end())] += execute(*++it));
+      case code_assignment_difference:
+        r = (*++it).get<spirit::utf8_string_range_type>();
+        return (vars[std::string(r.begin(), r.end())] -= execute(*++it));
+      case code_assignment_product:
+        r = (*++it).get<spirit::utf8_string_range_type>();
+        return (vars[std::string(r.begin(), r.end())] *= execute(*++it));
+      case code_assignment_quotient:
+        r = (*++it).get<spirit::utf8_string_range_type>();
+        return (vars[std::string(r.begin(), r.end())] /= execute(*++it));
+      case code_assignment_remainder:
+        r = (*++it).get<spirit::utf8_string_range_type>();
+        return (vars[std::string(r.begin(), r.end())] %= execute(*++it));
+      case code_assignment_bitshift_left:
+        r = (*++it).get<spirit::utf8_string_range_type>();
+        return (vars[std::string(r.begin(), r.end())] <<= execute(*++it));
+      case code_assignment_bitshift_right:
+        r = (*++it).get<spirit::utf8_string_range_type>();
+        return (vars[std::string(r.begin(), r.end())] >>= execute(*++it));
+      case code_assignment_bitwise_and:
+        r = (*++it).get<spirit::utf8_string_range_type>();
+        return (vars[std::string(r.begin(), r.end())] &= execute(*++it));
+      case code_assignment_bitwise_xor:
+        r = (*++it).get<spirit::utf8_string_range_type>();
+        return (vars[std::string(r.begin(), r.end())] ^= execute(*++it));
+      case code_assignment_bitwise_or:
+        r = (*++it).get<spirit::utf8_string_range_type>();
+        return (vars[std::string(r.begin(), r.end())] |= execute(*++it));
+      case code_prefix_increment:
+        r = (*++it).get<spirit::utf8_string_range_type>();
+        return ++vars[std::string(r.begin(), r.end())];
+      case code_prefix_decrement:
+        r = (*++it).get<spirit::utf8_string_range_type>();
+        return --vars[std::string(r.begin(), r.end())];
+      case code_postfix_increment:
+        r = (*++it).get<spirit::utf8_string_range_type>();
+        return vars[std::string(r.begin(), r.end())]++;
+      case code_postfix_decrement:
+        r = (*++it).get<spirit::utf8_string_range_type>();
+        return vars[std::string(r.begin(), r.end())]--;
+      case code_binary_comma:
+        return execute(*++it), execute(*++it);
       case code_conditional: default:
         if (!execute(*++it)) ++it;
         return execute(*++it);
@@ -208,12 +322,13 @@
   typedef std::string::const_iterator iterator_type;
   typedef int value_type;
   client::calculator<iterator_type, boost::spirit::utree> calc;
+  std::map<std::string, value_type> vars;
   std::string str;
   while (std::getline(std::cin, str)) {
     iterator_type it = str.begin(), end = str.end();
     boost::spirit::utree ut;
     bool r = phrase_parse(it, end, calc, boost::spirit::ascii::space, ut);
-    client::calculator_utree_machine<value_type> vmachine;
+    client::calculator_utree_machine<value_type> vmachine(vars);
     if (r && it == end) {
       std::cout << "succeeded:\t" << ut << std::endl;
       std::cout << vmachine.execute(ut) << std::endl;

変数のサポートのために、std::map<std::string, value_type> vars が導入され、クラス utreespirit::utf8_string_range_type 型として変数が格納されていることがわかるだろう。

0 コメント
ゲストコメント認証用なぞなぞ:
キーボードのLから左に全部打って下さい。それを二回やって下さい。 ...