Hatena::ブログ(Diary)

菊やんの雑記帳

2009-09-22 C++で再帰下降パーザつづき

[] 連休に頑張って書いたコード 11:03  連休に頑張って書いたコード - 菊やんの雑記帳 を含むブックマーク

http://github.com/kik/cpppeg に置いた。

http://wiki.github.com/kik/cpppeg/usage 説明はここ。

まだ色々と不完全

[] 大量の意味不明なエラーメッセージと格闘した結果 04:20  大量の意味不明なエラーメッセージと格闘した結果 - 菊やんの雑記帳 を含むブックマーク

今回のターゲット文法は


epxr ::= int_lit
       | '(' op expr expr ')'

int_lit ::= token(int_lit_s)
op ::= token(op_s)

int_lit_s ::= digit digit*
op ::= alpha alpha*

token(T) ::= T ' '*

token(T)はジェネリックなルールであり、Tに対してTの後ろの空白文字を食ってしまうルール。

パーザのソース

#include <iostream>
#include <string>
#include <boost/lexical_cast.hpp>
#include <boost/function.hpp>
#include "peg.hpp"

using namespace peg;
using namespace std;

template<class T>
struct token : public T
{
  void action(const T& t, const rep<peg::ws>&) {
    *static_cast<T*>(this) = t;
  }
};

struct int_lit_s
{
  int v;
  void action(digit x, const rep<digit>& y) {
    string s;
    s += x;
    s.append(y.begin(), y.end());
    v = boost::lexical_cast<int>(s);
  }
};

typedef token<int_lit_s> int_lit;

struct op_s
{
  boost::function<int(int,int)> v;
  
  void action(alpha x, const rep<alpha>& name) {
    string s;
    s += x;
    s.append(name.begin(), name.end());
    if (s == "add") {
      v = std::plus<int>();
    } else {
      v = std::minus<int>();
    }
  }
};

typedef token<op_s> op;
typedef token<ch<'('>::type> open_paren;
typedef token<ch<')'>::type> close_paren;

struct expr
{
  int v;
  void action(int_lit e, sor orr,
              open_paren, op o, const expr& e1, const expr& e2, close_paren) {
    if (orr.left) {
      v = e.v;
    } else {
      v = o.v(e1.v, e2.v);
    }
  }
};

void test(const string& s)
{
  cout << s << " ---> ";
  parser<expr> parser;
  expr e;

  if (!parser.parse(e, s.begin(), s.end())) {
    cout << "parse error" << endl;
  } else {
    cout << e.v << endl;
  }
  
}

int main()
{
  test("1");
  test("42");
  test("(add 10 23)");
  test("(add 100 (sub 20 5))");
  test("(add (add 10 20) (add 5 13))");
  test("(add)");
  return 0;
}

実行結果

1 ---> 1
42 ---> 42
(add 10 23) ---> 33
(add 100 (sub 20 5)) ---> 115
(add (add 10 20) (add 5 13)) ---> 48
(add) ---> parse error

例によって、少しでも間違えると意味不明なエラーメッセージが大量に出る。

例えば、const参照で受け取るべき引数を参照にしてしまっていたら…

/home/kik/include/boost/fusion/functional/invocation/invoke.hpp: In static member function ‘static typename boost::function_types::result_type<Function>::type boost::fusion::detail::invoke_mem_fn<Function, Sequence, 8, false>::call(F&, Sequence&) [with F = void (expr::*)(int_lit, peg::sor, open_paren, op, expr&, const expr&, close_paren), Function = void (expr::*)(int_lit, peg::sor, open_paren, op, expr&, const expr&, close_paren), Sequence = const boost::fusion::joint_view<const boost::fusion::single_view<expr*>, const boost::fusion::vector<token<int_lit_s>, peg::sor, token<peg::chr<peg::chr_range_constraint<'(', '('> > >, token<op_s>, expr, expr, token<peg::chr<peg::chr_range_constraint<')', ')'> > >, boost::fusion::void_, boost::fusion::void_, boost::fusion::void_> >]’:
/home/kik/include/boost/fusion/functional/invocation/invoke.hpp:178:   instantiated from ‘typename boost::fusion::result_of::invoke<Function, const Sequence>::type boost::fusion::invoke(Function, const Sequence&) [with Function = void (expr::*)(int_lit, peg::sor, open_paren, op, expr&, const expr&, close_paren), Sequence = boost::fusion::joint_view<const boost::fusion::single_view<expr*>, const boost::fusion::vector<token<int_lit_s>, peg::sor, token<peg::chr<peg::chr_range_constraint<'(', '('> > >, token<op_s>, expr, expr, token<peg::chr<peg::chr_range_constraint<')', ')'> > >, boost::fusion::void_, boost::fusion::void_, boost::fusion::void_> >]’
peg.hpp:228:   instantiated from ‘bool peg::pi::parse_fun(Fun, T&, Input&, Input) [with Fun = void (expr::*)(int_lit, peg::sor, open_paren, op, expr&, const expr&, close_paren), T = expr, Input = __gnu_cxx::__normal_iterator<const char*, std::basic_string<char, std::char_traits<char>, std::allocator<char> > >]’
peg.hpp:240:   instantiated from ‘bool peg::parser<T>::parse(T&, Input, Input) [with Input = __gnu_cxx::__normal_iterator<const char*, std::basic_string<char, std::char_traits<char>, std::allocator<char> > >, T = expr]’
sample-1.cpp:70:   instantiated from here
/home/kik/include/boost/fusion/functional/invocation/invoke.hpp:279: error: invalid initialization of reference of type ‘expr&’ from expression of type ‘const expr’
/home/kik/include/boost/fusion/functional/invocation/invoke.hpp:279: error: return-statement with a value, in function returning 'void'

かろうじて、最後から2行目を読めば何が悪いのか分かる

2009-09-20 C++で再帰下降パーザ

[] C++再帰下降パーザライブラリ 08:22  C++で再帰下降パーザライブラリ - 菊やんの雑記帳 を含むブックマーク

再帰下降パーザを書くだけなら誰でもできることなのだが、本当に一日中悩んでやっと低機能なものが動くようになった。

最終的にはPEGをサポートしたいところである。

たとえば

<hoge> ::= alpha alpha digit <foo>
<foo> ::= digit

なんて文法があったら

#include <iostream>
#include "peg.hpp"

struct Foo
{
  char v;
  void action(digit x) {
    v = x.v;
  }
};

struct Hoge
{
  string v;
  
  void action(alpha x, alpha y, digit z, const Foo& f) {
    v += x.v;
    v += y.v;
    v += z.v;
    v += f.v;
  }
};


int main()
{
  parser<Hoge> parser;
  bool failed;
  Hoge h = parser.parse(&failed, "ya1234");

  std::cout << h.v << std::endl;
  
  return 0;
}

って書くだけで、カスタムアクションとASTつきのパーザがなぜか出来上がるという謎実装。

そして、異様にコンパイルに時間がかかる。

さらにちょっと書き間違えただけで、大量の謎エラーメッセージ

2009-09-11 C++が分かった

[] 17:08 C++が分かった - 菊やんの雑記帳 を含むブックマーク

struct X {
  typedef int Y;
};

struct Y {
  struct Z {
    X foo();
  };
};

struct Z {
  X::Y foo();
};

X::Y::Z::foo()
{
  return 0;
}

これだと、最後の関数定義は

X::Y (::Z::foo)();

を定義してるのね

2009-09-10 C++が分からない

[] C++の文法が複雑すぎて分からない 22:56  C++の文法が複雑すぎて分からない - 菊やんの雑記帳 を含むブックマーク

struct X {
  struct Y {
    struct Z {
    };
  };
};

struct Y {
  struct Z {
    X foo();
  };
};

struct Z {
  X::Y foo();
};

X::Y::Z foo();

X::Y::Z::foo()
{
  return 0;
}

最後の関数定義はいったいどの関数を定義しているのだろう…。C++仕様書の生成規則だと

X::Y::Z (::foo)();
X::Y    (::Z::foo)();
X       (::Y::Z::foo)();
        (X::Y::Z::foo)();

この4種類のツリーが生成できるように読めるし、どれか一つに制限しているような規則も見当たらない…。

kikxkikx 2009/09/10 23:10 defectsにあった
http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_defects.html#125
最終版では直るのかな

2007-03-07 謎挙動

[] 07:14 謎挙動 - 菊やんの雑記帳 を含むブックマーク

#include <stdio.h>

int main()
{
  enum E { E_A, E_B};
  int x = 10;
  printf("%d, %d, %d, %d, %d, %d\n", E_A, E_B, 10, (E)10, x, (E)x);
}

結果

$ g++ --version
g++ (GCC) 3.4.1
Copyright (C) 2004 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
$ ./test
0, 1, 10, 0, 10, 10

とりあえず、新しいバージョンいれようかな…