Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

X3: Support recursive rules that modify the context #237

Open
wants to merge 1 commit into
base: develop
Choose a base branch
from

Conversation

togermer
Copy link

@togermer togermer commented Aug 1, 2017

make_context() always pushed a new node to the context list when the context is modified (e.g., for "with", "skip", or "no_skip" directives). If such directives are used in recursive rules, this leads to an infinite loop of template instantiations, effectively limiting the use of recursive rules in x3.

To fix this, we first check if the tag for the new node is already contained in the context. If we find the tag, we remove the corresponding existing node from the context before pushing the new node with the requested tag.

In order to remove a context entry, we have to rebuild all context nodes up to this entry. We can reuse (i.e. link to) the tail of the old context after this entry.

In order to resolve life-time issues of newly created context nodes we added an aggregating implementation of struct context.

make_context() always pushed a new node to the context list when the context is modified (e.g. for "with", "skip" or "no_skip" directives). If such directives are used in recursive rules, this leads to an infinite loop of template instantiations, effectively limiting the use of recursive rules in x3.

To fix this, we first check if the tag for the new node is already contained in the context. If we find the tag, we remove the corresponding existing node from the context before pushing the new node with the requested tag.

In order to remove a context entry, we have to rebuild all context nodes up to this entry. We can reuse (i.e. link to) the tail of the old context after this entry.

In order to resolve life-time issues of newly created context nodes we added an aggregating implementation of struct context.
@djowel
Copy link
Member

djowel commented Aug 2, 2017

Nice catch! Seems you have good familiarity with X3!

@djowel
Copy link
Member

djowel commented Aug 2, 2017

I assume all tests pass?

@togermer
Copy link
Author

togermer commented Aug 2, 2017

Yes, all X3 tests pass (at least using msvc-14.1).

@cppljevans
Copy link
Contributor

cppljevans commented Aug 9, 2017

What is the compile-time cost? The paragraph in your 1st comment which starts with:

To fix this,

and the following paragraph worry me that compile times will suffer. Are there any
benchmarks to measure compile times?

Also, a test case demonstrating the problem, but simpler, if possible, than
the one here:

https://github.com/cppljevans/spirit/blob/get_rhs/workbench/x3/HanWang_make_context/main.orig.cpp

would be helpful in guarding against the problem occurring again, and would give
a concrete illustration of the problem.

An even better test is:

https://github.com/cppljevans/spirit/blob/get_rhs/workbench/x3/HanWang_make_context/main.altcomment.cpp

It's better because it detects that the solution in the get_rhs fork when:

-DBOOST_SPIRIT_X3_EXPERIMENTAL_SKIP_MAKE_UNIQUE=1

fails but with this pull_request, it succeeds.

An even better test is:

namespace z3=boost::spirit::x3;
namespace blockn_parser
{
    z3::rule<struct blk0_id> const blk0_ru;
    z3::rule<struct blk1_id> const blk1_ru;
    z3::rule<struct blk2_id> const blk2_ru;
    
    auto const com0_def = z3::space;
    auto const com1_def = z3::lit("/*1*/") | z3::space;
    auto const com2_def = z3::lit("/*2*/") | z3::space;
    
    auto const blk0_ru_def 
      = z3::lit("{0") 
      >> *( blk0_ru
          |    z3::lit("skip1")
            >> z3::skip(com1_def)[blk1_ru]
          ) 
      >> z3::lit("0}");
    auto const blk1_ru_def 
      = z3::lit("{1") 
      >> *( blk1_ru
          |    z3::lit("skip2") 
            >> z3::skip(com2_def)[blk2_ru]
          ) 
      >> z3::lit("1}");
    auto const blk2_ru_def 
      = z3::lit("{2") 
      >> *( blk2_ru
          |    z3::lit("skip0") 
            >> z3::skip(com0_def)[blk0_ru]
          ) 
      >> z3::lit("2}");
      
    BOOST_SPIRIT_DEFINE
    ( blk0_ru
    , blk1_ru
    , blk2_ru
    )
}//namespace blockn_parser

With this test, the get_rhs fork fails at compile time instead
of at runtime; thus, this test could detect a flaw in a
some future proposed alternative solution to this
problem earlier.

An even better (because it's shorter) test is:

    auto const com0 = z3::lit(":0") | z3::space;
    z3::rule< lsti<0> > const lst0;
    auto const lst0_def
      =     z3::lit("a") 
         >> 
            (    z3::lit(",")
              >> lst0
            |    z3::lit(";0") 
              >> z3::skip(com0)[lst0]
            | z3::lit(".")
            )
      ;
    BOOST_SPIRIT_DEFINE
    ( lst0
    )

The following location for the test looks good to me:

https://github.com/boostorg/spirit/blob/develop/test/x3/skip.cpp#L36

What about that, Joel?

cppljevans pushed a commit to cppljevans/spirit-experiments that referenced this pull request Aug 11, 2017
  main.altcommit.cpp
for problem discussed here:
  https://sourceforge.net/p/spirit/mailman/message/35963822
with current code in the get_rhs fork, it fails.  However,
when run with the pull_request here:
  boostorg#237
it passes.  Hence, this test case would filter out the
incorrect sollution in this get_rhs branch.
cppljevans pushed a commit to cppljevans/spirit-experiments that referenced this pull request Aug 11, 2017
incorrect get_rhs fork and the "more" correct togermer
pull request (boostorg#237).
Copy link
Contributor

@cppljevans cppljevans left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you elaborate on those life-time issues?

cppljevans pushed a commit to cppljevans/spirit-experiments that referenced this pull request Sep 24, 2017
are the new files.  These illustrate an alternative to:
  boostorg#237
This alternative is simpler to understand, OTOH, it
requires changing the parser::parse interface from one taking
iterator's for args 1 and 2 to one scanner arg
(which aggregates the 2 iterators).  This scanner arg
also contains a skipper.  This is in contrast to the
pull/237 where the skipper is stored in the context
arg (another arg to the parse function).

Advantages of this alternative approach are outlined in
comments near beginnning of the y3-scanner.hpp file.

The test driver is in inf_skipper_context.cpp.
cppljevans pushed a commit to cppljevans/spirit-experiments that referenced this pull request Sep 25, 2017
  skip.hpp
  ../support/context.hpp
In these files, rm'ed the erroneous patch for solving
problem which this PR correctly solves:
  boostorg#237
@Kojoley Kojoley changed the title Support recursive rules that modify the context X3: Support recursive rules that modify the context Dec 23, 2017
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants