1

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.

  • 1
    Yeah. That's interesting. but there's nothing to be said without your code. Because we can't see it. I can probably make up my own code using your fragments, but 99$ it doesn't have the problem you describe. The only "special" thing is your `attribute` strong-typedeffing pattern, but that's not alarming (I use it on some attributes) – sehe May 14 '20 at 16:53
  • 1
    @sehe I'll try to isolate the parsers that cause the problem from my code and add it to the post tomorrow. Also, in case you've encountered it in the context of x3 before, I forgot to mention that MSVC specifically instead of just crashing actually says "compiler is out of heap space". Could that mean something specific? –  May 14 '20 at 17:51
  • 1
    Yeah. Something is blowing up. My sipdey sense says something to do with changing skippers (I've seen that before) but we'll see – sehe May 14 '20 at 18:14
  • @sehe by changing skippers do you mean the act of changing skippers or skippers that change by themselves? –  May 14 '20 at 18:17
  • Changing by use of e.g. `x3::skip()[]` – sehe May 14 '20 at 18:35
  • @sehe I've added the code that causes the error in the post. I simplified it significantly but the error is still there. –  May 15 '20 at 10:15
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/213927/discussion-between-sehe-and-low-on-chromosomes). – sehe May 15 '20 at 11:46

1 Answers1

0

It was a bug in Spirit; fixed by PR651; the fix will be in 1.76 release.

Nikita Kniazev
  • 3,728
  • 2
  • 16
  • 30