我想使用boost spirit来解析一个可以有多种类型的值;例如类似的东西:
singleValueToBeParsed %= (double_ | int_ | bool_ | genericString);
genericString %= +(char_("a-zA-Z"));
解析为int或double的情况看起来相当简单:
Parse int or double using boost spirit (longest_d)
..但我不确定如何扩展它以包含其他类型,包括通用字符串和bools ..
有什么想法吗?
谢谢,
本。
编辑:基于答案,我更新了我的语法如下:
genericString %= +(char_("a-zA-Z"));
intRule %= int_;
doubleRule %= (&int_ >> (double_ >> 'f'))
| (!int_ >> double_ >> -lit('f'));
boolRule %= bool_;
其中每个qi规则都有一个字符串,int,double或bool迭代器
然后我有一条规则
add %= string("add") >> '('
>> (intRule | doubleRule | genericString) >> ','
>> (intRule | doubleRule | genericString) >> ','
>> genericString
>> ')' >> ';';
期望采用语法add(5,6.1,result);或添加(a,b,结果);但到目前为止,只有解析前两个参数是整数。
请注意,添加规则指定为:
qi::rule<Iterator, Function(), ascii::space_type> add;
功能指定为:
typedef boost::any DirectValue;
struct Function
{
//
// name of the Function; will always be a string
//
std::string name;
//
// the function parameters which can be initialized to any type
//
DirectValue paramA;
DirectValue paramB;
DirectValue paramC;
DirectValue paramD;
DirectValue paramE;
};
BOOST_FUSION_ADAPT_STRUCT(
Function,
(std::string, name)
(DirectValue, paramA)
(DirectValue, paramB)
(DirectValue, paramC)
(DirectValue, paramD)
(DirectValue, paramE)
)
编辑2:
现在正确解析。见http://liveworkspace.org/code/3asg0X%247由llonesmiz提供。欢呼声。
答案 0 :(得分:4)
这是一项有趣的练习。
当然,一切都取决于您无法指定的输入语法。
但是,为了演示而假设一个文字语法(非常)松散地基于C ++文字,我们可以提出以下来解析十进制(带符号)整数值,浮点值,bool文字和简单的字符串文字:
typedef boost::variant<
double, unsigned int,
long, unsigned long, int,
bool, std::string> attr_t;
// ...
start =
(
// number formats with mandatory suffixes first
ulong_rule | uint_rule | long_rule |
// then those (optionally) without suffix
double_rule | int_rule |
// and the simple, unambiguous cases
bool_rule | string_rule
);
double_rule =
(&int_ >> (double_ >> 'f')) // if it could be an int, the suffix is required
| (!int_ >> double_ >> -lit('f')) // otherwise, optional
;
int_rule = int_;
uint_rule = uint_ >> 'u' ;
long_rule = long_ >> 'l' ;
ulong_rule = ulong_ >> "ul" ;
bool_rule = bool_;
string_rule = '"' >> *~char_('"') >> '"';
请参阅测试用例输出的链接实时演示: http://liveworkspace.org/code/goPNP
注意只有一个测试输入(“无效”)应该失败。其余的应解析为文字,可选择留下未解析的剩余输入。
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/karma.hpp>
namespace qi = boost::spirit::qi;
namespace karma = boost::spirit::karma;
typedef boost::variant<double, unsigned int, long, unsigned long, int, bool, std::string> attr_t;
template <typename It, typename Skipper = qi::space_type>
struct parser : qi::grammar<It, attr_t(), Skipper>
{
parser() : parser::base_type(start)
{
using namespace qi;
start =
(
// number formats with mandatory suffixes first
ulong_rule | uint_rule | long_rule |
// then those (optionally) without suffix
double_rule | int_rule |
// and the simple, unambiguous cases
bool_rule | string_rule
);
double_rule =
(&int_ >> (double_ >> 'f')) // if it could be an int, the suffix is required
| (!int_ >> double_ >> -lit('f')) // otherwise, optional
;
int_rule = int_;
uint_rule = uint_ >> 'u' ;
long_rule = long_ >> 'l' ;
ulong_rule = ulong_ >> "ul" ;
bool_rule = bool_;
string_rule = '"' >> *~char_('"') >> '"';
BOOST_SPIRIT_DEBUG_NODE(start);
BOOST_SPIRIT_DEBUG_NODE(double_rule);
BOOST_SPIRIT_DEBUG_NODE(ulong_rule);
BOOST_SPIRIT_DEBUG_NODE(long_rule);
BOOST_SPIRIT_DEBUG_NODE(uint_rule);
BOOST_SPIRIT_DEBUG_NODE(int_rule);
BOOST_SPIRIT_DEBUG_NODE(bool_rule);
BOOST_SPIRIT_DEBUG_NODE(string_rule);
}
private:
qi::rule<It, attr_t(), Skipper> start;
// no skippers in here (important):
qi::rule<It, double()> double_rule;
qi::rule<It, int()> int_rule;
qi::rule<It, unsigned int()> uint_rule;
qi::rule<It, long()> long_rule;
qi::rule<It, unsigned long()> ulong_rule;
qi::rule<It, bool()> bool_rule;
qi::rule<It, std::string()> string_rule;
};
struct effective_type : boost::static_visitor<std::string> {
template <typename T>
std::string operator()(T const& v) const {
return typeid(v).name();
}
};
bool testcase(const std::string& input)
{
typedef std::string::const_iterator It;
auto f(begin(input)), l(end(input));
parser<It, qi::space_type> p;
attr_t data;
try
{
std::cout << "parsing '" << input << "': ";
bool ok = qi::phrase_parse(f,l,p,qi::space,data);
if (ok)
{
std::cout << "success\n";
std::cout << "parsed data: " << karma::format_delimited(karma::auto_, ' ', data) << "\n";
std::cout << "effective typeid: " << boost::apply_visitor(effective_type(), data) << "\n";
}
else std::cout << "failed at '" << std::string(f,l) << "'\n";
if (f!=l) std::cout << "trailing unparsed: '" << std::string(f,l) << "'\n";
std::cout << "------\n\n";
return ok;
} catch(const qi::expectation_failure<It>& e)
{
std::string frag(e.first, e.last);
std::cout << e.what() << "'" << frag << "'\n";
}
return false;
}
int main()
{
for (auto const& s : std::vector<std::string> {
"1.3f",
"0.f",
"0.",
"0f",
"0", // int will be preferred
"1u",
"1ul",
"1l",
"1",
"false",
"true",
"\"hello world\"",
// interesting cases
"invalid",
"4.5e+7f",
"-inf",
"-nan",
"42 is the answer", // 'is the answer' is simply left unparsed, it's up to the surrounding grammar/caller
" 0\n ", // whitespace is fine
"42\n.0", // but not considered as part of a literal
})
{
testcase(s);
}
}