Is this the correct way of defining a set of recursive rules?

China☆狼群 提交于 2021-02-10 14:44:45

问题


PREFACE: I am asking this question because for some reason I can not get my code to compile. I just want to know whether an incorrect understanding of spirit::x3 is the cause or not

Hello, I'd just like to verify something; is the following the correct way (at least technically) of defining rules for a large set of heavily recursive parsers? So for each parser, I do the following operations:

// This is for reference only, it greatly simplifies the process of defining attributes.

template <class Tag, class... Bases> // the tag is for uniqueness
struct Access_base_s : Bases... {
    using base = Access_base_s;
    using Bases::Bases..., Bases::operator=...;
};

here's what I actually do for each parser:

struct attribute : Access_base_s<class attribute_tag, underlying_type /*eg. std::string*/> {
    using base::base, base::operator=; // Access_base_s helps here
};

x3::rule<class rule_tag, attribute, true> rule_name = "rule_name";

auto rule_name_def = some_definition;

BOOST_SPIRIT_DEFINE(rule_name);

I have this all automated with macros so it actually looks more like this:

DECLARE_RULE(rulename1, "rulename1", rule1_attribute_type);
DECLARE_RULE(rulename2, "rulename2", rule2_attribute_type);
DECLARE_RULE(rulename3, "rulename3", rule3_attribute_type);

DEFINE_RULE(rulename1, rule1_definition);
DEFINE_RULE(rulename2, rule2_definition);
DEFINE_RULE(rulename3, rule3_definition);

I have organized the declarations and definitions of the different parser so that they're in two sections; they are all declared first and defined later when all of them are already declared. By declared I mean that their attribute struct is defined and that the rule is declared, and by defined I mean that the rule_name_def is implemented and BOOST_SPIRIT_DEFINE is called on the rule.

I have tried the latest versions of MSVC, G++, and clang++, and all of them:

  • take at least 3 minutes
  • encounter some internal compiler error with no proper error message
  • abort and leave me with no executable

when I try to compile my code. I've been having this same issue for 2 days now, which is also why, sehe, if you're reading this, which you probably are, I haven't marked your answer to my last spirit question as the answer; I haven't had the opportunity to test it out while I've been too busy wrestling with the compiler.

EDIT: Here's the simplified code with the parsers that still causes the same error:

DECLARE_RULE(string_literal, "", std::string);
DECLARE_RULE(identifier    , "", std::string);

struct list;
struct dictionary;
struct expression;

DECLARE_RULE(literal       , "", x3::variant<double, int, string_literal, x3::forward_ast<list>, x3::forward_ast<dictionary>>);
DECLARE_RULE(parameter_pack, "", std::vector<expression>);
DECLARE_RULE(invocation    , "", parameter_pack);
DECLARE_RULE(expression    , "", x3::variant<literal, identifier, invocation>);
DECLARE_RULE(list          , "", std::vector<expression>);
DECLARE_RULE(dictionary    , "", std::vector<fusion::vector<expression, expression>>);


struct statement;
struct declaration;

DECLARE_RULE(compound_statement    , "", std::vector<statement>);
DECLARE_RULE(control_block_body    , "", x3::variant<x3::forward_ast<statement>, compound_statement>);
DECLARE_RULE(identifier_sequence   , "", std::vector<identifier>);
DECLARE_RULE(function_definition   , "", fusion::vector<identifier, std::vector<identifier>, control_block_body>);
DECLARE_RULE(structure_definition  , "", fusion::vector<identifier, std::vector<declaration>>);
DECLARE_RULE(enumeration_definition, "", fusion::vector<identifier, std::vector<fusion::vector<identifier, int>>>);
DECLARE_RULE(namespace_scope       , "", std::vector<declaration>);
DECLARE_RULE(namespace_extension   , "", fusion::vector<identifier, namespace_scope>);
DECLARE_RULE(declaration           , "", x3::variant<function_definition, structure_definition, enumeration_definition, namespace_extension>);
DECLARE_RULE(for_loop              , "", fusion::vector<identifier, expression, control_block_body>);
DECLARE_RULE(while_loop            , "", fusion::vector<expression, control_block_body>);
DECLARE_RULE(if_else_statement     , "", fusion::vector<expression, control_block_body, control_block_body>);
DECLARE_RULE(switch_statement      , "", fusion::vector<expression, std::vector<fusion::vector<expression, control_block_body>>>);
DECLARE_RULE(control_statement     , "", x3::variant<for_loop, while_loop, if_else_statement, switch_statement>);
DECLARE_RULE(statement_terminator  , "", std::string);
DECLARE_RULE(statement             , "", fusion::vector<x3::variant<expression, declaration, control_statement>, statement_terminator>);



DEFINE_RULE(string_literal, x3::lexeme['"' > *x3::char_ > '"']); // just a placeholder
DEFINE_RULE(identifier    , x3::lexeme[(x3::alpha | x3::char_('_')) >> *(x3::alnum | x3::char_('_'))]);
DEFINE_RULE(literal       , x3::double_ | x3::int_ | string_literal_ | list_ | dictionary_)
DEFINE_RULE(parameter_pack, +expression_);
DEFINE_RULE(invocation    , '(' > parameter_pack_ > ')');
DEFINE_RULE(expression    , literal_ | identifier_ | invocation_);
DEFINE_RULE(list          , '[' > *expression_ > ']');
DEFINE_RULE(dictionary    , '{' > *(expression_ > ':' > expression_) > '}');


DEFINE_RULE(compound_statement, '{' > *statement_ > '}');
DEFINE_RULE(control_block_body, (':' > statement_) | compound_statement_);
DEFINE_RULE(identifier_sequence, +identifier_);
DEFINE_RULE(function_definition, x3::lit("") > '(' > identifier_ > *identifier_ > ')' > control_block_body_);
DEFINE_RULE(structure_definition, "" > identifier_ > namespace_scope_);
DEFINE_RULE(enumeration_definition, "" > identifier_ > '{' > *(identifier_ > '=' > x3::int_) > '}');
DEFINE_RULE(namespace_scope, '{' > *declaration_ > '}');
DEFINE_RULE(namespace_extension, "" > identifier_ > namespace_scope_);
DEFINE_RULE(declaration, function_definition_ | structure_definition_ | enumeration_definition_ | namespace_extension_);
DEFINE_RULE(for_loop, "" > identifier_ > "" > expression_ > control_block_body_);
DEFINE_RULE(while_loop, "" > expression_ > control_block_body_);
DEFINE_RULE(if_else_statement, "" > expression_ > control_block_body_ > "" > control_block_body_);
DEFINE_RULE(switch_statement, "" > expression_ > '{' > *("" > expression_ > control_block_body_) > '}');
DEFINE_RULE(control_statement, for_loop_ | while_loop_ | if_else_statement_ | switch_statement_);
DEFINE_RULE(statement_terminator, x3::string("") | x3::string("") | x3::string(""));
DEFINE_RULE(statement, (expression_ | declaration_ | control_statement_) > statement_terminator_);

where DECLARE_RULE and DEFINE_RULE are defined as follows:

class empty_t {};

#define DEFINE_ATTRIBUTE(name, ...)                                                                                                                                                \
struct name : Access_base_s<class name ## _base_access_tag, std::conditional_t<!std::is_base_of_v<x3::position_tagged, __VA_ARGS__>, x3::position_tagged, empty_t>, __VA_ARGS__> { \
    using base::base, base::operator=;                                                                                                                                             \
} // the conditional is there to make sure each attribute inherits from position_tagged no more than once.

#define DECLARE_RULE(name, text, ...) \
DEFINE_ATTRIBUTE(name, __VA_ARGS__);  \
x3::rule<class name ## _tag, name, true> name ## _ = text

#define DEFINE_RULE(name, definition) \
auto name ## __def = definition;      \
BOOST_SPIRIT_DEFINE(name ## _)

I made the macros variadic to cheat a little bit; it allows me to pass in the templated types without having to worry about the preprocessor's idiocy. For example, x3::variant<std::string, int> would count as 2 separate macro arguments, and __VA_ARGS__ fixes that. Wherever you see it it's always just the underlying type of the attribute. The macros result in the attribute type being the name itself, and the parser being that but with an underscore appended to it's end to conform with x3 stuff like x3::int_.

Here's the driver code:

int main() {
    std::string str = read_file("filename");
    auto begin = std::begin(str), end = std::end(str);
    std::cout << std::boolalpha << x3::phrase_parse(begin, end, statement_, x3::ascii::space);
}

Also, if you couldn't tell by the names, I'm trying to write a parser for a simple DSL. I could use something like YACC but I think it's fairly useful to be familiar with a general purpose parsing library such as spirit::x3 so I'm trying to get familiar with it by doing this.

来源:https://stackoverflow.com/questions/61800010/is-this-the-correct-way-of-defining-a-set-of-recursive-rules

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!