gpt4 book ai didi

c++ - Boost Spirit解析器规则可检测语句中的特殊结尾

转载 作者:行者123 更新时间:2023-12-02 10:13:29 25 4
gpt4 key购买 nike

这个小样本语法只是解析此语句

a        <--special (but ok because rule in grammer)
a()
a.b <--special
a.b()
a.b().c <--special
a().b.c()
a().b <--special
所有结尾都带有()的情况都是特殊的,并且在本质上应该是单独的规则。到目前为止,只有规则(特殊情况1)是正确的。如何定义一个规则来捕获所有其他不带()结尾的情况?
  lvalue_statement =

(
name >> +(
(lit('(') >> paralistopt >> lit(')')[_normal_action_call]
| (lit('.') >> name) [_normal_action_dot]
)

| name [_special_action_] // special case 1
)

另一个解释“特殊”含义的示例,您可以看到ROOT节点应具有特殊的AST节点或操作
a.b         -> SPECIAL_DOT(a,b)
a.b.c -> SPECIAL_DOT(a,NORMAL_DOT(b,c))
a(para).b.c -> SEPCIAL_DOT(NORMAL_DOT(CALL(a,para),c)

最佳答案

我对这么多语义 Action ¹颇为反感。
我也认为这不是您的问题。
用语言来讲,您希望a.b是成员取消引用,a()是调用,因此a.b()将是成员取消引用之后a.b的调用。
从这种意义上讲,a.b是正常情况,因为它不执行调用。 a.b()是相同的PLUS调用,因此会“更特殊”。
我想表达我的表达语法以反射(reflect)这一点:

lvalue = name >> *(
'.' >> name
| '(' >> paralistopt >> ')'
);
这将解析所有内容。现在您可以进行语义 Action 或属性传播
语义 Action #1
auto lvalue = name [ action("normal") ] >> *(
'.' >> name [ action("member_access") ]
| ('(' >> paralistopt >> ')') [ action("call") ]
);
妳去让我们提出一个记录 Material 的通用操作:
auto action = [](auto type) {
return [=](auto& ctx){
auto& attr = _attr(ctx);
using A = std::decay_t<decltype(attr)>;

std::cout << type << ":";
if constexpr(boost::fusion::traits::is_sequence<A>::value) {
std::cout << boost::fusion::as_vector(attr);
} else if constexpr(x3::traits::is_container<A>::value && not std::is_same_v<std::string, A>) {
std::string_view sep;
std::cout << "{";
for (auto& el : attr) { std::cout << sep << el; sep = ", "; }
std::cout << "}";
} else {
std::cout << attr;
}
std::cout << "\n";
};
};
现在我们可以解析所有样本(以及更多样本):
Live On Coliru 打印:
 === "a"
normal:a
Ok
=== "a()"
normal:a
call:{}
Ok
=== "a.b"
normal:a
member_access:b
Ok
=== "a.b()"
normal:a
member_access:b
call:{}
Ok
=== "a.b().c"
normal:a
member_access:b
call:{}
member_access:c
Ok
=== "a().b.c()"
normal:a
call:{}
member_access:b
member_access:c
call:{}
Ok
=== "a().b.c()"
normal:a
call:{}
member_access:b
member_access:c
call:{}
Ok
=== "a(q,r,s).b"
normal:a
call:{q, r, s}
member_access:b
Ok
SA#2:构建AST
让我们为AST建模:
namespace Ast {
using name = std::string;
using params = std::vector<name>;

struct member_access;
struct call;

using lvalue = boost::variant<
name,
boost::recursive_wrapper<member_access>,
boost::recursive_wrapper<call>
>;

using params = std::vector<name>;
struct member_access { lvalue obj; name member; } ;
struct call { lvalue f; params args; } ;
}
现在我们可以替换操作:
auto lvalue
= rule<struct lvalue_, Ast::lvalue> {"lvalue"}
= name [ ([](auto& ctx){ _val(ctx) = _attr(ctx); }) ] >> *(
'.' >> name [ ([](auto& ctx){ _val(ctx) = Ast::member_access{ _val(ctx), _attr(ctx) }; }) ]
| ('(' >> paralistopt >> ')') [ ([](auto& ctx){ _val(ctx) = Ast::call{ _val(ctx), _attr(ctx) }; }) ]
);

That's ugly - I don't recommend writing your code this way, but at least it drives home how few steps are involved.


还添加了一些输出运算符:
namespace Ast { // debug output
static inline std::ostream& operator<<(std::ostream& os, Ast::member_access const& ma) {
return os << ma.obj << "." << ma.member;
}
static inline std::ostream& operator<<(std::ostream& os, Ast::call const& c) {
std::string_view sep;
os << c.f << "(";
for (auto& arg: c.args) { os << sep << arg; sep = ", "; }
return os << ")";
}
}
现在可以使用完整的AST解析所有内容: Live On Coliru ,打印:
"a" -> a
"a()" -> a()
"a.b" -> a.b
"a.b()" -> a.b()
"a.b().c" -> a.b().c
"a().b.c()" -> a().b.c()
"a().b" -> a().b
"a(q,r,s).b" -> a(q, r, s).b
自动传播
其实我有点受困于此。我花了太长时间才做对并以一种有用的方式解析关联性,所以我停止尝试。让我们通过清理第二个SA总结来总结一下:
概要
使操作更具可读性:
auto passthrough =
[](auto& ctx) { _val(ctx) = _attr(ctx); };
template <typename T> auto binary_ =
[](auto& ctx) { _val(ctx) = T { _val(ctx), _attr(ctx) }; };

auto lvalue
= rule<struct lvalue_, Ast::lvalue> {"lvalue"}
= name [ passthrough ] >> *(
'.' >> name [ binary_<Ast::member_access> ]
| ('(' >> paralistopt >> ')') [ binary_<Ast::call> ]
);
现在还有许多问题:
您可能需要一个不只是解析左值表达式的更通用的表达式语法(例如 f(foo, 42)应该解析, len("foo") + 17吗?)。
为此,左值/右值区分不属于语法:它主要是语义区分。

I happen to have created an extended parser that does all that + evaluation against proper LValues (while supporting general values). I'd suggest looking at the [extended chat][3] at this answer and the resulting code on github: https://github.com/sehe/qi-extended-parser-evaluator .


完整 list
Live On Coliru
#include <boost/spirit/home/x3.hpp>
#include <iostream>
#include <iomanip>
namespace x3 = boost::spirit::x3;

namespace Ast {
using name = std::string;
using params = std::vector<name>;

struct member_access;
struct call;

using lvalue = boost::variant<
name,
boost::recursive_wrapper<member_access>,
boost::recursive_wrapper<call>
>;

using params = std::vector<name>;
struct member_access { lvalue obj; name member; } ;
struct call { lvalue f; params args; } ;
}

namespace Ast { // debug output
static inline std::ostream& operator<<(std::ostream& os, Ast::member_access const& ma) {
return os << ma.obj << "." << ma.member;
}
static inline std::ostream& operator<<(std::ostream& os, Ast::call const& c) {
std::string_view sep;
os << c.f << "(";
for (auto& arg: c.args) { os << sep << arg; sep = ", "; }
return os << ")";
}
}

namespace Parser {
using namespace x3;

auto name
= rule<struct string_, Ast::name> {"name"}
= lexeme[alpha >> *(alnum|char_("_"))];

auto paralistopt
= rule<struct params_, Ast::params> {"params"}
= -(name % ',');

auto passthrough =
[](auto& ctx) { _val(ctx) = _attr(ctx); };
template <typename T> auto binary_ =
[](auto& ctx) { _val(ctx) = T { _val(ctx), _attr(ctx) }; };

auto lvalue
= rule<struct lvalue_, Ast::lvalue> {"lvalue"}
= name [ passthrough ] >> *(
'.' >> name [ binary_<Ast::member_access> ]
| ('(' >> paralistopt >> ')') [ binary_<Ast::call> ]
);

auto start = skip(space) [ lvalue ];
}

int main() {
for (std::string const input: {
"a", // special (but ok because rule in grammer)
"a()",
"a.b", // special
"a.b()",
"a.b().c", // special
"a().b.c()",
"a().b", // special
"a(q,r,s).b",
})
{
std::cout << std::quoted(input) << " -> ";

auto f = begin(input), l = end(input);
Ast::lvalue parsed;
if (parse(f, l, Parser::start, parsed)) {
std::cout << parsed << "\n";;
} else {
std::cout << "Failed\n";
}
if (f!=l) {
std::cout << " -- Remainig: " << std::quoted(std::string(f,l)) << "\n";
}
}
}
版画
"a" -> a
"a()" -> a()
"a.b" -> a.b
"a.b()" -> a.b()
"a.b().c" -> a.b().c
"a().b.c()" -> a().b.c()
"a().b" -> a().b
"a(q,r,s).b" -> a(q, r, s).b
¹(在存在回溯的情况下会导致困惑,请参阅 Boost Spirit: "Semantic actions are evil"?)

关于c++ - Boost Spirit解析器规则可检测语句中的特殊结尾,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/62714352/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com