忍者ブログ
ゲーム関連の話題に絞っていましたが、全然書かないのでいろいろ解禁してみたり。
[1]  [2
×

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

今回は組み込み関数に対応する引数に応じて後々のルールを変更する方法と言うことでひとつ。

#include <iostream>
#include <string>

#pragma warning(push)
#pragma warning(disable:4100) //引数は関数の本体部で 1 度も参照されません。
#pragma warning(disable:4512) //代入演算子を生成できません。
#pragma warning(disable:4819) //ファイルは、現在のコード ページ (932) で表示できない文字を含んでいます。
#pragma warning(disable:4127) //条件式が定数です。

#include <boost/spirit/include/qi_action.hpp>
#include <boost/spirit/include/qi_grammar.hpp>
#include <boost/spirit/include/qi_lexeme.hpp>
#include <boost/spirit/include/qi_operator.hpp>
#include <boost/spirit/include/qi_parse.hpp>
#include <boost/spirit/include/qi_rule.hpp>

#include <boost/spirit/include/qi_char_.hpp>
#include <boost/spirit/include/qi_string.hpp>
#include <boost/spirit/include/qi_numeric.hpp>

#include <boost/spirit/include/phoenix_bind.hpp>
#include <boost/spirit/include/phoenix_operator.hpp>
#include <boost/spirit/include/phoenix_statement.hpp>

#pragma warning(pop)

#pragma warning(disable:4503) //装飾された名前の長さが限界を越えました。

template<typename String, typename Skipper>
struct Parser : boost::spirit::qi::grammar<typename String::const_iterator, Skipper>{
    typedef typename String::const_iterator Iterator;
   
    boost::spirit::qi::rule<Iterator, Skipper> func1;
    boost::spirit::qi::rule<Iterator, Skipper> func2;
    boost::spirit::qi::symbols<char, boost::spirit::qi::rule<Iterator, Skipper>> funcs;
   
    boost::spirit::qi::rule<Iterator, Skipper> statement;
    boost::spirit::qi::rule<Iterator, boost::spirit::qi::locals<boost::spirit::qi::rule<Iterator, Skipper>>, Skipper> statement2;

    Parser() : Parser::base_type(statement){
        //文法定義
        {
            using namespace boost::spirit;
            using namespace boost::spirit::ascii;
            using namespace boost::spirit::labels;
            namespace phoenix = boost::phoenix;

            //テストコード

            func1 = '(' >> int_ >> ')';
            func2 = '(' >> char_ >> "," >> char_ >> ')';
            funcs.add
                ("func1", func1)
                ("func2", func2)
                ;
            statement2 = (funcs[_a = _1] >> lazy(_a));
            statement = statement2;
        }
    }
};

template<typename String>
struct SkipParser : boost::spirit::qi::grammar<typename String::const_iterator>{
    typedef typename String::const_iterator Iterator;

    SkipParser() : SkipParser::base_type(comment){
        using namespace boost::spirit;

        comment  = boost::spirit::ascii::space
            | ("//" >> *(qi::char_ - '\n') >> '\n')
            | ("/*" >> *(qi::char_ - "*/") >> "*/");
    }

    boost::spirit::qi::rule<Iterator> comment;
};

int main(void)
{
    namespace phoenix = boost::phoenix;

    typedef SkipParser<std::string> SkipperType;
    typedef Parser<std::string, SkipperType> ParserType;
    SkipperType skipper;

    ParserType parser;

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

        std::string::const_iterator it = str.begin();
        std::string::const_iterator end = str.end();

        bool result = phrase_parse(it, end, parser, skipper);

        if (result && it == end)
        {
            std::cout << "-------------------------\n";
            std::cout << "Parsing succeeded\n";
            std::cout << str << " Parses OK: " << 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;
        }
        else
        {
            std::cout << "-------------------------\n";
            std::cout << "Parsing failed\n";
            std::cout << "-------------------------\n";
        }
    }

    return 0;
}

symbolsさんを使います。
boost::spirit::qi::symbols<char, boost::spirit::qi::rule<Iterator, Skipper>> funcs;
コレは、前者の値に対応して後者を返すルールを簡単に作れるもの……です。多分。
今回は関数名と対応するルールで作りましたが、もっとわかりやすい例としては
boost::spirit::qi::symbols<char, int> nums;

nums.add
    ("one", 1)
    ("two", 2)
    ("three", 3)
    ;

とかして英語と数字のマッピングなんかも出来ちゃいます。多分。

あと、この部分
func1 = '(' >> int_ >> ')';
func2 = '(' >> char_ >> "," >> char_ >> ')';
funcs.add
    ("func1", func1)
    ("func2", func2)
    ;
statement2 = (funcs[_a = _1] >> lazy(_a));
statement = statement2;
を、こんな感じ
statement = statement2;
statement2 = (funcs[_a = _1] >> lazy(_a));
funcs.add
    ("func1", func1)
    ("func2", func2)
    ;

func1 = '(' >> int_ >> ')';
func2 = '(' >> char_ >> "," >> char_ >> ')';

にすると、うまく動きません。実行時エラー起こします。
設定の順番が大事みたいです。

さらに、この部分
statement2 = (funcs[_a = _1] >> lazy(_a));
statement = statement2;

を、こんな感じ
statement = (funcs[_a = _1] >> lazy(_a));

にしたい場合、
struct Parser : boost::spirit::qi::grammar<typename String::const_iterator, boost::spirit::qi::locals<boost::spirit::qi::rule<typename String::const_iterator, Skipper>>, Skipper>{

    <中略>

    boost::spirit::qi::rule<Iterator, boost::spirit::qi::locals<boost::spirit::qi::rule<Iterator, Skipper>>, Skipper> statement;

ってしないとダメみたいです。

とりあえず今日はこんなところで。
PR
今回は、定義されていない変数があった場合、セマンティックアクションから構文解析を失敗させる方法です。

なお、以下のコードはboostの1.44.0とVC++2010 Expressで動作確認しています。
#include <iostream>
#include <string>
#include <map>

#pragma warning(push)
#pragma warning(disable:4100) //引数は関数の本体部で 1 度も参照されません。
#pragma warning(disable:4512) //代入演算子を生成できません。
#pragma warning(disable:4819) //ファイルは、現在のコード ページ (932) で表示できない文字を含んでいます。
#pragma warning(disable:4127) //条件式が定数です。

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

#pragma warning(pop)

#pragma warning(disable:4503) //装飾された名前の長さが限界を越えました。

template<typename String, typename Skipper>
struct Parser : boost::spirit::qi::grammar<typename String::const_iterator, Skipper>{
    typedef typename String::const_iterator Iterator;

    struct Callback{
        boost::function<void (const String&, int)> SetValiable;    //変数名と値を渡して、変数を保持する関数
        boost::function<int (const String&)> GetValiable;        //変数名を渡して、変数を返す関数

        Callback(boost::function<void (const String&, int)> SetValiable,
            boost::function<int (const String&)> GetValiable) :
            SetValiable(SetValiable),
            GetValiable(GetValiable){}

        Callback(){}
    };

    Parser(const Callback& callback) : Parser::base_type(line){
        //その他初期化
        if(!SetCallback(callback)) throw std::invalid_argument("callback");

        //文法定義
        {
            using namespace boost::spirit;
            using namespace boost::spirit::ascii;

            //式
            //expression_group = '(' >> expression[_val = _1] >> ')';
            expression_group = '(' >> expression >> ')';
            expression_factor = int_  //数字
                | expression_group//部分式の演算結果
                | variable_reference;    //変数

            expression_term    = expression_factor[_val = _1] >> *(
                ('*' >> expression_factor[_val *= _1])
                | ('/' >> expression_factor[_val /= _1])
                );
            expression    = expression_term[_val = _1] >> *(
                ('+' >> expression_term[_val += _1])
                | ('-' >> expression_term[_val -= _1])
                );
            //変数
            variable = lexeme[
                    //alpha[_val += _1] >> +alnum[_val += _1]
                    alpha >> +alnum
                ];
            variable_reference    = variable[
                boost::phoenix::try_[
                    _val = boost::phoenix::bind(&Parser::GetVariableValue, this, _1)
                ].catch_all[
                    _pass = false
                ]];
            variable_assignment    = ( variable >> "=" >> expression )[boost::phoenix::bind(&Parser::SetVariableValue, this, _1, _2)];

            line = variable_assignment;
        }
    }

private:
    boost::spirit::qi::rule<Iterator, int(), Skipper> expression;
    boost::spirit::qi::rule<Iterator, int(), Skipper> expression_term;
    boost::spirit::qi::rule<Iterator, int(), Skipper> expression_factor;
    boost::spirit::qi::rule<Iterator, int(), Skipper> expression_group;

    boost::spirit::qi::rule<Iterator, String(), Skipper> variable;
    boost::spirit::qi::rule<Iterator, int(), Skipper> variable_reference;
    boost::spirit::qi::rule<Iterator, Skipper> variable_assignment;

    boost::spirit::qi::rule<Iterator, Skipper> line;

    bool SetCallback(const Callback& callback){
        if(callback.SetValiable
            && callback.GetValiable){
                this->callback = callback;
                return true;
        }

        return false;
    }

    int GetVariableValue(String name){
        return callback.GetValiable(name);
    }
    void SetVariableValue(String name, int i){
        callback.SetValiable(name, i);
    }

    void CallFunction(String name, String arg){
        callback.CallFunction(name, arg);
    }

    Callback callback;
};

template<typename String>
struct SkipParser : boost::spirit::qi::grammar<typename String::const_iterator>{
    typedef typename String::const_iterator Iterator;

    SkipParser() : SkipParser::base_type(comment){
        using namespace boost::spirit;
        //using namespace boost::spirit::arg_names;

        comment  = boost::spirit::ascii::space
            | ("//" >> *(qi::char_ - '\n') >> '\n')
            | ("/*" >> *(qi::char_ - "*/") >> "*/");
    }

    boost::spirit::qi::rule<Iterator> comment;
};

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

    int Get(std::string name){
        auto it = container.find(name);
        if(it == container.end())    throw std::exception();

        return it->second;
    }

    void View(){
        BOOST_FOREACH(auto pair, container){
            std::cout << pair.first << ":" << pair.second << std::endl;
        }
    }

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

int main(void)
{
    namespace phoenix = boost::phoenix;

    typedef SkipParser<std::string> SkipperType;
    typedef Parser<std::string, SkipperType> ParserType;
    SkipperType skipper;

    VariableManager vm;

    ParserType::Callback callback(phoenix::bind(&VariableManager::Set, &vm, phoenix::arg_names::arg1, phoenix::arg_names::arg2),
        phoenix::bind(&VariableManager::Get, &vm, phoenix::arg_names::arg1));

    ParserType parser(callback);

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

        std::string::const_iterator it = str.begin();
        std::string::const_iterator end = str.end();

        bool result = phrase_parse(it, end, parser, skipper);

        if (result && it == end)
        {
            std::cout << "-------------------------\n";
            std::cout << "Parsing succeeded\n";
            std::cout << str << " Parses OK: " << std::endl;
            vm.View();
        }
        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;
            vm.View();
        }
        else
        {
            std::cout << "-------------------------\n";
            std::cout << "Parsing failed\n";
            std::cout << "-------------------------\n";
            vm.View();
        }
    }

    return 0;
}
前のコードから変数管理部分を外だしにしたり、ちょっとずつ違いますけど……

ポイントはここ。
variable_reference    = variable[
boost::phoenix::try_[
    _val = boost::phoenix::bind(&Parser::GetVariableValue, this, _1)
].catch_all[
    _pass = false
]];
こっそりphoenixの例外処理とか使ってますが。
_pass(boost::sprit::_pass)にfalseを代入すると構文解析に失敗してくれます。
ただし、バックトラックとかしてくれる関係で、変数が一つも存在しないときに
hoge = 3 + fuga
を解析するとhoge = 3までは解析成功してしまいます。

後、本筋とは関係ないのですけど、以前まで
//expression_group = '(' >> expression[_val = _1] >> ')';
とかしていた部分は単純に
expression_group = '(' >> expression >> ')';
でよきに計らってくれるみたいです。

というわけで本日の最終回。
早速いってみましょうっ♪
今回は前回のコードを全然踏まえません。
前回1+2+3……って感じの解析が出来ないレベルに退化したので、何とか戻そうとがんばった結果こうなりました。
たぶん正しい使い方が出来たんじゃないかな……と自画自賛するのですっ♪
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]