灵气文法可以作为灵业文法复用吗?
Is it possible to reuse a Spirit Qi grammar as a Spirit Karma grammar?
我有一个用于解析输入的 Qi 语法定义。后来我有一个 Karma 生成器以类似于输入的方式输出。
这可能吗?似乎可以自动将解析器语法转换为生成器语法(??)。
#include <boost/config/warning_disable.hpp>
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/karma.hpp>
#include <iostream>
int main(){
//test input
std::string s = "Xx 1.233 pseudo";
//input variables
std::string element;
double mass;
std::string pseudo;
auto GRAMMAR =
boost::spirit::qi::lexeme[+(boost::spirit::qi::char_ - ' ' - '\n')]
>> boost::spirit::qi::double_
>> boost::spirit::qi::lexeme[+(boost::spirit::qi::char_ - ' ' - '\n')];
bool r = boost::spirit::qi::phrase_parse(
s.begin(), s.end(),
GRAMMAR,
boost::spirit::qi::space, element, mass, pseudo
);
std::cout << boost::spirit::karma::format(
GRAMMAR ??? is it possible?
,
element,
mass,
pseudo
);
}
遗憾的是,不可能以一般方式实现您想要的(或者至少我不知道如何实现),但如果您愿意只使用 Spirit.Qi 的有限子集,则可以使用以下方法可以工作。
首先要知道的是,当您使用类似以下内容时:
int_ >> double_
您只有一个 Boost.Proto 表达式来描述多个终端以及它们之间的关系。该表达式本身并没有 "know" 关于如何解析一个 int 然后一个 double 的任何信息。每当您使用 parse
/phrase_parse
或将这些 Proto 表达式之一分配给 rule
精神 "compiles" 该表达式用于域(Qi 或 Karma)并创建 parsers/generators 做实际工作。
Here 你可以看到一个小例子,它显示了 Proto 和编译后的 Qi 表达式的确切类型:
Raw proto type:
boost::proto::exprns_::expr<boost::proto::tagns_::tag::shift_right, boost::proto::argsns_::list2<boost::spirit::terminal<boost::spirit::tag::int_> const&, boost::spirit::terminal<boost::spirit::tag::double_> const&>, 2l>
"Pretty" proto type:
shift_right(
terminal(boost::spirit::tag::int_)
, terminal(boost::spirit::tag::double_)
)
Compiled Qi type:
boost::spirit::qi::sequence<boost::fusion::cons<boost::spirit::qi::any_int_parser<int, 10u, 1u, -1>, boost::fusion::cons<boost::spirit::qi::any_real_parser<double, boost::spirit::qi::real_policies<double> >, boost::fusion::nil_> > >
只要您可以访问原始表达式,就可以使用 Proto transforms/grammars 将其转换为合适的 Karma 表达式。
在下面的示例中,我使用了以下转换:
Qi |Karma |Reason
------------|---------------|------
lexeme[expr]|verbatim[expr] | lexeme does not exist in Karma
omit[expr] |no_delimit[eps]| omit consumes an attribute in Karma
a >> b |a << b |
a > b |a << b | < does not exist in Karma
a - b |a | - does not exist in Karma
为了实现这种转换,您可以使用 boost::proto::or_
得到类似于:
struct Grammar : proto::or_<
proto::when<Matcher1,Transform1>,
proto::when<Matcher2,Transform2>,
Matcher3,
Matcher4
>{};
我会尝试解释它是如何工作的。
MatcherN
在下面的例子中可以是:
proto::terminal<boost::spirit::tag::omit>
: 只匹配特定的终端。
proto::terminal<proto::_>
:匹配之前没有具体匹配的任何终端。
proto::subscript<proto::terminal<boost::spirit::tag::omit>,proto::_>
:匹配 omit[expr]
其中 expr
可以是任何东西。
proto::shift_right<ToKarma,ToKarma>
:匹配expr1 >> expr2
,其中expr1
和expr2
必须递归地符合ToKarma
语法。
proto::nary_expr<proto::_,proto::vararg<ToKarma> >
:匹配任何 n 元(一元、二进制或实际上像函数调用 a(b,c,d,e)
的 n 元),其中表达式的每个元素都符合 ToKarma 语法。
本例中的所有TransformN
都是表达式构建器,这里做一些解释:
_make_terminal(boost::spirit::tag::lexeme())
:构建一个proto::terminal<boost::spirit::tag::lexeme>
(注意标签后面必须加()
,忘记了会报错)
_make_subscript(_make_terminal(tag::no_delimit()), _make_terminal(tag::eps()))
:构建一个proto::subscript<proto::terminal<tag::no_delimit>, proto::terminal<tag::eps> >
,或者相当于no_delimit[eps]
。
_make_shift_left(ToKarma(proto::_left), ToKarma(proto::_right))
:proto::_left
表示取原表达式的lhs。 ToKarma(proto::_left)
表示递归地将 ToKarma grammar/transform 应用于原始表达式的 lhs。整个_make_shift_left
基本搭建transformed_lhs << transformed_rhs
.
A MatcherN
本身(不在 proto::when
内)是一个 shorthand 用于构建相同类型的表达式,使用递归地将转换应用于原始结果的结果作为元素元素。
#include <iostream>
#include <string>
#include <tuple>
#include <boost/config/warning_disable.hpp>
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/karma.hpp>
#include <boost/fusion/include/std_tuple.hpp>
namespace proto= boost::proto;
struct ToKarma: proto::or_<
//translation of directives
proto::when<proto::terminal<boost::spirit::tag::lexeme>, proto::_make_terminal(boost::spirit::tag::verbatim())>, //lexeme -> verbatim
proto::when<
proto::subscript<proto::terminal<boost::spirit::tag::omit>,proto::_>, //omit[expr] -> no_delimit[eps]
proto::_make_subscript(proto::_make_terminal(boost::spirit::tag::no_delimit()),proto::_make_terminal(boost::spirit::tag::eps()))
>,
proto::terminal<proto::_>, //if the expression is any other terminal leave it as is
//translation of operators
proto::when<proto::shift_right<ToKarma,ToKarma>, proto::_make_shift_left(ToKarma(proto::_left),ToKarma(proto::_right)) >, //changes '>>' into '<<'
proto::when<proto::greater<ToKarma,ToKarma>, proto::_make_shift_left(ToKarma(proto::_left),ToKarma(proto::_right)) >, //changes '>' into '<<'
proto::when<proto::minus<ToKarma,ToKarma>, ToKarma(proto::_left)>, //changes 'expr-whatever' into 'expr'
proto::nary_expr<proto::_,proto::vararg<ToKarma> > //if it's anything else leave it unchanged and recurse into the expression tree
>{};
template <typename ... Attr, typename Parser>
void test(const std::string& input, const Parser& parser)
{
std::cout << "Original: \"" << input << "\"\n";
std::tuple<Attr...> attr;
std::string::const_iterator iter = input.begin(), end = input.end();
bool result = boost::spirit::qi::phrase_parse(iter,end,parser,boost::spirit::qi::space,attr);
if(result && iter==end)
{
ToKarma to_karma;
std::cout << "Generated: \"" << boost::spirit::karma::format_delimited(to_karma(parser), boost::spirit::karma::space, attr) << '"' << std::endl;
}
else
{
std::cout << "Parsing failed. Unparsed: ->" << std::string(iter,end) << "<-" << std::endl;
}
}
int main(){
using namespace boost::spirit::qi;
test<std::string,double,std::string >("Xx 1.233 pseudo", lexeme[+(char_-' '-'\n')] >> double_ >> lexeme[+(char_-' '-'\n')]);
test<int,double>("foo 1 2.5", omit[lexeme[+alpha]] > int_ > double_);
}
PS:
绝对行不通的事情:
qi::rule
qi::grammar
qi::symbols
Karma 中不存在的东西:
qi::attr
qi::matches
qi::hold
- 排列解析器
^
- 顺序或解析器
||
Karma 中具有不同语义的事物:
qi::skip
- 与谓词解析器
&
- 非谓词解析器
!
我有一个用于解析输入的 Qi 语法定义。后来我有一个 Karma 生成器以类似于输入的方式输出。
这可能吗?似乎可以自动将解析器语法转换为生成器语法(??)。
#include <boost/config/warning_disable.hpp>
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/karma.hpp>
#include <iostream>
int main(){
//test input
std::string s = "Xx 1.233 pseudo";
//input variables
std::string element;
double mass;
std::string pseudo;
auto GRAMMAR =
boost::spirit::qi::lexeme[+(boost::spirit::qi::char_ - ' ' - '\n')]
>> boost::spirit::qi::double_
>> boost::spirit::qi::lexeme[+(boost::spirit::qi::char_ - ' ' - '\n')];
bool r = boost::spirit::qi::phrase_parse(
s.begin(), s.end(),
GRAMMAR,
boost::spirit::qi::space, element, mass, pseudo
);
std::cout << boost::spirit::karma::format(
GRAMMAR ??? is it possible?
,
element,
mass,
pseudo
);
}
遗憾的是,不可能以一般方式实现您想要的(或者至少我不知道如何实现),但如果您愿意只使用 Spirit.Qi 的有限子集,则可以使用以下方法可以工作。
首先要知道的是,当您使用类似以下内容时:
int_ >> double_
您只有一个 Boost.Proto 表达式来描述多个终端以及它们之间的关系。该表达式本身并没有 "know" 关于如何解析一个 int 然后一个 double 的任何信息。每当您使用 parse
/phrase_parse
或将这些 Proto 表达式之一分配给 rule
精神 "compiles" 该表达式用于域(Qi 或 Karma)并创建 parsers/generators 做实际工作。
Here 你可以看到一个小例子,它显示了 Proto 和编译后的 Qi 表达式的确切类型:
Raw proto type:
boost::proto::exprns_::expr<boost::proto::tagns_::tag::shift_right, boost::proto::argsns_::list2<boost::spirit::terminal<boost::spirit::tag::int_> const&, boost::spirit::terminal<boost::spirit::tag::double_> const&>, 2l>
"Pretty" proto type:
shift_right(
terminal(boost::spirit::tag::int_)
, terminal(boost::spirit::tag::double_)
)
Compiled Qi type:
boost::spirit::qi::sequence<boost::fusion::cons<boost::spirit::qi::any_int_parser<int, 10u, 1u, -1>, boost::fusion::cons<boost::spirit::qi::any_real_parser<double, boost::spirit::qi::real_policies<double> >, boost::fusion::nil_> > >
只要您可以访问原始表达式,就可以使用 Proto transforms/grammars 将其转换为合适的 Karma 表达式。
在下面的示例中,我使用了以下转换:
Qi |Karma |Reason
------------|---------------|------
lexeme[expr]|verbatim[expr] | lexeme does not exist in Karma
omit[expr] |no_delimit[eps]| omit consumes an attribute in Karma
a >> b |a << b |
a > b |a << b | < does not exist in Karma
a - b |a | - does not exist in Karma
为了实现这种转换,您可以使用 boost::proto::or_
得到类似于:
struct Grammar : proto::or_<
proto::when<Matcher1,Transform1>,
proto::when<Matcher2,Transform2>,
Matcher3,
Matcher4
>{};
我会尝试解释它是如何工作的。
MatcherN
在下面的例子中可以是:
proto::terminal<boost::spirit::tag::omit>
: 只匹配特定的终端。proto::terminal<proto::_>
:匹配之前没有具体匹配的任何终端。proto::subscript<proto::terminal<boost::spirit::tag::omit>,proto::_>
:匹配omit[expr]
其中expr
可以是任何东西。proto::shift_right<ToKarma,ToKarma>
:匹配expr1 >> expr2
,其中expr1
和expr2
必须递归地符合ToKarma
语法。proto::nary_expr<proto::_,proto::vararg<ToKarma> >
:匹配任何 n 元(一元、二进制或实际上像函数调用a(b,c,d,e)
的 n 元),其中表达式的每个元素都符合 ToKarma 语法。
本例中的所有TransformN
都是表达式构建器,这里做一些解释:
_make_terminal(boost::spirit::tag::lexeme())
:构建一个proto::terminal<boost::spirit::tag::lexeme>
(注意标签后面必须加()
,忘记了会报错)_make_subscript(_make_terminal(tag::no_delimit()), _make_terminal(tag::eps()))
:构建一个proto::subscript<proto::terminal<tag::no_delimit>, proto::terminal<tag::eps> >
,或者相当于no_delimit[eps]
。_make_shift_left(ToKarma(proto::_left), ToKarma(proto::_right))
:proto::_left
表示取原表达式的lhs。ToKarma(proto::_left)
表示递归地将 ToKarma grammar/transform 应用于原始表达式的 lhs。整个_make_shift_left
基本搭建transformed_lhs << transformed_rhs
.
A MatcherN
本身(不在 proto::when
内)是一个 shorthand 用于构建相同类型的表达式,使用递归地将转换应用于原始结果的结果作为元素元素。
#include <iostream>
#include <string>
#include <tuple>
#include <boost/config/warning_disable.hpp>
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/karma.hpp>
#include <boost/fusion/include/std_tuple.hpp>
namespace proto= boost::proto;
struct ToKarma: proto::or_<
//translation of directives
proto::when<proto::terminal<boost::spirit::tag::lexeme>, proto::_make_terminal(boost::spirit::tag::verbatim())>, //lexeme -> verbatim
proto::when<
proto::subscript<proto::terminal<boost::spirit::tag::omit>,proto::_>, //omit[expr] -> no_delimit[eps]
proto::_make_subscript(proto::_make_terminal(boost::spirit::tag::no_delimit()),proto::_make_terminal(boost::spirit::tag::eps()))
>,
proto::terminal<proto::_>, //if the expression is any other terminal leave it as is
//translation of operators
proto::when<proto::shift_right<ToKarma,ToKarma>, proto::_make_shift_left(ToKarma(proto::_left),ToKarma(proto::_right)) >, //changes '>>' into '<<'
proto::when<proto::greater<ToKarma,ToKarma>, proto::_make_shift_left(ToKarma(proto::_left),ToKarma(proto::_right)) >, //changes '>' into '<<'
proto::when<proto::minus<ToKarma,ToKarma>, ToKarma(proto::_left)>, //changes 'expr-whatever' into 'expr'
proto::nary_expr<proto::_,proto::vararg<ToKarma> > //if it's anything else leave it unchanged and recurse into the expression tree
>{};
template <typename ... Attr, typename Parser>
void test(const std::string& input, const Parser& parser)
{
std::cout << "Original: \"" << input << "\"\n";
std::tuple<Attr...> attr;
std::string::const_iterator iter = input.begin(), end = input.end();
bool result = boost::spirit::qi::phrase_parse(iter,end,parser,boost::spirit::qi::space,attr);
if(result && iter==end)
{
ToKarma to_karma;
std::cout << "Generated: \"" << boost::spirit::karma::format_delimited(to_karma(parser), boost::spirit::karma::space, attr) << '"' << std::endl;
}
else
{
std::cout << "Parsing failed. Unparsed: ->" << std::string(iter,end) << "<-" << std::endl;
}
}
int main(){
using namespace boost::spirit::qi;
test<std::string,double,std::string >("Xx 1.233 pseudo", lexeme[+(char_-' '-'\n')] >> double_ >> lexeme[+(char_-' '-'\n')]);
test<int,double>("foo 1 2.5", omit[lexeme[+alpha]] > int_ > double_);
}
PS:
绝对行不通的事情:
qi::rule
qi::grammar
qi::symbols
Karma 中不存在的东西:
qi::attr
qi::matches
qi::hold
- 排列解析器
^
- 顺序或解析器
||
Karma 中具有不同语义的事物:
qi::skip
- 与谓词解析器
&
- 非谓词解析器
!