summaryrefslogtreecommitdiff
path: root/src/expand/mod.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/expand/mod.cpp')
-rw-r--r--src/expand/mod.cpp133
1 files changed, 102 insertions, 31 deletions
diff --git a/src/expand/mod.cpp b/src/expand/mod.cpp
index f152b1ab..b5bbc1a4 100644
--- a/src/expand/mod.cpp
+++ b/src/expand/mod.cpp
@@ -67,7 +67,7 @@ void Expand_Attrs(/*const */::AST::MetaItems& attrs, AttrStage stage, ::std::fu
}
void Expand_Attrs(::AST::MetaItems& attrs, AttrStage stage, ::AST::Crate& crate, const ::AST::Path& path, ::AST::Module& mod, ::AST::Item& item)
{
- Expand_Attrs(attrs, stage, [&](const auto& sp, const auto& d, const auto& a){ d.handle(sp, a, crate, path, mod, item); });
+ Expand_Attrs(attrs, stage, [&](const auto& sp, const auto& d, const auto& a){ if(!item.is_None()) d.handle(sp, a, crate, path, mod, item); });
}
void Expand_Attrs(::AST::MetaItems& attrs, AttrStage stage, ::AST::Crate& crate, ::AST::Module& mod, ::AST::ImplDef& impl)
{
@@ -109,7 +109,8 @@ void Expand_Attrs(::AST::MetaItems& attrs, AttrStage stage, ::AST::Crate& crate
return e;
}
}
- // TODO: Shouldn't this use the _last_ located macro? Allowing later (local) defininitions to override it?
+ // Find the last macro of this name (allows later #[macro_use] definitions to override)
+ const MacroRules* last_mac = nullptr;
for( const auto& mri : mac_mod.macro_imports_res() )
{
//DEBUG("- " << mri.name);
@@ -118,10 +119,14 @@ void Expand_Attrs(::AST::MetaItems& attrs, AttrStage stage, ::AST::Crate& crate
if( input_ident != "" )
ERROR(mi_span, E0000, "macro_rules! macros can't take an ident");
- auto e = Macro_Invoke(name.c_str(), *mri.data, mv$(input_tt), mod);
- return e;
+ last_mac = mri.data;
}
}
+ if( last_mac )
+ {
+ auto e = Macro_Invoke(name.c_str(), *last_mac, mv$(input_tt), mod);
+ return e;
+ }
}
// Error - Unknown macro name
@@ -307,11 +312,9 @@ struct CExpandExpr:
assert( ! this->replacement );
}
void visit_vector(::std::vector< ::std::unique_ptr<AST::ExprNode> >& cnodes) {
- for( auto& child : cnodes ) {
- this->visit(child);
- }
- // Delete null children
for( auto it = cnodes.begin(); it != cnodes.end(); ) {
+ assert( it->get() );
+ this->visit(*it);
if( it->get() == nullptr ) {
it = cnodes.erase( it );
}
@@ -321,48 +324,83 @@ struct CExpandExpr:
}
}
- void visit(::AST::ExprNode_Macro& node) override
+ ::AST::ExprNodeP visit_macro(::AST::ExprNode_Macro& node, ::std::vector< ::AST::ExprNodeP>* nodes_out)
{
- TRACE_FUNCTION_F("ExprNode_Macro - name = " << node.m_name);
+ TRACE_FUNCTION_F(node.m_name << "!");
if( node.m_name == "" ) {
- return ;
+ return ::AST::ExprNodeP();
}
+ ::AST::ExprNodeP rv;
auto& mod = this->cur_mod();
- auto ttl = Expand_Macro(
- crate, modstack, mod,
- Span(node.get_pos()),
- node.m_name, node.m_ident, node.m_tokens
- );
- if( ttl.get() != nullptr )
+ auto ttl = Expand_Macro( crate, modstack, mod, Span(node.get_pos()), node.m_name, node.m_ident, node.m_tokens );
+ if( !ttl.get() )
+ {
+ // No expansion
+ }
+ else
{
- if( ttl->lookahead(0) != TOK_EOF )
+ while( ttl->lookahead(0) != TOK_EOF )
{
SET_MODULE( (*ttl), mod );
+
// Reparse as expression / item
bool add_silence_if_end = false;
::std::shared_ptr< AST::Module> tmp_local_mod;
auto& local_mod_ptr = (this->current_block ? this->current_block->m_local_mod : tmp_local_mod);
- DEBUG("-- Parsing as expression line (legacy)");
+ DEBUG("-- Parsing as expression line");
auto newexpr = Parse_ExprBlockLine_WithItems(*ttl, local_mod_ptr, add_silence_if_end);
+
+ if( tmp_local_mod )
+ TODO(node.get_pos(), "Handle edge case where a macro expansion outside of a _Block creates an item");
+
if( newexpr )
{
- // TODO: use add_silence_if_end - Applies if this node is the last node in the block.
-
- // Then call visit on it again
- DEBUG("--- Visiting new node");
- this->visit(newexpr);
- // And schedule it to replace the previous
- replacement = mv$(newexpr);
+ if( nodes_out ) {
+ nodes_out->push_back( mv$(newexpr) );
+ }
+ else {
+ assert( !rv );
+ rv = mv$(newexpr);
+ }
}
else
{
- // TODO: Delete this node somehow? Or just leave it for later.
+ // Expansion line just added a new item
+ }
+
+ if( ttl->lookahead(0) != TOK_EOF )
+ {
+ if( !nodes_out ) {
+ ERROR(node.get_pos(), E0000, "Unused tokens at the end of macro expansion - " << ttl->getToken());
+ }
}
- ASSERT_BUG(node.get_pos(), !tmp_local_mod, "TODO: Handle edge case where a macro expansion outside of a _Block creates an item");
}
- DEBUG("ExprNode_Macro - replacement = " << replacement.get());
- node.m_name = "";
+ }
+
+ node.m_name = "";
+ return mv$(rv);
+ }
+
+ void visit(::AST::ExprNode_Macro& node) override
+ {
+ TRACE_FUNCTION_F("ExprNode_Macro - name = " << node.m_name);
+ if( node.m_name == "" ) {
+ return ;
+ }
+
+ replacement = this->visit_macro(node, nullptr);
+
+ if( this->replacement )
+ {
+ DEBUG("--- Visiting new node");
+ auto n = mv$(this->replacement);
+ this->visit(n);
+ if( n )
+ {
+ assert( !this->replacement );
+ this->replacement = mv$(n);
+ }
}
}
@@ -377,7 +415,40 @@ struct CExpandExpr:
auto saved = this->current_block;
this->current_block = &node;
- this->visit_vector(node.m_nodes);
+
+ for( auto it = node.m_nodes.begin(); it != node.m_nodes.end(); )
+ {
+ assert( it->get() );
+
+ if( auto* node_mac = dynamic_cast<::AST::ExprNode_Macro*>(it->get()) )
+ {
+ Expand_Attrs((*it)->attrs(), AttrStage::Pre, [&](const auto& sp, const auto& d, const auto& a){ d.handle(sp, a, this->crate, *it); });
+ if( !it->get() ) {
+ it = node.m_nodes.erase( it );
+ continue ;
+ }
+
+ assert(it->get() == node_mac);
+
+ ::std::vector< ::AST::ExprNodeP> new_nodes;
+ this->visit_macro(*node_mac, &new_nodes);
+
+ it = node.m_nodes.erase(it);
+ it = node.m_nodes.insert(it, ::std::make_move_iterator(new_nodes.begin()), ::std::make_move_iterator(new_nodes.end()));
+ // NOTE: Doesn't advance the iterator above, we want to re-visit the new node
+ }
+ else
+ {
+ this->visit(*it);
+ if( it->get() == nullptr ) {
+ it = node.m_nodes.erase( it );
+ }
+ else {
+ ++ it;
+ }
+ }
+ }
+
this->current_block = saved;
// HACK! Run Expand_Mod twice on local modules.