替代属性综合和 AST 设计

Alternative attribute synthesis and AST design

在下面的语法中,当我将替代项 (| 属性) 添加到开始规则时,出现此错误

'boost::spirit::x3::traits::detail::move_to': none of the 3 overloads could convert all the argument types e:\data\boost\boost_1_65_1\boost\spirit\home\x3\support\traits\move_to.hpp 180

我怀疑问题是 属性 属性是一个结构,而 property_list 是一个向量(x3 不应该创建一个条目的向量吗?)。设计 AST 结构以支持替代方案的推荐方法是什么? Boost 变体?

#include <string>
#include <vector>
#include <iostream>
#include <iomanip>
#include <map>

#pragma warning(push)
#pragma warning(disable : 4348)
#include <boost/config/warning_disable.hpp>
#include <boost/spirit/home/x3.hpp>
#include <boost/variant.hpp>
#include <boost/fusion/adapted/struct.hpp>
#pragma warning(pop)

namespace x3 = boost::spirit::x3;

namespace scl_ast
    {

    struct KEYWORD : std::string
        {
        using std::string::string;
        using std::string::operator=;
        };

    struct NIL
        {
        };

    using VALUE = boost::variant <NIL, std::string, int, double, KEYWORD>;

    struct PROPERTY
        {
        KEYWORD     name;
        VALUE       value;
        };

    static inline std::ostream& operator<< (std::ostream& os, VALUE const& v)
        {
        struct
            {
            std::ostream&   _os;

            void operator () (std::string const& s) const { _os << std::quoted (s); }
            void operator () (int i)                const { _os << i; }
            void operator () (double d)             const { _os << d; }
            void operator () (KEYWORD const& k)     const { _os << k; }
            void operator () (NIL)                  const { }
            } vis { os };

            boost::apply_visitor (vis, v);
            return os;
        }

    static inline std::ostream& operator<< (std::ostream& os, PROPERTY const& prop)
        {
        os << prop.name;

        if (prop.value.which ())
            {
            os << "=" << prop.value;
            }

        return os;
        }

    static inline std::ostream& operator<< (std::ostream& os, std::vector <PROPERTY> const& props)
        {
        for (auto const& prop : props)
            {
            os << prop << " ";
            }

        return os;
        }

    };  // End namespace scl_ast

BOOST_FUSION_ADAPT_STRUCT (scl_ast::PROPERTY, name, value)

//
// Keyword-value grammar for simple command language
//

namespace scl
    {
    using namespace x3;


    auto const  keyword = rule <struct _keyword, std::string> { "keyword" }
                    = lexeme [+char_ ("a-zA-Z0-9$_")];

    auto const  quoted_string
                    = lexeme ['"' >> *('\' > char_ | ~char_ ('"')) >> '"'];

    auto const  value
                    = quoted_string
                    | x3::real_parser<double, x3::strict_real_policies<double>>{}
                    | x3::int_
                    | keyword;

    auto const  property = rule <struct _property, scl_ast::PROPERTY> { "property" }
                    = keyword >> -(("=" >> value));

    auto const  property_list = rule <struct _property_list, std::vector <scl_ast::PROPERTY>> { "property_list" }
                    = lit ('(') >> property % ',' >> lit (')');

    auto const  start = skip (blank) [property_list | property];

    };  // End namespace scl


int
main ()

{
std::vector <std::string>   input =
    {
    "(abc=1.,def=.5,ghi=2.0)",
    "(ghi = 1, jkl = 3)",
    "(abc,def=1,ghi=2.4,jkl=\"mno 123\", pqr = stu)",
    "(abc = test, def, ghi=2)",
    "abc=1",
    "def = 2.7",
    "ghi"
    };


    for (auto const& str : input)
        {
        std::vector <scl_ast::PROPERTY>     result;
        auto    b = str.begin (), e = str.end ();


        bool    ok = x3::parse (b, e, scl::start, result);
        std::cout << (ok ? "OK" : "FAIL") << '\t' << std::quoted (str) << std::endl;

        if (ok)
            {
            std::cout << " -- Parsed: " << result << std::endl;

            if (b != e)
                {
                std::cout << " -- Unparsed: " << std::quoted (std::string (b, e)) << std::endl;
                }

            }

        std::cout << std::endl;
        }   // End for

    return 0;
}                           // End main

I suspect that the problem is that the property attribute is a struct, and property_list is a vector (shouldn't x3 create a vector of one entry?)

是的,是的,是的,视情况而定。

我看到大约有 3 种方法可以解决这个问题。让我们从简单的开始:

  1. 强制Container-like合成:Live On Coliru

    = skip(blank)[property_list | repeat(1)[property] ];
    
  2. 强制属性类型。原来我在这里错了:它曾经对 Qi 有效,但显然已经被删除了。在这里,not-working 和所有:

    auto coerce = [](auto p) { return rule<struct _, std::vector<scl_ast::PROPERTY> > {} = p; };
    
    auto const start 
        = skip(blank)[property_list | coerce(property)];
    
  3. 第三个实际上没有实际意义,因为同样的问题。所以我想我欠你一个人为的解决方法,使用语义操作:Live On Coliru

    auto push_back = [](auto& ctx) {
        _val(ctx).push_back(_attr(ctx));
    };
    
    auto const start 
        = rule<struct _start, std::vector<scl_ast::PROPERTY>, true>{ "start" } 
        = skip(blank)[property_list | omit[property[push_back]]];