diff options
author | John Hodge <tpg@mutabah.net> | 2016-11-22 12:31:11 +0800 |
---|---|---|
committer | John Hodge <tpg@mutabah.net> | 2016-11-22 12:32:19 +0800 |
commit | 85baf063e5b99b18850b06174f9474e47407967c (patch) | |
tree | 3c5174d6f8cabf0eb0ab289d80d981a6cf174e03 | |
parent | 44fec397409530d6bc61e9fe9fbccd08d19d2c26 (diff) | |
download | mrust-85baf063e5b99b18850b06174f9474e47407967c.tar.gz |
macro_rules! - Handle cases where :ident and :expr appear in the same position
-rw-r--r-- | src/macro_rules/eval.cpp | 91 |
1 files changed, 89 insertions, 2 deletions
diff --git a/src/macro_rules/eval.cpp b/src/macro_rules/eval.cpp index 5f2ac6b0..bfdb5b08 100644 --- a/src/macro_rules/eval.cpp +++ b/src/macro_rules/eval.cpp @@ -125,6 +125,9 @@ class MacroPatternStream ::std::vector<SimplePatEnt> m_stack; unsigned int m_skip_count; + + SimplePatEnt m_peek_cache; + bool m_peek_cache_valid = false; bool m_break_if_not = false; bool m_condition_fired = false; @@ -137,6 +140,15 @@ public: /// Get the next pattern entry SimplePatEnt next(); + + const SimplePatEnt& peek() { + if( !m_peek_cache_valid ) { + m_peek_cache = next(); + m_peek_cache_valid = true; + } + return m_peek_cache; + } + /// Inform the stream that the `if` rule that was just returned succeeded void if_succeeded(); /// Get the current loop iteration count @@ -305,6 +317,11 @@ SimplePatEnt MacroPatternStream::next() TRACE_FUNCTION_F("m_pos=[" << m_pos << "], m_stack.size()=" << m_stack.size()); assert(m_pos.size() >= 1); + if( m_peek_cache_valid ) { + m_peek_cache_valid = false; + return mv$(m_peek_cache); + } + // Pop off the generation stack if( ! m_stack.empty() ) { auto rv = mv$(m_stack.back()); @@ -902,8 +919,11 @@ unsigned int Macro_InvokeRules_MatchPattern(const MacroRules& rules, TokenTree i // 3. If there is a token pattern in the list, take that arm (and any other token arms) const SimplePatEnt* tok_pat = nullptr; - for( const auto& pat : arm_pats ) + unsigned int ident_pat_idx = arm_pats.size(); + bool has_non_ident_pat = false; + for( unsigned int i = 0; i < arm_pats.size(); i ++ ) { + const auto& pat = arm_pats[i]; TU_IFLET(SimplePatEnt, pat, ExpectTok, e, if( tok_pat ) { if( e != tok_pat->as_ExpectTok() ) @@ -913,6 +933,14 @@ unsigned int Macro_InvokeRules_MatchPattern(const MacroRules& rules, TokenTree i tok_pat = &pat; } ) + else TU_IFLET(SimplePatEnt, pat, ExpectPat, e, + if( e.type == MacroPatEnt::PAT_IDENT ) { + ident_pat_idx = i; + } + else { + has_non_ident_pat = true; + } + ) } if( tok_pat ) @@ -926,6 +954,65 @@ unsigned int Macro_InvokeRules_MatchPattern(const MacroRules& rules, TokenTree i } else { + if( has_non_ident_pat && ident_pat_idx < arm_pats.size() ) + { + // For all :ident patterns present, check the next rule. + // - If this rule would fail, remove the arm. + bool ident_rule_kept = false; + for( unsigned int i = 0; i < arm_pats.size(); ) + { + bool discard = false; + const auto& pat = arm_pats[i]; + const auto& e = pat.as_ExpectPat(); + if( e.type == MacroPatEnt::PAT_IDENT ) + { + const auto& next = active_arms[i].second.peek(); + TU_MATCHA( (next), (ne), + (IfPat, TODO(sp, "Handle IfPat following a conflicting :ident");), + (IfTok, TODO(sp, "IfTok following a conflicting :ident");), + (ExpectTok, + if( ne.type() != lex.lookahead(1) ) { + DEBUG("Discard active arm " << i << " due to next token mismatch"); + discard = true; + } + else { + ident_rule_kept = true; + } + ), + (ExpectPat, + TODO(sp, "Handle ExpectPat following a conflicting :ident"); + ), + (End, TODO(sp, "Handle End following a conflicting :ident"); ) + ) + } + + if( discard ) { + arm_pats.erase( arm_pats.begin() + i ); + active_arms.erase( active_arms.begin() + i ); + } + else { + ++ i; + } + } + + // If there are any remaining ident rules, erase the non-ident rules. + if( ident_rule_kept ) { + // If no rules were discarded, remove the non-ident rules + for( unsigned int i = 0; i < arm_pats.size(); ) + { + if( arm_pats[i].as_ExpectPat().type != MacroPatEnt::PAT_IDENT ) { + arm_pats.erase( arm_pats.begin() + i ); + active_arms.erase( active_arms.begin() + i ); + } + else { + ++ i; + } + } + } + assert(arm_pats.size() > 0); + assert(arm_pats.size() == active_arms.size()); + } + // 3. Check that all remaining arms are the same pattern. const auto& active_pat = arm_pats[0]; for(unsigned int i = 1; i < arm_pats.size(); i ++) @@ -945,7 +1032,7 @@ unsigned int Macro_InvokeRules_MatchPattern(const MacroRules& rules, TokenTree i (ExpectPat, // Can fail, as :expr and :stmt overlap in their trigger set if( e1.type != e2.type ) { - ERROR(lex.getPosition(), E0000, "Incompatible macro arms - mismatched patterns"); + ERROR(lex.getPosition(), E0000, "Incompatible macro arms - mismatched patterns " << e1.type << " and " << e2.type); } if( e1.idx != e2.idx ) { ERROR(lex.getPosition(), E0000, "Incompatible macro arms - mismatched pattern bindings " << e1.idx << " and " << e2.idx); |