1

This code works. This question is about making it (look) better. I have seen known the article about utrees but I'm not not sure that is the best way.

Let me show you the "ugly" version of the code, which uses construct<>

newcall =(nocaselit(L"new") > tyname)
            [_val = construct<common_node>(type_cmd_new,key_typename, construct<std::wstring>(_1))];

With the rules declared as:

qi::rule<Iterator, common_node(), Skipper> newcall
qi::rule<Iterator, std::wstring()> tyname;

The target common AST node is:

struct common_node {
    template <typename X>
    common_node(node_type t, node_key k1, const X & m1)

The first parameter is the node type, the second some kind of member key and the last the payload given as template argument (later stored in a variant).

Can we avoid the construct template?

sehe
  • 374,641
  • 47
  • 450
  • 633
Markus
  • 373
  • 1
  • 11

1 Answers1

1

This is the classic case where I always link to Boost Spirit: "Semantic actions are evil"?: avoid semantic actions.

In this case I don't know what your AST really looks like (what is node_key, where does key_typename come from etc.) so I can't really show you much.

Usually I'd adapt the node types and declare rules for the concrete node types. If that doesn't work, I prefer phoenix::function<> wrappers:

Live On Coliru

#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix.hpp>
namespace qi = boost::spirit::qi;

struct SomeComplicatedType {
    enum Type { None, NewCall };
    struct Key{};
    SomeComplicatedType(Type = {}, Key = {}, std::string n = "") : name(std::move(n)) { }

    std::string name;
};

static SomeComplicatedType::Key const s_default_key;

template <typename It>
struct Grammar : qi::grammar<It, SomeComplicatedType()>
{
    Grammar() : Grammar::base_type(start) {
        using namespace qi;
        start  = skip(space) [new_];
        tyname = raw[(alpha|'_') >> +(alnum|'_')];

        new_   = no_case["new"] > tyname [_val = make_new(_1)];

        BOOST_SPIRIT_DEBUG_NODES((start)(new_)(tyname))
    }
  private:
    qi::rule<It, SomeComplicatedType()> start;
    qi::rule<It, SomeComplicatedType(), qi::space_type> new_;
    qi::rule<It, std::string()> tyname;

    struct make_complicated_t {
        SomeComplicatedType::Type _type;

        SomeComplicatedType operator()(std::string const& s) const {
            return SomeComplicatedType{_type, s_default_key, s};
        }
    };
    boost::phoenix::function<make_complicated_t> make_new { make_complicated_t{SomeComplicatedType::NewCall } };
};

int main() {
    std::string const input = "new Sandwich";

    SomeComplicatedType result;
    if (parse(input.begin(), input.end(), Grammar<std::string::const_iterator>{}, result))
        std::cout << "Parsed: " << result.name << "\n";

}

Prints

Parsed: Sandwich
sehe
  • 374,641
  • 47
  • 450
  • 633
  • Thx. Your sample works for me .. so I can exchange the construct with the Phoenix stuff. Maybe I will restruct my parser to avoid semantic actions in some way. – Markus Nov 12 '17 at 06:23