C++ 未应用Boost spirit分析规则
我看不出我的错误。。这个规则可以解析一些东西,但最后两个示例不可以。谁能给我一个提示吗 目标是一个解析器,它可以识别成员属性访问和成员函数调用。也以某种方式被锁住了C++ 未应用Boost spirit分析规则,c++,parsing,boost-spirit,C++,Parsing,Boost Spirit,我看不出我的错误。。这个规则可以解析一些东西,但最后两个示例不可以。谁能给我一个提示吗 目标是一个解析器,它可以识别成员属性访问和成员函数调用。也以某种方式被锁住了 a() a(para) x.a() x.a(para) x.a(para).g(para).j() x.y x.y.z x.y.z() <---fail y.z.z(para) <--- fail a() a(第7段) x、 () x、 a(第7段) x、 a(第.g(第.j段)() x、 y
a()
a(para)
x.a()
x.a(para)
x.a(para).g(para).j()
x.y
x.y.z
x.y.z() <---fail
y.z.z(para) <--- fail
a()
a(第7段)
x、 ()
x、 a(第7段)
x、 a(第.g(第.j段)()
x、 y
整数解
x、 y.z()名称[\u val=\u 1]
>>*(lit(“(”)>paralistopt>lit(“)”)>>iter\u pos)[\u val=construct(type\u cmd\u fnc\u call,LOCATION\u NODE\u iter(\u val,\u 2),key\u this,construct(\u val),key\u参数,construct(\u 1))]
>>*(lit('.')>>name_pure>>lit('(')>paralistopt>lit(')>>iter_pos)[u val=construct(type_cmd_fnc_call,LOCATION_NODE_iter(_val,_3),key_this,constructure(_val),key_callname,constructure(_1),key_参数,construct(_2))]
>>*(lit('.')>>name\u pure>>iter\u pos)[u val=construct(键入命令点调用,位置节点调用,键为this,键为[u val],键为[u val],键为propname,键为[u 1])]
;
多谢各位
马库斯试着改变一下
>> *(lit('.') >> name_pure >> lit('(') > paralistopt > lit(')'))
到
你提供的信息很少。让我来跟你说说我的猜谜游戏: 让我们假设您想要解析一种简单的“语言”,它只允许成员表达式和函数调用,但被链接 现在,您的语法没有说明参数(尽管参数列表可以是空的),所以让我继续下一步,假设您希望在那里接受相同类型的表达式(因此
foo(a)
是可以的,但是bar(foo(a))
或bar(b.foo(a))
)
由于您接受函数调用的链接,因此函数似乎是一级对象(函数可以返回函数),因此也应该接受foo(a)(b,c,d)
您没有提到它,但是参数通常包括文本(sqrt(9)
,或者println(“hello world”)
)
其他项目:
- 您没有说,但可能希望忽略某些点中的空白
- 从
(ab)使用,您似乎对跟踪生成的AST中的原始源位置感兴趣iter\u pos
namespace Ast {
using Identifier = boost::iterator_range<It>;
struct MemberExpression;
struct FunctionCall;
using Expression = boost::variant<
double, // some literal types
std::string,
// non-literals
Identifier,
boost::recursive_wrapper<MemberExpression>,
boost::recursive_wrapper<FunctionCall>
>;
struct MemberExpression {
Expression object; // antecedent
Identifier member; // function or field
};
using Parameter = Expression;
using Parameters = std::vector<Parameter>;
struct FunctionCall {
Expression function; // could be a member function
Parameters parameters;
};
}
我们会发现我们不使用这些,因为语义动作在这里更方便
2.匹配语法
在此框架中,大多数规则都受益于自动属性传播。没有的是表达式
:
qi::rule<It, Expression()> start;
using Skipper = qi::space_type;
qi::rule<It, Expression(), Skipper> expression, literal;
qi::rule<It, Parameters(), Skipper> parameters;
// lexemes
qi::rule<It, Identifier()> identifier;
qi::rule<It, std::string()> string_;
就这些。我们准备出发了
3.演示
我创建了一个如下所示的测试台:
int main() {
using It = std::string::const_iterator;
Parser::Grammar<It> const g;
for (std::string const input : {
"a()", "a(para)", "x.a()", "x.a(para)", "x.a(para).g(para).j()", "x.y", "x.y.z",
"x.y.z()",
"y.z.z(para)",
// now let's add some funkyness that you didn't mention
"bar(foo(a))",
"bar(b.foo(a))",
"foo(a)(b, c, d)", // first class functions
"sqrt(9)",
"println(\"hello world\")",
"allocate(strlen(\"aaaaa\"))",
"3.14",
"object.rotate(180)",
"object.rotate(event.getAngle(), \"torque\")",
"app.mainwindow().find_child(\"InputBox\").font().size(12)",
"app.mainwindow().find_child(\"InputBox\").font(config().preferences.baseFont(style.PROPORTIONAL))"
}) {
std::cout << " =========== '" << input << "' ========================\n";
It f(input.begin()), l(input.end());
Ast::Expression parsed;
bool ok = parse(f, l, g, parsed);
if (ok) {
std::cout << "Parsed: " << parsed << "\n";
}
else
std::cout << "Parse failed\n";
if (f != l)
std::cout << "Remaining unparsed input: '" << std::string(f, l) << "'\n";
}
}
4.好得难以置信?
你说得对。我作弊了。我没有向您展示调试打印解析的AST所需的代码:
namespace Ast {
static inline std::ostream& operator<<(std::ostream& os, MemberExpression const& me) {
return os << me.object << "." << me.member;
}
static inline std::ostream& operator<<(std::ostream& os, FunctionCall const& fc) {
os << fc.function << "(";
bool first = true;
for (auto& p : fc.parameters) { if (!first) os << ", "; first = false; os << p; }
return os << ")";
}
}
关于标识符提取:
std::vector<Identifier> all_identifiers(Expression const& expr) {
std::vector<Identifier> result;
struct Harvest {
using result_type = void;
std::back_insert_iterator<std::vector<Identifier> > out;
void operator()(Identifier const& id) { *out++ = id; }
void operator()(MemberExpression const& me) { apply_visitor(*this, me.object); *out++ = me.member; }
void operator()(FunctionCall const& fc) {
apply_visitor(*this, fc.function);
for (auto& p : fc.parameters) apply_visitor(*this, p);
}
// non-identifier expressions
void operator()(std::string const&) { }
void operator()(double) { }
} harvest { back_inserter(result) };
boost::apply_visitor(harvest, expr);
return result;
}
这个评论栏让我抓狂(8-)我把规则和动作放在了原来的帖子里。。按照你的规则,我不知道如何运用我的行动。你能描述一下你想要达到的目标吗?你在解析什么,转换成什么?我很确定你的代码是不必要的复杂。(请参阅)您的代码示例似乎也缺少相关位。如果您包含代码,请使其独立。您好,非常感谢您的提示。您的解决方案工作得非常好,我能够进行所有调用,并且能够在解析器上进行链接工作!救命啊!我需要一些时间来回顾“iter_pos”的内容,您的解决方案看起来更自然,但我有一些更复杂的项目来跟踪位置,而不仅仅是标识符。。因此,我必须检查您的解决方案将如何在这里工作Markus我很高兴看到更大的项目。事实上,我建议注释AST节点可以与语法正交(IIRC有一个编译器演示使用
qi::on_error
)我记得有一半是正确的:calc[78]、Concure[123]和mini_c教程使用qi::on_success
和“annotation_function
”。你可能有兴趣看看thosegot吧。。。很好的工作流程,解析器中没有更多的iter_pos!谢谢again@Markus我从中得到了更多的乐趣——有很多微妙之处被评论到了。请注意如何为String
取消注释AnnotateTag
基类,并获得“神奇的”注释字符串节点(当前仅注释父表达式
节点)。
qi::rule<It, Expression()> start;
using Skipper = qi::space_type;
qi::rule<It, Expression(), Skipper> expression, literal;
qi::rule<It, Parameters(), Skipper> parameters;
// lexemes
qi::rule<It, Identifier()> identifier;
qi::rule<It, std::string()> string_;
struct mme_f { MemberExpression operator()(Expression lhs, Identifier rhs) const { return { lhs, rhs }; } };
struct mfc_f { FunctionCall operator()(Expression f, Parameters params) const { return { f, params }; } };
phx::function<mme_f> make_member_expression;
phx::function<mfc_f> make_function_call;
expression
= literal [_val=_1]
| identifier [_val=_1] >> *(
('.' >> identifier) [ _val = make_member_expression(_val, _1)]
| ('(' >> parameters >> ')') [ _val = make_function_call(_val, _1) ]
);
int main() {
using It = std::string::const_iterator;
Parser::Grammar<It> const g;
for (std::string const input : {
"a()", "a(para)", "x.a()", "x.a(para)", "x.a(para).g(para).j()", "x.y", "x.y.z",
"x.y.z()",
"y.z.z(para)",
// now let's add some funkyness that you didn't mention
"bar(foo(a))",
"bar(b.foo(a))",
"foo(a)(b, c, d)", // first class functions
"sqrt(9)",
"println(\"hello world\")",
"allocate(strlen(\"aaaaa\"))",
"3.14",
"object.rotate(180)",
"object.rotate(event.getAngle(), \"torque\")",
"app.mainwindow().find_child(\"InputBox\").font().size(12)",
"app.mainwindow().find_child(\"InputBox\").font(config().preferences.baseFont(style.PROPORTIONAL))"
}) {
std::cout << " =========== '" << input << "' ========================\n";
It f(input.begin()), l(input.end());
Ast::Expression parsed;
bool ok = parse(f, l, g, parsed);
if (ok) {
std::cout << "Parsed: " << parsed << "\n";
}
else
std::cout << "Parse failed\n";
if (f != l)
std::cout << "Remaining unparsed input: '" << std::string(f, l) << "'\n";
}
}
=========== 'a()' ========================
Parsed: a()
=========== 'a(para)' ========================
Parsed: a(para)
=========== 'x.a()' ========================
Parsed: x.a()
=========== 'x.a(para)' ========================
Parsed: x.a(para)
=========== 'x.a(para).g(para).j()' ========================
Parsed: x.a(para).g(para).j()
=========== 'x.y' ========================
Parsed: x.y
=========== 'x.y.z' ========================
Parsed: x.y.z
=========== 'x.y.z()' ========================
Parsed: x.y.z()
=========== 'y.z.z(para)' ========================
Parsed: y.z.z(para)
=========== 'bar(foo(a))' ========================
Parsed: bar(foo(a))
=========== 'bar(b.foo(a))' ========================
Parsed: bar(b.foo(a))
=========== 'foo(a)(b, c, d)' ========================
Parsed: foo(a)(b, c, d)
=========== 'sqrt(9)' ========================
Parsed: sqrt(9)
=========== 'println("hello world")' ========================
Parsed: println(hello world)
=========== 'allocate(strlen("aaaaa"))' ========================
Parsed: allocate(strlen(aaaaa))
=========== '3.14' ========================
Parsed: 3.14
=========== 'object.rotate(180)' ========================
Parsed: object.rotate(180)
=========== 'object.rotate(event.getAngle(), "torque")' ========================
Parsed: object.rotate(event.getAngle(), torque)
=========== 'app.mainwindow().find_child("InputBox").font().size(12)' ========================
Parsed: app.mainwindow().find_child(InputBox).font().size(12)
=========== 'app.mainwindow().find_child("InputBox").font(config().preferences.baseFont(style.PROPORTIONAL))' ========================
Parsed: app.mainwindow().find_child(InputBox).font(config().preferences.baseFont(style.PROPORTIONAL))
namespace Ast {
static inline std::ostream& operator<<(std::ostream& os, MemberExpression const& me) {
return os << me.object << "." << me.member;
}
static inline std::ostream& operator<<(std::ostream& os, FunctionCall const& fc) {
os << fc.function << "(";
bool first = true;
for (auto& p : fc.parameters) { if (!first) os << ", "; first = false; os << p; }
return os << ")";
}
}
using IOManip::showpos;
for (auto& id : all_identifiers(parsed)) {
std::cout << " - " << id << " at " << showpos(id, input) << "\n";
}
namespace IOManip {
struct showpos_t {
boost::iterator_range<It> fragment;
std::string const& source;
friend std::ostream& operator<<(std::ostream& os, showpos_t const& manip) {
auto ofs = [&](It it) { return it - manip.source.begin(); };
return os << "[" << ofs(manip.fragment.begin()) << ".." << ofs(manip.fragment.end()) << ")";
}
};
showpos_t showpos(boost::iterator_range<It> fragment, std::string const& source) {
return {fragment, source};
}
}
std::vector<Identifier> all_identifiers(Expression const& expr) {
std::vector<Identifier> result;
struct Harvest {
using result_type = void;
std::back_insert_iterator<std::vector<Identifier> > out;
void operator()(Identifier const& id) { *out++ = id; }
void operator()(MemberExpression const& me) { apply_visitor(*this, me.object); *out++ = me.member; }
void operator()(FunctionCall const& fc) {
apply_visitor(*this, fc.function);
for (auto& p : fc.parameters) apply_visitor(*this, p);
}
// non-identifier expressions
void operator()(std::string const&) { }
void operator()(double) { }
} harvest { back_inserter(result) };
boost::apply_visitor(harvest, expr);
return result;
}
=========== 'app.mainwindow().find_child("InputBox").font(config().preferences.baseFont(style.PROPORTIONAL))' ========================
Parsed: app.mainwindow().find_child(InputBox).font(config().preferences.baseFont(style.PROPORTIONAL))
- app at [0..3)
- mainwindow at [4..14)
- find_child at [17..27)
- font at [40..44)
- config at [45..51)
- preferences at [54..65)
- baseFont at [66..74)
- style at [75..80)
- PROPORTIONAL at [81..93)