From abaa6987622a9f5c26fc471fbca210e281f67d17 Mon Sep 17 00:00:00 2001 From: John Hodge Date: Tue, 27 Sep 2016 18:19:30 +0800 Subject: Parse - Support expanding macros that yield items in expressions --- src/expand/mod.cpp | 34 ++++++++++--- src/parse/common.hpp | 1 + src/parse/expr.cpp | 138 ++++++++++++++++++++++++++++++--------------------- 3 files changed, 109 insertions(+), 64 deletions(-) diff --git a/src/expand/mod.cpp b/src/expand/mod.cpp index 9537e5d3..55fd7d63 100644 --- a/src/expand/mod.cpp +++ b/src/expand/mod.cpp @@ -238,6 +238,8 @@ struct CExpandExpr: LList modstack; ::std::unique_ptr<::AST::ExprNode> replacement; + AST::ExprNode_Block* current_block = nullptr; + CExpandExpr(bool is_early, ::AST::Crate& crate, LList ms): is_early(is_early), crate(crate), @@ -297,6 +299,10 @@ struct CExpandExpr: void visit(::AST::ExprNode_Macro& node) override { TRACE_FUNCTION_F("ExprNode_Macro - name = " << node.m_name); + if( node.m_name == "" ) { + return ; + } + auto& mod = this->cur_mod(); auto ttl = Expand_Macro( is_early, crate, modstack, mod, @@ -310,13 +316,24 @@ struct CExpandExpr: SET_MODULE( (*ttl), mod ); // Reparse as expression / item bool add_silence_if_end = false; - auto newexpr = Parse_ExprBlockLine(*ttl, &add_silence_if_end); - // 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); + ::std::unique_ptr< AST::Module> tmp_local_mod; + auto& local_mod_ptr = (this->current_block ? this->current_block->m_local_mod : tmp_local_mod); + auto newexpr = Parse_ExprBlockLine_WithItems(*ttl, local_mod_ptr, add_silence_if_end); + 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); + } + else + { + // TODO: Delete this node somehow? Or just leave it for later. + } + 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 = ""; @@ -327,7 +344,10 @@ struct CExpandExpr: if( node.m_local_mod ) { Expand_Mod(is_early, crate, modstack, node.m_local_mod->path(), *node.m_local_mod); } + auto saved = this->current_block; + this->current_block = &node; this->visit_vector(node.m_nodes); + this->current_block = saved; } void visit(::AST::ExprNode_Flow& node) override { this->visit_nodelete(node, node.m_value); diff --git a/src/parse/common.hpp b/src/parse/common.hpp index f7168d33..c883c493 100644 --- a/src/parse/common.hpp +++ b/src/parse/common.hpp @@ -56,6 +56,7 @@ extern AST::ExprNodeP Parse_Expr0(TokenStream& lex); extern AST::ExprNodeP Parse_ExprVal(TokenStream& lex); extern AST::ExprNodeP Parse_ExprBlockNode(TokenStream& lex, bool is_unsafe=false); extern AST::ExprNodeP Parse_ExprBlockLine(TokenStream& lex, bool *add_silence); +extern AST::ExprNodeP Parse_ExprBlockLine_WithItems(TokenStream& lex, ::std::unique_ptr& local_mod, bool& add_silence_if_end); extern AST::ExprNodeP Parse_Stmt(TokenStream& lex); diff --git a/src/parse/expr.cpp b/src/parse/expr.cpp index 9d7748eb..774d7acc 100644 --- a/src/parse/expr.cpp +++ b/src/parse/expr.cpp @@ -24,6 +24,7 @@ static inline ExprNodeP mk_exprnodep(const TokenStream& lex, AST::ExprNode* en){ #define NEWNODE(type, ...) mk_exprnodep(lex, new type(__VA_ARGS__)) //ExprNodeP Parse_ExprBlockNode(TokenStream& lex, bool is_unsafe=false); // common.hpp +//ExprNodeP Parse_ExprBlockLine_WithItems(TokenStream& lex, ::std::unique_ptr& local_mod, bool& add_silence_if_end); //ExprNodeP Parse_ExprBlockLine(TokenStream& lex, bool *add_silence); ExprNodeP Parse_ExprBlockLine_Stmt(TokenStream& lex, bool *add_silence); //ExprNodeP Parse_Stmt(TokenStream& lex); // common.hpp @@ -58,82 +59,105 @@ ExprNodeP Parse_ExprBlockNode(TokenStream& lex, bool is_unsafe/*=false*/) GET_CHECK_TOK(tok, lex, TOK_BRACE_OPEN); - while( GET_TOK(tok, lex) != TOK_BRACE_CLOSE ) + while( LOOK_AHEAD(lex) != TOK_BRACE_CLOSE ) { DEBUG("tok = " << tok); - AST::MetaItems item_attrs; - while( tok.type() == TOK_ATTR_OPEN ) + + // NOTE: Doc comments can appear within a function and apply to the function + // TODO: Use these attributes + while( GET_TOK(tok, lex) == TOK_CATTR_OPEN ) { - item_attrs.push_back( Parse_MetaItem(lex) ); + /*node_attrs.push_back(*/ Parse_MetaItem(lex) /*)*/; GET_CHECK_TOK(tok, lex, TOK_SQUARE_CLOSE); - GET_TOK(tok, lex); } + PUTBACK(tok, lex); - switch(tok.type()) + bool add_silence_if_end = false; + auto rv = Parse_ExprBlockLine_WithItems(lex, local_mod, add_silence_if_end); + if( rv ) { - case TOK_CATTR_OPEN: - // TODO: Handle `#![` by having a pointer to the parent item (e.g. function)'s attribute list. - /*node_attrs.push_back(*/ Parse_MetaItem(lex) /*)*/; - GET_CHECK_TOK(tok, lex, TOK_SQUARE_CLOSE); - break; - // Items: - case TOK_RWORD_PUB: - ERROR(lex.getPosition(), E0000, "`pub` is useless within expression modules"); - case TOK_RWORD_TYPE: - case TOK_RWORD_USE: - case TOK_RWORD_EXTERN: - case TOK_RWORD_CONST: - case TOK_RWORD_STATIC: - case TOK_RWORD_STRUCT: - case TOK_RWORD_ENUM: - case TOK_RWORD_TRAIT: - case TOK_RWORD_IMPL: - case TOK_RWORD_FN: - case TOK_RWORD_MOD: - PUTBACK(tok, lex); - if( !local_mod ) { - local_mod = lex.parse_state().get_current_mod().add_anon(); - } - Parse_Mod_Item(lex, *local_mod, mv$(item_attrs)); - break; - // 'unsafe' - Check if the next token isn't a `{`, if so it's an item. Otherwise, fall through - case TOK_RWORD_UNSAFE: - if( LOOK_AHEAD(lex) != TOK_BRACE_OPEN ) - { - PUTBACK(tok, lex); - if( !local_mod ) { - local_mod = lex.parse_state().get_current_mod().add_anon(); - } - Parse_Mod_Item(lex, *local_mod, mv$(item_attrs)); - break; - } - // fall - default: { - PUTBACK(tok, lex); - bool add_silence_if_end = false; - nodes.push_back(Parse_ExprBlockLine(lex, &add_silence_if_end)); - if( nodes.back() ) { - nodes.back()->set_attrs( mv$(item_attrs) ); - } - else { - // TODO: Error if attribute on void expression? - } // Set to TRUE if there was no semicolon after a statement if( LOOK_AHEAD(lex) == TOK_BRACE_CLOSE && add_silence_if_end ) { DEBUG("expect_end == false, end of block"); - //nodes.push_back( NEWNODE(AST::ExprNode_Tuple, ::std::vector()) ); yields_final_value = false; - // NOTE: Would break out of the loop, but we're in a switch - } - break; + // Since the next token is TOK_BRACE_CLOSE, the loop will terminate } + nodes.push_back( mv$(rv) ); + } + else { + assert( !add_silence_if_end ); } } + GET_CHECK_TOK(tok, lex, TOK_BRACE_CLOSE); return NEWNODE( AST::ExprNode_Block, is_unsafe, yields_final_value, mv$(nodes), mv$(local_mod) ); } +/// Parse a single line in a block, handling items added to the local module +/// +/// - If an item was parsed, this returns an empty ExprNodeP +ExprNodeP Parse_ExprBlockLine_WithItems(TokenStream& lex, ::std::unique_ptr& local_mod, bool& add_silence_if_end) +{ + Token tok; + + AST::MetaItems item_attrs; + while( GET_TOK(tok, lex) == TOK_ATTR_OPEN ) + { + item_attrs.push_back( Parse_MetaItem(lex) ); + GET_CHECK_TOK(tok, lex, TOK_SQUARE_CLOSE); + } + + switch(tok.type()) + { + // Items: + case TOK_RWORD_PUB: + ERROR(lex.getPosition(), E0000, "`pub` is useless within expression modules"); + case TOK_RWORD_TYPE: + case TOK_RWORD_USE: + case TOK_RWORD_EXTERN: + case TOK_RWORD_CONST: + case TOK_RWORD_STATIC: + case TOK_RWORD_STRUCT: + case TOK_RWORD_ENUM: + case TOK_RWORD_TRAIT: + case TOK_RWORD_IMPL: + case TOK_RWORD_FN: + case TOK_RWORD_MOD: + PUTBACK(tok, lex); + if( !local_mod ) { + local_mod = lex.parse_state().get_current_mod().add_anon(); + } + Parse_Mod_Item(lex, *local_mod, mv$(item_attrs)); + return ExprNodeP(); + // 'unsafe' - Check if the next token isn't a `{`, if so it's an item. Otherwise, fall through + case TOK_RWORD_UNSAFE: + if( LOOK_AHEAD(lex) != TOK_BRACE_OPEN ) + { + PUTBACK(tok, lex); + if( !local_mod ) { + local_mod = lex.parse_state().get_current_mod().add_anon(); + } + Parse_Mod_Item(lex, *local_mod, mv$(item_attrs)); + return ExprNodeP(); + } + // fall + default: { + PUTBACK(tok, lex); + auto rv = Parse_ExprBlockLine(lex, &add_silence_if_end); + if( rv ) { + rv->set_attrs( mv$(item_attrs) ); + } + else if( item_attrs.m_items.size() > 0 ) { + // TODO: Is this an error? - Attributes on a expression that didn't yeild a node. + } + else { + } + return rv; + } break; + } +} + /// Parse a single line from a block /// /// Handles: -- cgit v1.2.3