C++ Boost Spirit在服务器应用程序上实现小型单线DSL
如果这个问题以前被回答过,我表示歉意 我想在我工作的服务器应用程序中插入一个小型DSL。语法非常简单,即使在这个早期阶段,我也被难倒了。我就是不知道如何在精神上构造语法 下面是我要测试的语法示例:C++ Boost Spirit在服务器应用程序上实现小型单线DSL,c++,boost,dsl,boost-spirit,boost-spirit-qi,C++,Boost,Dsl,Boost Spirit,Boost Spirit Qi,如果这个问题以前被回答过,我表示歉意 我想在我工作的服务器应用程序中插入一个小型DSL。语法非常简单,即使在这个早期阶段,我也被难倒了。我就是不知道如何在精神上构造语法 下面是我要测试的语法示例: WHERE [not] <condition> [ and | or <condition> ] <command> [parameters] 及 我知道这是一个很大的问题,但我想知道是否有灵魂专家可以在几秒钟内完成这个语法?我尽可能地解析LIKE and=,但在
WHERE [not] <condition> [ and | or <condition> ] <command> [parameters]
及
我知道这是一个很大的问题,但我想知道是否有灵魂专家可以在几秒钟内完成这个语法?我尽可能地解析LIKE and=,但在尝试将其与and、OR和NOT混合时遇到了问题。我的问题是,当思考spirit将如何解决这个问题时,不知道从哪里开始。有关概念证明,请参阅
我通常首先要做的是想象一下我希望如何存储解析后的数据
数据类型
我喜欢使用标准容器,boost::variant
(有时是boost::optional
)。自下而上阅读,看看它有多简单,自上而下:
struct regex {
std::string _pattern;
explicit regex(std::string const& pattern) : _pattern(pattern) {}
};
typedef boost::variant<double, int, std::string, regex> value;
enum logicOp { logicOr, logicAnd, logicPositive };
struct condition {
bool _negated;
std::string _propertyname;
value _operand; // value or regex
};
struct filter {
logicOp _op;
condition _cond;
};
struct setcommand {
typedef std::list<std::pair<std::string, value> > pairs;
pairs _propvals;
};
struct printcommand {
std::vector<std::string> _propnames;
};
typedef boost::variant<printcommand, setcommand> command;
struct statement {
std::vector<filter> _filters;
command _command;
};
注:
- 我已经决定字符串应该能够包含引号,所以我将
设置为转义字符\
- 唯一“棘手”的事情是确保过滤器(条件)以“WHERE”开头,并且每个后续条件必须以“and”/“OR”开头。它使用语义动作
在解析过程中,检查过滤器的结果列表([ _pass = (phx::size(_val) == 0) ]
)当时是否为空vector
习惯用法用于获取可选关键字(attr(…)
)的默认值。关键字仅在语法中可选,在AST中不可选:NOT
no_case["NOT"] >> attr(true) | attr(false)
like
运算符被打印为与正则表达式相等(m/../
)解析器
之外,它还包含一个生成器
,用于将解析后的AST数据类型打印回来
//#define BOOST_SPIRIT_DEBUG
#define BOOST_SPIRIT_USE_PHOENIX_V3
#include <boost/fusion/adapted.hpp>
#include <boost/variant.hpp>
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/karma.hpp>
#include <boost/spirit/include/phoenix.hpp>
namespace qi = boost::spirit::qi;
namespace karma = boost::spirit::karma;
namespace phx = boost::phoenix;
struct regex
{
std::string _pattern;
explicit regex(std::string const& pattern) : _pattern(pattern) {}
};
typedef boost::variant<double, int, std::string, regex> value;
enum logicOp { logicOr, logicAnd, logicPositive };
struct condition
{
bool _negated;
std::string _propertyname;
value _operand; // value or regex
};
struct filter
{
logicOp _op;
condition _cond;
};
struct setcommand
{
typedef std::list<std::pair<std::string, value> > pairs;
pairs _propvals;
};
struct printcommand
{
std::vector<std::string> _propnames;
};
typedef boost::variant<printcommand, setcommand> command;
struct statement
{
std::vector<filter> _filters;
command _command;
};
BOOST_FUSION_ADAPT_STRUCT(regex, (std::string, _pattern))
BOOST_FUSION_ADAPT_STRUCT(printcommand, (std::vector<std::string>, _propnames))
BOOST_FUSION_ADAPT_STRUCT(setcommand, (setcommand::pairs, _propvals))
BOOST_FUSION_ADAPT_STRUCT(condition, (bool, _negated)(std::string, _propertyname)(value, _operand))
BOOST_FUSION_ADAPT_STRUCT(filter, (logicOp, _op)(condition, _cond))
BOOST_FUSION_ADAPT_STRUCT(statement, (std::vector<filter>, _filters)(command, _command))
// see http://stackoverflow.com/a/14206443/85371
namespace boost { namespace phoenix { namespace stl {
template <typename This, typename Key, typename Value, typename Compare, typename Allocator, typename Index>
struct at_impl::result<This(std::map<Key,Value,Compare,Allocator>&, Index)>
{ typedef Value & type; };
template <typename This, typename Key, typename Value, typename Compare, typename Allocator, typename Index>
struct at_impl::result<This(std::map<Key,Value,Compare,Allocator> const&, Index)>
{ typedef Value const& type; };
}}}
template <typename It, typename Delim>
struct generator : karma::grammar<It, statement(), Delim>
{
generator() : generator::base_type(start)
{
using namespace karma;
property_ = karma::string;
strlit_ = '"' << karma::string << '"';
regex_ = "m/" << karma::string << "/";
value_ = (double_ | int_ | strlit_ | regex_);
negate_ = eps [ _pass = !_val ] | lit("NOT");
condition_ = negate_ << property_ << '=' << value_;
print_ = "PRINT " << property_ % ", ";
set_ = "SET " << (property_ << '=' << value_) % ", ";
command_ = print_ | set_;
static const auto logicOpNames = std::map<logicOp, std::string> {
{ logicPositive, "WHERE" },
{ logicAnd, "AND" },
{ logicOr, "OR" } };
logic_ = string [ _1 = phx::at(phx::cref(logicOpNames), _val) ];
filters_ = +(logic_ << condition_);
statement_ = filters_ << command_;
start = statement_;
}
private:
karma::rule<It, logicOp() , Delim> logic_;
karma::rule<It, statement() , Delim> statement_;
karma::rule<It, std::vector<filter>(), Delim> filters_;
karma::rule<It, command() , Delim> command_;
karma::rule<It, condition() , Delim> condition_;
karma::rule<It, statement() , Delim> start;
karma::rule<It, bool() > negate_;
karma::rule<It, printcommand()> print_;
karma::rule<It, setcommand() > set_;
karma::rule<It, std::string() > strlit_, property_;
karma::rule<It, value() > value_;
karma::rule<It, regex() > regex_;
};
template <typename It, typename Skipper = qi::space_type>
struct parser : qi::grammar<It, statement(), Skipper>
{
parser() : parser::base_type(start)
{
using namespace qi;
// no-skipper rules
property_ = alpha >> *alnum;
strlit_ = '"' >> *( (lit('\\') >> char_) | ~char_('"') ) > '"';
// with-skipper rules
regex_ = strlit_ [ _val = phx::construct<regex>(_1) ];
value_ = double_ | int_ | strlit_;
condition_ = (no_case["NOT"] >> attr(true) | attr(false))
>> property_
>> (
no_case["LIKE"] >> regex_ | '=' >> value_
);
print_ = no_case["PRINT"] >> property_ % ',';
set_ = no_case["SET"] >> (property_ >> '=' >> value_) % ',';
command_ = print_ | set_;
filters_ %= +(
(
no_case["WHERE"] [ _pass = (phx::size(_val) == 0) ] >> attr(logicPositive)
| no_case["AND"] [ _pass = (phx::size(_val) > 0) ] >> attr(logicAnd)
| no_case["OR"] [ _pass = (phx::size(_val) > 0) ] >> attr(logicOr)
)
>> condition_);
statement_ = filters_ >> command_;
start = statement_;
BOOST_SPIRIT_DEBUG_NODES((start)(condition_)(value_)(strlit_)(regex_)(property_)(statement_)(filters_)(print_)(set_)(command_));
}
private:
qi::rule<It, statement() , Skipper> statement_;
qi::rule<It, std::vector<filter>(), Skipper> filters_;
qi::rule<It, printcommand() , Skipper> print_;
qi::rule<It, setcommand() , Skipper> set_;
qi::rule<It, command() , Skipper> command_;
qi::rule<It, value() , Skipper> value_, regex_;
qi::rule<It, condition() , Skipper> condition_;
qi::rule<It, statement() , Skipper> start;
// lexemes
qi::rule<It, std::string()> strlit_, property_; // no skipper
};
bool doParse(std::string const& input)
{
auto f(begin(input)), l(end(input));
parser<decltype(f), qi::space_type> p;
statement parsed;
bool ok = qi::phrase_parse(f,l,p,qi::space,parsed);
if (ok)
{
std::cout << "parse success: '" << input << "'\n";
generator<boost::spirit::ostream_iterator, karma::space_type> gen;
std::cout << "parsed: " << karma::format_delimited(gen, karma::space, parsed) << "\n";
}
else
std::cerr << "parse failed: '" << std::string(f,l) << "'\n";
if (f!=l)
std::cerr << "trailing unparsed: '" << std::string(f,l) << "'\n";
return ok;
}
int main()
{
doParse("where currency like \"GBP|USD\" set logging = 1, logfile = \"myfile\"");
doParse("where not status = \"ok\" print ident, errorMessage");
doParse("where status = \"ok\" or not currency like \"GBP|USD\" print ident, errorMessage");
// All the extra levels of escaping get a bit ugly here. Of course, you'd be reading from a file/database/etc...
doParse("where status = \"\\\"special\\\"\" set logfile = \"C:\\\\path\\\\to\\\\logfile.txt\"");
}
/#定义BOOST_SPIRIT_调试
#定义增强\u精神\u使用\u凤凰\u V3
#包括
#包括
#包括
#包括
#包括
名称空间qi=boost::spirit::qi;
名称空间业力=提升::精神::业力;
名称空间phx=boost::phoenix;
结构正则表达式
{
std::string\u模式;
显式正则表达式(std::string const&pattern):\u pattern(pattern){}
};
typedef boost::变量值;
枚举logicOp{logicOr,logicAnd,logicPositive};
结构条件
{
布尔否定;
std::string_propertyname;
值_操作数;//值或正则表达式
};
结构过滤器
{
逻辑运算;
条件(cond),;
};
结构设置命令
{
typedef std::列表对;
成对_propvals;
};
结构打印命令
{
std::vector_propname;
};
typedef boost::variant命令;
结构语句
{
std::矢量滤波器;
命令(u命令),;
};
BOOST_FUSION_ADAPT_STRUCT(regex,(std::string,_pattern))
BOOST_FUSION_ADAPT_STRUCT(printcommand,(std::vector,_propnames))
BOOST_FUSION_ADAPT_STRUCT(setcommand,(setcommand::pairs,_propvals))
BOOST_FUSION_ADAPT_STRUCT(条件,(bool,_否定)(std::string,_propertyname)(值,_操作数))
增强融合自适应结构(滤波器,(逻辑运算,运算)(条件,条件))
BOOST_FUSION_ADAPT_STRUCT(语句,(标准::向量,_过滤器)(命令,_命令))
//看http://stackoverflow.com/a/14206443/85371
名称空间boost{名称空间phoenix{名称空间stl{
模板
位于_impl::result的结构
{typedef Value&type;};
模板
位于_impl::result的结构
{typedef Value const&type;};
}}}
模板
结构生成器:karma::grammar
{
生成器():生成器::基本类型(启动)
{
利用业力;
属性=业力::字符串;
斯特利特=“”(
无大小写[“LIKE”]>>正则表达式='>>值_
);
打印=无大小写[“打印”]>>属性“,”;
set_>=no_ucase[“set”]>>(property_>>“='>>value_>>)%;
命令=打印|设置|;
过滤器%=+(
(
无大小写[“其中”][\u pass=(phx::size(\u val)==0)]>>attr(逻辑正)
|无大小写[”和“][\u pass=(phx::size(\u val)>0)]>>attr(逻辑与)
|无大小写[”或“][\u pass=(phx::size(\u val)>0)]>>attr(logicOr)
)
>>条件),;
语句=过滤器\u>>命令;
开始=语句;
BOOST_-SPIRIT_-DEBUG_节点((开始)(条件)(值)(strlit_)(正则表达式)(属性)(语句)(过滤器)(打印)(设置)(命令));
}
私人:
qi::规则语句;
qi::规则过滤器;
qi::规则打印;
qi::规则集;
qi::规则命令;
qi::规则值,正则表达式;
规则条件;
qi::规则开始;
//词素
qi::规则strlit,属性///无跳过程序
};
booldoparse(标准::字符串常量和输入)
{
自动f(开始(输入)),l(结束(输入));
语法分析器p;
解析的语句;
bool ok=qi::phrase_parse(f,l,p,qi::space,parsed);
如果(确定)
{
cout“如果这个问题以前被回答过,我会道歉。”-这似乎有点可笑。不过,为了好玩,我编写了一个示例,它可以解析你的语法(并且可以打印出来检查),修复了一个缺少的大括号并制作了这个示例
where currency like "GBP|USD" set logging = 1, logfile = "myfile"
where not status = "ok" print ident, errorMessage
struct regex {
std::string _pattern;
explicit regex(std::string const& pattern) : _pattern(pattern) {}
};
typedef boost::variant<double, int, std::string, regex> value;
enum logicOp { logicOr, logicAnd, logicPositive };
struct condition {
bool _negated;
std::string _propertyname;
value _operand; // value or regex
};
struct filter {
logicOp _op;
condition _cond;
};
struct setcommand {
typedef std::list<std::pair<std::string, value> > pairs;
pairs _propvals;
};
struct printcommand {
std::vector<std::string> _propnames;
};
typedef boost::variant<printcommand, setcommand> command;
struct statement {
std::vector<filter> _filters;
command _command;
};
using namespace qi;
// no-skipper rules
property_ = alpha >> *alnum;
strlit_ = '"' >> *( (lit('\\') >> char_) | ~char_('"') ) > '"';
// with-skipper rules
regex_ = strlit_ [ _val = phx::construct<regex>(_1) ];
value_ = double_ | int_ | strlit_;
condition_ = (no_case["NOT"] >> attr(true) | attr(false))
>> property_
>> (
no_case["LIKE"] >> regex_ | '=' >> value_
);
print_ = no_case["PRINT"] >> property_ % ',';
set_ = no_case["SET"] >> (property_ >> '=' >> value_) % ',';
command_ = print_ | set_;
filters_ %= +(
(
no_case["WHERE"] [ _pass = (phx::size(_val) == 0) ] >> attr(logicPositive)
| no_case["AND"] [ _pass = (phx::size(_val) > 0) ] >> attr(logicAnd)
| no_case["OR"] [ _pass = (phx::size(_val) > 0) ] >> attr(logicOr)
)
>> condition_);
statement_ = filters_ >> command_;
[ _pass = (phx::size(_val) == 0) ]
no_case["NOT"] >> attr(true) | attr(false)
parse success: 'where currency like "GBP|USD" set logging = 1, logfile = "myfile"'
parsed: WHERE currency = m/GBP|USD/ SET logging=1.0, logfile="myfile"
parse success: 'where not status = "ok" print ident, errorMessage'
parsed: WHERE NOT status = "ok" PRINT ident, errorMessage
parse success: 'where status = "ok" or not currency like "GBP|USD" print ident, errorMessage'
parsed: WHERE status = "ok" OR NOT currency = m/GBP|USD/ PRINT ident, errorMessage
parse success: 'where status = "\"special\"" set logfile = "C:\\path\\to\\logfile.txt"'
parsed: WHERE status = ""special"" SET logfile="C:\path\to\logfile.txt"
//#define BOOST_SPIRIT_DEBUG
#define BOOST_SPIRIT_USE_PHOENIX_V3
#include <boost/fusion/adapted.hpp>
#include <boost/variant.hpp>
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/karma.hpp>
#include <boost/spirit/include/phoenix.hpp>
namespace qi = boost::spirit::qi;
namespace karma = boost::spirit::karma;
namespace phx = boost::phoenix;
struct regex
{
std::string _pattern;
explicit regex(std::string const& pattern) : _pattern(pattern) {}
};
typedef boost::variant<double, int, std::string, regex> value;
enum logicOp { logicOr, logicAnd, logicPositive };
struct condition
{
bool _negated;
std::string _propertyname;
value _operand; // value or regex
};
struct filter
{
logicOp _op;
condition _cond;
};
struct setcommand
{
typedef std::list<std::pair<std::string, value> > pairs;
pairs _propvals;
};
struct printcommand
{
std::vector<std::string> _propnames;
};
typedef boost::variant<printcommand, setcommand> command;
struct statement
{
std::vector<filter> _filters;
command _command;
};
BOOST_FUSION_ADAPT_STRUCT(regex, (std::string, _pattern))
BOOST_FUSION_ADAPT_STRUCT(printcommand, (std::vector<std::string>, _propnames))
BOOST_FUSION_ADAPT_STRUCT(setcommand, (setcommand::pairs, _propvals))
BOOST_FUSION_ADAPT_STRUCT(condition, (bool, _negated)(std::string, _propertyname)(value, _operand))
BOOST_FUSION_ADAPT_STRUCT(filter, (logicOp, _op)(condition, _cond))
BOOST_FUSION_ADAPT_STRUCT(statement, (std::vector<filter>, _filters)(command, _command))
// see http://stackoverflow.com/a/14206443/85371
namespace boost { namespace phoenix { namespace stl {
template <typename This, typename Key, typename Value, typename Compare, typename Allocator, typename Index>
struct at_impl::result<This(std::map<Key,Value,Compare,Allocator>&, Index)>
{ typedef Value & type; };
template <typename This, typename Key, typename Value, typename Compare, typename Allocator, typename Index>
struct at_impl::result<This(std::map<Key,Value,Compare,Allocator> const&, Index)>
{ typedef Value const& type; };
}}}
template <typename It, typename Delim>
struct generator : karma::grammar<It, statement(), Delim>
{
generator() : generator::base_type(start)
{
using namespace karma;
property_ = karma::string;
strlit_ = '"' << karma::string << '"';
regex_ = "m/" << karma::string << "/";
value_ = (double_ | int_ | strlit_ | regex_);
negate_ = eps [ _pass = !_val ] | lit("NOT");
condition_ = negate_ << property_ << '=' << value_;
print_ = "PRINT " << property_ % ", ";
set_ = "SET " << (property_ << '=' << value_) % ", ";
command_ = print_ | set_;
static const auto logicOpNames = std::map<logicOp, std::string> {
{ logicPositive, "WHERE" },
{ logicAnd, "AND" },
{ logicOr, "OR" } };
logic_ = string [ _1 = phx::at(phx::cref(logicOpNames), _val) ];
filters_ = +(logic_ << condition_);
statement_ = filters_ << command_;
start = statement_;
}
private:
karma::rule<It, logicOp() , Delim> logic_;
karma::rule<It, statement() , Delim> statement_;
karma::rule<It, std::vector<filter>(), Delim> filters_;
karma::rule<It, command() , Delim> command_;
karma::rule<It, condition() , Delim> condition_;
karma::rule<It, statement() , Delim> start;
karma::rule<It, bool() > negate_;
karma::rule<It, printcommand()> print_;
karma::rule<It, setcommand() > set_;
karma::rule<It, std::string() > strlit_, property_;
karma::rule<It, value() > value_;
karma::rule<It, regex() > regex_;
};
template <typename It, typename Skipper = qi::space_type>
struct parser : qi::grammar<It, statement(), Skipper>
{
parser() : parser::base_type(start)
{
using namespace qi;
// no-skipper rules
property_ = alpha >> *alnum;
strlit_ = '"' >> *( (lit('\\') >> char_) | ~char_('"') ) > '"';
// with-skipper rules
regex_ = strlit_ [ _val = phx::construct<regex>(_1) ];
value_ = double_ | int_ | strlit_;
condition_ = (no_case["NOT"] >> attr(true) | attr(false))
>> property_
>> (
no_case["LIKE"] >> regex_ | '=' >> value_
);
print_ = no_case["PRINT"] >> property_ % ',';
set_ = no_case["SET"] >> (property_ >> '=' >> value_) % ',';
command_ = print_ | set_;
filters_ %= +(
(
no_case["WHERE"] [ _pass = (phx::size(_val) == 0) ] >> attr(logicPositive)
| no_case["AND"] [ _pass = (phx::size(_val) > 0) ] >> attr(logicAnd)
| no_case["OR"] [ _pass = (phx::size(_val) > 0) ] >> attr(logicOr)
)
>> condition_);
statement_ = filters_ >> command_;
start = statement_;
BOOST_SPIRIT_DEBUG_NODES((start)(condition_)(value_)(strlit_)(regex_)(property_)(statement_)(filters_)(print_)(set_)(command_));
}
private:
qi::rule<It, statement() , Skipper> statement_;
qi::rule<It, std::vector<filter>(), Skipper> filters_;
qi::rule<It, printcommand() , Skipper> print_;
qi::rule<It, setcommand() , Skipper> set_;
qi::rule<It, command() , Skipper> command_;
qi::rule<It, value() , Skipper> value_, regex_;
qi::rule<It, condition() , Skipper> condition_;
qi::rule<It, statement() , Skipper> start;
// lexemes
qi::rule<It, std::string()> strlit_, property_; // no skipper
};
bool doParse(std::string const& input)
{
auto f(begin(input)), l(end(input));
parser<decltype(f), qi::space_type> p;
statement parsed;
bool ok = qi::phrase_parse(f,l,p,qi::space,parsed);
if (ok)
{
std::cout << "parse success: '" << input << "'\n";
generator<boost::spirit::ostream_iterator, karma::space_type> gen;
std::cout << "parsed: " << karma::format_delimited(gen, karma::space, parsed) << "\n";
}
else
std::cerr << "parse failed: '" << std::string(f,l) << "'\n";
if (f!=l)
std::cerr << "trailing unparsed: '" << std::string(f,l) << "'\n";
return ok;
}
int main()
{
doParse("where currency like \"GBP|USD\" set logging = 1, logfile = \"myfile\"");
doParse("where not status = \"ok\" print ident, errorMessage");
doParse("where status = \"ok\" or not currency like \"GBP|USD\" print ident, errorMessage");
// All the extra levels of escaping get a bit ugly here. Of course, you'd be reading from a file/database/etc...
doParse("where status = \"\\\"special\\\"\" set logfile = \"C:\\\\path\\\\to\\\\logfile.txt\"");
}