[1]
[2]
×
[PR]上記の広告は3ヶ月以上新規記事投稿のないブログに表示されています。新しい記事を書く事で広告が消えます。
今回は組み込み関数に対応する引数に応じて後々のルールを変更する方法と言うことでひとつ。
symbolsさんを使います。
今回は関数名と対応するルールで作りましたが、もっとわかりやすい例としては
とかして英語と数字のマッピングなんかも出来ちゃいます。多分。
あと、この部分
にすると、うまく動きません。実行時エラー起こします。
設定の順番が大事みたいです。
さらに、この部分
を、こんな感じ
にしたい場合、
ってしないとダメみたいです。
とりあえず今日はこんなところで。
#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で動作確認しています。
ポイントはここ。
_pass(boost::sprit::_pass)にfalseを代入すると構文解析に失敗してくれます。
ただし、バックトラックとかしてくれる関係で、変数が一つも存在しないときに
hoge = 3 + fuga
を解析するとhoge = 3までは解析成功してしまいます。
後、本筋とは関係ないのですけど、以前まで
なお、以下のコードは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;
}
ポイントはここ。
こっそりphoenixの例外処理とか使ってますが。variable_reference = variable[
boost::phoenix::try_[
_val = boost::phoenix::bind(&Parser::GetVariableValue, this, _1)
].catch_all[
_pass = false
]];
_pass(boost::sprit::_pass)にfalseを代入すると構文解析に失敗してくれます。
ただし、バックトラックとかしてくれる関係で、変数が一つも存在しないときに
hoge = 3 + fuga
を解析するとhoge = 3までは解析成功してしまいます。
後、本筋とは関係ないのですけど、以前まで
とかしていた部分は単純に//expression_group = '(' >> expression[_val = _1] >> ')';
でよきに計らってくれるみたいです。expression_group = '(' >> expression >> ')';
今回はコメントの実装です。
というわけで本日の最終回。
早速いってみましょうっ♪
早速いってみましょうっ♪
今回は前回のコードを全然踏まえません。
前回1+2+3……って感じの解析が出来ないレベルに退化したので、何とか戻そうとがんばった結果こうなりました。
たぶん正しい使い方が出来たんじゃないかな……と自画自賛するのですっ♪
前回1+2+3……って感じの解析が出来ないレベルに退化したので、何とか戻そうとがんばった結果こうなりました。
たぶん正しい使い方が出来たんじゃないかな……と自画自賛するのですっ♪