summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJohn Hodge <tpg@mutabah.net>2016-11-22 12:31:11 +0800
committerJohn Hodge <tpg@mutabah.net>2016-11-22 12:32:19 +0800
commit85baf063e5b99b18850b06174f9474e47407967c (patch)
tree3c5174d6f8cabf0eb0ab289d80d981a6cf174e03
parent44fec397409530d6bc61e9fe9fbccd08d19d2c26 (diff)
downloadmrust-85baf063e5b99b18850b06174f9474e47407967c.tar.gz
macro_rules! - Handle cases where :ident and :expr appear in the same position
-rw-r--r--src/macro_rules/eval.cpp91
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);