kyy*_*kyy 3 c++ parsing boost-spirit boost-phoenix boost-spirit-qi
我有两种类型的表达式,我想解析并计算结果.
拟合表达式:+, - ,*,/和sqrt()函数; 例如:"2 + 3*sqrt(100*25)" - >应计算为152
功能:GetSubString()和ConcatenateStrings()如: "GetSubString( '100str1',0,3)" - >应被计算为100
我有2个单独的语法来解析这些表达式类型.现在我想结合这两个语法,并可以将这些表达式一起定义.
例如:
我试图通过使用置换运算符组合下面的2个语法.但它没有编译.
expr_ =
( *( (function_call_ ^ arithmeticexpression_)| string_ ));
Run Code Online (Sandbox Code Playgroud)
那么这是一个合并我的function_call_和arithmeticexpression_规则的正确方法,或者我该怎么做?
typedef boost::variant<int, float, double, std::wstring> RetValue;
RetValue CTranslationFunctions::GetSubString(RetValue const& str, RetValue position, RetValue len)
{
std::wstring strToCut;
size_t posInt = 0;
size_t lenInt = 0;
try
{
strToCut = boost::get<std::wstring>(str);
posInt = boost::get<int>(position);
lenInt = boost::get<int>(len);
}
catch (const boost::bad_get&)
{
throw;
}
return strToCut.substr(posInt, lenInt);
}
RetValue CTranslationFunctions::ConcatenateStrings(RetValue const& a, RetValue const& b)
{
wostringstream woss;
woss << a << b;
return woss.str();
}
double CTranslationFunctions::Negate(double num)
{
return -num;
}
double CTranslationFunctions::Add(double num1 , const double num2)
{
return num1 + num2;
};
double CTranslationFunctions::Subtruct(double num1 , double num2)
{
return num1 - num2;
};
double CTranslationFunctions::Multiply(double num1 , double num2)
{
return num1 * num2;
};
double CTranslationFunctions::Divide(double num1 , double num2)
{
return num1 / num2;
};
double CTranslationFunctions::Sqrt(double num)
{
return sqrt(num);
}
class InvalidParamEx{};
double CTranslationFunctions::ConvertStringToDouble(RetValue val)
{
wostringstream wss;
double dNum;
wss << val;
std::wistringstream iss;
iss.str(wss.str());
try
{
iss >> dNum;
}
catch (...)
{
throw InvalidParamEx();
}
return dNum;
}
BOOST_PHOENIX_ADAPT_FUNCTION(RetValue, ConcatenateStrings_, ConcatenateStrings, 2)
BOOST_PHOENIX_ADAPT_FUNCTION(RetValue, GetContainerId_, GetContainerId, 2)
BOOST_PHOENIX_ADAPT_FUNCTION(double, Add_, Add, 2)
BOOST_PHOENIX_ADAPT_FUNCTION(double, Subtruct_, Subtruct, 2)
BOOST_PHOENIX_ADAPT_FUNCTION(double, Multiply_, Multiply, 2)
BOOST_PHOENIX_ADAPT_FUNCTION(double, Divide_, Divide, 2)
BOOST_PHOENIX_ADAPT_FUNCTION(double, Negate_, Negate, 1)
BOOST_PHOENIX_ADAPT_FUNCTION(double, Sqrt_, Sqrt, 1)
BOOST_PHOENIX_ADAPT_FUNCTION(double, ConvertStringToDouble_, ConvertStringToDouble, 1)
// Grammar to parse map functions
template <typename It, typename Skipper = qi::space_type >
struct MapFunctionParser : qi::grammar<It, RetValue(), Skipper, qi::locals<char> >
{
MapFunctionParser() : MapFunctionParser::base_type(expr_)
{
using namespace qi;
function_call_ =
| (lit(L"GetSubString") > '(' > expr_ > ',' > expr_ > ',' > expr_ > ')')
[ _val = GetSubString_(_1, _2, _3) ]
| (lit(L"ConcatenateStrings") > '(' > expr_ > lit(',') > expr_ > ')')
[ _val = ConcatenateStrings_(_1, _2) ];
string_ = as_wstring[omit [ char_("'\"") [_a =_1] ]
>> no_skip [ *(char_ - char_(_a)) ]
>> lit(_a)];
arithmeticexpression_ =
term_ [_val = _1]
>> *( ('+' >> term_ [_val = Add_(_val,_1)])
| ('-' >> term_ [_val = Subtruct_(_val, _1)])
);
term_ =
factor_ [_val = _1]
>> *( ('*' >> factor_ [_val = Multiply_(_val, _1)])
| ('/' >> factor_ [_val = Divide_(_val, _1)])
);
factor_ =
double_ [_val = _1]
string_ [_val = ConvertStringToDouble(_1)]
| ('-' >> factor_ [_val = Negate_(_1)])
| ('+' >> factor_ [_val = _1])
| (L"Sqrt" > '(' > double_ > ')' ) [_val = Sqrt_(_1)]);
expr_ =
( *( (function_call_ ^ arithmeticexpression_)| string_ ));
on_error<fail> ( expr_, std::cout
<< phx::val("Error! Expecting ") << _4 << phx::val(" here: \"")
<< phx::construct<std::string>(_3, _2) << phx::val("\"\n"));
BOOST_SPIRIT_DEBUG_NODE(function_call_);
BOOST_SPIRIT_DEBUG_NODE(expr_);
BOOST_SPIRIT_DEBUG_NODE(string_);
BOOST_SPIRIT_DEBUG_NODE(funcparameter_);
BOOST_SPIRIT_DEBUG_NODE(arithmeticexpression_);
BOOST_SPIRIT_DEBUG_NODE(factor_);
BOOST_SPIRIT_DEBUG_NODE(term_);
}
private:
qi::rule<It, RetValue(), Skipper, qi::locals<char> > function_call_, expr_, funcparameter_;
qi::rule<It, wstring(), Skipper, qi::locals<char> > string_;
qi::rule<It, double(), Skipper> arithmeticexpression_, factor_, term_;
};
Run Code Online (Sandbox Code Playgroud)
编辑 将我的早期反应移至底部
这需要一段时间.主要是因为显示的代码有奇怪的问题:
function_call和factor_GetContainerId和GetSubstring从未凤凰适应CTranslationFunctions不存在,并且正在声明成员函数所以我基本上最终做的是重写.是的,我知道.我疯了.尽管如此,让我引导您完成它,解释我改变的一些事情以及原因.
#define BOOST_SPIRIT_USE_PHOENIX_V3
// #define BOOST_SPIRIT_DEBUG
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix.hpp>
#include <boost/phoenix/function/adapt_function.hpp>
#include <boost/lexical_cast.hpp>
namespace qi = boost::spirit::qi;
namespace phx = boost::phoenix;
typedef boost::variant<int, double> NumValue;
typedef boost::variant<int, double, std::wstring> GenericValue;
Run Code Online (Sandbox Code Playgroud)
我立刻分开了数值和通用值的概念.这是因为区别对于某些表达式(主要是算术表达式)很重要.我本来可以在GenericValue任何地方使用,但我们稍后会看到如何NumValue使算术评估更简单.
struct InvalidParamEx : public virtual std::exception
{
const char* what() const noexcept { return "Invalid type of operand/parameter"; }
};
Run Code Online (Sandbox Code Playgroud)
有你的例外类型,显示了一些好的做法.我们在预期数字值时抛出它,但GenericValue包含的内容不兼容.怎么样?让我们来看看:
struct AsNumValue : boost::static_visitor<NumValue>
{
int operator()(int i) const { return i; }
double operator()(double d) const { return d; }
NumValue operator()(std::wstring const& s) const
{
try { return boost::lexical_cast<int>(s); } catch(...) {}
try { return boost::lexical_cast<double>(s); } catch(...) {}
throw InvalidParamEx();
}
};
class CTranslationFunctions
{
// helper
NumValue static num(GenericValue const& v) { return boost::apply_visitor(AsNumValue(), v); }
Run Code Online (Sandbox Code Playgroud)
那里.我为你定义了缺少的类,并立即添加了转换的帮助器GenericValue ? NumValue.正如你所看到的,我使用过,boost::lexical_cast因为重新发明轮子是没有用的.请注意,您之前的方法ConvertStringToDouble有几个大问题:
double值,而你的功能可能需要int'100str1'在100没有警告的情况下作为价值来对待term都将被转换为double,即使它确实是一个字符串.(为什么这是相关的,会当你看到修改后变得清晰expr_和term_规则.)让我们继续:
public:
static GenericValue GetSubString(GenericValue const& str, GenericValue position, GenericValue len);
static GenericValue ConcatenateStrings(GenericValue const& a, GenericValue const& b);
Run Code Online (Sandbox Code Playgroud)
是的,我们稍后会定义它们.现在,为自己的算术运算功能做好准备:
#define DEFUNOP(name, expr) private: struct do_##name : boost::static_visitor<NumValue> { \
template <typename T1> NumValue operator()(T1 const& a) const { return expr; } \
}; \
public: static NumValue name(GenericValue const& a) { auto na=num(a); return boost::apply_visitor(do_##name(), na); }
#define DEFBINOP(name, infix) struct do_##name : boost::static_visitor<NumValue> { \
template <typename T1, typename T2> NumValue operator()(T1 const&a, T2 const&b) const\
{ return a infix b; } \
}; \
public: static NumValue name(GenericValue const& a, GenericValue const& b) { auto na=num(a), nb=num(b); return boost::apply_visitor(do_##name(), na, nb); }
// define the operators polymorphically, so `int` + `double` becomes `double`, but `int` * `int` stays `int`
DEFBINOP(Add , +);
DEFBINOP(Subtruct, -);
DEFBINOP(Multiply, *);
DEFBINOP(Divide , /);
DEFUNOP (Negate , -a);
DEFUNOP (Sqrt , sqrt(a));
};
Run Code Online (Sandbox Code Playgroud)
Whoaaaaah那里发生了什么?好吧,评论说明了一切:
int+ int与double+ int等.这称为多态评估.例子:GetSubString('100str1', 0, 2+1)永远不会工作,因为2+1需要评估一个int(3),但你double Add(double,double)总是生产一个double.decltype在混合情况下检测结果类型NumValue具有上述优点GenericValue:因为NumValue只能是int或double,我们知道,一般的 operator()落实涵盖了所有的法律的组合.asNumeric在调用函数对象之前传递.这很好地解决了你的算术运算,还有另一个好处:它消除了"需要"ConvertStringToDouble,因为你需要转换到NumValue需要的时候,即算术运算的评估.当我们修改你的语法以支持你想要的输入表达式时,这是一件非常重要的事情.
GenericValue CTranslationFunctions::GetSubString(GenericValue const& str, GenericValue position, GenericValue len)
{
using boost::get;
return get<std::wstring>(str).substr(get<int>(position), get<int>(len));
}
Run Code Online (Sandbox Code Playgroud)
是的,我缩短了一点.
GenericValue CTranslationFunctions::ConcatenateStrings(GenericValue const& a, GenericValue const& b)
{
std::wostringstream woss;
woss << a << b;
return woss.str();
}
BOOST_PHOENIX_ADAPT_FUNCTION(GenericValue, ConcatenateStrings_, CTranslationFunctions::ConcatenateStrings, 2)
BOOST_PHOENIX_ADAPT_FUNCTION(GenericValue, GetSubString_ , CTranslationFunctions::GetSubString , 3)
BOOST_PHOENIX_ADAPT_FUNCTION(NumValue , Add_ , CTranslationFunctions::Add , 2)
BOOST_PHOENIX_ADAPT_FUNCTION(NumValue , Subtruct_ , CTranslationFunctions::Subtruct , 2)
BOOST_PHOENIX_ADAPT_FUNCTION(NumValue , Multiply_ , CTranslationFunctions::Multiply , 2)
BOOST_PHOENIX_ADAPT_FUNCTION(NumValue , Divide_ , CTranslationFunctions::Divide , 2)
BOOST_PHOENIX_ADAPT_FUNCTION(NumValue , Negate_ , CTranslationFunctions::Negate , 1)
BOOST_PHOENIX_ADAPT_FUNCTION(NumValue , Sqrt_ , CTranslationFunctions::Sqrt , 1)
Run Code Online (Sandbox Code Playgroud)
打哈欠.我们知道如何适应凤凰城的功能; 让我们来看看语法定义吧!
// Grammar to parse map functions
template <typename It, typename Skipper = qi::space_type >
struct MapFunctionParser : qi::grammar<It, GenericValue(), Skipper>
{
MapFunctionParser() : MapFunctionParser::base_type(expr_)
{
using namespace qi;
function_call_ =
(no_case["GetSubString"] > '(' > expr_ > ',' > expr_ > ',' > expr_ > ')') [ _val = GetSubString_(_1, _2, _3) ]
| (no_case["ConcatenateStrings"] > '(' > expr_ > ',' > expr_ > ')') [ _val = ConcatenateStrings_(_1, _2) ]
| (no_case["Sqrt"] > '(' > expr_ > ')') [ _val = Sqrt_(_1) ]
;
string_ = // keep it simple, silly (KISS)
(L'"' > *~char_('"') > L'"')
| (L"'" > *~char_("'") > L"'");
arithmeticexpression_ =
term_ [ _val = _1 ]
>> *( ('+' >> term_ [ _val = Add_(_val,_1) ])
| ('-' >> term_ [ _val = Subtruct_(_val, _1) ])
);
term_ =
factor_ [ _val = _1 ]
>> *( ('*' >> factor_ [ _val = Multiply_(_val, _1) ])
| ('/' >> factor_ [ _val = Divide_(_val, _1) ])
);
factor_ =
int_ [ _val = _1 ]
| double_ [ _val = _1 ]
| string_ [ _val = _1 ]
| ('-' >> factor_) [ _val = Negate_(_1) ]
| ('+' >> factor_) [ _val = _1 ]
| function_call_ [ _val = _1 ]
;
expr_ = arithmeticexpression_;
on_error<fail> ( expr_, std::cout
<< phx::val("Error! Expecting ") << _4 << phx::val(" here: \"")
<< phx::construct<std::string>(_3, _2) << phx::val("\"\n"));
BOOST_SPIRIT_DEBUG_NODES((function_call_) (expr_) (string_) (funcparameter_) (arithmeticexpression_) (factor_) (term_))
}
private:
qi::rule<It, std::wstring()>
string_; // NO SKIPPER (review)
qi::rule<It, GenericValue(), Skipper>
function_call_, expr_, funcparameter_, // NO LOCALS (review)
arithmeticexpression_, term_, factor_;
};
Run Code Online (Sandbox Code Playgroud)
好.我们在这里有什么 改变了什么?
qi::locals了原本只在string_规则中使用过的东西,我重写了这一点,以兑现KISS原则" oops "相同"oops").我是通过从string_声明中删除船长来实现的.这与封装整个规则具有相同的效果qi::lexeme[].Sqrt的function_call_规则,因为,这是一个函数调用.no_case[]不区分大小写,因为您的示例表明sqrt(9)应该可以正常工作请注意,Sqrt现在采用任何表达式,而旧的情况
| (L"Sqrt" > '(' > double_ > ')') // Wait, whaaat?
Run Code Online (Sandbox Code Playgroud)
是的,这永远不会解析你的第二个例子,真的:|
现在真正的操作来了.为了让sqrt(GetSubstring(....))解析,我们必须让function_call_a成为可能的值term_.一旦出现这种情况,我们就不需要再进行任何操作了,expr_因为expr_可能factor_包含一个term_代表function_call_已经存在的单个,因此
expr_ = ( *( (function_call_ ^ arithmeticexpression_)| string_ ));
Run Code Online (Sandbox Code Playgroud)
蒸发成
expr_ = arithmeticexpression_;
Run Code Online (Sandbox Code Playgroud)
string_那里发生了什么事?嗯,它仍在,它在term_哪里,但在ConvertStringToDouble那里被删除.字符串很乐意成为字符串,除非在需要s 的算术运算的上下文中需要它们NumValue.那时他们将被强制转换为一个数字,而不是之前(如上所示).
int main()
{
static const MapFunctionParser<std::wstring::const_iterator> p;
std::wstring input;
while (std::getline(std::wcin, input))
{
std::wstring::const_iterator f(begin(input)), l(end(input));
GenericValue value;
assert(qi::phrase_parse(f, l, p, qi::space, value));
if (f!=l)
std::wcout << L"remaining unparsed: '" << std::wstring(f,l) << L"'\n";
std::wcout << input << " --> " << value << std::endl;
}
}
Run Code Online (Sandbox Code Playgroud)
当我从你的问题中给这个小测试程序提供两行时,它尽职地制作了以下文本:
GetSubString('100str1', 0, 2+1) + sqrt(9) --> 103
2 + 3 * sqrt(GetSubString('100str1', 0, 2+1)) --> 32
Run Code Online (Sandbox Code Playgroud)
您可以在Coliru上看到完整的代码(遗憾的是,编译需要很长时间).
最初这个答案从以下开始:
问:我尝试使用置换运算符组合如下2个语法.但它没有编译
您期望排列运算符做什么?文件说明:
置换运算符a ^ b以任何顺序匹配一个或多个操作数(a,b,...等)
......
如您所见,它会产生一个属性
boost::variant<
fusion::vector2<optional<RetValue>, optional<double>>,
std::wstring>
Run Code Online (Sandbox Code Playgroud)
这显然不兼容.现在,我假设您只需要/或语义,所以
expr_ = string_ | function_call_ | arithmeticexpression_;
Run Code Online (Sandbox Code Playgroud)
应该做得很好,导致boost::variant<RetValue, double, std::wstring>
哪个可分配给a RetValue.
现在,通过跳过十几个箍来使你的示例代码编译(为什么......)这里有一个修复:
| 归档时间: |
|
| 查看次数: |
351 次 |
| 最近记录: |