ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

c-不应用Boost Spirit解析规则

2019-10-09 14:16:20  阅读:193  来源: 互联网

标签: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
  lvalue =
         iter_pos >> name[_val = _1]
          >> *(lit('(') > paralistopt  > lit(')') >> iter_pos)[_val = construct<common_node>(type_cmd_fnc_call, LOCATION_NODE_ITER(_val, _2), key_this, construct<common_node>(_val), key_parameter, construct<std::vector<common_node> >(_1))]        
       >> *(lit('.') >> name_pure >> lit('(') > paralistopt > lit(')') >> iter_pos)[_val = construct<common_node>(type_cmd_fnc_call, LOCATION_NODE_ITER(_val, _3), key_this, construct<common_node>(_val), key_callname, construct<std::wstring>(_1), key_parameter, construct<std::vector<common_node> >(_2))]
       >> *(lit('.') >> name_pure >> iter_pos)[_val = construct<common_node>(type_cmd_dot_call, LOCATION_NODE_ITER(_val, _2), key_this, construct<common_node>(_val), key_propname, construct<std::wstring>(_1))]
    ;

谢谢
  马库斯

解决方法:

您提供的信息很少.让我通过这个猜谜游戏让您感到幽默:

让我们假设您想解析一个简单的“语言”,该语言仅允许成员表达式和函数调用,但被链接在一起.

现在,您的语法对参数一无所知(尽管很显然参数列表可以为空),所以让我继续前进,假设您要在那里接受相同类型的表达式(因此foo(a)可以,而且还有bar(foo(a))或bar(b.foo(a))).

由于您接受函数调用的链接,因此看来函数是一类对象(并且函数可以返回函数),因此也应该接受foo(a)(b,c,d).

您没有提到它,但是参数通常包含文字(sqrt(9)或println(“ hello world”)).

其他项目:

>您未说,但您可能想忽略某些地方的空白
>从iter_pos(ab)中使用,看来您有兴趣跟踪生成的AST中的原始源位置.

1.定义一个AST

我们应该像以往一样简单:

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;
    };
}

NOTE We’re not going to focus on showing source locations, but already made one provision, storing identifiers as an iterator-range.

NOTE Fusion-adapting the only types not directly supported by Spirit:

06001

We will find that we don’t use these, because Semantic Actions are more convenient here.

2.匹配语法

Grammar() : Grammar::base_type(start) {
    using namespace qi;
    start = skip(space) [expression];

    identifier = raw [ (alpha|'_') >> *(alnum|'_') ];
    parameters = -(expression % ',');

    expression 
        = literal 
        | identifier  >> *(
                    ('.' >> identifier)        
                  | ('(' >> parameters >> ')') 
                );

    literal = double_ | string_;
    string_ = '"' >> *('\\' >> char_ | ~char_('"')) >> '"';

    BOOST_SPIRIT_DEBUG_NODES(
            (identifier)(start)(parameters)(expression)(literal)(string_)
        );
}

在此框架中,大多数规则都受益于自动属性传播.一个不是表达式:

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_;

因此,让我们为语义动作创建一些帮助器.

NOTE An important take-away here is to create your own higher-level building blocks instead of toiling away with boost::phoenix::construct<> etc.

定义两个简单的构造函数:

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) ]
            );

就这样.我们准备开始了!

3.演示

Live On Coliru

我创建了一个如下所示的测试床:

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))

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 << ")";
    }
}

这只是调试打印,因为字符串文字没有正确往返.但这只是10行代码,这是一个好处.

5.蒙蒂山:来源位置

这引起了您的兴趣,因此让我们展示一下它的工作原理.让我们添加一个简单的循环来打印标识符的所有位置:

using IOManip::showpos;

for (auto& id : all_identifiers(parsed)) {
    std::cout << " - " << id << " at " << showpos(id, input) << "\n";
}

当然,这引出了一个问题,showpos和all_identifiers是什么?

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;
}

那是一个树访客,它递归地获取所有标识符,并将它们插入到容器的后面.

Live On Coliru

输出如下所示(摘录):

 =========== '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)

标签:c,parsing,boost-spirit
来源: https://codeday.me/bug/20191009/1879904.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有